def _prompt_issue(recent_command_list): if recent_command_list: max_idx = len(recent_command_list) - 1 ans = -1 help_string = 'Please choose between 0 and {}, or enter q to quit: '.format( max_idx) while ans < 0 or ans > max_idx: try: ans = prompt(_MSG_CMD_ISSUE.format(max_idx), help_string=help_string) if ans.lower() in ["q", "quit"]: ans = ans.lower() break ans = int(ans) except ValueError: logger.warning(help_string) ans = -1 else: ans = None help_string = 'Please choose between Y and N: ' while not ans: ans = prompt(_MSG_ISSUE, help_string=help_string) if ans.lower() not in ["y", "n", "yes", "no", "q"]: ans = None continue # strip to short form ans = ans[0].lower() if ans else None if ans in ["y", "n"]: if ans == "y": browser_instruction, url, issue_body = _build_issue_info_tup() else: return False else: if ans in ["q", "quit"]: return False if ans == 0: browser_instruction, url, issue_body = _build_issue_info_tup() else: browser_instruction, url, issue_body = _build_issue_info_tup( recent_command_list[ans]) logger.info(issue_body) print(browser_instruction) # if we are not in cloud shell and can launch a browser, launch it with the issue draft if can_launch_browser() and not in_cloud_console(): open_page_in_browser(url) else: print( "There isn't an available browser to create an issue draft. You can copy and paste the url" " below in a browser to submit.\n\n{}\n\n".format(url)) return True
def _prompt_issue(recent_command_list): if recent_command_list: max_idx = len(recent_command_list) - 1 ans = -1 help_string = 'Please choose between 0 and {}, or enter q to quit: '.format( max_idx) while ans < 0 or ans > max_idx: try: ans = prompt(_MSG_CMD_ISSUE.format(max_idx), help_string=help_string) if ans.lower() in ["q", "quit"]: ans = ans.lower() break ans = int(ans) except ValueError: logger.warning(help_string) ans = -1 else: ans = None help_string = 'Please choose between Y and N: ' while not ans: ans = prompt(_MSG_ISSUE, help_string=help_string) if ans.lower() not in ["y", "n", "yes", "no", "q"]: ans = None continue # strip to short form ans = ans[0].lower() if ans else None if ans in ["y", "n"]: if ans == "y": prefix, body, url = _build_issue_info_tup() else: return False else: if ans in ["q", "quit"]: return False if ans == 0: prefix, body, url = _build_issue_info_tup() else: prefix, body, url = _build_issue_info_tup(recent_command_list[ans]) print(prefix) # open issues page in browser and copy issue body to clipboard try: pyperclip.copy(body) except pyperclip.PyperclipException as ex: logger.debug(ex) logger.info(body) open_page_in_browser(url) return True
def resolve_registry_from_cli_args(registry_server, registry_user, registry_pass): if registry_server is not None: if registry_user is None and registry_pass is None: registry_user = prompt("Please enter the registry's username: "******"Please enter the registry's password: "******"Please enter the registry's password: ") return (registry_server, registry_user, registry_pass)
def _create_project(self): project_name = prompt("Please enter a name for your new project: ") project = self.adbp.create_project(self.organization_name, project_name) # Keep retrying to create a new project if it fails while not project.valid: self.logger.error(project.message) project_name = prompt("Please enter a name for your new project: ") project = self.adbp.create_project(self.organization_name, project_name) self.project_name = project.name self.created_project = True
def _create_project(self): project_name = prompt("Please enter the name of the new project: ") project = self.adbp.create_project(self.organization_name, project_name) # Keep retrying to create a new project if it fails while not project.valid: self.logger.error(project.message) project_name = prompt("Please enter the name of the new project: ") project = self.adbp.create_project(self.organization_name, project_name) url = "https://dev.azure.com/" + self.organization_name + "/" + project.name + "/" self.logger.info("Finished creating the new project. Click the link to see your new project: %s", url) self.project_name = project.name self.created_project = True
def handle_feedback(): try: print(MSG_INTR) score = _prompt_net_promoter_score() if score == 10: suggestion = prompt(MSG_GOOD) else: suggestion = prompt(MSG_BAD) email_address = prompt(MSG_EMIL) set_feedback('[{}]{}[{}]'.format(score, suggestion, email_address)) print(MSG_THNK) except NoTTYException: raise CLIError('This command is interactive and no tty available.') except (EOFError, KeyboardInterrupt): print()
def _prompt_repair_username(namespace): from knack.prompting import prompt, NoTTYException try: namespace.repair_username = prompt('Repair VM admin username: '******'Please specify username in non-interactive mode.')
def _create_organization(self): self.logger.info( "Starting process to create a new Azure DevOps organization") regions = self.adbp.list_regions() region_names = sorted( [region.display_name for region in regions.value]) self.logger.info( "The region for an Azure DevOps organization is where the organization will be located. " "Try locate it near your other resources and your location") choice_index = prompt_choice_list( 'Please select a region for the new organization: ', region_names) region = [ region for region in regions.value if region.display_name == region_names[choice_index] ][0] while True: organization_name = prompt( "Please enter a name for your new organization: ") new_organization = self.adbp.create_organization( organization_name, region.name) if new_organization.valid is False: self.logger.warning(new_organization.message) self.logger.warning("Note: all names must be globally unique") else: break self.organization_name = new_organization.name
def process_github_repository(self): while ( not self.github_repository or not AzureDevopsBuildProvider.check_github_repository(self.github_pat, self.github_repository) ): self.github_repository = prompt(msg="Github Repository Path (e.g. Azure/azure-cli): ").strip() self.logger.warning("Successfully found Github repository.")
def _prompt_for_prop_input(prop_name, prop_type): verify_is_a_tty_or_raise_error('The template requires a few inputs. These cannot be provided as in command ' 'arguments. It can only be input interatively.') val = prompt(msg='Please enter a value for {prop_name}: '.format(prop_name=prop_name), help_string='Value of type {prop_type} is required.'.format(prop_type=prop_type)) print('') return val
def _create_organization(self): self.logger.info( "Starting process to create a new Azure DevOps organization") regions = self.adbp.list_regions() region_names = sorted( [region.display_name for region in regions.value]) self.logger.info("The region for an Azure DevOps organization is where the organization will be located. Try locate it near your other resources and your location") # pylint: disable=line-too-long choice_index = prompt_choice_list( 'Please select a region for the new organization: ', region_names) region = [ region for region in regions.value if region.display_name == region_names[choice_index] ][0] while True: organization_name = prompt( "Please enter the name of the new organization: ") new_organization = self.adbp.create_organization( organization_name, region.name) if new_organization.valid is False: self.logger.warning(new_organization.message) self.logger.warning("Note: any name must be globally unique") else: break url = "https://dev.azure.com/" + new_organization.name + "/" self.logger.info( "Finished creating the new organization. Click the link to see your new organization: %s", url) self.organization_name = new_organization.name
def _handle_yml_props(params_required, template_id, cix_client, repo_name, organization, project): logger.warning('The template requires a few inputs. We will help you fill them out') params_to_render = {} for param in params_required: param_name_for_user = param.name # override with more user friendly name if available if param.display_name: param_name_for_user = param.display_name logger.debug('Looking for param %s in props', param.name) prop_found = False if param.default_value: prop_found = True user_input_val = prompt(msg='Enter a value for {param_name} [Press Enter for default: {param_default}]:' .format(param_name=param_name_for_user, param_default=param.default_value)) print('') if user_input_val: params_to_render[param.name] = user_input_val else: params_to_render[param.name] = param.default_value elif _is_intelligent_handling_enabled_for_prop_type(prop_name=param.name, prop_type=param.type): logger.debug('This property is handled intelligently (Name: %s) (Type: %s)', param.name, param.type) fetched_value = fetch_yaml_prop_intelligently(param.name, param.type, organization, project, repo_name) if fetched_value is not None: logger.debug('Auto filling param %s with value %s', param.name, fetched_value) params_to_render[param.name] = fetched_value prop_found = True if not prop_found: input_value = _prompt_for_prop_input(param_name_for_user, param.type) params_to_render[param.name] = input_value prop_found = True rendered_template = cix_client.render_template(template_id=template_id, template_parameters={'tokens': params_to_render}) return rendered_template.content, rendered_template.assets
def _add_to_codeowners(repo_path, prefix, name, github_alias): # add the user Github alias to the CODEOWNERS file for new packages if not github_alias: display( '\nWhat is the Github alias of the person responsible for maintaining this package?' ) while not github_alias: github_alias = prompt('Alias: ') # accept a raw alias or @alias github_alias = '@{}'.format( github_alias) if not github_alias.startswith('@') else github_alias try: codeowners = find_files(repo_path, 'CODEOWNERS')[0] except IndexError: raise CLIError('unexpected error: unable to find CODEOWNERS file.') if prefix == EXTENSION_PREFIX: new_line = '/src/{}{}/ {}'.format(prefix, name, github_alias) else: # ensure Linux-style separators when run on Windows new_line = '/{} {}'.format( os.path.join('', _MODULE_ROOT_PATH, name, ''), github_alias).replace('\\', '/') with open(codeowners, 'a') as f: f.write(new_line) f.write('\n')
def create_connection(connection_info_json, prompt_prefix, typeOfInfo): user_name = connection_info_json.get('userName', None) or prompt(prompt_prefix + 'Username: '******'password', None) or prompt_pass(msg=prompt_prefix + 'Password: '******'serverName', None) if "mysql" in typeOfInfo: port = connection_info_json.get('port', 3306) return MySqlConnectionInfo(user_name=user_name, password=password, server_name=server_name, port=port) elif "postgres" in typeOfInfo: database_name = connection_info_json.get('databaseName', "postgres") port = connection_info_json.get('port', 5432) return PostgreSqlConnectionInfo(user_name=user_name, password=password, server_name=server_name, database_name=database_name, port=port) elif "mongo" in typeOfInfo: connection_string = connection_info_json['connectionString'] # Strip out the username and password from the connection string (if they exist) to store them securely. rex_conn_string = re.compile(r'^(mongodb://|mongodb\+srv://|http://|https://)(.*:.*@)?(.*)') connection_string_match = rex_conn_string.search(connection_string) connection_string = connection_string_match.group(1) + connection_string_match.group(3) if connection_string_match.group(2) is not None and not user_name and not password: rex_un_pw = re.compile('^(.*):(.*)@') un_pw_match = rex_un_pw.search(connection_string_match.group(2)) user_name = un_pw_match.group(1) password = un_pw_match.group(2) return MongoDbConnectionInfo(connection_string=connection_string, user_name=user_name, password=password) else: # If no match, Pass the connection info through return connection_info_json
def create_connection(connection_info_json, prompt_prefix, typeOfInfo): typeOfInfo = typeOfInfo.lower() user_name = connection_info_json.get( 'userName', None) or prompt(prompt_prefix + 'Username: '******'password', None) or prompt_pass(msg=prompt_prefix + 'Password: '******'serverName', None) if "mysql" in typeOfInfo: port = connection_info_json.get('port', 3306) return MySqlConnectionInfo(user_name=user_name, password=password, server_name=server_name, port=port) elif "postgre" in typeOfInfo: database_name = connection_info_json.get('databaseName', "postgres") port = connection_info_json.get('port', 5432) return PostgreSqlConnectionInfo(user_name=user_name, password=password, server_name=server_name, database_name=database_name, port=port) data_source = connection_info_json.get('dataSource', None) authentication = connection_info_json.get('authentication', None) encrypt_connection = connection_info_json.get('encryptConnection', None) trust_server_certificate = connection_info_json.get( 'trustServerCertificate', None) additional_settings = connection_info_json.get('additionalSettings', None) return SqlConnectionInfo(user_name=user_name, password=password, data_source=data_source, authentication=authentication, encrypt_connection=encrypt_connection, trust_server_certificate=trust_server_certificate, additional_settings=additional_settings)
def _config_env_public_azure(cli_ctx, _): from adal.adal_error import AdalError from azure.cli.core.commands.client_factory import get_mgmt_service_client from azure.cli.core._profile import Profile from azure.cli.core.profiles import ResourceType # Determine if user logged in try: list( get_mgmt_service_client( cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES).resources.list()) except CLIError: # Not logged in login_successful = False while not login_successful: method_index = prompt_choice_list(MSG_PROMPT_LOGIN, LOGIN_METHOD_LIST) answers['login_index'] = method_index answers['login_options'] = str(LOGIN_METHOD_LIST) profile = Profile(cli_ctx=cli_ctx) interactive = False username = None password = None service_principal = None tenant = None if method_index == 0: # device auth interactive = True elif method_index == 1: # username and password username = prompt('Username: '******'Password: '******'Service principal: ') tenant = prompt('Tenant: ') password = prompt_pass(msg='Client secret: ') elif method_index == 3: # skip return try: profile.find_subscriptions_on_login(interactive, username, password, service_principal, tenant) login_successful = True logger.warning('Login successful!') except AdalError as err: logger.error('Login error!') logger.error(err)
def _get_credentials(cli_ctx, registry_name, resource_group_name, username, password, only_refresh_token, repository=None, permission='*'): """Try to get AAD authorization tokens or admin user credentials. :param str registry_name: The name of container registry :param str resource_group_name: The name of resource group :param str username: The username used to log into the container registry :param str password: The password used to log into the container registry :param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens :param str repository: Repository for which the access token is requested :param str permission: The requested permission on the repository, '*' or 'pull' """ registry, resource_group_name = get_registry_by_name(cli_ctx, registry_name, resource_group_name) login_server = registry.login_server # 1. if username was specified, verify that password was also specified if username: if not password: try: password = prompt_pass(msg='Password: '******'Please specify both username and password in non-interactive mode.') return login_server, username, password # 2. if we don't yet have credentials, attempt to get a refresh token if not password and registry.sku.name in MANAGED_REGISTRY_SKU: try: username = '******' if only_refresh_token else None password = _get_aad_token(cli_ctx, login_server, only_refresh_token, repository, permission) return login_server, username, password except CLIError as e: logger.warning("Unable to get AAD authorization tokens with message: %s", str(e)) # 3. if we still don't have credentials, attempt to get the admin credentials (if enabled) if not password and registry.admin_user_enabled: try: cred = cf_acr_registries(cli_ctx).list_credentials(resource_group_name, registry_name) username = cred.username password = cred.passwords[0].value return login_server, username, password except CLIError as e: logger.warning("Unable to get admin user credentials with message: %s", str(e)) # 4. if we still don't have credentials, prompt the user if not password: try: username = prompt('Username: '******'Password: '******'Unable to authenticate using AAD or admin login credentials. ' + 'Please specify both username and password in non-interactive mode.')
def get_upload_oci_driver_input(task_options_json): driver_path = task_options_json.get('ociDriverPath', None) user_name = task_options_json.get('userName', None) or prompt('Share Path Username: '******'password', None) or prompt_pass(msg='Share Path Password: ') return UploadOCIDriverTaskInput(driver_share=FileShare( path=driver_path, user_name=user_name, password=password))
def handle_feedback(): try: print(MESSAGES['intro']) score = _prompt_net_promoter_score() response_do_well = None response_what_changes = None if score == 10: response_do_well = prompt(MESSAGES['prompt_do_well']) else: response_what_changes = prompt(MESSAGES['prompt_what_changes']) email_address = prompt(MESSAGES['prompt_email_addr']) _send_feedback(score, response_what_changes, response_do_well, email_address) print(MESSAGES['thanks']) except NoTTYException: raise CLIError('This command is interactive and no tty available.') except (EOFError, KeyboardInterrupt): print()
def _config_env_public_azure(cli_ctx, _): from adal.adal_error import AdalError from azure.cli.core.commands.client_factory import get_mgmt_service_client from azure.mgmt.resource import ResourceManagementClient from azure.cli.core._profile import Profile # Determine if user logged in try: list(get_mgmt_service_client(cli_ctx, ResourceManagementClient).resources.list()) except CLIError: # Not logged in login_successful = False while not login_successful: method_index = prompt_choice_list(MSG_PROMPT_LOGIN, LOGIN_METHOD_LIST) answers['login_index'] = method_index answers['login_options'] = str(LOGIN_METHOD_LIST) profile = Profile(cli_ctx=cli_ctx) interactive = False username = None password = None service_principal = None tenant = None if method_index == 0: # device auth interactive = True elif method_index == 1: # username and password username = prompt('Username: '******'Password: '******'Service principal: ') tenant = prompt('Tenant: ') password = prompt_pass(msg='Client secret: ') elif method_index == 3: # skip return try: profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant) login_successful = True logger.warning('Login successful!') except AdalError as err: logger.error('Login error!') logger.error(err)
def create_sql_connection_info(connection_info_json, prompt_prefix): return SqlConnectionInfo( user_name=connection_info_json.get('userName', None) or prompt(prompt_prefix + 'Username: '******'password', None) or prompt_pass(msg=prompt_prefix + 'Password: '******'dataSource', None), authentication=connection_info_json.get('authentication', None), encrypt_connection=connection_info_json.get('encryptConnection', None), trust_server_certificate=connection_info_json.get('trustServerCertificate', None), additional_settings=connection_info_json.get('additionalSettings', None))
def _prompt_net_promoter_score(): while True: try: score = int(prompt(MESSAGES['prompt_how_likely'])) if 0 <= score <= 10: return score raise ValueError except ValueError: logger.warning('Valid values are %s', list(range(11)))
def prompt_user_friendly_choice_list(msg, a_list, default=1, help_string=None, error_msg=None): """Prompt user to select from a list of possible choices. :param msg: A message displayed to the user before the choice list :type msg: str :param a_list: The list of choices (list of strings or list of dicts with 'name' & 'desc') "type a_list: list :param default: The default option that should be chosen if user doesn't enter a choice :type default: int :param help_string: Help message to be displayed on the input terminal :type help_string: str :param error_msg: Error message to display if the terminal is non interactive :type error_msg: str :returns: The list index of the item chosen. """ verify_is_a_tty_or_raise_error(error_msg=error_msg) options = '\n'.join([ ' [{}] {}{}'.format( i + 1, x['name'] if isinstance(x, dict) and 'name' in x else x, ' - ' + x['desc'] if isinstance(x, dict) and 'desc' in x else '') for i, x in enumerate(a_list) ]) allowed_vals = list(range(1, len(a_list) + 1)) linesToDelete = len(a_list) + 1 while True: val = prompt( '{}\n{}\nPlease enter a choice [Default choice({})]: '.format( msg, options, default)) if val == '?' and help_string is not None: for x in range(0, linesToDelete): delete_last_line() print('Please enter a choice [Default choice({})]: {}'.format( default, '?')) print(help_string) continue if not val: val = '{}'.format(default) try: ans = int(val) if ans in allowed_vals: for x in range(0, linesToDelete): delete_last_line() print('Please enter a choice [Default choice({})]: {}'.format( default, a_list[ans - 1])) print('') # array index is 0-based, user input is 1-based return ans - 1 raise ValueError except ValueError: for x in range(0, linesToDelete): delete_last_line() print('Please enter a choice [Default choice({})]: {}'.format( default, val)) logger.warning('Valid values are %s', allowed_vals)
def process_query(cli_term): print(random.choice(WAIT_MESSAGE)) response = call_aladdin_service(cli_term) if response.status_code != 200: err_msg = '[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format( response.status_code, response.content) logger.error(err_msg) else: if (platform.system() == 'Windows' and should_enable_styling()): colorama.init(convert=True) answer_list = json.loads(response.content) if (not answer_list or answer_list[0]['source'] == 'bing'): print("\nSorry I am not able to help with [" + cli_term + "]." "\nTry typing the beginning of a command e.g. " + style_message('az vm') + ".") else: print("\nHere are the most common ways to use [" + cli_term + "]: \n") num_results_to_show = min(3, len(answer_list)) for i in range(num_results_to_show): current_title = answer_list[i]['title'].strip() current_snippet = answer_list[i]['snippet'].strip() if current_title.startswith("az "): current_title, current_snippet = current_snippet, current_title current_title = current_title.split('\r\n')[0] elif '```azurecli\r\n' in current_snippet: start_index = current_snippet.index( '```azurecli\r\n') + len('```azurecli\r\n') current_snippet = current_snippet[start_index:] current_snippet = current_snippet.replace('```', '').replace( current_title, '').strip() current_snippet = re.sub(r'\[.*\]', '', current_snippet).strip() print(style_message(current_title)) print(current_snippet) print("") feedback = prompt( "[Enter to close. Press + or - to give feedback]:") if feedback in ['+', '-']: print('Wow, you are a true hero!') print("""\ O_ """ + style_message("""<T>""") + """`-. """ + style_message("""|""") + """`-‘ """ + style_message("""I""") + """ """) print( 'My human overlords review each of these reports; I\'m told these reports makes me smarter.' ) print( 'Send us more feedback by email: [email protected]') properties = {} set_custom_properties(properties, 'Feedback', feedback) telemetry_core.add_extension_event(EXTENSION_NAME, properties)
def prompt_not_empty(msg, help_string=None): """ Wrapper on knacks prompt function which does not return until non none value is recieved from user input. """ if not help_string: help_string = 'This field cannot be left blank.' user_input = None while not user_input: user_input = prompt(msg=msg, help_string=help_string) return user_input
def test_prompt_msg_question_with_help_string(self, _): expected_result = 'My response' with mock.patch('knack.prompting._input', side_effect=['?', expected_result]): with mock.patch('sys.stdout', new_callable=StringIO) as mock_stdout: actual_result = prompt('Please enter some text: ', help_string='Anything you want!') self.assertEqual(expected_result, actual_result) self.assertIn('Anything you want!', mock_stdout.getvalue())
def _prompt_net_promoter_score(): score = -1 help_string = 'Please rate between 0 and 10' while score < 0 or score > 10: try: score = int(prompt(MSG_RATE, help_string=help_string)) except ValueError: logger.warning(help_string) return score
def _create_image_registry_credentials(registry_login_server, registry_username, registry_password, image): """Create image registry credentials. """ image_registry_credentials = None if registry_login_server: if not registry_username: raise CLIError( 'Please specify --registry-username in order to use custom image registry.' ) if not registry_password: try: registry_password = prompt_pass( msg='Image registry password: '******'Please specify --registry-password in order to use custom image registry.' ) image_registry_credentials = [ ImageRegistryCredential(server=registry_login_server, username=registry_username, password=registry_password) ] elif SERVER_DELIMITER in image.split("/")[0]: if not registry_username: try: registry_username = prompt(msg='Image registry username: '******'Please specify --registry-username in order to use Azure Container Registry.' ) if not registry_password: try: registry_password = prompt_pass( msg='Image registry password: '******'Please specify --registry-password in order to use Azure Container Registry.' ) acr_server = image.split("/")[0] if image.split("/") else None if acr_server: image_registry_credentials = [ ImageRegistryCredential(server=acr_server, username=registry_username, password=registry_password) ] else: raise CLIError( 'Failed to parse ACR server from image name; please explicitly specify --registry-server.' ) return image_registry_credentials
def process_github_personal_access_token(self): if not self.github_pat: self.logger.warning("If you need to create a Github Personal Access Token, " "please follow the steps found at the following link:") self.logger.warning("https://help.github.com/en/articles/" "creating-a-personal-access-token-for-the-command-line{ls}".format(ls=os.linesep)) self.logger.warning("The required Personal Access Token permissions can be found here:") self.logger.warning("https://aka.ms/azure-devops-source-repos") while not self.github_pat or not AzureDevopsBuildProvider.check_github_pat(self.github_pat): self.github_pat = prompt(msg="Github Personal Access Token: ").strip() self.logger.warning("Successfully validated Github personal access token.")
def process_local_repository(self): has_local_git_repository = AzureDevopsBuildProvider.check_git_local_repository() if has_local_git_repository: self.logger.warning("Detected a local Git repository already exists.") # Collect repository name on Azure Devops if not self.repository_name: self.repository_name = prompt("Push to which Azure Devops repository (default: {repo}): ".format( repo=self.project_name )) if not self.repository_name: # Select default value self.repository_name = self.project_name expected_remote_name = self.adbp.get_local_git_remote_name( self.organization_name, self.project_name, self.repository_name ) expected_remote_url = self.adbp.get_azure_devops_repo_url( self.organization_name, self.project_name, self.repository_name ) # If local repository already has a remote # Let the user to know s/he can push to the remote directly for context update # Or let s/he remove the git remote manually has_local_git_remote = self.adbp.check_git_remote( self.organization_name, self.project_name, self.repository_name ) if has_local_git_remote: raise CLIError("There's a git remote bound to {url}.{ls}" "To update the repository and trigger an Azure Devops build, please use " "'git push {remote} master'".format( url=expected_remote_url, remote=expected_remote_name, ls=os.linesep) ) # Setup a local git repository and create a new commit on top of this context try: self.adbp.setup_local_git_repository(self.organization_name, self.project_name, self.repository_name) except GitOperationException as goe: raise CLIError("Failed to setup local git repository when running '{message}'{ls}" "Please ensure you have setup git user.email and user.name".format( message=goe.message, ls=os.linesep )) self.repository_remote_name = expected_remote_name self.logger.warning("Added git remote {remote}".format(remote=expected_remote_name))
def _create_image_registry_credentials(cmd, resource_group_name, registry_login_server, registry_username, registry_password, image, identity): from msrestazure.tools import is_valid_resource_id image_registry_credentials = None if registry_login_server: if not registry_username: raise RequiredArgumentMissingError('Please specify --registry-username in order to use custom image registry.') if not registry_password: try: registry_password = prompt_pass(msg='Image registry password: '******'Please specify --registry-password in order to use custom image registry.') image_registry_credentials = [ImageRegistryCredential(server=registry_login_server, username=registry_username, password=registry_password)] elif ACR_SERVER_DELIMITER in image.split("/")[0]: acr_server = image.split("/")[0] if image.split("/") else None if identity: if not is_valid_resource_id(identity): msi_client = cf_msi(cmd.cli_ctx) identity = msi_client.user_assigned_identities.get(resource_group_name=resource_group_name, resource_name=identity).id if acr_server: image_registry_credentials = [ImageRegistryCredential(server=acr_server, username=registry_username, password=registry_password, identity=identity)] else: if not registry_username: try: registry_username = prompt(msg='Image registry username: '******'Please specify --registry-username in order to use Azure Container Registry.') if not registry_password: try: registry_password = prompt_pass(msg='Image registry password: '******'Please specify --registry-password in order to use Azure Container Registry.') if acr_server: image_registry_credentials = [ImageRegistryCredential(server=acr_server, username=registry_username, password=registry_password)] elif registry_username and registry_password and SERVER_DELIMITER in image.split("/")[0]: login_server = image.split("/")[0] if image.split("/") else None if login_server: image_registry_credentials = [ImageRegistryCredential(server=login_server, username=registry_username, password=registry_password)] else: raise RequiredArgumentMissingError('Failed to parse login server from image name; please explicitly specify --registry-server.') return image_registry_credentials
def _create_token(self, note=None): logger.warning('We need to create a Personal Access Token to communicate with GitHub. ' 'A new PAT with scopes (admin:repo_hook, repo, user) will be created.') logger.warning('You can set the PAT in the environment variable (%s) to avoid getting prompted.', AZ_DEVOPS_GITHUB_PAT_ENVKEY) self.username = prompt(msg='Enter your GitHub username (leave blank for using already generated PAT): ') print('') if not self.username: while not self.token: self.token = prompt_pass(msg='Enter your GitHub PAT: ', help_string='Generate a Personal Access Token ' 'with approproate permissions from GitHub Developer settings and paste here.') print('') return self.password = prompt_pass(msg='Enter your GitHub password: '******'') if not note: note = "AzureDevopsCLIExtensionToken_" + datetime_now_as_string() encoded_pass = base64.b64encode(self.username.encode('utf-8') + b':' + self.password.encode('utf-8')) basic_auth = 'basic ' + encoded_pass.decode("utf-8") request_body = { 'scopes': [ 'admin:repo_hook', 'repo', 'user' ], 'note': note } headers = {'Content-Type': 'application/json' + '; charset=utf-8', 'Accept': 'application/json', 'Authorization': basic_auth} response = self.post_authorization_request(headers=headers, body=request_body) if (response.status_code == 401 and response.headers.get('X-GitHub-OTP') and response.headers.get('X-GitHub-OTP').startswith('required')): two_factor_code = None while not two_factor_code: two_factor_code = prompt_pass(msg='Enter your two factor authentication code: ') print('') headers = {'Content-Type': 'application/json' + '; charset=utf-8', 'Accept': 'application/json', 'Authorization': basic_auth, 'X-GitHub-OTP': two_factor_code} response = self.post_authorization_request(headers=headers, body=request_body) import json response_json = json.loads(response.content) if response.status_code == 200 or response.status_code == 201: logger.warning('Created new personal access token with scopes (admin:repo_hook, repo, user). Name: %s ' 'You can revoke this from your GitHub settings if the pipeline is no longer required.', note) self.token = response_json['token'] else: raise CLIError('Could not create a Personal Access Token for GitHub. Check your credentials and try again.')
def _handle_global_configuration(config, cloud_forbid_telemetry): # print location of global configuration print(MSG_GLOBAL_SETTINGS_LOCATION.format(config.config_path)) # set up the config parsers file_config = configparser.ConfigParser() config_exists = file_config.read([config.config_path]) should_modify_global_config = False if config_exists: # print current config and prompt to allow global config modification _print_cur_configuration(file_config) should_modify_global_config = prompt_y_n(MSG_PROMPT_MANAGE_GLOBAL, default='n') answers['modify_global_prompt'] = should_modify_global_config if not config_exists or should_modify_global_config: # no config exists yet so configure global config or user wants to modify global config with ConfiguredDefaultSetter(config, False): output_index = prompt_choice_list(MSG_PROMPT_GLOBAL_OUTPUT, OUTPUT_LIST, default=get_default_from_config( config, 'core', 'output', OUTPUT_LIST)) answers['output_type_prompt'] = output_index answers['output_type_options'] = str(OUTPUT_LIST) enable_file_logging = prompt_y_n(MSG_PROMPT_FILE_LOGGING, default='n') if cloud_forbid_telemetry: allow_telemetry = False else: allow_telemetry = prompt_y_n(MSG_PROMPT_TELEMETRY, default='y') answers['telemetry_prompt'] = allow_telemetry cache_ttl = None while not cache_ttl: try: cache_ttl = prompt( MSG_PROMPT_CACHE_TTL) or DEFAULT_CACHE_TTL # ensure valid int by casting cache_value = int(cache_ttl) if cache_value < 1: raise ValueError except ValueError: logger.error('TTL must be a positive integer') cache_ttl = None # save the global config config.set_value('core', 'output', OUTPUT_LIST[output_index]['name']) config.set_value('core', 'collect_telemetry', 'yes' if allow_telemetry else 'no') config.set_value('core', 'cache_ttl', cache_ttl) config.set_value('logging', 'enable_log_file', 'yes' if enable_file_logging else 'no')
def process_query(cli_term): print(random.choice(WAIT_MESSAGE)) response = call_aladdin_service(cli_term) if response.status_code != 200: logger.error('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content)) else: if (platform.system() == 'Windows' and should_enable_styling()): colorama.init(convert=True) answer_list = json.loads(response.content) if (not answer_list or answer_list[0]['source'] == 'bing'): print("\nSorry I am not able to help with [" + cli_term + "]." "\nTry typing the beginning of a command e.g. " + style_message('az vm') + ".") else: print("\nHere are the most common ways to use [" + cli_term + "]: \n") num_results_to_show = min(3, len(answer_list)) for i in range(num_results_to_show): current_title = answer_list[i]['title'].strip() current_snippet = answer_list[i]['snippet'].strip() if current_title.startswith("az "): current_title, current_snippet = current_snippet, current_title current_title = current_title.split('\r\n')[0] elif '```azurecli\r\n' in current_snippet: start_index = current_snippet.index('```azurecli\r\n') + len('```azurecli\r\n') current_snippet = current_snippet[start_index:] current_snippet = current_snippet.replace('```', '').replace(current_title, '').strip() current_snippet = re.sub(r'\[.*\]', '', current_snippet).strip() print(style_message(current_title)) print(current_snippet) print("") feedback = prompt("[Enter to close. Press + or - to give feedback]:") if feedback in ['+', '-']: print('Wow, you are a true hero!') print("""\ O_ """ + style_message("""<T>""") + """`-. """ + style_message("""|""") + """`-‘ """ + style_message("""I""") + """ """) print('My human overlords review each of these reports; I\'m told these reports makes me smarter.') print('Send us more feedback by email: [email protected]') properties = {} set_custom_properties(properties, 'Feedback', feedback) telemetry_core.add_extension_event(EXTENSION_NAME, properties)
def _create_image_registry_credentials(registry_login_server, registry_username, registry_password, image): """Create image registry credentials. """ image_registry_credentials = None if registry_login_server: if not registry_username: raise CLIError('Please specify --registry-username in order to use custom image registry.') if not registry_password: try: registry_password = prompt_pass(msg='Image registry password: '******'Please specify --registry-password in order to use custom image registry.') image_registry_credentials = [ImageRegistryCredential(server=registry_login_server, username=registry_username, password=registry_password)] elif ACR_SERVER_DELIMITER in image.split("/")[0]: if not registry_username: try: registry_username = prompt(msg='Image registry username: '******'Please specify --registry-username in order to use Azure Container Registry.') if not registry_password: try: registry_password = prompt_pass(msg='Image registry password: '******'Please specify --registry-password in order to use Azure Container Registry.') acr_server = image.split("/")[0] if image.split("/") else None if acr_server: image_registry_credentials = [ImageRegistryCredential(server=acr_server, username=registry_username, password=registry_password)] elif registry_username and registry_password and SERVER_DELIMITER in image.split("/")[0]: login_server = image.split("/")[0] if image.split("/") else None if login_server: image_registry_credentials = [ImageRegistryCredential(server=login_server, username=registry_username, password=registry_password)] else: raise CLIError('Failed to parse login server from image name; please explicitly specify --registry-server.') return image_registry_credentials
def acr_import(cmd, client, registry_name, source, source_registry=None, source_registry_username=None, source_registry_password=None, target_tags=None, resource_group_name=None, repository=None, force=False): _, resource_group_name = validate_managed_registry( cmd.cli_ctx, registry_name, resource_group_name, IMPORT_NOT_SUPPORTED) if not source: raise CLIError(INVALID_SOURCE_IMAGE) source_image = source slash = source.find('/') if slash < 0: if not source_registry: from knack.prompting import prompt, NoTTYException try: source_registry = prompt(SOURCE_REGISTRY_MISSING) except NoTTYException: raise CLIError(NO_TTY_ERROR) if not is_valid_resource_id(source_registry): registry = get_registry_from_name_or_login_server(cmd.cli_ctx, source_registry, source_registry) if registry: source_registry = registry.id source = ImportSource(resource_id=source_registry, source_image=source_image) else: source_registry_login_server = source_image[:slash] source_image = source_image[slash + 1:] if not source_image or not source_registry_login_server: raise CLIError(INVALID_SOURCE_IMAGE) registry = get_registry_from_name_or_login_server(cmd.cli_ctx, source_registry_login_server) if registry: if source_registry and \ source_registry.lower() != registry.id.lower() and \ source_registry.lower() != registry.name.lower() and \ source_registry.lower() != registry.login_server.lower(): raise CLIError(REGISTRY_MISMATCH) source = ImportSource(resource_id=registry.id, source_image=source_image) else: if source_registry_password: if source_registry_username: source = ImportSource(registry_uri=source_registry_login_server, source_image=source_image, credentials=ImportSourceCredentials(password=source_registry_password, username=source_registry_username)) else: source = ImportSource(registry_uri=source_registry_login_server, source_image=source_image, credentials=ImportSourceCredentials(password=source_registry_password)) else: source = ImportSource(registry_uri=source_registry_login_server, source_image=source_image) if not target_tags and not repository: index = source_image.find("@") if index > 0: target_tags = [source_image[:index]] else: target_tags = [source_image] import_parameters = ImportImageParameters(source=source, target_tags=target_tags, untagged_target_repositories=repository, mode=ImportMode.force.value if force else ImportMode.no_force.value) return client.import_image( resource_group_name=resource_group_name, registry_name=registry_name, parameters=import_parameters)
def _prompt_for_parameters(missing_parameters, fail_on_no_tty=True): # pylint: disable=too-many-statements prompt_list = missing_parameters.keys() if isinstance(missing_parameters, OrderedDict) \ else sorted(missing_parameters) result = OrderedDict() no_tty = False for param_name in prompt_list: param = missing_parameters[param_name] param_type = param.get('type', 'string') description = 'Missing description' metadata = param.get('metadata', None) if metadata is not None: description = metadata.get('description', description) allowed_values = param.get('allowedValues', None) prompt_str = "Please provide {} value for '{}' (? for help): ".format(param_type, param_name) while True: if allowed_values is not None: try: ix = prompt_choice_list(prompt_str, allowed_values, help_string=description) result[param_name] = allowed_values[ix] except NoTTYException: result[param_name] = None no_tty = True break elif param_type == 'securestring': try: value = prompt_pass(prompt_str, help_string=description) except NoTTYException: value = None no_tty = True result[param_name] = value break elif param_type == 'int': try: int_value = prompt_int(prompt_str, help_string=description) result[param_name] = int_value except NoTTYException: result[param_name] = 0 no_tty = True break elif param_type == 'bool': try: value = prompt_t_f(prompt_str, help_string=description) result[param_name] = value except NoTTYException: result[param_name] = False no_tty = True break elif param_type in ['object', 'array']: try: value = prompt(prompt_str, help_string=description) except NoTTYException: value = '' no_tty = True if value == '': value = {} if param_type == 'object' else [] else: try: value = shell_safe_json_parse(value) except Exception as ex: # pylint: disable=broad-except logger.error(ex) continue result[param_name] = value break else: try: result[param_name] = prompt(prompt_str, help_string=description) except NoTTYException: result[param_name] = None no_tty = True break if no_tty and fail_on_no_tty: raise NoTTYException return result
def _get_credentials(cli_ctx, registry_name, resource_group_name, username, password, only_refresh_token, repository=None, artifact_repository=None, permission=None): """Try to get AAD authorization tokens or admin user credentials. :param str registry_name: The name of container registry :param str resource_group_name: The name of resource group :param str username: The username used to log into the container registry :param str password: The password used to log into the container registry :param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens :param str repository: Repository for which the access token is requested :param str artifact_repository: Artifact repository for which the access token is requested :param str permission: The requested permission on the repository, '*' or 'pull' """ # 1. if username was specified, verify that password was also specified if username: # Try to use the pre-defined login server suffix to construct login server from registry name. # This is to avoid a management server request if username/password are already provided. # In all other cases, including the suffix not defined, login server will be obtained from server. login_server_suffix = get_login_server_suffix(cli_ctx) if login_server_suffix: login_server = '{}{}'.format(registry_name, login_server_suffix) else: registry, _ = get_registry_by_name(cli_ctx, registry_name, resource_group_name) login_server = registry.login_server if not password: try: password = prompt_pass(msg='Password: '******'Please specify both username and password in non-interactive mode.') return login_server, username, password registry, resource_group_name = get_registry_by_name(cli_ctx, registry_name, resource_group_name) login_server = registry.login_server # 2. if we don't yet have credentials, attempt to get a refresh token if not password and registry.sku.name in MANAGED_REGISTRY_SKU: try: password = _get_aad_token( cli_ctx, login_server, only_refresh_token, repository, artifact_repository, permission) return login_server, EMPTY_GUID, password except CLIError as e: logger.warning("Unable to get AAD authorization tokens with message: %s", str(e)) # 3. if we still don't have credentials, attempt to get the admin credentials (if enabled) if not password and registry.admin_user_enabled: try: cred = cf_acr_registries(cli_ctx).list_credentials(resource_group_name, registry_name) username = cred.username password = cred.passwords[0].value return login_server, username, password except CLIError as e: logger.warning("Unable to get admin user credentials with message: %s", str(e)) # 4. if we still don't have credentials, prompt the user if not password: try: username = prompt('Username: '******'Password: '******'Unable to authenticate using AAD or admin login credentials. ' + 'Please specify both username and password in non-interactive mode.') return login_server, None, None
def _get_credentials(cmd, # pylint: disable=too-many-statements registry_name, tenant_suffix, username, password, only_refresh_token, repository=None, artifact_repository=None, permission=None): """Try to get AAD authorization tokens or admin user credentials. :param str registry_name: The name of container registry :param str tenant_suffix: The registry login server tenant suffix :param str username: The username used to log into the container registry :param str password: The password used to log into the container registry :param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens :param str repository: Repository for which the access token is requested :param str artifact_repository: Artifact repository for which the access token is requested :param str permission: The requested permission on the repository, '*' or 'pull' """ # Raise an error if password is specified but username isn't if not username and password: raise CLIError('Please also specify username if password is specified.') cli_ctx = cmd.cli_ctx resource_not_found, registry = None, None try: registry, resource_group_name = get_registry_by_name(cli_ctx, registry_name) login_server = registry.login_server if tenant_suffix: logger.warning( "Obtained registry login server '%s' from service. The specified suffix '%s' is ignored.", login_server, tenant_suffix) except (ResourceNotFound, CLIError) as e: resource_not_found = str(e) logger.debug("Could not get registry from service. Exception: %s", resource_not_found) if not isinstance(e, ResourceNotFound) and _AZ_LOGIN_MESSAGE not in resource_not_found: raise # Try to use the pre-defined login server suffix to construct login server from registry name. login_server_suffix = get_login_server_suffix(cli_ctx) if not login_server_suffix: raise login_server = '{}{}{}'.format( registry_name, '-{}'.format(tenant_suffix) if tenant_suffix else '', login_server_suffix).lower() # Validate the login server is reachable url = 'https://' + login_server + '/v2/' try: challenge = requests.get(url, verify=(not should_disable_connection_verify())) if challenge.status_code in [403]: raise CLIError("Looks like you don't have access to registry '{}'. " "Are firewalls and virtual networks enabled?".format(login_server)) except RequestException as e: logger.debug("Could not connect to registry login server. Exception: %s", str(e)) if resource_not_found: logger.warning("%s\nUsing '%s' as the default registry login server.", resource_not_found, login_server) raise CLIError("Could not connect to the registry login server '{}'. ".format(login_server) + "Please verify that the registry exists and " + "the URL '{}' is reachable from your environment.".format(url)) # 1. if username was specified, verify that password was also specified if username: if not password: try: password = prompt_pass(msg='Password: '******'Please specify both username and password in non-interactive mode.') return login_server, username, password # 2. if we don't yet have credentials, attempt to get a refresh token if not registry or registry.sku.name in get_managed_sku(cmd): try: return login_server, EMPTY_GUID, _get_aad_token( cli_ctx, login_server, only_refresh_token, repository, artifact_repository, permission) except CLIError as e: logger.warning("%s: %s", AAD_TOKEN_BASE_ERROR_MESSAGE, str(e)) # 3. if we still don't have credentials, attempt to get the admin credentials (if enabled) if registry: if registry.admin_user_enabled: try: cred = cf_acr_registries(cli_ctx).list_credentials(resource_group_name, registry_name) return login_server, cred.username, cred.passwords[0].value except CLIError as e: logger.warning("%s: %s", ADMIN_USER_BASE_ERROR_MESSAGE, str(e)) else: logger.warning("%s: %s", ADMIN_USER_BASE_ERROR_MESSAGE, "Admin user is disabled.") else: logger.warning("%s: %s", ADMIN_USER_BASE_ERROR_MESSAGE, resource_not_found) # 4. if we still don't have credentials, prompt the user try: username = prompt('Username: '******'Password: '******'Unable to authenticate using AAD or admin login credentials. ' + 'Please specify both username and password in non-interactive mode.') return login_server, None, None