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
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
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
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
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, }
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', )
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')
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')
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
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')