def _get_aad_token(cli_ctx, login_server, only_refresh_token, repository=None, artifact_repository=None, permission=None): """Obtains refresh and access tokens for an AAD-enabled registry. :param str login_server: The registry login server URL to log in to :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' """ if repository and artifact_repository: raise ValueError( "Only one of repository and artifact_repository can be provided.") if (repository or artifact_repository) and permission not in ACCESS_TOKEN_PERMISSION: raise ValueError( "Permission is required for a repository or artifact_repository. Allowed access token permission: {}" .format(ACCESS_TOKEN_PERMISSION)) login_server = login_server.rstrip('/') challenge = requests.get('https://' + login_server + '/v2/', verify=(not should_disable_connection_verify())) if challenge.status_code not in [ 401 ] or 'WWW-Authenticate' not in challenge.headers: raise CLIError( "Registry '{}' did not issue a challenge.".format(login_server)) authenticate = challenge.headers['WWW-Authenticate'] tokens = authenticate.split(' ', 2) if len(tokens) < 2 or tokens[0].lower() != 'bearer': raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) params = { y[0]: y[1].strip('"') for y in (x.strip().split('=', 2) for x in tokens[1].split(',')) } if 'realm' not in params or 'service' not in params: raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) authurl = urlparse(params['realm']) authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) creds, _, tenant = profile.get_raw_token() headers = {'Content-Type': 'application/x-www-form-urlencoded'} content = { 'grant_type': 'access_token', 'service': params['service'], 'tenant': tenant, 'access_token': creds[1] } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: raise CLIError( "Access to registry '{}' was denied. Response code: {}.".format( login_server, response.status_code)) refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] if only_refresh_token: return refresh_token authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/token', '', '', '')) if repository: scope = 'repository:{}:{}'.format(repository, permission) elif artifact_repository: scope = 'artifact-repository:{}:{}'.format(artifact_repository, permission) else: # catalog only has * as permission, even for a read operation scope = 'registry:catalog:*' content = { 'grant_type': 'refresh_token', 'service': login_server, 'scope': scope, 'refresh_token': refresh_token } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) access_token = loads(response.content.decode("utf-8"))["access_token"] return access_token
def _invalid_sku_downgrade(): raise CLIError( "Managed registries could not be downgraded to Classic SKU.")
def get_one_of_subscription_locations(cli_ctx): result = get_subscription_locations(cli_ctx) if result: return next((r.name for r in result if r.name.lower() == 'westus'), result[0].name) raise CLIError('Current subscription does not have valid location list')
def _build_odata_filter(default_filter, field_name, field_value, field_label): if not field_value: from knack.util import CLIError raise CLIError('Value for {} can not be empty.'.format(field_name)) return _add_condition(default_filter, field_label, field_value)
def validate_timeout_value(namespace): """Validates that zip deployment timeout is set to a reasonable min value""" if isinstance(namespace.timeout, int): if namespace.timeout <= 29: raise CLIError('--timeout value should be a positive value in seconds and should be at least 30')
def create_pull_request(project=None, repository=None, source_branch=None, target_branch=None, title=None, description=None, auto_complete=False, squash=False, delete_source_branch=False, bypass_policy=False, bypass_policy_reason=None, merge_commit_message=None, reviewers=None, work_items=None, draft=None, open=False, organization=None, detect=None, transition_work_items=False): # pylint: disable=redefined-builtin """Create a pull request. :param project: Name or ID of the team project. :type project: str :param repository: Name or ID of the repository to create the pull request in. :type repository: str :param source_branch: Name of the source branch. Example: "dev". :type source_branch: str :param target_branch: Name of the target branch. If not specified, defaults to the default branch of the target repository. :type target_branch: str :param title: Title for the new pull request. :type title: str :param draft: Use this flag to create the pull request in draft/work in progress mode. :type draft: bool :param description: Description for the new pull request. Can include markdown. Each value sent to this arg will be a new line. For example: --description "First Line" "Second Line" :type description: list of str :param auto_complete: Set the pull request to complete automatically when all policies have passed and the source branch can be merged into the target branch. :type auto_complete: bool :param squash: Squash the commits in the source branch when merging into the target branch. :type squash: bool :param delete_source_branch: Delete the source branch after the pull request has been completed and merged into the target branch. :type delete_source_branch: bool :param bypass_policy: Bypass required policies (if any) and completes the pull request once it can be merged. :type bypass_policy: bool :param bypass_policy_reason: Reason for bypassing the required policies. :type bypass_policy_reason: str :param merge_commit_message: Message displayed when commits are merged. :type merge_commit_message: str :param reviewers: Additional users or groups to include as reviewers on the new pull request. Space separated. :type reviewers: list of str :param work_items: IDs of the work items to link to the new pull request. Space separated. :type work_items: list of str :param open: Open the pull request in your web browser. :type open: bool :param transition_work_items: Transition any work items linked to the pull request into the next logical state. (e.g. Active -> Resolved) :type transition_work_items: bool :rtype: :class:`GitPullRequest <v5_0.git.models.GitPullRequest>` """ organization, project, repository = resolve_instance_project_and_repo( detect=detect, organization=organization, project=project, repo=repository) source_branch, target_branch = _get_branches_for_pull_request( organization, project, repository, source_branch, target_branch, detect) client = get_git_client(organization) multi_line_description = None if description is not None: multi_line_description = '\n'.join(description) pr = GitPullRequest(description=multi_line_description, source_ref_name=source_branch, target_ref_name=target_branch) if draft is not None: pr.is_draft = draft if title is not None: pr.title = title else: pr.title = 'Merge ' + source_branch + ' to ' + target_branch pr.source_ref_name = resolve_git_ref_heads(source_branch) pr.target_ref_name = resolve_git_ref_heads(target_branch) if pr.source_ref_name == pr.target_ref_name: raise CLIError( 'The source branch, "{}", can not be the same as the target branch.' .format(pr.source_ref_name)) pr.reviewers = _resolve_reviewers_as_refs(reviewers, organization) if work_items is not None and work_items: resolved_work_items = [] for work_item in work_items: resolved_work_items.append(ResourceRef(id=work_item)) pr.work_item_refs = resolved_work_items pr = client.create_pull_request(git_pull_request_to_create=pr, project=project, repository_id=repository) title_from_commit = None if title is None: # if title wasn't specified and only one commit, we will set the PR title to the comment of that commit commits = client.get_pull_request_commits( repository_id=repository, pull_request_id=pr.pull_request_id, project=project) if len(commits) == 1: title_from_commit = commits[0].comment set_completion_options = (bypass_policy or bypass_policy_reason is not None or squash or merge_commit_message is not None or delete_source_branch or transition_work_items) if auto_complete or set_completion_options or title_from_commit is not None: pr_for_update = GitPullRequest() if auto_complete: # auto-complete will not get set on create, so a subsequent update is required. pr_for_update.auto_complete_set_by = IdentityRef( id=resolve_identity_as_id(ME, organization)) if set_completion_options: completion_options = GitPullRequestCompletionOptions() completion_options.bypass_policy = bypass_policy completion_options.bypass_reason = bypass_policy_reason completion_options.delete_source_branch = delete_source_branch completion_options.squash_merge = squash completion_options.merge_commit_message = merge_commit_message completion_options.transition_work_items = transition_work_items pr_for_update.completion_options = completion_options if title_from_commit is not None: pr_for_update.title = title_from_commit pr = client.update_pull_request( git_pull_request_to_update=pr_for_update, project=pr.repository.project.id, repository_id=pr.repository.id, pull_request_id=pr.pull_request_id) if open: _open_pull_request(pr, organization) return pr
def process_ts_create_or_update_namespace(namespace): from azure.cli.core.commands.validators import validate_tags validate_tags(namespace) if namespace.template_file and not os.path.isfile(namespace.template_file): raise CLIError('Please enter a valid file path')
def send_raw_request( cli_ctx, method, url, headers=None, uri_parameters=None, # pylint: disable=too-many-locals,too-many-branches,too-many-statements body=None, skip_authorization_header=False, resource=None, output_file=None, generated_client_request_id_name='x-ms-client-request-id'): import uuid from requests import Session, Request from requests.structures import CaseInsensitiveDict result = CaseInsensitiveDict() for s in headers or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value headers = result # If Authorization header is already provided, don't bother with the token if 'Authorization' in headers: skip_authorization_header = True # Handle User-Agent agents = [get_az_user_agent()] # Borrow AZURE_HTTP_USER_AGENT from msrest # https://github.com/Azure/msrest-for-python/blob/4cc8bc84e96036f03b34716466230fb257e27b36/msrest/pipeline/universal.py#L70 _ENV_ADDITIONAL_USER_AGENT = 'AZURE_HTTP_USER_AGENT' import os if _ENV_ADDITIONAL_USER_AGENT in os.environ: agents.append(os.environ[_ENV_ADDITIONAL_USER_AGENT]) # Custom User-Agent provided as command argument if 'User-Agent' in headers: agents.append(headers['User-Agent']) headers['User-Agent'] = ' '.join(agents) if generated_client_request_id_name: headers[generated_client_request_id_name] = str(uuid.uuid4()) # try to figure out the correct content type if body: try: _ = shell_safe_json_parse(body) if 'Content-Type' not in headers: headers['Content-Type'] = 'application/json' except Exception: # pylint: disable=broad-except pass # add telemetry headers['CommandName'] = cli_ctx.data['command'] if cli_ctx.data.get('safe_params'): headers['ParameterSetName'] = ' '.join(cli_ctx.data['safe_params']) result = {} for s in uri_parameters or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value uri_parameters = result or None endpoints = cli_ctx.cloud.endpoints # If url is an ARM resource ID, like /subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01, # default to Azure Resource Manager. # https://management.azure.com + /subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01 if '://' not in url: url = endpoints.resource_manager.rstrip('/') + url # Replace common tokens with real values. It is for smooth experience if users copy and paste the url from # Azure Rest API doc from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) if '{subscriptionId}' in url: url = url.replace( '{subscriptionId}', cli_ctx.data['subscription_id'] or profile.get_subscription_id()) # Prepare the Bearer token for `Authorization` header if not skip_authorization_header and url.lower().startswith('https://'): # Prepare `resource` for `get_raw_token` if not resource: # If url starts with ARM endpoint, like `https://management.azure.com/`, # use `active_directory_resource_id` for resource, like `https://management.core.windows.net/`. # This follows the same behavior as `azure.cli.core.commands.client_factory._get_mgmt_service_client` if url.lower().startswith(endpoints.resource_manager.rstrip('/')): resource = endpoints.active_directory_resource_id else: from azure.cli.core.cloud import CloudEndpointNotSetException for p in [x for x in dir(endpoints) if not x.startswith('_')]: try: value = getattr(endpoints, p) except CloudEndpointNotSetException: continue if isinstance(value, six.string_types) and url.lower().startswith( value.lower()): resource = value break if resource: # Prepare `subscription` for `get_raw_token` # If this is an ARM request, try to extract subscription ID from the URL. # But there are APIs which don't require subscription ID, like /subscriptions, /tenants # TODO: In the future when multi-tenant subscription is supported, we won't be able to uniquely identify # the token from subscription anymore. token_subscription = None if url.lower().startswith(endpoints.resource_manager.rstrip('/')): token_subscription = _extract_subscription_id(url) if token_subscription: logger.debug( 'Retrieving token for resource %s, subscription %s', resource, token_subscription) token_info, _, _ = profile.get_raw_token( resource, subscription=token_subscription) else: logger.debug('Retrieving token for resource %s', resource) token_info, _, _ = profile.get_raw_token(resource) token_type, token, _ = token_info headers = headers or {} headers['Authorization'] = '{} {}'.format(token_type, token) else: logger.warning( "Can't derive appropriate Azure AD resource from --url to acquire an access token. " "If access token is required, use --resource to specify the resource" ) try: # https://requests.readthedocs.io/en/latest/user/advanced/#prepared-requests s = Session() req = Request(method=method, url=url, headers=headers, params=uri_parameters, data=body) prepped = s.prepare_request(req) # Merge environment settings into session settings = s.merge_environment_settings( prepped.url, {}, None, not should_disable_connection_verify(), None) _log_request(prepped) r = s.send(prepped, **settings) _log_response(r) except Exception as ex: # pylint: disable=broad-except raise CLIError(ex) if not r.ok: reason = r.reason if r.text: reason += '({})'.format(r.text) raise CLIError(reason) if output_file: with open(output_file, 'wb') as fd: for chunk in r.iter_content(chunk_size=128): fd.write(chunk) return r
def _check_stale(self): if self.is_stale: message = "command authoring error: argument context '{}' is stale! " \ "Check that the subsequent block for has a corresponding `as` statement.".format(self.scope) logger.error(message) raise CLIError(message)
def acr_repository_delete(cmd, registry_name, repository=None, image=None, tag=None, manifest=None, resource_group_name=None, username=None, password=None, yes=False): if bool(repository) == bool(image): raise CLIError('Usage error: --image IMAGE | --repository REPOSITORY') # Check if this is a legacy command. --manifest can be used as a flag so None is checked. if repository and (tag or manifest is not None): return _legacy_delete(cmd=cmd, registry_name=registry_name, repository=repository, tag=tag, manifest=manifest, resource_group_name=resource_group_name, username=username, password=password, yes=yes) # At this point the specified command must not be a legacy command so we process it as a new command. # If --tag/--manifest are specified with --repository, it's a legacy command handled above. # If --tag/--manifest are specified with --image, error out here. if tag: raise CLIError("The parameter --tag is redundant and deprecated. Please use --image to delete an image.") if manifest is not None: raise CLIError("The parameter --manifest is redundant and deprecated. Please use --image to delete an image.") _, resource_group_name = validate_managed_registry( cmd.cli_ctx, registry_name, resource_group_name, DELETE_NOT_SUPPORTED) if image: # If --image is specified, repository/tag/manifest must be empty. repository, tag, manifest = _parse_image_name(image, allow_digest=True) login_server, username, password = get_access_credentials( cli_ctx=cmd.cli_ctx, registry_name=registry_name, resource_group_name=resource_group_name, username=username, password=password, repository=repository, permission='*') if tag or manifest: manifest = _delete_manifest_confirmation( login_server=login_server, username=username, password=password, repository=repository, tag=tag, manifest=manifest, yes=yes) path = '/v2/{}/manifests/{}'.format(repository, manifest) else: _user_confirmation("Are you sure you want to delete the repository '{}' " "and all images under it?".format(repository), yes) path = '/v2/_acr/{}/repository'.format(repository) return _delete_data_from_registry( login_server=login_server, path=path, username=username, password=password)
def _legacy_delete(cmd, registry_name, repository, tag=None, manifest=None, resource_group_name=None, username=None, password=None, yes=False): _, resource_group_name = validate_managed_registry( cmd.cli_ctx, registry_name, resource_group_name, DELETE_NOT_SUPPORTED) login_server, username, password = get_access_credentials( cli_ctx=cmd.cli_ctx, registry_name=registry_name, resource_group_name=resource_group_name, username=username, password=password, repository=repository, permission='*') _INVALID = "Please specify either a tag name with --tag or a manifest digest with --manifest." # If manifest is not specified if manifest is None: if not tag: _user_confirmation("Are you sure you want to delete the repository '{}' " "and all images under it?".format(repository), yes) path = '/v2/_acr/{}/repository'.format(repository) else: logger.warning( "This command is deprecated. The new command for this operation " "is 'az acr repository untag --name %s --image %s:%s'.", registry_name, repository, tag) _user_confirmation("Are you sure you want to delete the tag '{}:{}'?".format(repository, tag), yes) path = '/v2/_acr/{}/tags/{}'.format(repository, tag) # If --manifest is specified as a flag elif not manifest: # Raise if --tag is empty if not tag: raise CLIError(_INVALID) logger.warning( "This command is deprecated. The new command for this operation " "is 'az acr repository delete --name %s --image %s:%s'.", registry_name, repository, tag) manifest = _delete_manifest_confirmation( login_server=login_server, username=username, password=password, repository=repository, tag=tag, manifest=manifest, yes=yes) path = '/v2/{}/manifests/{}'.format(repository, manifest) # If --manifest is specified with a value else: # Raise if --tag is not empty if tag: raise CLIError(_INVALID) logger.warning( "This command is deprecated. The new command for this operation " "is 'az acr repository delete --name %s --image %s@%s'.", registry_name, repository, manifest) manifest = _delete_manifest_confirmation( login_server=login_server, username=username, password=password, repository=repository, tag=tag, manifest=manifest, yes=yes) path = '/v2/{}/manifests/{}'.format(repository, manifest) return _delete_data_from_registry( login_server=login_server, path=path, username=username, password=password)
def acr_connected_registry_repo(cmd, client, connected_registry_name, registry_name, add_repos=None, remove_repos=None, resource_group_name=None): if not (add_repos or remove_repos): raise CLIError('No repository permissions to update.') _, resource_group_name = validate_managed_registry(cmd, registry_name, resource_group_name) add_repos_set = set(add_repos) if add_repos is not None else set() remove_repos_set = set(remove_repos) if remove_repos is not None else set() duplicate_repos = set.intersection(add_repos_set, remove_repos_set) if duplicate_repos: errors = sorted( map(lambda action: action[action.rfind('/') + 1:], duplicate_repos)) raise CLIError( 'Update ambiguity. Duplicate repository names were provided with ' + '--add and --remove arguments.\n{}'.format(errors)) connected_registry_list = list( client.list(resource_group_name, registry_name)) family_tree, target_connected_registry = _get_family_tree( connected_registry_list, connected_registry_name) if target_connected_registry is None: raise CLIError("Connected registry '{}' doesn't exist.".format( connected_registry_name)) # remove repo permissions from connected registry descendants. remove_actions = REPO_SCOPES_BY_MODE[ConnectedRegistryModes.REGISTRY.value] if remove_repos is not None: remove_repos_txt = ", ".join(remove_repos) remove_repos_set = _get_scope_map_actions_set(remove_repos, remove_actions) descendants = _get_descendants(family_tree, target_connected_registry.id) for connected_registry in descendants: msg = "Removing '{}' permissions from {}".format( remove_repos_txt, connected_registry.name) _update_repo_permissions(cmd, resource_group_name, registry_name, connected_registry, set(), remove_repos_set, msg=msg) else: remove_repos_set = set() # add repo permissions to ancestors. add_actions = REPO_SCOPES_BY_MODE[target_connected_registry.mode] if add_repos is not None: add_repos_txt = ", ".join(add_repos) add_repos_set = _get_scope_map_actions_set(add_repos, add_actions) parent_id = target_connected_registry.parent.id while parent_id and not parent_id.isspace(): connected_registry = family_tree[parent_id]["connectedRegistry"] msg = "Adding '{}' permissions to {}".format( add_repos_txt, connected_registry.name) _update_repo_permissions(cmd, resource_group_name, registry_name, connected_registry, add_repos_set, set(), msg=msg) parent_id = connected_registry.parent.id else: add_repos_set = set() # update target connected registry repo permissions. if add_repos and remove_repos: msg = "Adding '{}' and removing '{}' permissions in {}".format( add_repos_txt, remove_repos_txt, target_connected_registry.name) elif add_repos: msg = "Adding '{}' permissions to {}".format( add_repos_txt, target_connected_registry.name) else: msg = "Removing '{}' permissions from {}".format( remove_repos_txt, target_connected_registry.name) _update_repo_permissions(cmd, resource_group_name, registry_name, target_connected_registry, add_repos_set, remove_repos_set, msg=msg)
def acr_connected_registry_create( cmd, # pylint: disable=too-many-locals, too-many-statements client, registry_name, connected_registry_name, repositories=None, sync_token_name=None, client_token_list=None, resource_group_name=None, mode=None, parent_name=None, sync_schedule=None, sync_message_ttl=None, sync_window=None, log_level=None, sync_audit_logs_enabled=False): if bool(sync_token_name) == bool(repositories): raise CLIError( "usage error: you need to provide either --sync-token-name or --repository, but not both." ) registry, resource_group_name = get_registry_by_name( cmd.cli_ctx, registry_name, resource_group_name) subscription_id = get_subscription_id(cmd.cli_ctx) if not registry.data_endpoint_enabled: raise CLIError( "Can't create the connected registry '{}' ".format( connected_registry_name) + "because the cloud registry '{}' data endpoint is disabled. ". format(registry_name) + "Enabling the data endpoint might affect your firewall rules.\nTo enable data endpoint run:" + "\n\taz acr update -n {} --data-endpoint-enabled true".format( registry_name)) ErrorResponseException = cmd.get_models('ErrorResponseException') parent = None mode = mode.capitalize() if parent_name: try: parent = acr_connected_registry_show(cmd, client, parent_name, registry_name, resource_group_name) connected_registry_list = list( client.list(resource_group_name, registry_name)) family_tree, _ = _get_family_tree(connected_registry_list, None) except ErrorResponseException as ex: if ex.response.status_code == 404: raise CLIError( "The parent connected registry '{}' could not be found.". format(parent_name)) raise CLIError(ex) if parent.mode != ConnectedRegistryModes.REGISTRY.value and parent.mode != mode: raise CLIError( "Can't create the registry '{}' with mode '{}' ".format( connected_registry_name, mode) + "when the connected registry parent '{}' mode is '{}'. ". format(parent_name, parent.mode) + "For more information on connected registries " + "please visit https://aka.ms/acr/connected-registry.") _update_ancestor_permissions(cmd, family_tree, resource_group_name, registry_name, parent.id, connected_registry_name, repositories, mode, False) if sync_token_name: sync_token_id = build_token_id(subscription_id, resource_group_name, registry_name, sync_token_name) else: sync_token_id = _create_sync_token(cmd, resource_group_name, registry_name, connected_registry_name, repositories, mode) if client_token_list is not None: for i, client_token_name in enumerate(client_token_list): client_token_list[i] = build_token_id(subscription_id, resource_group_name, registry_name, client_token_name) ConnectedRegistry, LoggingProperties, SyncProperties, ParentProperties = cmd.get_models( 'ConnectedRegistry', 'LoggingProperties', 'SyncProperties', 'ParentProperties') connected_registry_create_parameters = ConnectedRegistry( provisioning_state=None, mode=mode, parent=ParentProperties(id=parent.id if parent else None, sync_properties=SyncProperties( token_id=sync_token_id, schedule=sync_schedule, message_ttl=sync_message_ttl, sync_window=sync_window)), client_token_ids=client_token_list, logging=LoggingProperties(log_level=log_level, audit_log_status='Enabled' if sync_audit_logs_enabled else 'Disabled')) try: return client.create(subscription_id=subscription_id, resource_group_name=resource_group_name, registry_name=registry_name, connected_registry_name=connected_registry_name, connected_registry_create_parameters= connected_registry_create_parameters) except ValidationError as e: raise CLIError(e)
def acr_connected_registry_update( cmd, # pylint: disable=too-many-locals, too-many-statements client, registry_name, connected_registry_name, add_client_token_list=None, remove_client_token_list=None, resource_group_name=None, sync_schedule=None, sync_window=None, log_level=None, sync_message_ttl=None, sync_audit_logs_enabled=None): _, resource_group_name = validate_managed_registry(cmd, registry_name, resource_group_name) subscription_id = get_subscription_id(cmd.cli_ctx) current_connected_registry = acr_connected_registry_show( cmd, client, connected_registry_name, registry_name, resource_group_name) # Add or remove from the current client token id list if add_client_token_list is not None: for i, client_token_name in enumerate(add_client_token_list): add_client_token_list[i] = build_token_id(subscription_id, resource_group_name, registry_name, client_token_name) add_client_token_set = set(add_client_token_list) else: add_client_token_set = set() if remove_client_token_list is not None: for i, client_token_name in enumerate(remove_client_token_list): remove_client_token_list[i] = build_token_id( subscription_id, resource_group_name, registry_name, client_token_name) remove_client_token_set = set(remove_client_token_list) else: remove_client_token_set = set() duplicate_client_token = set.intersection(add_client_token_set, remove_client_token_set) if duplicate_client_token: errors = sorted( map(lambda action: action[action.rfind('/') + 1:], duplicate_client_token)) raise CLIError( 'Update ambiguity. Duplicate client token ids were provided with ' + '--add-client-tokens and --remove-client-tokens arguments.\n{}'. format(errors)) current_client_token_set = set(current_connected_registry.client_token_ids) \ if current_connected_registry.client_token_ids else set() client_token_set = current_client_token_set.union( add_client_token_set).difference(remove_client_token_set) client_token_list = list( client_token_set ) if client_token_set != current_client_token_set else None ConnectedRegistryUpdateParameters, SyncUpdateProperties, LoggingProperties = cmd.get_models( 'ConnectedRegistryUpdateParameters', 'SyncUpdateProperties', 'LoggingProperties') connected_registry_update_parameters = ConnectedRegistryUpdateParameters( sync_properties=SyncUpdateProperties( token_id=current_connected_registry.parent.sync_properties. token_id, schedule=sync_schedule, message_ttl=sync_message_ttl, sync_window=sync_window), logging=LoggingProperties(log_level=log_level, audit_log_status=sync_audit_logs_enabled), client_token_ids=client_token_list) try: return client.update(resource_group_name=resource_group_name, registry_name=registry_name, connected_registry_name=connected_registry_name, connected_registry_update_parameters= connected_registry_update_parameters) except ValidationError as e: raise CLIError(e)
def _get_aad_token_after_challenge(cli_ctx, token_params, login_server, only_refresh_token, repository, artifact_repository, permission, is_diagnostics_context): authurl = urlparse(token_params['realm']) authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) creds, _, tenant = profile.get_raw_token() headers = {'Content-Type': 'application/x-www-form-urlencoded'} content = { 'grant_type': 'access_token', 'service': token_params['service'], 'tenant': tenant, 'access_token': creds[1] } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: from ._errors import CONNECTIVITY_REFRESH_TOKEN_ERROR if is_diagnostics_context: return CONNECTIVITY_REFRESH_TOKEN_ERROR.format_error_message( login_server, response.status_code) raise CLIError( CONNECTIVITY_REFRESH_TOKEN_ERROR.format_error_message( login_server, response.status_code).get_error_message()) refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] if only_refresh_token: return refresh_token authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/token', '', '', '')) if repository: scope = 'repository:{}:{}'.format(repository, permission) elif artifact_repository: scope = 'artifact-repository:{}:{}'.format(artifact_repository, permission) else: # catalog only has * as permission, even for a read operation scope = 'registry:catalog:*' content = { 'grant_type': 'refresh_token', 'service': login_server, 'scope': scope, 'refresh_token': refresh_token } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: from ._errors import CONNECTIVITY_ACCESS_TOKEN_ERROR if is_diagnostics_context: return CONNECTIVITY_ACCESS_TOKEN_ERROR.format_error_message( login_server, response.status_code) raise CLIError( CONNECTIVITY_ACCESS_TOKEN_ERROR.format_error_message( login_server, response.status_code).get_error_message()) return loads(response.content.decode("utf-8"))["access_token"]
def validate_worker_count(namespace): if namespace.worker_count: if namespace.worker_count < 3: raise CLIError( '--worker-count must be greater than or equal to 3.')
def acr_import(cmd, client, registry_name, source_image, source_registry=None, source_registry_username=None, source_registry_password=None, target_tags=None, resource_group_name=None, repository=None, force=False): if source_registry_username and not source_registry_password: raise CLIError(CREDENTIALS_INVALID) _, resource_group_name = validate_managed_registry(cmd, registry_name, resource_group_name, IMPORT_NOT_SUPPORTED) ImportImageParameters, ImportSource, ImportMode = cmd.get_models( 'ImportImageParameters', 'ImportSource', 'ImportMode') registry = None if source_registry: if is_valid_resource_id(source_registry): source = ImportSource(resource_id=source_registry, source_image=source_image) else: registry = get_registry_from_name_or_login_server( cmd.cli_ctx, source_registry, source_registry) if registry: # For Azure container registry source = ImportSource(resource_id=registry.id, source_image=source_image) else: # For non-Azure container registry raise CLIError(SOURCE_REGISTRY_NOT_FOUND) else: registry_uri, source_image = _split_registry_and_image(source_image) if source_registry_password: ImportSourceCredentials = cmd.get_models('ImportSourceCredentials') source = ImportSource(registry_uri=registry_uri, source_image=source_image, credentials=ImportSourceCredentials( password=source_registry_password, username=source_registry_username)) else: registry = get_registry_from_name_or_login_server( cmd.cli_ctx, registry_uri) if registry: # For Azure container registry source = ImportSource(resource_id=registry.id, source_image=source_image) else: # For non-Azure container registry source = ImportSource(registry_uri=registry_uri, 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) result_poller = client.import_image( resource_group_name=resource_group_name, registry_name=registry_name, parameters=import_parameters) return _handle_result(cmd, result_poller, source_registry, source_image, registry)
def validate_worker_vm_disk_size_gb(namespace): if namespace.worker_vm_disk_size_gb: if namespace.worker_vm_disk_size_gb < 128: raise CLIError( '--worker-vm-disk-size-gb must be greater than or equal to 128.')
def validate_subscription_lock(namespace): if getattr(namespace, 'ids', None): for lock_id in getattr(namespace, 'ids'): if _parse_lock_id(lock_id).get('resource_group'): raise CLIError('{} is not a valid subscription-level lock id.'.format(lock_id))
def validate_client_secret(namespace): if namespace.client_secret is not None: if namespace.client_id is None or not str(namespace.client_id): raise CLIError('Must specify --client-id with --client-secret.')
def _validate_template_spec_out(namespace): _validate_template_spec(namespace) if namespace.output_folder and not os.path.isdir(namespace.output_folder): raise CLIError('Please enter a valid output folder')
def parse_zone_file(text, zone_name, ignore_invalid=False): """ Parse a zonefile into a dict """ text = _remove_comments(text) text = _flatten(text) text = _add_record_names(text) zone_obj = OrderedDict() record_lines = text.split("\n") current_origin = zone_name.rstrip('.') + '.' current_ttl = 3600 soa_processed = False for record_line in record_lines: parse_match = False record = None for record_type, regex in _COMPILED_REGEX.items(): match = regex.match(record_line) if not match: continue parse_match = True record = match.groupdict() if not parse_match and not ignore_invalid: raise CLIError('Unable to parse: {}'.format(record_line)) record_type = record['delim'].lower() if record_type == '$origin': origin_value = record['val'] if not origin_value.endswith('.'): logger.warning( "$ORIGIN '{}' should have terminating dot.".format( origin_value)) current_origin = origin_value.rstrip('.') + '.' elif record_type == '$ttl': current_ttl = _convert_to_seconds(record['val']) else: record_name = record['name'] if '@' in record_name: record_name = record_name.replace('@', current_origin) elif not record_name.endswith('.'): record_name = '{}.{}'.format(record_name, current_origin) print(current_origin, record_name) # special record-specific fix-ups if record_type == 'ptr': record['fullname'] = record_name + '.' + current_origin elif record_type == 'soa': for key in ['refresh', 'retry', 'expire', 'minimum']: record[key] = _convert_to_seconds(record[key]) _expand_with_origin(record, 'email', current_origin) elif record_type == 'cname': _expand_with_origin(record, 'alias', current_origin) elif record_type == 'mx': _expand_with_origin(record, 'host', current_origin) elif record_type == 'ns': _expand_with_origin(record, 'host', current_origin) elif record_type == 'srv': _expand_with_origin(record, 'target', current_origin) elif record_type == 'spf': record_type = 'txt' record['ttl'] = _convert_to_seconds(record['ttl'] or current_ttl) # handle quotes for CAA and TXT if record_type == 'caa': _post_process_caa_record(record) elif record_type == 'txt': # handle TXT concatenation and splitting separately _post_process_txt_record(record) if record_name not in zone_obj: zone_obj[record_name] = OrderedDict() if record_type == 'soa': if soa_processed: raise CLIError( 'Zone file can contain only one SOA record.') if record_name != current_origin: raise CLIError("Zone SOA record must be at the apex '@'.") zone_obj[record_name][record_type] = record soa_processed = True continue if not soa_processed: raise CLIError('First record in zone file must be SOA.') if record_type == 'cname': if record_type in zone_obj[record_name]: logger.warning( "CNAME record already exists for '{}'. Ignoring '{}'.". format(record_name, record['alias'])) continue zone_obj[record_name][record_type] = record continue # any other record can have multiple entries if record_type not in zone_obj[record_name]: zone_obj[record_name][record_type] = [] zone_obj[record_name][record_type].append(record) _post_process_ttl(zone_obj) _post_check_names(zone_obj) return zone_obj
def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None, proj_name=None, version='v3'): """Publish local bot code to Azure. This method is directly called via "bot publish" :param cmd: :param client: :param resource_group_name: :param resource_name: :param code_dir: :param proj_name: :param version: :return: """ if version == 'v3': return publish_appv3(cmd, client, resource_group_name, resource_name, code_dir) # Get the bot information and ensure it's not only a registration bot. bot = client.bots.get( resource_group_name=resource_group_name, resource_name=resource_name ) if bot.kind == 'bot': raise CLIError('Bot kind is \'bot\', meaning it is a registration bot. ' 'Source publish is not supported for registration only bots.') # If the user does not pass in a path to the local bot project, get the current working directory. if not code_dir: code_dir = os.getcwd() logger.info('Parameter --code-dir not provided, defaulting to current working directory, %s. ' 'For more information, run \'az bot publish -h\'', code_dir) if not os.path.isdir(code_dir): raise CLIError('The path %s is not a valid directory. ' 'Please supply a valid directory path containing your source code.' % code_dir) # Ensure that the directory contains appropriate post deploy scripts folder if 'PostDeployScripts' not in os.listdir(code_dir): BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_name) logger.info('Creating upload zip file.') zip_filepath = BotPublishPrep.create_upload_zip(logger, code_dir, include_node_modules=False) logger.info('Zip file path created, at %s.', zip_filepath) kudu_client = KuduClient(cmd, resource_group_name, resource_name, bot) output = kudu_client.publish(zip_filepath) logger.info('Bot source published. Preparing bot application to run the new source.') os.remove('upload.zip') if os.path.exists(os.path.join('.', 'package.json')): logger.info('Detected language javascript. Installing node dependencies in remote bot.') __install_node_dependencies(kudu_client) if output.get('active'): logger.info('Deployment successful!') if not output.get('active'): scm_url = output.get('url') deployment_id = output.get('id') # Instead of replacing "latest", which would could be in the bot name, we replace "deployments/latest" deployment_url = scm_url.replace('deployments/latest', 'deployments/%s' % deployment_id) logger.error('Deployment failed. To find out more information about this deployment, please visit %s.' % deployment_url) return output
def _get_aad_token(cli_ctx, login_server, only_refresh_token, repository=None, artifact_repository=None, permission=None, is_diagnostics_context=False): """Obtains refresh and access tokens for an AAD-enabled registry. :param str login_server: The registry login server URL to log in to :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' """ if repository and artifact_repository: raise ValueError( "Only one of repository and artifact_repository can be provided.") if (repository or artifact_repository) and permission not in ACCESS_TOKEN_PERMISSION: raise ValueError( "Permission is required for a repository or artifact_repository. Allowed access token permission: {}" .format(ACCESS_TOKEN_PERMISSION)) login_server = login_server.rstrip('/') challenge = requests.get('https://' + login_server + '/v2/', verify=(not should_disable_connection_verify())) if challenge.status_code not in [ 401 ] or 'WWW-Authenticate' not in challenge.headers: from ._errors import CONNECTIVITY_CHALLENGE_ERROR if is_diagnostics_context: return CONNECTIVITY_CHALLENGE_ERROR.format_error_message( login_server) raise CLIError( CONNECTIVITY_CHALLENGE_ERROR.format_error_message( login_server).get_error_message()) authenticate = challenge.headers['WWW-Authenticate'] tokens = authenticate.split(' ', 2) if len(tokens) < 2 or tokens[0].lower() != 'bearer': from ._errors import CONNECTIVITY_AAD_LOGIN_ERROR if is_diagnostics_context: return CONNECTIVITY_AAD_LOGIN_ERROR.format_error_message( login_server) raise CLIError( CONNECTIVITY_AAD_LOGIN_ERROR.format_error_message( login_server).get_error_message()) token_params = { y[0]: y[1].strip('"') for y in (x.strip().split('=', 2) for x in tokens[1].split(',')) } if 'realm' not in token_params or 'service' not in token_params: from ._errors import CONNECTIVITY_AAD_LOGIN_ERROR if is_diagnostics_context: return CONNECTIVITY_AAD_LOGIN_ERROR.format_error_message( login_server) raise CLIError( CONNECTIVITY_AAD_LOGIN_ERROR.format_error_message( login_server).get_error_message()) return _get_aad_token_after_challenge(cli_ctx, token_params, login_server, only_refresh_token, repository, artifact_repository, permission, is_diagnostics_context)
def _invalid_sku_update(cmd): raise CLIError("Please specify SKU by '--sku SKU' or '--set sku.name=SKU'. Allowed SKUs: {0}".format( get_managed_sku(cmd)))
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
def validate_claim_vm(namespace): if namespace.name is None and namespace.lab_name is None or namespace.resource_group_name is None: raise CLIError( "usage error: --ids IDs | --lab-name LabName --resource-group ResourceGroup --name VMName" " | --lab-name LabName --resource-group ResourceGroup")
def request_data_from_registry(http_method, login_server, path, username, password, result_index=None, json_payload=None, file_payload=None, params=None, retry_times=3, retry_interval=5): if http_method not in ALLOWED_HTTP_METHOD: raise ValueError("Allowed http method: {}".format(ALLOWED_HTTP_METHOD)) if json_payload and file_payload: raise ValueError( "One of json_payload and file_payload can be specified.") if http_method in ['get', 'delete'] and (json_payload or file_payload): raise ValueError( "Empty payload is required for http method: {}".format( http_method)) if http_method in ['patch', 'put'] and not (json_payload or file_payload): raise ValueError( "Non-empty payload is required for http method: {}".format( http_method)) url = 'https://{}{}'.format(login_server, path) headers = get_authorization_header(username, password) for i in range(0, retry_times): errorMessage = None try: if file_payload: with open(file_payload, 'rb') as data_payload: response = requests.request( method=http_method, url=url, headers=headers, params=params, data=data_payload, verify=(not should_disable_connection_verify())) else: response = requests.request( method=http_method, url=url, headers=headers, params=params, json=json_payload, verify=(not should_disable_connection_verify())) log_registry_response(response) if response.status_code == 200: result = response.json( )[result_index] if result_index else response.json() next_link = response.headers[ 'link'] if 'link' in response.headers else None return result, next_link elif response.status_code == 201 or response.status_code == 202: result = None try: result = response.json( )[result_index] if result_index else response.json() except ValueError as e: logger.debug( 'Response is empty or is not a valid json. Exception: %s', str(e)) return result, None elif response.status_code == 204: return None, None elif response.status_code == 401: raise RegistryException( parse_error_message('Authentication required.', response), response.status_code) elif response.status_code == 404: raise RegistryException( parse_error_message('The requested data does not exist.', response), response.status_code) elif response.status_code == 405: raise RegistryException( parse_error_message('This operation is not supported.', response), response.status_code) elif response.status_code == 409: raise RegistryException( parse_error_message( 'Failed to request data due to a conflict.', response), response.status_code) else: raise Exception( parse_error_message( 'Could not {} the requested data.'.format(http_method), response)) except CLIError: raise except Exception as e: # pylint: disable=broad-except errorMessage = str(e) logger.debug('Retrying %s with exception %s', i + 1, errorMessage) time.sleep(retry_interval) raise CLIError(errorMessage)
def _create_package(prefix, repo_path, is_ext, name='test', display_name=None, display_name_plural=None, required_sdk=None, client_name=None, operation_name=None, sdk_property=None, not_preview=False, local_sdk=None): from jinja2 import Environment, PackageLoader if local_sdk and required_sdk: raise CLIError( 'usage error: --local-sdk PATH | --required-sdk NAME==VER') if name.startswith(prefix): name = name[len(prefix):] heading('Create CLI {}: {}{}'.format('Extension' if is_ext else 'Module', prefix, name)) # package_name is how the item should show up in `pip list` package_name = '{}{}'.format(prefix, name.replace( '_', '-')) if not is_ext else name display_name = display_name or name.capitalize() kwargs = { 'name': name, 'mod_path': '{}{}'.format(prefix, name) if is_ext else 'azure.cli.command_modules.{}'.format(name), 'display_name': display_name, 'display_name_plural': display_name_plural or '{}s'.format(display_name), 'loader_name': '{}CommandsLoader'.format(name.capitalize()), 'pkg_name': package_name, 'ext_long_name': '{}{}'.format(prefix, name) if is_ext else None, 'is_ext': is_ext, 'is_preview': not not_preview } new_package_path = os.path.join(repo_path, package_name) if os.path.isdir(new_package_path): if not prompt_y_n("{} '{}' already exists. Overwrite?".format( 'Extension' if is_ext else 'Module', package_name), default='n'): raise CLIError('aborted by user') ext_folder = '{}{}'.format(prefix, name) if is_ext else None # create folder tree if is_ext: _ensure_dir( os.path.join(new_package_path, ext_folder, 'tests', 'latest')) _ensure_dir(os.path.join(new_package_path, ext_folder, 'vendored_sdks')) else: _ensure_dir(os.path.join(new_package_path, 'tests', 'latest')) env = Environment(loader=PackageLoader('azdev', 'mod_templates')) # determine dependencies dependencies = [] if is_ext: dependencies.append("'azure-cli-core'") if required_sdk: _download_vendored_sdk(required_sdk, path=os.path.join(new_package_path, ext_folder, 'vendored_sdks')) elif local_sdk: _copy_vendored_sdk( local_sdk, os.path.join(new_package_path, ext_folder, 'vendored_sdks')) sdk_path = None if any([local_sdk, required_sdk]): sdk_path = '{}{}.vendored_sdks'.format(prefix, package_name) kwargs.update({ 'sdk_path': sdk_path, 'client_name': client_name, 'operation_name': operation_name, 'sdk_property': sdk_property or '{}_name'.format(name) }) else: if required_sdk: version_regex = r'(?P<name>[a-zA-Z-]+)(?P<op>[~<>=]*)(?P<version>[\d.]*)' version_comps = re.compile(version_regex).match(required_sdk) sdk_kwargs = version_comps.groupdict() kwargs.update({ 'sdk_path': sdk_kwargs['name'].replace('-', '.'), 'client_name': client_name, 'operation_name': operation_name, }) dependencies.append("'{}'".format(required_sdk)) else: dependencies.append('# TODO: azure-mgmt-<NAME>==<VERSION>') kwargs.update({'sdk_property': sdk_property or '{}_name'.format(name)}) kwargs['dependencies'] = dependencies # generate code for root level dest_path = new_package_path root_files = [ 'HISTORY.rst', 'MANIFEST.in', 'README.rst', 'setup.cfg', 'setup.py' ] if not is_ext: root_files.append('azure_bdist_wheel.py') _generate_files(env, kwargs, root_files, dest_path) dest_path = dest_path if not is_ext else os.path.join( dest_path, ext_folder) module_files = [{ 'name': '__init__.py', 'template': 'module__init__.py' }, '_client_factory.py', '_help.py', '_params.py', '_validators.py', 'commands.py', 'custom.py'] if is_ext: module_files.append('azext_metadata.json') _generate_files(env, kwargs, module_files, dest_path) dest_path = os.path.join(dest_path, 'tests') blank_init = {'name': '__init__.py', 'template': 'blank__init__.py'} _generate_files(env, kwargs, blank_init, dest_path) dest_path = os.path.join(dest_path, 'latest') test_files = [ blank_init, { 'name': 'test_{}_scenario.py'.format(name), 'template': 'test_service_scenario.py' } ] _generate_files(env, kwargs, test_files, dest_path) if is_ext: result = pip_cmd('install -e {}'.format(new_package_path), "Installing `{}{}`...".format(prefix, name)) if result.error: raise result.error # pylint: disable=raising-bad-type
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