Esempio n. 1
0
def choose_git_provider(repo: Repo) -> str:
    """Choose git provider.

    Try automatically, if git remotes specified. Otherwise - ask user.

    Args:
        repo: (git.Repo) Package with general repository related functions

    Returns:
        git_provider string.
    """
    if not repo.remotes:  # Remotes not exist in locally init repos
        return ask_user(
            name='choose_git_provider',
            type='list',
            message='Select your Git Provider',
            choices=GIT_PROVIDERS,
        )

    git = repo.git
    remote = git.remote('-v')

    if remote.find('github'):
        git_provider = 'Github'
    elif remote.find('bitbucket'):
        git_provider = 'Bitbucket'
    elif remote.find('gitlab'):
        git_provider = 'Gitlab'
    else:
        git_provider = ask_user(
            name='choose_git_provider',
            type='list',
            message='Select your Git Provider',
            # choices=GIT_PROVIDERS,  # noqa: E800 #? Should be use after implementation
            choices=[
                {
                    'name': 'Github',
                },
                {
                    'name': 'Bitbucket',
                    'disabled': 'Unavailable at this time',
                },
                {
                    'name': 'Gitlab',
                    'disabled': 'Unavailable at this time',
                },
            ],
        )

    return git_provider
Esempio n. 2
0
def get_git_token(provider: str) -> str:
    """Get github token from user input or settings.

    Args:
        provider: (str) Provider name.

    Returns:
        git_token string.
    """
    # Required env variables from host: GITHUB_TOKEN
    if provider == 'Github':
        git_token = os.environ.get('GITHUB_TOKEN')
        if git_token is not None:
            logger.info('Use GITHUB_TOKEN from env')
        else:
            git_token = ask_user(
                name='github_token',
                type='password',
                message='Please enter GITHUB_TOKEN. ' +
                'It can be generated at https://github.com/settings/tokens',
            )

    if not git_token:
        sys.exit(
            f'ERROR: Provider "{provider}" not exist in function "get_git_token"'
        )

    return git_token
Esempio n. 3
0
def choose_section(config: Union[ConfigParser, Literal[False]]) -> str:
    """Ask user which section in config should be use for extracting credentials.

    Args:
        config (configparser.ConfigParser|False): INI config.

    Returns:
        str: section name, if exists. Otherwise - empty string.
    """
    # Skip if CLI args provided or configs doesn't exist
    if not config or not config.sections():
        return ''

    if len(config.sections()) == 1:
        section = config.sections()[0]
        logger.info(f'Use AWS creds from file, section "{section}"')
    else:
        # User can have multiply creds, ask him what should be used
        # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#configuring-credentials
        section = ask_user(
            name='choose_config_section',
            type='list',
            message='Select credentials section that will be used to deploy cluster.dev',
            choices=config.sections(),
        )

    return section
Esempio n. 4
0
def get_session(  # noqa: WPS212
    config: Union[ConfigParser, Literal[False]],
    config_section: str,
    mfa_disabled: str = '',
) -> str:
    """Get cloud session from settings or from user input.

    Args:
        config: (configparser.ConfigParser|False) INI config.
        config_section: (str) INI config section.
        mfa_disabled: (str) CLI argument provided by user.
            If not provided - set to empty string.

    Returns:
        session_token string.
    """
    # If login provided but session - not, user may not have MFA enabled
    if mfa_disabled:
        logger.info('SESSION_TOKEN not found, try without MFA')
        return ''

    if not config:
        return ask_user(
            name='aws_session_token',
            type='password',
            message='Please enter your AWS Session token',
        )

    try:
        session_token = config.get(config_section, 'aws_session_token')
    except NoOptionError:
        logger.info('SESSION_TOKEN not found, try without MFA')
        return ''

    return session_token
Esempio n. 5
0
def ask_user_for_provide_keys(
    user: str,
    login: str,
    keys: list,
) -> Dict[str, Union[str, FalseL]]:
    """Give user chance to specify right keys without program restart.

    Args:
        user: (str) Cluster.dev user name.
        login: (str) Cloud programatic login.
        keys: (list) WS_ACCESS_KEY_ID's.

    Returns:
        Dict[str, Union[str, bool]]:
        `{'key': 'AWS_ACCESS_KEY_ID', 'secret': 'AWS_SECRET_ACCESS_KEY', 'created': False}`
    """
    have_secret = ask_user(
        type='confirm',
        message=f'User {user} have 2 programmatic keys.\n' +
        f"Key '{login}' used for IAM interactions not belong to user '{user}'\n"
        + f'that have: {keys} and it maximum supply.\n\n' +
        'Would you have Secret Key for on of this keys?',
        default=False,
    )

    if not have_secret:
        sys.exit(
            'Well, you need remove unused key and then try again.\n' +
            f'https://console.aws.amazon.com/iam/home#/users/{user}' +
            '?section=security_credentials', )

    key = ask_user(
        type='list',
        message='Select key for what you known AWS_SECRET_ACCESS_KEY',
        choices=keys,
    )
    secret = ask_user(
        type='password',
        message='Please enter AWS_SECRET_ACCESS_KEY:',
        # TODO: add validator
    )

    return {
        'key': key,
        'secret': secret,
        'created': False,
    }
Esempio n. 6
0
def get_git_password() -> str:
    """Get SSH key from settings or password from user input.

    Returns:
        empty string if SSH-key provided (mounted).
        Otherwise - return password string.
    """
    with suppress(FileNotFoundError):
        # Try use ssh as password
        # Required mount: $HOME/.ssh:/home/cluster.dev/.ssh:ro
        os.listdir(f'{os.environ["HOME"]}/.ssh')
        logger.info('Password type: ssh-key')
        return ''

    return ask_user(
        name='git_password',
        type='password',
        message='Please enter your git password',
    )
Esempio n. 7
0
def get_login(config: Union[ConfigParser, Literal[False]], config_section: str) -> str:
    """Get cloud programatic login from settings or from user input.

    Args:
        config (configparser.ConfigParser|False): INI config.
        config_section: (str) INI config section.

    Returns:
        cloud_login string.
    """
    if not config or not config_section:
        # TODO: Add validation (and for cli arg)
        return ask_user(
            name='cloud_login',
            type='input',
            message='Please enter your Cloud programatic key',
        )

    return config.get(config_section, 'aws_access_key_id')
Esempio n. 8
0
def get_password(config: Union[ConfigParser, Literal[False]], config_section: str) -> str:
    """Get cloud programatic password from settings or from user input.

    Args:
        config: (configparser.ConfigParser|False) INI config.
        config_section: (str) INI config section.

    Returns:
        cloud_password string.
    """
    if not config:
        # TODO: Add validation (and for cli arg)
        return ask_user(
            name='cloud_password',
            type='password',
            message='Please enter your Cloud programatic secret',
        )

    return config.get(config_section, 'aws_secret_access_key')
Esempio n. 9
0
def get_git_username(git: Git) -> str:
    """Get username from settings or from user input.

    Args:
        git: (git.cmd.Git) Manages communication with the Git binary.

    Returns:
        username string.
    """
    try:  # Required mount: $HOME/.gitconfig:/home/cluster.dev/.gitconfig:ro
        user = git.config('--get', 'user.name')
    except GitCommandError:
        user = ask_user(
            name='git_user_name',
            type='input',
            message='Please enter your git username',
            validate=validate.UserName,
        )
    else:
        logger.info(f'Username: {user}')

    return user
Esempio n. 10
0
def main() -> None:  # pylint: disable=too-many-statements,R0914 # noqa: WPS231, WPS213, WPS210
    """Logic."""
    cli = parse_cli_args()
    dir_path = os.path.relpath('current_dir')

    logger.info('Hi, we gonna create an infrastructure for you.\n')

    if not dir_is_git_repo(dir_path):
        create_repo = True
        if not cli.repo_name:
            logger.info(
                'As this is a GitOps approach we need to start with the git repo.'
            )
            create_repo = ask_user(
                type='list',
                message='Create repo for you?',
                choices=[
                    {
                        'name':
                        'No, I create or clone repo and then run tool there',
                        'value': False,
                    },
                    {
                        'name': 'Yes',
                        'value': True,
                        'disabled': 'Unavailable at this time',
                    },
                ],
            )

        if not create_repo:
            sys.exit('OK. See you soon!')

        repo_name = cli.repo_name or ask_user(
            type='input',
            message='Please enter the name of your infrastructure repository',
            default='infrastructure',
            validate=validate.RepoName,
        )

        # TODO: setup remote origin and so on. Can be useful:
        # user = cli.git_user_name or get_git_username(git)  # noqa: E800
        # password = cli.git_password or get_git_password()  # noqa: E800
        sys.exit('TODO')

    logger.info('Inside git repo, use it.')

    repo = Repo(dir_path)
    git = repo.git

    if repo.heads:  # Heads exist only after first commit
        cleanup_repo = ask_user(
            type='confirm',
            message=
            'This is not empty repo. Delete all existing configurations?',
            default=False,
        )

        if cleanup_repo:
            remove_all_except_git(dir_path)
            git.add('-A')
            git.commit('-m', 'Cleanup repo')

    git_provider = cli.git_provider or choose_git_provider(repo)
    git_token = cli.git_token or get_git_token(git_provider)

    if not repo.remotes:
        publish_repo = ask_user(
            name='publish_repo_to_git_provider',
            type='confirm',
            message=
            'Your repo not published to Git Provider yet. Publish it now?',
            default=True,
        )
        if publish_repo:
            # TODO: push repo to Git Provider
            sys.exit('TODO')

    user = cli.git_user_name or get_git_username(git)  # pylint: disable=W0612 # noqa: F841 # TODO
    password = cli.git_password or get_git_password()  # pylint: disable=W0612 # noqa: F841 # TODO

    cloud = cli.cloud or ask_user(
        type='list',
        message='Select your Cloud',
        # choices=CLOUDS  # noqa: E800 #? Should be use after implementation
        choices=[
            {
                'name': 'AWS',
            },
            {
                'name': 'DigitalOcean',
                'disabled': 'Unavailable at this time',
            },
        ],
    )
    cloud_user = cli.cloud_user or ask_user(
        name='aws_cloud_user',
        type='input',
        message='Please enter username for cluster.dev user',
        validate=validate.AWSUserName,
        default='cluster.dev',
    )

    if cloud == 'AWS':
        config = aws_config.parse_file(cli.cloud_login, cli.cloud_password)
        config_section = aws_config.choose_section(config)

        access_key = cli.cloud_login or aws_config.get_login(
            config, config_section)
        secret_key = cli.cloud_password or aws_config.get_password(
            config, config_section)
        session_token = (cli.cloud_token or aws_config.get_session(
            config, config_section, cli.cloud_login))

        release_version = cli.release_version or github.get_last_release()

        creds = aws_create_user_and_permissions(
            cloud_user,
            access_key,
            secret_key,
            session_token,
            release_version,
        )
        if creds['created']:
            logger.info(
                f'Credentials for user "{cloud_user}":\n' +
                f'aws_access_key_id={creds["key"]}\n' +
                f'aws_secret_access_key={creds["secret"]}', )
    # elif cloud == 'DigitalOcean':  # noqa: E800
    # TODO
    # https://www.digitalocean.com/docs/apis-clis/doctl/how-to/install/
    # cloud_login = cli.cloud_login or get_do_login()  # noqa: E800
    # cloud_password = cli.cloud_password or get_do_password()  # noqa: E800
    # cloud_token = cli.cloud_token or ask_user(  # noqa: E800
    #     type='password',  # noqa: E800
    #     message='Please enter your Cloud token',  # noqa: E800
    # )  # noqa: E800

    cloud_provider = (cli.cloud_provider or ask_user(
        type='list',
        message='Select your Cloud Provider',
        choices=CLOUD_PROVIDERS[cloud],
    ))

    remote = repo.remotes.origin.url
    owner = get_repo_owner_from_url(remote)
    repo_name = get_repo_name_from_url(remote)

    if git_provider == 'Github':
        github.create_secrets(creds, cloud, owner, repo_name, git_token)

    add_sample_cluster_dev_files(cloud, cloud_provider, git_provider, dir_path,
                                 release_version)
    commit_and_push(git, 'cluster.dev: Add sample files')

    config_path = last_edited_config_path(dir_path)
    set_cluster_installed(config_path, installed=True)
    # Open editor
    os.system(f'editor "{config_path}"')  # noqa: S605
    commit_and_push(git, 'cluster.dev: Up cluster')

    # Show link to logs. Temporary solution
    if git_provider == 'Github':
        logger.info(
            f'See logs at: https://github.com/{owner}/{repo_name}/actions')