From cbd026a26f4f4c67fb91eb876d47c01bb0649991 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Tue, 15 Mar 2022 12:07:03 -0400 Subject: [PATCH 01/24] Moved dapr arguments to env as a subgroup. --- src/containerapp/azext_containerapp/_help.py | 76 ++++++++++--------- .../azext_containerapp/_params.py | 4 +- .../azext_containerapp/commands.py | 11 ++- 3 files changed, 48 insertions(+), 43 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 228343f5dee..6046fd4c592 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -238,6 +238,47 @@ az containerapp env list -g MyResourceGroup """ +helps['containerapp env dapr-component'] = """ + type: group + short-summary: Commands to manage Container App environment dapr components. +""" + +helps['containerapp env dapr-component list'] = """ + type: command + short-summary: List dapr components for a Containerapp environment. + examples: + - name: List dapr components for a Containerapp environment. + text: | + az containerapp env dapr-component list -g MyResourceGroup --environment-name MyEnvironment +""" + +helps['containerapp env dapr-component show'] = """ + type: command + short-summary: Show the details of a dapr component. + examples: + - name: Show the details of a dapr component. + text: | + az containerapp env dapr-component show -g MyResourceGroup --dapr-component-name MyDaprComponenetName --environment-name MyEnvironment +""" + +helps['containerapp env dapr-component set'] = """ + type: command + short-summary: Create or update a dapr component. + examples: + - name: Create a dapr component. + text: | + az containerapp env dapr-component set -g MyResourceGroup --environment-name MyEnv --yaml MyYAMLPath --name MyDaprName +""" + +helps['containerapp env dapr-component remove'] = """ + type: command + short-summary: Remove a dapr componenet from a Containerapp environment. + examples: + - name: Remove a dapr componenet from a Containerapp environment. + text: | + az containerapp env dapr-component delete -g MyResourceGroup --dapr-component-name MyDaprComponenetName --environment-name MyEnvironment +""" + # Identity Commands helps['containerapp identity'] = """ type: group @@ -500,38 +541,3 @@ az containerapp dapr disable -n MyContainerapp -g MyResourceGroup """ -helps['containerapp dapr list'] = """ - type: command - short-summary: List dapr components for a Containerapp environment. - examples: - - name: List dapr components for a Containerapp environment. - text: | - az containerapp dapr list -g MyResourceGroup --environment-name MyEnvironment -""" - -helps['containerapp dapr show'] = """ - type: command - short-summary: Show the details of a dapr component. - examples: - - name: Show the details of a dapr component. - text: | - az containerapp dapr show -g MyResourceGroup --dapr-component-name MyDaprComponenetName --environment-name MyEnvironment -""" - -helps['containerapp dapr set'] = """ - type: command - short-summary: Create or update a dapr component. - examples: - - name: Create a dapr component. - text: | - az containerapp dapr set -g MyResourceGroup --environment-name MyEnv --yaml MyYAMLPath --name MyDaprName -""" - -helps['containerapp dapr remove'] = """ - type: command - short-summary: Remove a dapr componenet from a Containerapp environment. - examples: - - name: Remove a dapr componenet from a Containerapp environment. - text: | - az containerapp dapr delete -g MyResourceGroup --dapr-component-name MyDaprComponenetName --environment-name MyEnvironment -""" \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index c592ed5363d..d33872732db 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -150,9 +150,9 @@ def load_arguments(self, _): with self.argument_context('containerapp secret delete') as c: c.argument('secret_names', nargs='+', help="A list of secret(s) for the container app. Space-separated secret values names.") - with self.argument_context('containerapp dapr') as c: + with self.argument_context('containerapp env dapr-component') as c: c.argument('dapr_app_id', help="The dapr app id.") c.argument('dapr_app_port', help="The port of your app.") c.argument('dapr_app_protocol', help="Tells Dapr which protocol your application is using. Allowed values: grpc, http.") c.argument('dapr_component_name', help="The dapr component name.") - c.argument('environment_name', help="The dapr component environment name.") + c.argument('environment_name', options_list=['--name','-n'], help="The environment name.") diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 40e422bb532..0fabe223679 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -50,7 +50,6 @@ def load_command_table(self, _): g.custom_command('update', 'update_containerapp', supports_no_wait=True, exception_handler=ex_handler_factory()) g.custom_command('delete', 'delete_containerapp', exception_handler=ex_handler_factory()) - with self.command_group('containerapp env') as g: g.custom_command('show', 'show_managed_environment') g.custom_command('list', 'list_managed_environments') @@ -58,13 +57,17 @@ def load_command_table(self, _): # g.custom_command('update', 'update_managed_environment', supports_no_wait=True, exception_handler=ex_handler_factory()) g.custom_command('delete', 'delete_managed_environment', supports_no_wait=True, confirmation=True, exception_handler=ex_handler_factory()) + with self.command_group('containerapp env dapr-component') as g: + g.custom_command('list', 'list_dapr_components') + g.custom_command('show', 'show_dapr_component') + g.custom_command('set', 'create_or_update_dapr_component') + g.custom_command('remove', 'remove_dapr_component') with self.command_group('containerapp identity') as g: g.custom_command('assign', 'assign_managed_identity', supports_no_wait=True, exception_handler=ex_handler_factory()) g.custom_command('remove', 'remove_managed_identity', supports_no_wait=True, exception_handler=ex_handler_factory()) g.custom_command('show', 'show_managed_identity') - with self.command_group('containerapp github-action') as g: g.custom_command('add', 'create_or_update_github_action', exception_handler=ex_handler_factory()) g.custom_command('show', 'show_github_action', exception_handler=ex_handler_factory()) @@ -105,8 +108,4 @@ def load_command_table(self, _): with self.command_group('containerapp dapr') as g: g.custom_command('enable', 'enable_dapr', exception_handler=ex_handler_factory()) g.custom_command('disable', 'disable_dapr', exception_handler=ex_handler_factory()) - g.custom_command('list', 'list_dapr_components') - g.custom_command('show', 'show_dapr_component') - g.custom_command('set', 'create_or_update_dapr_component') - g.custom_command('remove', 'remove_dapr_component') From e37a07cfe050119051af4b12c3eba499b3ecd6a3 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Tue, 15 Mar 2022 13:52:30 -0400 Subject: [PATCH 02/24] Added env variable options. --- .../azext_containerapp/_params.py | 9 ++- src/containerapp/azext_containerapp/_utils.py | 21 ++++++- src/containerapp/azext_containerapp/custom.py | 57 +++++++++++++++---- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index d33872732db..54f802f9ee2 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -35,11 +35,16 @@ def load_arguments(self, _): c.argument('container_name', type=str, options_list=['--container-name'], help="Name of the container.") c.argument('cpu', type=float, validator=validate_cpu, options_list=['--cpu'], help="Required CPU in cores, e.g. 0.5") c.argument('memory', type=str, validator=validate_memory, options_list=['--memory'], help="Required memory, e.g. 1.0Gi") - c.argument('env_vars', nargs='*', options_list=['--env-vars'], help="A list of environment variable(s) for the container. Space-separated values in 'key=value' format. Empty string to clear existing values") - c.argument('startup_command', nargs='*', options_list=['--command'], help="A list of supported commands on the container that will executed during startup. Space-separated values e.g. \"/bin/queue\" \"mycommand\". Empty string to clear existing values") c.argument('args', nargs='*', options_list=['--args'], help="A list of container startup command argument(s). Space-separated values e.g. \"-c\" \"mycommand\". Empty string to clear existing values") c.argument('revision_suffix', type=str, options_list=['--revision-suffix'], help='User friendly suffix that is appended to the revision name') + # Env vars + with self.argument_context('containerapp', arg_group='Environment variables (Creates new revision)') as c: + c.argument('set_env_vars', options_list=['--set-env-vars, --env-vars'], nargs='*', help="A list of environment variable(s) to add to the container. Space-separated values in 'key=value' format. If stored as a secret, value must start with \'secretref:\' followed by the secret name.") + c.argument('remove_env_vars', nargs='*', help="A list of environment variable(s) to remove from container. Space-separated env var name values.") + c.argument('replace_env_vars', nargs='*', help="A list of environment variable(s) to replace from the container. Space-separated values in 'key=value' format. If stored as a secret, value must start with \'secretref:\' followed by the secret name.") + c.argument('remove_all_env_vars', help="Option to remove all environment variable(s) from the container.") + # Scale with self.argument_context('containerapp', arg_group='Scale (Creates new revision)') as c: c.argument('min_replicas', type=int, options_list=['--min-replicas'], help="The minimum number of replicas.") diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 297ce4904ba..d8cf2cbf761 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -328,7 +328,7 @@ def _remove_secret(containerapp_def, secret_name): containerapp_def["properties"]["configuration"]["secrets"].pop(i) break -def _add_or_update_env_vars(existing_env_vars, new_env_vars): +def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): for new_env_var in new_env_vars: # Check if updating existing env var @@ -336,6 +336,8 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars): for existing_env_var in existing_env_vars: if existing_env_var["name"].lower() == new_env_var["name"].lower(): is_existing = True + if is_add: + logger.warning("Env var {} already exists. Replacing env var value.".format(new_env_var["name"])) if "value" in new_env_var: existing_env_var["value"] = new_env_var["value"] @@ -350,8 +352,25 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars): # If not updating existing env var, add it as a new env var if not is_existing: + if not is_add: + logger.warning("Env var {} does not exist. Adding as new env var.".format(new_env_var["name"])) existing_env_vars.append(new_env_var) +def _remove_env_vars(existing_env_vars, remove_env_vars): + for old_env_var in remove_env_vars: + + # Check if updating existing env var + is_existing = False + for i in range(0, len(existing_env_vars)): + existing_env_var = existing_env_vars[i] + if existing_env_var["name"].lower() == old_env_var.lower(): + is_existing = True + existing_env_vars.pop(i) + break + + # If not updating existing env var, add it as a new env var + if not is_existing: + logger.warning("Env var {} does not exist.".format(old_env_var)) def _add_or_update_tags(containerapp_def, tags): if 'tags' not in containerapp_def: diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 1fcb7c2176b..6454a1c2195 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -45,7 +45,7 @@ _object_to_dict, _add_or_update_secrets, _remove_additional_attributes, _remove_readonly_attributes, _add_or_update_env_vars, _add_or_update_tags, update_nested_dictionary, _update_traffic_weights, _get_app_from_revision, raise_missing_token_suggestion, _infer_acr_credentials, _remove_registry_secret, _remove_secret, - _ensure_identity_resource_id, _remove_dapr_readonly_attributes, _registry_exists) + _ensure_identity_resource_id, _remove_dapr_readonly_attributes, _registry_exists, _remove_env_vars) logger = get_logger(__name__) @@ -497,7 +497,10 @@ def update_containerapp(cmd, max_replicas=None, revisions_mode=None, secrets=None, - env_vars=None, + set_env_vars=None, + remove_env_vars=None, + replace_env_vars=None, + remove_all_env_vars=False, cpu=None, memory=None, registry_server=None, @@ -512,7 +515,7 @@ def update_containerapp(cmd, if yaml: if image or min_replicas or max_replicas or\ - revisions_mode or secrets or env_vars or cpu or memory or registry_server or\ + revisions_mode or secrets or set_env_vars or remove_env_vars or replace_env_vars or remove_all_env_vars or cpu or memory or registry_server or\ registry_user or registry_pass or\ startup_command or args or tags: logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') @@ -539,7 +542,7 @@ def update_containerapp(cmd, update_map['secrets'] = secrets is not None update_map['registries'] = registry_server or registry_user or registry_pass update_map['scale'] = min_replicas or max_replicas - update_map['container'] = image or container_name or env_vars is not None or cpu or memory or startup_command is not None or args is not None + update_map['container'] = image or container_name or set_env_vars is not None or remove_env_vars is not None or replace_env_vars is not None or remove_all_env_vars or cpu or memory or startup_command is not None or args is not None update_map['configuration'] = update_map['secrets'] or update_map['registries'] or revisions_mode is not None if tags: @@ -564,13 +567,28 @@ def update_containerapp(cmd, if image is not None: c["image"] = image - if env_vars is not None: - if isinstance(env_vars, list) and not env_vars: + + if set_env_vars is not None: + if "env" not in c or not c["env"]: c["env"] = [] - else: - if "env" not in c or not c["env"]: - c["env"] = [] - _add_or_update_env_vars(c["env"], parse_env_var_flags(env_vars)) + # env vars + _add_or_update_env_vars(c["env"], parse_env_var_flags(set_env_vars), is_add=True) + + if replace_env_vars is not None: + if "env" not in c or not c["env"]: + c["env"] = [] + # env vars + _add_or_update_env_vars(c["env"], parse_env_var_flags(replace_env_vars)) + + if remove_env_vars is not None: + if "env" not in c or not c["env"]: + c["env"] = [] + # env vars + _remove_env_vars(c["env"], remove_env_vars) + + if remove_all_env_vars: + c["env"] = [] + if startup_command is not None: if isinstance(startup_command, list) and not startup_command: c["command"] = None @@ -607,8 +625,23 @@ def update_containerapp(cmd, container_def = ContainerModel container_def["name"] = container_name container_def["image"] = image - if env_vars is not None: - container_def["env"] = parse_env_var_flags(env_vars) + container_def["env"] = [] + + if set_env_vars is not None: + # env vars + _add_or_update_env_vars(container_def["env"], parse_env_var_flags(set_env_vars), is_add=True) + + if replace_env_vars is not None: + # env vars + _add_or_update_env_vars(container_def["env"], parse_env_var_flags(replace_env_vars)) + + if remove_env_vars is not None: + # env vars + _remove_env_vars(container_def["env"], remove_env_vars) + + if remove_all_env_vars: + container_def["env"] = [] + if startup_command is not None: if isinstance(startup_command, list) and not startup_command: container_def["command"] = None From b582910b585d5594ec9c0d320506d394bf7ee342 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Tue, 15 Mar 2022 14:04:41 -0400 Subject: [PATCH 03/24] Changed revision mode set to revision set-mode. --- src/containerapp/azext_containerapp/_help.py | 13 ++----------- src/containerapp/azext_containerapp/_params.py | 3 +++ src/containerapp/azext_containerapp/commands.py | 4 +--- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 6046fd4c592..680ea1fe63c 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -145,13 +145,13 @@ az containerapp revision deactivate -n MyContainerapp -g MyResourceGroup --revision-name MyContainerappRevision """ -helps['containerapp revision mode set'] = """ +helps['containerapp revision set-mode'] = """ type: command short-summary: Set the revision mode of a container app. examples: - name: Set a container app to single revision mode. text: | - az containerapp revision mode set-n MyContainerapp -g MyResourceGroup --mode Single + az containerapp revision set-mode -n MyContainerapp -g MyResourceGroup --mode Single """ helps['containerapp revision copy'] = """ @@ -164,15 +164,6 @@ --from-revision PreviousRevisionName --cpu 0.75 --memory 1.5Gi """ -helps['containerapp revision mode set'] = """ - type: command - short-summary: Set the revision mode of a Containerapp. - examples: - - name: Set the revision mode of a Containerapp. - text: | - az containerapp revision set --mode Single -n MyContainerapp -g MyResourceGroup -""" - helps['containerapp revision copy'] = """ type: command short-summary: Create a revision based on a previous revision. diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 54f802f9ee2..0479dc5972d 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -161,3 +161,6 @@ def load_arguments(self, _): c.argument('dapr_app_protocol', help="Tells Dapr which protocol your application is using. Allowed values: grpc, http.") c.argument('dapr_component_name', help="The dapr component name.") c.argument('environment_name', options_list=['--name','-n'], help="The environment name.") + + with self.argument_context('containerapp revision set-mode') as c: + c.argument('mode', arg_type=get_enum_type(['single', 'multiple']), help="The active revisions mode for the container app.") diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 0fabe223679..c1047eb1434 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -80,9 +80,7 @@ def load_command_table(self, _): g.custom_command('restart', 'restart_revision') g.custom_command('show', 'show_revision', table_transformer=transform_revision_output, exception_handler=ex_handler_factory()) g.custom_command('copy', 'copy_revision', exception_handler=ex_handler_factory()) - - with self.command_group('containerapp revision mode') as g: - g.custom_command('set', 'set_revision_mode', exception_handler=ex_handler_factory()) + g.custom_command('set-mode', 'set_revision_mode', exception_handler=ex_handler_factory()) with self.command_group('containerapp ingress') as g: g.custom_command('enable', 'enable_ingress', exception_handler=ex_handler_factory()) From 7b33faa34859f1cdbab9121bcae97bbaa010f083 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Tue, 15 Mar 2022 14:39:06 -0400 Subject: [PATCH 04/24] Added env var options to revision copy. --- src/containerapp/azext_containerapp/custom.py | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 6454a1c2195..3961d6dc326 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -1326,7 +1326,10 @@ def copy_revision(cmd, container_name=None, min_replicas=None, max_replicas=None, - env_vars=None, + set_env_vars=None, + replace_env_vars=None, + remove_env_vars=None, + remove_all_env_vars=False, cpu=None, memory=None, revision_suffix=None, @@ -1341,7 +1344,8 @@ def copy_revision(cmd, if yaml: if image or min_replicas or max_replicas or\ - env_vars or cpu or memory or \ + set_env_vars or replace_env_vars or remove_env_vars or \ + remove_all_env_vars or cpu or memory or \ startup_command or args or tags: logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') return update_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, from_revision=from_revision, no_wait=no_wait) @@ -1373,7 +1377,7 @@ def copy_revision(cmd, update_map = {} update_map['scale'] = min_replicas or max_replicas - update_map['container'] = image or container_name or env_vars or cpu or memory or startup_command is not None or args is not None + update_map['container'] = image or container_name or set_env_vars or replace_env_vars or remove_env_vars or remove_all_env_vars or cpu or memory or startup_command is not None or args is not None if tags: _add_or_update_tags(containerapp_def, tags) @@ -1397,10 +1401,28 @@ def copy_revision(cmd, if image is not None: c["image"] = image - if env_vars is not None: + + if set_env_vars is not None: + if "env" not in c or not c["env"]: + c["env"] = [] + # env vars + _add_or_update_env_vars(c["env"], parse_env_var_flags(set_env_vars), is_add=True) + + if replace_env_vars is not None: + if "env" not in c or not c["env"]: + c["env"] = [] + # env vars + _add_or_update_env_vars(c["env"], parse_env_var_flags(replace_env_vars)) + + if remove_env_vars is not None: if "env" not in c or not c["env"]: c["env"] = [] - _add_or_update_env_vars(c["env"], parse_env_var_flags(env_vars)) + # env vars + _remove_env_vars(c["env"], remove_env_vars) + + if remove_all_env_vars: + c["env"] = [] + if startup_command is not None: if isinstance(startup_command, list) and not startup_command: c["command"] = None @@ -1437,8 +1459,22 @@ def copy_revision(cmd, container_def = ContainerModel container_def["name"] = container_name container_def["image"] = image - if env_vars is not None: - container_def["env"] = parse_env_var_flags(env_vars) + + if set_env_vars is not None: + # env vars + _add_or_update_env_vars(container_def["env"], parse_env_var_flags(set_env_vars), is_add=True) + + if replace_env_vars is not None: + # env vars + _add_or_update_env_vars(container_def["env"], parse_env_var_flags(replace_env_vars)) + + if remove_env_vars is not None: + # env vars + _remove_env_vars(container_def["env"], remove_env_vars) + + if remove_all_env_vars: + container_def["env"] = [] + if startup_command is not None: if isinstance(startup_command, list) and not startup_command: container_def["command"] = None From 35e6ca214870656497d8da1824f6bb30f867e1e4 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Tue, 15 Mar 2022 15:13:12 -0400 Subject: [PATCH 05/24] Fixed revision copy bug related to env secret refs. --- src/containerapp/azext_containerapp/_utils.py | 6 +++++ src/containerapp/azext_containerapp/custom.py | 23 ++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index d8cf2cbf761..f28552deffa 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -116,6 +116,12 @@ def parse_secret_flags(secret_list): return secret_var_def +def _update_revision_env_secretrefs(containers, name): + for container in containers: + if "env" in container: + for var in container["env"]: + if "secretRef" in var: + var["secretRef"] = var["secretRef"].replace("{}-".format(name), "") def store_as_secret_and_return_secret_ref(secrets_list, registry_user, registry_server, registry_pass, update_existing_secret=False): if registry_pass.startswith("secretref:"): diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 3961d6dc326..1f1364b1230 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -45,7 +45,7 @@ _object_to_dict, _add_or_update_secrets, _remove_additional_attributes, _remove_readonly_attributes, _add_or_update_env_vars, _add_or_update_tags, update_nested_dictionary, _update_traffic_weights, _get_app_from_revision, raise_missing_token_suggestion, _infer_acr_credentials, _remove_registry_secret, _remove_secret, - _ensure_identity_resource_id, _remove_dapr_readonly_attributes, _registry_exists, _remove_env_vars) + _ensure_identity_resource_id, _remove_dapr_readonly_attributes, _registry_exists, _remove_env_vars, _update_revision_env_secretrefs) logger = get_logger(__name__) @@ -126,8 +126,10 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) except CLIError as e: handle_raw_exception(e) + + _update_revision_env_secretrefs(r["properties"]["template"]["containers"], name) current_containerapp_def["properties"]["template"] = r["properties"]["template"] - + # Deserialize the yaml into a ContainerApp object. Need this since we're not using SDK try: deserializer = create_deserializer() @@ -1339,9 +1341,6 @@ def copy_revision(cmd, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") - if not from_revision: - from_revision = containerapp_def["properties"]["latestRevisionName"] - if yaml: if image or min_replicas or max_replicas or\ set_env_vars or replace_env_vars or remove_env_vars or \ @@ -1359,13 +1358,15 @@ def copy_revision(cmd, if not containerapp_def: raise CLIError("The containerapp '{}' does not exist".format(name)) - try: - r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) - except CLIError as e: - # Error handle the case where revision not found? - handle_raw_exception(e) + if from_revision: + try: + r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) + except CLIError as e: + # Error handle the case where revision not found? + handle_raw_exception(e) - containerapp_def["properties"]["template"] = r["properties"]["template"] + _update_revision_env_secretrefs(r["properties"]["template"]["containers"], name) + containerapp_def["properties"]["template"] = r["properties"]["template"] # Doing this while API has bug. If env var is an empty string, API doesn't return "value" even though the "value" should be an empty string if "properties" in containerapp_def and "template" in containerapp_def["properties"] and "containers" in containerapp_def["properties"]["template"]: From 43e98f58ae9b8de222911bdc119e3fc28e291b23 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Tue, 15 Mar 2022 17:15:50 -0400 Subject: [PATCH 06/24] Changed registry and secret delete to remove. Added registry param helps. Removed replica from table output and added trafficWeight. --- src/containerapp/azext_containerapp/_help.py | 14 +++++++------- src/containerapp/azext_containerapp/_params.py | 7 ++++++- src/containerapp/azext_containerapp/commands.py | 6 +++--- src/containerapp/azext_containerapp/custom.py | 4 ++-- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 680ea1fe63c..a8cb4648295 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -267,7 +267,7 @@ examples: - name: Remove a dapr componenet from a Containerapp environment. text: | - az containerapp env dapr-component delete -g MyResourceGroup --dapr-component-name MyDaprComponenetName --environment-name MyEnvironment + az containerapp env dapr-component remove -g MyResourceGroup --dapr-component-name MyDaprComponenetName --environment-name MyEnvironment """ # Identity Commands @@ -406,13 +406,13 @@ """ -helps['containerapp registry delete'] = """ +helps['containerapp registry remove'] = """ type: command short-summary: Remove a container registry's details. examples: - name: Remove a registry from a Containerapp. text: | - az containerapp registry delete -n MyContainerapp -g MyResourceGroup --server MyContainerappRegistry.azurecr.io + az containerapp registry remove -n MyContainerapp -g MyResourceGroup --server MyContainerappRegistry.azurecr.io """ # Secret Commands @@ -439,13 +439,13 @@ az containerapp secret list -n MyContainerapp -g MyResourceGroup """ -helps['containerapp secret delete'] = """ +helps['containerapp secret remove'] = """ type: command - short-summary: Delete secrets from a container app. + short-summary: Remove secrets from a container app. examples: - - name: Delete secrets from a container app. + - name: Remove secrets from a container app. text: | - az containerapp secret delete -n MyContainerapp -g MyResourceGroup --secret-names MySecret MySecret2 + az containerapp secret remove -n MyContainerapp -g MyResourceGroup --secret-names MySecret MySecret2 """ helps['containerapp secret set'] = """ diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 0479dc5972d..eb89812ae43 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -152,7 +152,7 @@ def load_arguments(self, _): with self.argument_context('containerapp secret set') as c: c.argument('secrets', nargs='+', options_list=['--secrets', '-s'], help="A list of secret(s) for the container app. Space-separated values in 'key=value' format.") - with self.argument_context('containerapp secret delete') as c: + with self.argument_context('containerapp secret remove') as c: c.argument('secret_names', nargs='+', help="A list of secret(s) for the container app. Space-separated secret values names.") with self.argument_context('containerapp env dapr-component') as c: @@ -164,3 +164,8 @@ def load_arguments(self, _): with self.argument_context('containerapp revision set-mode') as c: c.argument('mode', arg_type=get_enum_type(['single', 'multiple']), help="The active revisions mode for the container app.") + + with self.argument_context('containerapp registry') as c: + c.argument('server', help="The container registry server, e.g. myregistry.azurecr.io") + c.argument('username', help='The username of the registry. If using Azure Container Registry, we will try to infer the credentials if not supplied') + c.argument('password', help='The password of the registry. If using Azure Container Registry, we will try to infer the credentials if not supplied') diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index c1047eb1434..9fd58c7575c 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -26,7 +26,7 @@ def transform_containerapp_list_output(apps): def transform_revision_output(rev): - props = ['name', 'replicas', 'active', 'createdTime'] + props = ['name', 'active', 'createdTime', 'trafficWeight'] result = {k: rev['properties'][k] for k in rev['properties'] if k in props} if 'name' in rev: @@ -95,12 +95,12 @@ def load_command_table(self, _): g.custom_command('set', 'set_registry', exception_handler=ex_handler_factory()) g.custom_command('show', 'show_registry') g.custom_command('list', 'list_registry') - g.custom_command('delete', 'delete_registry', exception_handler=ex_handler_factory()) + g.custom_command('remove', 'remove_registry', exception_handler=ex_handler_factory()) with self.command_group('containerapp secret') as g: g.custom_command('list', 'list_secrets') g.custom_command('show', 'show_secret') - g.custom_command('delete', 'delete_secrets', exception_handler=ex_handler_factory()) + g.custom_command('remove', 'remove_secrets', exception_handler=ex_handler_factory()) g.custom_command('set', 'set_secrets', exception_handler=ex_handler_factory()) with self.command_group('containerapp dapr') as g: diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 1f1364b1230..400625ddf5f 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -1776,7 +1776,7 @@ def set_registry(cmd, name, resource_group_name, server, username=None, password except Exception as e: handle_raw_exception(e) -def delete_registry(cmd, name, resource_group_name, server, no_wait=False): +def remove_registry(cmd, name, resource_group_name, server, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None @@ -1859,7 +1859,7 @@ def show_secret(cmd, name, resource_group_name, secret_name): return secret raise CLIError("The containerapp {} does not have a secret assigned with name {}.".format(name, secret_name)) -def delete_secrets(cmd, name, resource_group_name, secret_names, no_wait = False): +def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait = False): _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None From 7c90ee061a51ecb4d9b84c98c8c871df8cc4dbf1 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Wed, 16 Mar 2022 12:01:42 -0400 Subject: [PATCH 07/24] Updating warning text. --- src/containerapp/azext_containerapp/_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index f28552deffa..7ac4acb5306 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -343,7 +343,7 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): if existing_env_var["name"].lower() == new_env_var["name"].lower(): is_existing = True if is_add: - logger.warning("Env var {} already exists. Replacing env var value.".format(new_env_var["name"])) + logger.warning("Env var {} already exists. Replacing environment variable value.".format(new_env_var["name"])) if "value" in new_env_var: existing_env_var["value"] = new_env_var["value"] @@ -359,7 +359,7 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): # If not updating existing env var, add it as a new env var if not is_existing: if not is_add: - logger.warning("Env var {} does not exist. Adding as new env var.".format(new_env_var["name"])) + logger.warning("Env var {} does not exist. Adding as new environment variable.".format(new_env_var["name"])) existing_env_vars.append(new_env_var) def _remove_env_vars(existing_env_vars, remove_env_vars): @@ -376,7 +376,7 @@ def _remove_env_vars(existing_env_vars, remove_env_vars): # If not updating existing env var, add it as a new env var if not is_existing: - logger.warning("Env var {} does not exist.".format(old_env_var)) + logger.warning("Environment variable {} does not exist.".format(old_env_var)) def _add_or_update_tags(containerapp_def, tags): if 'tags' not in containerapp_def: From 80246822cbd5396d19145a1f953b6a57469763ef Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Wed, 16 Mar 2022 12:07:00 -0400 Subject: [PATCH 08/24] Updated warning text once more. --- src/containerapp/azext_containerapp/_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 7ac4acb5306..1c5a10e5d29 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -343,7 +343,7 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): if existing_env_var["name"].lower() == new_env_var["name"].lower(): is_existing = True if is_add: - logger.warning("Env var {} already exists. Replacing environment variable value.".format(new_env_var["name"])) + logger.warning("Environment variable {} already exists. Replacing environment variable value.".format(new_env_var["name"])) if "value" in new_env_var: existing_env_var["value"] = new_env_var["value"] @@ -359,7 +359,7 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): # If not updating existing env var, add it as a new env var if not is_existing: if not is_add: - logger.warning("Env var {} does not exist. Adding as new environment variable.".format(new_env_var["name"])) + logger.warning("Environment variable {} does not exist. Adding as new environment variable.".format(new_env_var["name"])) existing_env_vars.append(new_env_var) def _remove_env_vars(existing_env_vars, remove_env_vars): From 3544c8cbb43f05813e422104c1b6f72ba3df8b33 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Wed, 16 Mar 2022 14:04:12 -0400 Subject: [PATCH 09/24] Made name optional for revision copy if from-revision flag is passed. --- src/containerapp/azext_containerapp/custom.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 400625ddf5f..961fbd500f0 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -1319,10 +1319,10 @@ def deactivate_revision(cmd, resource_group_name, revision_name, name=None): handle_raw_exception(e) def copy_revision(cmd, - name, resource_group_name, from_revision=None, #label=None, + name=None, yaml=None, image=None, container_name=None, @@ -1341,6 +1341,12 @@ def copy_revision(cmd, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") + if not name and not from_revision: + raise RequiredArgumentMissingError('Usage error: --name is required if not using --from-revision.') + + if not name: + name = _get_app_from_revision(from_revision) + if yaml: if image or min_replicas or max_replicas or\ set_env_vars or replace_env_vars or remove_env_vars or \ From 0f7d7f2429d50be38e4bfc55fe9f05ca5918d1fb Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Wed, 16 Mar 2022 17:24:57 -0400 Subject: [PATCH 10/24] Fixed whitespace style issues. --- .../azext_containerapp/__init__.py | 1 + .../azext_containerapp/_client_factory.py | 2 +- .../azext_containerapp/_github_oauth.py | 5 +- .../azext_containerapp/_models.py | 7 +- .../azext_containerapp/_params.py | 11 +- .../azext_containerapp/_sdk_models.py | 5 +- src/containerapp/azext_containerapp/_utils.py | 44 +++---- .../azext_containerapp/_validators.py | 9 +- .../azext_containerapp/commands.py | 11 +- src/containerapp/azext_containerapp/custom.py | 123 +++++++++--------- 10 files changed, 109 insertions(+), 109 deletions(-) diff --git a/src/containerapp/azext_containerapp/__init__.py b/src/containerapp/azext_containerapp/__init__.py index f772766731c..ad67435e53e 100644 --- a/src/containerapp/azext_containerapp/__init__.py +++ b/src/containerapp/azext_containerapp/__init__.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=super-with-arguments from azure.cli.core import AzCommandsLoader diff --git a/src/containerapp/azext_containerapp/_client_factory.py b/src/containerapp/azext_containerapp/_client_factory.py index f998486c63e..0d0f61ad5a6 100644 --- a/src/containerapp/azext_containerapp/_client_factory.py +++ b/src/containerapp/azext_containerapp/_client_factory.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long, consider-using-f-string from azure.cli.core.commands.client_factory import get_mgmt_service_client from azure.cli.core.profiles import ResourceType @@ -13,7 +14,6 @@ def ex_handler_factory(no_throw=False): def _polish_bad_errors(ex): import json - from knack.util import CLIError try: content = json.loads(ex.response.content) if 'message' in content: diff --git a/src/containerapp/azext_containerapp/_github_oauth.py b/src/containerapp/azext_containerapp/_github_oauth.py index 3df73a6b1aa..fa058863369 100644 --- a/src/containerapp/azext_containerapp/_github_oauth.py +++ b/src/containerapp/azext_containerapp/_github_oauth.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=consider-using-f-string from azure.cli.core.azclierror import (ValidationError, CLIInternalError, UnclassifiedUserFault) from knack.log import get_logger @@ -81,6 +82,6 @@ def get_github_access_token(cmd, scope_list=None): # pylint: disable=unused-arg return parsed_confirmation_response['access_token'][0] except Exception as e: raise CLIInternalError( - 'Error: {}. Please try again, or retrieve personal access token from the Github website'.format(e)) + 'Error: {}. Please try again, or retrieve personal access token from the Github website'.format(e)) from e - raise UnclassifiedUserFault('Activation did not happen in time. Please try again') \ No newline at end of file + raise UnclassifiedUserFault('Activation did not happen in time. Please try again') diff --git a/src/containerapp/azext_containerapp/_models.py b/src/containerapp/azext_containerapp/_models.py index b356adaa2a8..3f6b5c60c18 100644 --- a/src/containerapp/azext_containerapp/_models.py +++ b/src/containerapp/azext_containerapp/_models.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long, too-many-statements, super-with-arguments VnetConfiguration = { "infrastructureSubnetId": None, @@ -184,7 +185,7 @@ "properties": { "componentType": None, #String "version": None, - "ignoreErrors": None, + "ignoreErrors": None, "initTimeout": None, "secrets": None, "metadata": None, @@ -200,8 +201,8 @@ SourceControl = { "properties": { - "repoUrl": None, - "branch": None, + "repoUrl": None, + "branch": None, "githubActionConfiguration": None # [GitHubActionConfiguration] } diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 1d3e3b6dc27..a85e0d375fb 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -2,14 +2,14 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -# pylint: disable=line-too-long +# pylint: disable=line-too-long, too-many-statements, consider-using-f-string from knack.arguments import CLIArgumentType from azure.cli.core.commands.parameters import (resource_group_name_type, get_location_type, - get_resource_name_completion_list, file_type, + file_type, get_three_state_flag, get_enum_type, tags_type) -from azure.cli.core.commands.validators import get_default_location_from_resource_group +#from azure.cli.core.commands.validators import get_default_location_from_resource_group from ._validators import (validate_memory, validate_cpu, validate_managed_env_name_or_id, validate_registry_server, validate_registry_user, validate_registry_pass, validate_target_port, validate_ingress) @@ -73,7 +73,7 @@ def load_arguments(self, _): c.argument('ingress', validator=validate_ingress, options_list=['--ingress'], default=None, arg_type=get_enum_type(['internal', 'external']), help="The ingress type.") c.argument('target_port', type=int, validator=validate_target_port, options_list=['--target-port'], help="The application port used for ingress traffic.") c.argument('transport', arg_type=get_enum_type(['auto', 'http', 'http2']), help="The transport protocol used for ingress traffic.") - + with self.argument_context('containerapp create') as c: c.argument('assign_identity', nargs='+', help="Space-separated identities. Use '[system]' to refer to the system assigned identity.") c.argument('traffic_weights', nargs='*', options_list=['--traffic-weight'], help="A list of revision weight(s) for the container app. Space-separated values in 'revision_name=weight' format. For latest revision, use 'latest=weight'") @@ -135,7 +135,7 @@ def load_arguments(self, _): with self.argument_context('containerapp github-action delete') as c: c.argument('token', help='A Personal Access Token with write access to the specified repository. For more information: https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line') c.argument('login_with_github', help='Interactively log in with Github to retrieve the Personal Access Token') - + with self.argument_context('containerapp revision') as c: c.argument('revision_name', options_list=['--revision'], type=str, help='Name of the revision.') @@ -171,4 +171,3 @@ def load_arguments(self, _): c.argument('server', help="The container registry server, e.g. myregistry.azurecr.io") c.argument('username', help='The username of the registry. If using Azure Container Registry, we will try to infer the credentials if not supplied') c.argument('password', help='The password of the registry. If using Azure Container Registry, we will try to infer the credentials if not supplied') - diff --git a/src/containerapp/azext_containerapp/_sdk_models.py b/src/containerapp/azext_containerapp/_sdk_models.py index 9472034039d..473bf5e19f1 100644 --- a/src/containerapp/azext_containerapp/_sdk_models.py +++ b/src/containerapp/azext_containerapp/_sdk_models.py @@ -4,6 +4,7 @@ # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- +# pylint: disable=line-too-long, super-with-arguments, too-many-instance-attributes from msrest.serialization import Model from msrest.exceptions import HttpOperationError @@ -196,8 +197,8 @@ class ProxyResource(Resource): 'system_data': {'key': 'systemData', 'type': 'SystemData'}, } - def __init__(self, **kwargs): - super(ProxyResource, self).__init__(**kwargs) + # def __init__(self, **kwargs): + # super(ProxyResource, self).__init__(**kwargs) class AuthConfig(ProxyResource): diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 1c5a10e5d29..dea73793b64 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -139,27 +139,27 @@ def store_as_secret_and_return_secret_ref(secrets_list, registry_user, registry_ return registry_pass else: # If user passed in registry password - if (urlparse(registry_server).hostname is not None): - registry_secret_name = "{server}-{user}".format(server=urlparse(registry_server).hostname.replace('.', ''), user=registry_user.lower()) - else: - registry_secret_name = "{server}-{user}".format(server=registry_server.replace('.', ''), user=registry_user.lower()) - - for secret in secrets_list: - if secret['name'].lower() == registry_secret_name.lower(): - if secret['value'].lower() != registry_pass.lower(): - if update_existing_secret: - secret['value'] = registry_pass - else: - raise ValidationError('Found secret with name \"{}\" but value does not equal the supplied registry password.'.format(registry_secret_name)) - return registry_secret_name - - logger.warning('Adding registry password as a secret with name \"{}\"'.format(registry_secret_name)) - secrets_list.append({ - "name": registry_secret_name, - "value": registry_pass - }) + if (urlparse(registry_server).hostname is not None): + registry_secret_name = "{server}-{user}".format(server=urlparse(registry_server).hostname.replace('.', ''), user=registry_user.lower()) + else: + registry_secret_name = "{server}-{user}".format(server=registry_server.replace('.', ''), user=registry_user.lower()) + + for secret in secrets_list: + if secret['name'].lower() == registry_secret_name.lower(): + if secret['value'].lower() != registry_pass.lower(): + if update_existing_secret: + secret['value'] = registry_pass + else: + raise ValidationError('Found secret with name \"{}\" but value does not equal the supplied registry password.'.format(registry_secret_name)) + return registry_secret_name + + logger.warning('Adding registry password as a secret with name \"{}\"'.format(registry_secret_name)) + secrets_list.append({ + "name": registry_secret_name, + "value": registry_pass + }) - return registry_secret_name + return registry_secret_name def parse_list_of_strings(comma_separated_string): @@ -312,7 +312,7 @@ def _add_or_update_secrets(containerapp_def, add_secrets): is_existing = True existing_secret["value"] = new_secret["value"] break - + if not is_existing: containerapp_def["properties"]["configuration"]["secrets"].append(new_secret) @@ -321,7 +321,7 @@ def _remove_registry_secret(containerapp_def, server, username): registry_secret_name = "{server}-{user}".format(server=urlparse(server).hostname.replace('.', ''), user=username.lower()) else: registry_secret_name = "{server}-{user}".format(server=server.replace('.', ''), user=username.lower()) - + _remove_secret(containerapp_def, secret_name=registry_secret_name) def _remove_secret(containerapp_def, secret_name): diff --git a/src/containerapp/azext_containerapp/_validators.py b/src/containerapp/azext_containerapp/_validators.py index 916d9eb5b57..090c355d7af 100644 --- a/src/containerapp/azext_containerapp/_validators.py +++ b/src/containerapp/azext_containerapp/_validators.py @@ -2,10 +2,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long -from unicodedata import name -from azure.cli.core.azclierror import (ValidationError, RequiredArgumentMissingError) - +from azure.cli.core.azclierror import (ValidationError) def _is_number(s): try: @@ -31,8 +30,8 @@ def validate_cpu(namespace): cpu = namespace.cpu try: float(cpu) - except ValueError: - raise ValidationError("Usage error: --cpu must be a number eg. \"0.5\"") + except ValueError as e: + raise ValidationError("Usage error: --cpu must be a number eg. \"0.5\"") from e def validate_managed_env_name_or_id(cmd, namespace): from azure.cli.core.commands.client_factory import get_subscription_id diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 9fd58c7575c..5f75b937bbe 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -3,9 +3,9 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -# pylint: disable=line-too-long -from azure.cli.core.commands import CliCommandType -from msrestazure.tools import is_valid_resource_id, parse_resource_id +# pylint: disable=line-too-long, too-many-statements, bare-except +# from azure.cli.core.commands import CliCommandType +# from msrestazure.tools import is_valid_resource_id, parse_resource_id from azext_containerapp._client_factory import ex_handler_factory @@ -15,7 +15,7 @@ def transform_containerapp_output(app): try: result['fqdn'] = app['properties']['configuration']['ingress']['fqdn'] - except Exception: + except: result['fqdn'] = None return result @@ -86,7 +86,7 @@ def load_command_table(self, _): g.custom_command('enable', 'enable_ingress', exception_handler=ex_handler_factory()) g.custom_command('disable', 'disable_ingress', exception_handler=ex_handler_factory()) g.custom_command('show', 'show_ingress') - + with self.command_group('containerapp ingress traffic') as g: g.custom_command('set', 'set_ingress_traffic', exception_handler=ex_handler_factory()) g.custom_command('show', 'show_ingress_traffic') @@ -106,4 +106,3 @@ def load_command_table(self, _): with self.command_group('containerapp dapr') as g: g.custom_command('enable', 'enable_dapr', exception_handler=ex_handler_factory()) g.custom_command('disable', 'disable_dapr', exception_handler=ex_handler_factory()) - diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 961fbd500f0..f123b3f95a6 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -33,10 +33,10 @@ Dapr as DaprModel, ContainerResources as ContainerResourcesModel, Scale as ScaleModel, - Container as ContainerModel, - GitHubActionConfiguration, - RegistryInfo as RegistryInfoModel, - AzureCredentials as AzureCredentialsModel, + Container as ContainerModel, + GitHubActionConfiguration, + RegistryInfo as RegistryInfoModel, + AzureCredentials as AzureCredentialsModel, SourceControl as SourceControlModel, ManagedServiceIdentity as ManagedServiceIdentityModel) from ._utils import (_validate_subscription_registered, _get_location_from_resource_group, _ensure_location_allowed, @@ -44,7 +44,7 @@ _generate_log_analytics_if_not_provided, _get_existing_secrets, _convert_object_from_snake_to_camel_case, _object_to_dict, _add_or_update_secrets, _remove_additional_attributes, _remove_readonly_attributes, _add_or_update_env_vars, _add_or_update_tags, update_nested_dictionary, _update_traffic_weights, - _get_app_from_revision, raise_missing_token_suggestion, _infer_acr_credentials, _remove_registry_secret, _remove_secret, + _get_app_from_revision, raise_missing_token_suggestion, _infer_acr_credentials, _remove_registry_secret, _remove_secret, _ensure_identity_resource_id, _remove_dapr_readonly_attributes, _registry_exists, _remove_env_vars, _update_revision_env_secretrefs) logger = get_logger(__name__) @@ -126,10 +126,10 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) except CLIError as e: handle_raw_exception(e) - + _update_revision_env_secretrefs(r["properties"]["template"]["containers"], name) current_containerapp_def["properties"]["template"] = r["properties"]["template"] - + # Deserialize the yaml into a ContainerApp object. Need this since we're not using SDK try: deserializer = create_deserializer() @@ -416,19 +416,19 @@ def create_containerapp(cmd, if assign_system_identity and assign_user_identities: identity_def["type"] = "SystemAssigned, UserAssigned" - elif assign_system_identity: + elif assign_system_identity: identity_def["type"] = "SystemAssigned" - elif assign_user_identities: + elif assign_user_identities: identity_def["type"] = "UserAssigned" if assign_user_identities: identity_def["userAssignedIdentities"] = {} subscription_id = get_subscription_id(cmd.cli_ctx) - + for r in assign_user_identities: r = _ensure_identity_resource_id(subscription_id, resource_group_name, r) identity_def["userAssignedIdentities"][r] = {} - + scale_def = None if min_replicas is not None or max_replicas is not None: scale_def = ScaleModel @@ -925,9 +925,9 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ if not identities: identities = ['[system]'] logger.warning('Identities not specified. Assigning managed system identity.') - + identities = [x.lower() for x in identities] - assign_system_identity = '[system]' in identities + assign_system_identity = '[system]' in identities assign_user_identities = [x for x in identities if x != '[system]'] containerapp_def = None @@ -944,7 +944,7 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) # If identity not returned - try: + try: containerapp_def["identity"] containerapp_def["identity"]["type"] except: @@ -956,30 +956,30 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ # Assign correct type try: - if containerapp_def["identity"]["type"] != "None": + if containerapp_def["identity"]["type"] != "None": if containerapp_def["identity"]["type"] == "SystemAssigned" and assign_user_identities: containerapp_def["identity"]["type"] = "SystemAssigned,UserAssigned" if containerapp_def["identity"]["type"] == "UserAssigned" and assign_system_identity: containerapp_def["identity"]["type"] = "SystemAssigned,UserAssigned" - else: + else: if assign_system_identity and assign_user_identities: containerapp_def["identity"]["type"] = "SystemAssigned,UserAssigned" - elif assign_system_identity: + elif assign_system_identity: containerapp_def["identity"]["type"] = "SystemAssigned" - elif assign_user_identities: + elif assign_user_identities: containerapp_def["identity"]["type"] = "UserAssigned" - except: - # Always returns "type": "None" when CA has no previous identities + except: + # Always returns "type": "None" when CA has no previous identities pass - + if assign_user_identities: - try: + try: containerapp_def["identity"]["userAssignedIdentities"] - except: + except: containerapp_def["identity"]["userAssignedIdentities"] = {} subscription_id = get_subscription_id(cmd.cli_ctx) - + for r in assign_user_identities: old_id = r r = _ensure_identity_resource_id(subscription_id, resource_group_name, r).replace("resourceGroup", "resourcegroup") @@ -987,7 +987,7 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ containerapp_def["identity"]["userAssignedIdentities"][r] logger.warning("User identity {} is already assigned to containerapp".format(old_id)) except: - containerapp_def["identity"]["userAssignedIdentities"][r] = {} + containerapp_def["identity"]["userAssignedIdentities"][r] = {} try: r = ContainerAppClient.create_or_update(cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) @@ -997,7 +997,7 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ except Exception as e: handle_raw_exception(e) - + def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1010,7 +1010,7 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= remove_user_identities = list(set(remove_user_identities)) if remove_id_size != len(remove_user_identities): logger.warning("At least one identity was passed twice.") - + containerapp_def = None # Get containerapp properties of CA we are updating try: @@ -1024,7 +1024,7 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) # If identity not returned - try: + try: containerapp_def["identity"] containerapp_def["identity"]["type"] except: @@ -1041,9 +1041,9 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= if remove_user_identities: subscription_id = get_subscription_id(cmd.cli_ctx) - try: + try: containerapp_def["identity"]["userAssignedIdentities"] - except: + except: containerapp_def["identity"]["userAssignedIdentities"] = {} for id in remove_user_identities: given_id = id @@ -1062,14 +1062,14 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= if containerapp_def["identity"]["userAssignedIdentities"] == {}: containerapp_def["identity"]["userAssignedIdentities"] = None containerapp_def["identity"]["type"] = ("None" if containerapp_def["identity"]["type"] == "UserAssigned" else "SystemAssigned") - + try: r = ContainerAppClient.create_or_update(cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) return r["identity"] except Exception as e: handle_raw_exception(e) - - + + def show_managed_identity(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1080,7 +1080,7 @@ def show_managed_identity(cmd, name, resource_group_name): try: return r["identity"] - except: + except: r["identity"] = {} r["identity"]["type"] = "None" return r["identity"] @@ -1115,7 +1115,7 @@ def create_or_update_github_action(cmd, repo = repo_url.split('/') if len(repo) >= 2: repo = '/'.join(repo[-2:]) - + if repo: g = Github(token) github_repo = None @@ -1201,13 +1201,13 @@ def create_or_update_github_action(cmd, headers = ["x-ms-github-auxiliary={}".format(token)] - try: + try: r = GitHubActionClient.create_or_update(cmd = cmd, resource_group_name=resource_group_name, name=name, github_action_envelope=source_control_info, headers = headers) return r except Exception as e: handle_raw_exception(e) - - + + def show_github_action(cmd, name, resource_group_name): try: return GitHubActionClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) @@ -1217,7 +1217,7 @@ def show_github_action(cmd, name, resource_group_name): def delete_github_action(cmd, name, resource_group_name, token=None, login_with_github=False): # Check if there is an existing source control to delete - try: + try: github_action_config = GitHubActionClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) except Exception as e: handle_raw_exception(e) @@ -1242,7 +1242,7 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_ repo = repo_url.split('/') if len(repo) >= 2: repo = '/'.join(repo[-2:]) - + if repo: g = Github(token) github_repo = None @@ -1264,7 +1264,7 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_ except Exception as ex: # If exception due to github package missing, etc just continue without validating the repo and rely on api validation pass - + headers = ["x-ms-github-auxiliary={}".format(token)] try: @@ -1539,7 +1539,7 @@ def set_revision_mode(cmd, resource_group_name, name, mode, no_wait=False): r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) return r["properties"]["configuration"]["activeRevisionsMode"] - except Exception as e: + except Exception as e: handle_raw_exception(e) def show_ingress(cmd, name, resource_group_name): @@ -1585,7 +1585,7 @@ def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, ingress_def["targetPort"] = target_port ingress_def["transport"] = transport ingress_def["allowInsecure"] = allow_insecure - + containerapp_def["properties"]["configuration"]["ingress"] = ingress_def _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -1594,7 +1594,7 @@ def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) return r["properties"]["configuration"]["ingress"] - except Exception as e: + except Exception as e: handle_raw_exception(e) def disable_ingress(cmd, name, resource_group_name, no_wait=False): @@ -1617,8 +1617,8 @@ def disable_ingress(cmd, name, resource_group_name, no_wait=False): r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) logger.warning("Ingress has been disabled successfully.") - return - except Exception as e: + return + except Exception as e: handle_raw_exception(e) def set_ingress_traffic(cmd, name, resource_group_name, traffic_weights, no_wait=False): @@ -1635,7 +1635,7 @@ def set_ingress_traffic(cmd, name, resource_group_name, traffic_weights, no_wait try: containerapp_def["properties"]["configuration"]["ingress"] - except: + except: raise CLIError("Ingress must be enabled to set ingress traffic. Try running `az containerapp ingress -h` for more info.") if traffic_weights is not None: @@ -1647,7 +1647,7 @@ def set_ingress_traffic(cmd, name, resource_group_name, traffic_weights, no_wait r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) return r["properties"]["configuration"]["ingress"]["traffic"] - except Exception as e: + except Exception as e: handle_raw_exception(e) def show_ingress_traffic(cmd, name, resource_group_name): @@ -1664,7 +1664,7 @@ def show_ingress_traffic(cmd, name, resource_group_name): try: return containerapp_def["properties"]["configuration"]["ingress"]["traffic"] - except: + except: raise CLIError("Ingress must be enabled to show ingress traffic. Try running `az containerapp ingress -h` for more info.") def show_registry(cmd, name, resource_group_name, server): @@ -1681,7 +1681,7 @@ def show_registry(cmd, name, resource_group_name, server): try: containerapp_def["properties"]["configuration"]["registries"] - except: + except: raise CLIError("The containerapp {} has no assigned registries.".format(name)) registries_def = containerapp_def["properties"]["configuration"]["registries"] @@ -1705,7 +1705,7 @@ def list_registry(cmd, name, resource_group_name): try: return containerapp_def["properties"]["configuration"]["registries"] - except: + except: raise CLIError("The containerapp {} has no assigned registries.".format(name)) def set_registry(cmd, name, resource_group_name, server, username=None, password=None, no_wait=False): @@ -1801,7 +1801,7 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): try: containerapp_def["properties"]["configuration"]["registries"] - except: + except: raise CLIError("The containerapp {} has no assigned registries.".format(name)) registries_def = containerapp_def["properties"]["configuration"]["registries"] @@ -1844,7 +1844,7 @@ def list_secrets(cmd, name, resource_group_name): try: return ContainerAppClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name)["value"] - except: + except: raise CLIError("The containerapp {} has no assigned secrets.".format(name)) def show_secret(cmd, name, resource_group_name, secret_name): @@ -1900,9 +1900,9 @@ def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait = False except Exception as e: handle_raw_exception(e) -def set_secrets(cmd, name, resource_group_name, secrets, +def set_secrets(cmd, name, resource_group_name, secrets, #secrets=None, - #yaml=None, + #yaml=None, no_wait = False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1911,7 +1911,7 @@ def set_secrets(cmd, name, resource_group_name, secrets, # if not secrets: # secrets = [] - + # if yaml: # yaml_secrets = load_yaml_file(yaml).split(' ') # try: @@ -1956,13 +1956,13 @@ def enable_dapr(cmd, name, resource_group_name, dapr_app_id=None, dapr_app_port= if 'dapr' not in containerapp_def['properties']: containerapp_def['properties']['dapr'] = {} - + if dapr_app_id: containerapp_def['properties']['dapr']['dapr_app_id'] = dapr_app_id - + if dapr_app_port: containerapp_def['properties']['dapr']['dapr_app_port'] = dapr_app_port - + if dapr_app_protocol: containerapp_def['properties']['dapr']['dapr_app_protocol'] = dapr_app_protocol @@ -1975,7 +1975,7 @@ def enable_dapr(cmd, name, resource_group_name, dapr_app_id=None, dapr_app_port= except Exception as e: handle_raw_exception(e) -def disable_dapr(cmd, name, resource_group_name, no_wait=False): +def disable_dapr(cmd, name, resource_group_name, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None @@ -2047,7 +2047,7 @@ def create_or_update_dapr_component(cmd, resource_group_name, environment_name, def remove_dapr_component(cmd, resource_group_name, dapr_component_name, environment_name): _validate_subscription_registered(cmd, "Microsoft.App") - try: + try: DaprComponentClient.show(cmd, resource_group_name, environment_name, name=dapr_component_name) except: raise CLIError("Dapr component not found.") @@ -2058,4 +2058,3 @@ def remove_dapr_component(cmd, resource_group_name, dapr_component_name, environ return r except Exception as e: handle_raw_exception(e) - From 858f8285de05a11c96b1581b33d17948f888815e Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Thu, 17 Mar 2022 13:27:32 -0400 Subject: [PATCH 11/24] Styled clients and utils to pass pylint. --- src/containerapp/azext_containerapp/_clients.py | 17 ++++++----------- src/containerapp/azext_containerapp/_utils.py | 1 + 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/containerapp/azext_containerapp/_clients.py b/src/containerapp/azext_containerapp/_clients.py index 108ee5b004f..0945a67b0e7 100644 --- a/src/containerapp/azext_containerapp/_clients.py +++ b/src/containerapp/azext_containerapp/_clients.py @@ -2,11 +2,12 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long, super-with-arguments, too-many-instance-attributes, consider-using-f-string, no-else-return, no-self-use + import json import time import sys -from sys import api_version from azure.cli.core.util import send_raw_request from azure.cli.core.commands.client_factory import get_subscription_id from knack.log import get_logger @@ -37,7 +38,7 @@ def flush(self): sys.stdout.write("\033[K") -def poll(cmd, request_url, poll_if_status): +def poll(cmd, request_url, poll_if_status): # pylint: disable=inconsistent-return-statements try: start = time.time() end = time.time() + POLLING_TIMEOUT @@ -59,14 +60,11 @@ def poll(cmd, request_url, poll_if_status): animation.flush() return r.json() - except Exception as e: + except Exception as e: # pylint: disable=broad-except animation.flush() - if poll_if_status == "scheduledfordelete": # Catch "not found" errors if polling for delete - return - - raise e - + if not poll_if_status == "scheduledfordelete": # Catch "not found" errors if polling for delete + raise e class ContainerAppClient(): @classmethod @@ -144,7 +142,6 @@ def delete(cls, cmd, resource_group_name, name): if r.status_code == 202: logger.warning('Containerapp successfully deleted') - return @classmethod def show(cls, cmd, resource_group_name, name): @@ -222,7 +219,6 @@ def list_by_resource_group(cls, cmd, resource_group_name, formatter=lambda x: x) @classmethod def list_secrets(cls, cmd, resource_group_name, name): - secrets = [] management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager api_version = NEW_API_VERSION @@ -705,4 +701,3 @@ def list(cls, cmd, resource_group_name, environment_name, formatter=lambda x: x) app_list.append(formatted) return app_list - diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index dea73793b64..83a8784441d 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long, consider-using-f-string from distutils.filelist import findall from operator import is_ From 570b6cb9002077c3c5b2b86a6cc959f21dbe3ae4 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Thu, 17 Mar 2022 13:50:11 -0400 Subject: [PATCH 12/24] Finished client.py pylint fixes. --- src/containerapp/azext_containerapp/_utils.py | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 83a8784441d..730b0aa8394 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -2,16 +2,14 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -# pylint: disable=line-too-long, consider-using-f-string +# pylint: disable=line-too-long, consider-using-f-string, no-else-return, duplicate-string-formatting-argument -from distutils.filelist import findall -from operator import is_ +from urllib.parse import urlparse from azure.cli.command_modules.appservice.custom import (_get_acr_cred) -from azure.cli.core.azclierror import (ResourceNotFoundError, ValidationError, RequiredArgumentMissingError) +from azure.cli.core.azclierror import (ValidationError, RequiredArgumentMissingError) from azure.cli.core.commands.client_factory import get_subscription_id from knack.log import get_logger from msrestazure.tools import parse_resource_id -from urllib.parse import urlparse from ._clients import ContainerAppClient from ._client_factory import handle_raw_exception, providers_client_factory, cf_resource_groups, log_analytics_client_factory, log_analytics_shared_key_client_factory @@ -39,7 +37,7 @@ def _validate_subscription_registered(cmd, resource_provider, subscription_id=No subscription_id, resource_provider, resource_provider)) except ValidationError as ex: raise ex - except Exception: + except Exception: # pylint: disable=broad-except pass @@ -63,7 +61,7 @@ def _ensure_location_allowed(cmd, location, resource_provider, resource_type): location, resource_provider, resource_type)) except ValidationError as ex: raise ex - except Exception: + except Exception: # pylint: disable=broad-except pass @@ -119,7 +117,7 @@ def parse_secret_flags(secret_list): def _update_revision_env_secretrefs(containers, name): for container in containers: - if "env" in container: + if "env" in container: for var in container["env"]: if "secretRef" in var: var["secretRef"] = var["secretRef"].replace("{}-".format(name), "") @@ -140,11 +138,11 @@ def store_as_secret_and_return_secret_ref(secrets_list, registry_user, registry_ return registry_pass else: # If user passed in registry password - if (urlparse(registry_server).hostname is not None): + if urlparse(registry_server).hostname is not None: registry_secret_name = "{server}-{user}".format(server=urlparse(registry_server).hostname.replace('.', ''), user=registry_user.lower()) else: registry_secret_name = "{server}-{user}".format(server=registry_server.replace('.', ''), user=registry_user.lower()) - + for secret in secrets_list: if secret['name'].lower() == registry_secret_name.lower(): if secret['value'].lower() != registry_pass.lower(): @@ -154,7 +152,7 @@ def store_as_secret_and_return_secret_ref(secrets_list, registry_user, registry_ raise ValidationError('Found secret with name \"{}\" but value does not equal the supplied registry password.'.format(registry_secret_name)) return registry_secret_name - logger.warning('Adding registry password as a secret with name \"{}\"'.format(registry_secret_name)) + logger.warning('Adding registry password as a secret with name \"{}\"'.format(registry_secret_name)) # pylint: disable=logging-format-interpolation secrets_list.append({ "name": registry_secret_name, "value": registry_pass @@ -185,18 +183,19 @@ def _get_default_log_analytics_location(cmd): if res and getattr(res, 'resource_type', "") == "workspaces": res_locations = getattr(res, 'locations', []) - if len(res_locations): + if len(res_locations) > 0: location = res_locations[0].lower().replace(" ", "").replace("(", "").replace(")", "") if location: return location - except Exception: + except Exception: # pylint: disable=broad-except return default_location return default_location # Generate random 4 character string def _new_tiny_guid(): - import random, string + import random + import string return ''.join(random.choices(string.ascii_letters + string.digits, k=4)) # Follow same naming convention as Portal @@ -230,7 +229,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc log_analytics_location = location try: _ensure_location_allowed(cmd, log_analytics_location, "Microsoft.OperationalInsights", "workspaces") - except Exception: + except Exception: # pylint: disable=broad-except log_analytics_location = _get_default_log_analytics_location(cmd) from azure.cli.core.commands import LongRunningOperation @@ -238,7 +237,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc workspace_name = _generate_log_analytics_workspace_name(resource_group_name) workspace_instance = Workspace(location=log_analytics_location) - logger.warning("Generating a Log Analytics workspace with name \"{}\"".format(workspace_name)) + logger.warning("Generating a Log Analytics workspace with name \"{}\"".format(workspace_name)) # pylint: disable=logging-format-interpolation poller = log_analytics_client.begin_create_or_update(resource_group_name, workspace_name, workspace_instance) log_analytics_workspace = LongRunningOperation(cmd.cli_ctx)(poller) @@ -249,7 +248,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc resource_group_name=resource_group_name).primary_shared_key except Exception as ex: - raise ValidationError("Unable to generate a Log Analytics workspace. You can use \"az monitor log-analytics workspace create\" to create one and supply --logs-customer-id and --logs-key") + raise ValidationError("Unable to generate a Log Analytics workspace. You can use \"az monitor log-analytics workspace create\" to create one and supply --logs-customer-id and --logs-key") from ex elif logs_customer_id is None: raise ValidationError("Usage error: Supply the --logs-customer-id associated with the --logs-key") elif logs_key is None: # Try finding the logs-key @@ -286,7 +285,7 @@ def _get_existing_secrets(cmd, resource_group_name, name, containerapp_def): secrets = [] try: secrets = ContainerAppClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name) - except Exception as e: + except Exception as e: # pylint: disable=broad-except handle_raw_exception(e) containerapp_def["properties"]["configuration"]["secrets"] = secrets["value"] @@ -318,7 +317,7 @@ def _add_or_update_secrets(containerapp_def, add_secrets): containerapp_def["properties"]["configuration"]["secrets"].append(new_secret) def _remove_registry_secret(containerapp_def, server, username): - if (urlparse(server).hostname is not None): + if urlparse(server).hostname is not None: registry_secret_name = "{server}-{user}".format(server=urlparse(server).hostname.replace('.', ''), user=username.lower()) else: registry_secret_name = "{server}-{user}".format(server=server.replace('.', ''), user=username.lower()) @@ -329,10 +328,10 @@ def _remove_secret(containerapp_def, secret_name): if "secrets" not in containerapp_def["properties"]["configuration"]: containerapp_def["properties"]["configuration"]["secrets"] = [] - for i in range(0, len(containerapp_def["properties"]["configuration"]["secrets"])): - existing_secret = containerapp_def["properties"]["configuration"]["secrets"][i] + for index, value in enumerate(containerapp_def["properties"]["configuration"]["secrets"]): + existing_secret = value if existing_secret["name"].lower() == secret_name.lower(): - containerapp_def["properties"]["configuration"]["secrets"].pop(i) + containerapp_def["properties"]["configuration"]["secrets"].pop(index) break def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): @@ -344,7 +343,7 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): if existing_env_var["name"].lower() == new_env_var["name"].lower(): is_existing = True if is_add: - logger.warning("Environment variable {} already exists. Replacing environment variable value.".format(new_env_var["name"])) + logger.warning("Environment variable {} already exists. Replacing environment variable value.".format(new_env_var["name"])) # pylint: disable=logging-format-interpolation if "value" in new_env_var: existing_env_var["value"] = new_env_var["value"] @@ -360,7 +359,7 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): # If not updating existing env var, add it as a new env var if not is_existing: if not is_add: - logger.warning("Environment variable {} does not exist. Adding as new environment variable.".format(new_env_var["name"])) + logger.warning("Environment variable {} does not exist. Adding as new environment variable.".format(new_env_var["name"])) # pylint: disable=logging-format-interpolation existing_env_vars.append(new_env_var) def _remove_env_vars(existing_env_vars, remove_env_vars): @@ -368,16 +367,16 @@ def _remove_env_vars(existing_env_vars, remove_env_vars): # Check if updating existing env var is_existing = False - for i in range(0, len(existing_env_vars)): - existing_env_var = existing_env_vars[i] + for index, value in enumerate(existing_env_vars): + existing_env_var = value if existing_env_var["name"].lower() == old_env_var.lower(): is_existing = True - existing_env_vars.pop(i) + existing_env_vars.pop(index) break # If not updating existing env var, add it as a new env var if not is_existing: - logger.warning("Environment variable {} does not exist.".format(old_env_var)) + logger.warning("Environment variable {} does not exist.".format(old_env_var)) # pylint: disable=logging-format-interpolation def _add_or_update_tags(containerapp_def, tags): if 'tags' not in containerapp_def: @@ -478,7 +477,7 @@ def update_nested_dictionary(orig_dict, new_dict): def _is_valid_weight(weight): try: n = int(weight) - if n >= 0 and n <= 100: + if 0 <= n <= 100: return True return False except ValueError: @@ -528,7 +527,7 @@ def _infer_acr_credentials(cmd, registry_server): registry_user, registry_pass = _get_acr_cred(cmd.cli_ctx, registry_name) return (registry_user, registry_pass) except Exception as ex: - raise RequiredArgumentMissingError('Failed to retrieve credentials for container registry {}. Please provide the registry username and password'.format(registry_name)) + raise RequiredArgumentMissingError('Failed to retrieve credentials for container registry {}. Please provide the registry username and password'.format(registry_name)) from ex def _registry_exists(containerapp_def, registry_server): From 01f1bc3c169db94606ca39bfdf2e72dfdfb078c6 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Thu, 17 Mar 2022 15:08:33 -0400 Subject: [PATCH 13/24] Fixed pylint issues. --- src/containerapp/azext_containerapp/custom.py | 195 ++++++------------ 1 file changed, 65 insertions(+), 130 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index f123b3f95a6..ea67aa4274d 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -2,23 +2,21 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long, consider-using-f-string, multiple-imports, logging-format-interpolation, inconsistent-return-statements, broad-except, bare-except, too-many-statements, too-many-locals, too-many-boolean-expressions, too-many-branches, too-many-nested-blocks, pointless-statement +from urllib.parse import urlparse from azure.cli.command_modules.appservice.custom import (_get_acr_cred) -from azure.cli.core.azclierror import (RequiredArgumentMissingError, ResourceNotFoundError, ValidationError) +from azure.cli.core.azclierror import (RequiredArgumentMissingError, ValidationError) from azure.cli.core.commands.client_factory import get_subscription_id -from azure.cli.core.util import sdk_no_wait from knack.util import CLIError from knack.log import get_logger -from urllib.parse import urlparse from msrestazure.tools import parse_resource_id, is_valid_resource_id from msrest.exceptions import DeserializationError -from azure.cli.command_modules.appservice.custom import _get_acr_cred -from urllib.parse import urlparse from ._client_factory import handle_raw_exception from ._clients import ManagedEnvironmentClient, ContainerAppClient, GitHubActionClient, DaprComponentClient -from ._sdk_models import * +from ._sdk_models import * # pylint: disable=wildcard-import, unused-wildcard-import from ._github_oauth import get_github_access_token from ._models import ( ManagedEnvironment as ManagedEnvironmentModel, @@ -40,7 +38,7 @@ SourceControl as SourceControlModel, ManagedServiceIdentity as ManagedServiceIdentityModel) from ._utils import (_validate_subscription_registered, _get_location_from_resource_group, _ensure_location_allowed, - parse_secret_flags, store_as_secret_and_return_secret_ref, parse_list_of_strings, parse_env_var_flags, + parse_secret_flags, store_as_secret_and_return_secret_ref, parse_env_var_flags, _generate_log_analytics_if_not_provided, _get_existing_secrets, _convert_object_from_snake_to_camel_case, _object_to_dict, _add_or_update_secrets, _remove_additional_attributes, _remove_readonly_attributes, _add_or_update_env_vars, _add_or_update_tags, update_nested_dictionary, _update_traffic_weights, @@ -70,14 +68,14 @@ def load_yaml_file(file_name): import errno try: - with open(file_name) as stream: + with open(file_name) as stream: # pylint: disable=unspecified-encoding return yaml.safe_load(stream) except (IOError, OSError) as ex: if getattr(ex, 'errno', 0) == errno.ENOENT: - raise CLIError('{} does not exist'.format(file_name)) + raise CLIError('{} does not exist'.format(file_name)) from ex raise except (yaml.parser.ParserError, UnicodeDecodeError) as ex: - raise CLIError('Error parsing {} ({})'.format(file_name, str(ex))) + raise CLIError('Error parsing {} ({})'.format(file_name, str(ex))) from ex def create_deserializer(): @@ -95,7 +93,7 @@ def create_deserializer(): def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_revision=None, no_wait=False): yaml_containerapp = process_loaded_yaml(load_yaml_file(file_name)) - if type(yaml_containerapp) != dict: + if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') if not yaml_containerapp.get('name'): @@ -114,7 +112,7 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev containerapp_def = None try: current_containerapp_def = ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except Exception as ex: + except Exception: pass if not current_containerapp_def: @@ -136,7 +134,7 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev containerapp_def = deserializer('ContainerApp', yaml_containerapp) except DeserializationError as ex: - raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') + raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') from ex # Remove tags before converting from snake case to camel case, then re-add tags. We don't want to change the case of the tags. Need this since we're not using SDK tags = None @@ -158,54 +156,6 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev _remove_additional_attributes(current_containerapp_def) _remove_readonly_attributes(current_containerapp_def) - ''' - # Not sure if update should replace items that are a list, or do createOrUpdate. This commented out section is the implementation for createOrUpdate. - # (If a property is a list, do createOrUpdate, rather than just replace with new list) - - if 'properties' in containerapp_def and 'template' in containerapp_def['properties']: - # Containers - if 'containers' in containerapp_def['properties']['template'] and containerapp_def['properties']['template']['containers']: - for new_container in containerapp_def['properties']['template']['containers']: - if "name" not in new_container or not new_container["name"]: - raise ValidationError("The container name is not specified.") - - # Check if updating existing container - updating_existing_container = False - for existing_container in current_containerapp_def["properties"]["template"]["containers"]: - if existing_container['name'].lower() == new_container['name'].lower(): - updating_existing_container = True - - if 'image' in new_container and new_container['image']: - existing_container['image'] = new_container['image'] - if 'env' in new_container and new_container['env']: - if 'env' not in existing_container or not existing_container['env']: - existing_container['env'] = [] - _add_or_update_env_vars(existing_container['env'], new_container['env']) - if 'command' in new_container and new_container['command']: - existing_container['command'] = new_container['command'] - if 'args' in new_container and new_container['args']: - existing_container['args'] = new_container['args'] - if 'resources' in new_container and new_container['resources']: - if 'cpu' in new_container['resources'] and new_container['resources']['cpu'] is not None: - existing_container['resources']['cpu'] = new_container['resources']['cpu'] - if 'memory' in new_container['resources'] and new_container['resources']['memory'] is not None: - existing_container['resources']['memory'] = new_container['resources']['memory'] - - # If not updating existing container, add as new container - if not updating_existing_container: - current_containerapp_def["properties"]["template"]["containers"].append(new_container) - - # Traffic Weights - - # Secrets - - # Registries - - # Scale rules - - # Source Controls - - ''' try: r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=current_containerapp_def, no_wait=no_wait) @@ -222,7 +172,7 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev def create_containerapp_yaml(cmd, name, resource_group_name, file_name, no_wait=False): yaml_containerapp = process_loaded_yaml(load_yaml_file(file_name)) - if type(yaml_containerapp) != dict: + if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') if not yaml_containerapp.get('name'): @@ -244,7 +194,7 @@ def create_containerapp_yaml(cmd, name, resource_group_name, file_name, no_wait= containerapp_def = deserializer('ContainerApp', yaml_containerapp) except DeserializationError as ex: - raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') + raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') from ex # Remove tags before converting from snake case to camel case, then re-add tags. We don't want to change the case of the tags. Need this since we're not using SDK tags = None @@ -271,7 +221,7 @@ def create_containerapp_yaml(cmd, name, resource_group_name, file_name, no_wait= env_rg = None env_info = None - if (is_valid_resource_id(env_id)): + if is_valid_resource_id(env_id): parsed_managed_env = parse_resource_id(env_id) env_name = parsed_managed_env['name'] env_rg = parsed_managed_env['resource_group'] @@ -334,14 +284,14 @@ def create_containerapp(cmd, args=None, tags=None, no_wait=False, - assign_identity=[]): + assign_identity=None): _validate_subscription_registered(cmd, "Microsoft.App") if yaml: if image or managed_env or min_replicas or max_replicas or target_port or ingress or\ revisions_mode or secrets or env_vars or cpu or memory or registry_server or\ registry_user or registry_pass or dapr_enabled or dapr_app_port or dapr_app_id or\ - location or startup_command or args or tags: + startup_command or args or tags: logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') return create_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, no_wait=no_wait) @@ -351,6 +301,9 @@ def create_containerapp(cmd, if managed_env is None: raise RequiredArgumentMissingError('Usage error: --environment is required if not using --yaml') + if assign_identity is None: + assign_identity = [] + # Validate managed environment parsed_managed_env = parse_resource_id(managed_env) managed_env_name = parsed_managed_env['name'] @@ -427,7 +380,7 @@ def create_containerapp(cmd, for r in assign_user_identities: r = _ensure_identity_resource_id(subscription_id, resource_group_name, r) - identity_def["userAssignedIdentities"][r] = {} + identity_def["userAssignedIdentities"][r] = {} # pylint: disable=unsupported-assignment-operation scale_def = None if min_replicas is not None or max_replicas is not None: @@ -793,7 +746,7 @@ def create_managed_environment(cmd, # Microsoft.ContainerService RP registration is required for vnet enabled environments if infrastructure_subnet_resource_id is not None or app_subnet_resource_id is not None: - if (is_valid_resource_id(app_subnet_resource_id)): + if is_valid_resource_id(app_subnet_resource_id): parsed_app_subnet_resource_id = parse_resource_id(app_subnet_resource_id) subnet_subscription = parsed_app_subnet_resource_id["subscription"] _validate_subscription_registered(cmd, "Microsoft.ContainerService", subnet_subscription) @@ -868,23 +821,6 @@ def update_managed_environment(cmd, no_wait=False): raise CLIError('Containerapp env update is not yet supported.') - _validate_subscription_registered(cmd, "Microsoft.App") - - managed_env_def = ManagedEnvironmentModel - managed_env_def["tags"] = tags - - try: - r = ManagedEnvironmentClient.update( - cmd=cmd, resource_group_name=resource_group_name, name=name, managed_environment_envelope=managed_env_def, no_wait=no_wait) - - if "properties" in r and "provisioningState" in r["properties"] and r["properties"]["provisioningState"].lower() == "waiting" and not no_wait: - logger.warning('Containerapp environment update in progress. Please monitor the creation using `az containerapp env show -n {} -g {}`'.format(name, resource_group_name)) - - return r - except Exception as e: - handle_raw_exception(e) - - def show_managed_environment(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -987,7 +923,7 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ containerapp_def["identity"]["userAssignedIdentities"][r] logger.warning("User identity {} is already assigned to containerapp".format(old_id)) except: - containerapp_def["identity"]["userAssignedIdentities"][r] = {} + containerapp_def["identity"]["userAssignedIdentities"][r] = {} try: r = ContainerAppClient.create_or_update(cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) @@ -1045,13 +981,13 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= containerapp_def["identity"]["userAssignedIdentities"] except: containerapp_def["identity"]["userAssignedIdentities"] = {} - for id in remove_user_identities: - given_id = id - id = _ensure_identity_resource_id(subscription_id, resource_group_name, id) + for remove_id in remove_user_identities: + given_id = remove_id + remove_id = _ensure_identity_resource_id(subscription_id, resource_group_name, remove_id) wasRemoved = False for old_user_identity in containerapp_def["identity"]["userAssignedIdentities"]: - if old_user_identity.lower() == id.lower(): + if old_user_identity.lower() == remove_id.lower(): containerapp_def["identity"]["userAssignedIdentities"].pop(old_user_identity) wasRemoved = True break @@ -1109,7 +1045,7 @@ def create_or_update_github_action(cmd, try: # Verify github repo from github import Github, GithubException - from github.GithubException import BadCredentialsException, UnknownObjectException + from github.GithubException import BadCredentialsException repo = None repo = repo_url.split('/') @@ -1129,20 +1065,20 @@ def create_or_update_github_action(cmd, error_msg = "Encountered GitHub error when accessing {} branch in {} repo.".format(branch, repo) if e.data and e.data['message']: error_msg += " Error: {}".format(e.data['message']) - raise CLIError(error_msg) + raise CLIError(error_msg) from e logger.warning('Verified GitHub repo and branch') - except BadCredentialsException: + except BadCredentialsException as e: raise CLIError("Could not authenticate to the repository. Please create a Personal Access Token and use " "the --token argument. Run 'az webapp deployment github-actions add --help' " - "for more information.") + "for more information.") from e except GithubException as e: error_msg = "Encountered GitHub error when accessing {} repo".format(repo) if e.data and e.data['message']: error_msg += " Error: {}".format(e.data['message']) - raise CLIError(error_msg) + raise CLIError(error_msg) from e except CLIError as clierror: raise clierror - except Exception as ex: + except Exception: # If exception due to github package missing, etc just continue without validating the repo and rely on api validation pass @@ -1154,7 +1090,7 @@ def create_or_update_github_action(cmd, except Exception as ex: if not service_principal_client_id or not service_principal_client_secret or not service_principal_tenant_id: - raise RequiredArgumentMissingError('Service principal client ID, secret and tenant ID are required to add github actions for the first time. Please create one using the command \"az ad sp create-for-rbac --name \{name\} --role contributor --scopes /subscriptions/\{subscription\}/resourceGroups/\{resourceGroup\} --sdk-auth\"') + raise RequiredArgumentMissingError('Service principal client ID, secret and tenant ID are required to add github actions for the first time. Please create one using the command \"az ad sp create-for-rbac --name {{name}} --role contributor --scopes /subscriptions/{{subscription}}/resourceGroups/{{resourceGroup}} --sdk-auth\"') from ex source_control_info = SourceControlModel source_control_info["properties"]["repoUrl"] = repo_url @@ -1185,7 +1121,7 @@ def create_or_update_github_action(cmd, try: registry_username, registry_password = _get_acr_cred(cmd.cli_ctx, registry_name) except Exception as ex: - raise RequiredArgumentMissingError('Failed to retrieve credentials for container registry. Please provide the registry username and password') + raise RequiredArgumentMissingError('Failed to retrieve credentials for container registry. Please provide the registry username and password') from ex registry_info = RegistryInfoModel registry_info["registryUrl"] = registry_url @@ -1236,7 +1172,7 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_ try: # Verify github repo from github import Github, GithubException - from github.GithubException import BadCredentialsException, UnknownObjectException + from github.GithubException import BadCredentialsException repo = None repo = repo_url.split('/') @@ -1250,18 +1186,18 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_ github_repo = g.get_repo(repo) if not github_repo.permissions.push or not github_repo.permissions.maintain: raise CLIError("The token does not have appropriate access rights to repository {}.".format(repo)) - except BadCredentialsException: + except BadCredentialsException as e: raise CLIError("Could not authenticate to the repository. Please create a Personal Access Token and use " "the --token argument. Run 'az webapp deployment github-actions add --help' " - "for more information.") + "for more information.") from e except GithubException as e: error_msg = "Encountered GitHub error when accessing {} repo".format(repo) if e.data and e.data['message']: error_msg += " Error: {}".format(e.data['message']) - raise CLIError(error_msg) + raise CLIError(error_msg) from e except CLIError as clierror: raise clierror - except Exception as ex: + except Exception: # If exception due to github package missing, etc just continue without validating the repo and rely on api validation pass @@ -1556,10 +1492,10 @@ def show_ingress(cmd, name, resource_group_name): try: return containerapp_def["properties"]["configuration"]["ingress"] - except: - raise CLIError("The containerapp '{}' does not have ingress enabled.".format(name)) + except Exception as e: + raise CLIError("The containerapp '{}' does not have ingress enabled.".format(name)) from e -def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, allow_insecure=False, no_wait=False): +def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, allow_insecure=False, no_wait=False): # pylint: disable=redefined-builtin _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None @@ -1614,7 +1550,7 @@ def disable_ingress(cmd, name, resource_group_name, no_wait=False): _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) try: - r = ContainerAppClient.create_or_update( + ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) logger.warning("Ingress has been disabled successfully.") return @@ -1635,8 +1571,8 @@ def set_ingress_traffic(cmd, name, resource_group_name, traffic_weights, no_wait try: containerapp_def["properties"]["configuration"]["ingress"] - except: - raise CLIError("Ingress must be enabled to set ingress traffic. Try running `az containerapp ingress -h` for more info.") + except Exception as e: + raise CLIError("Ingress must be enabled to set ingress traffic. Try running `az containerapp ingress -h` for more info.") from e if traffic_weights is not None: _update_traffic_weights(containerapp_def, traffic_weights) @@ -1664,8 +1600,8 @@ def show_ingress_traffic(cmd, name, resource_group_name): try: return containerapp_def["properties"]["configuration"]["ingress"]["traffic"] - except: - raise CLIError("Ingress must be enabled to show ingress traffic. Try running `az containerapp ingress -h` for more info.") + except Exception as e: + raise CLIError("Ingress must be enabled to show ingress traffic. Try running `az containerapp ingress -h` for more info.") from e def show_registry(cmd, name, resource_group_name, server): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1681,8 +1617,8 @@ def show_registry(cmd, name, resource_group_name, server): try: containerapp_def["properties"]["configuration"]["registries"] - except: - raise CLIError("The containerapp {} has no assigned registries.".format(name)) + except Exception as e: + raise CLIError("The containerapp {} has no assigned registries.".format(name)) from e registries_def = containerapp_def["properties"]["configuration"]["registries"] @@ -1705,8 +1641,8 @@ def list_registry(cmd, name, resource_group_name): try: return containerapp_def["properties"]["configuration"]["registries"] - except: - raise CLIError("The containerapp {} has no assigned registries.".format(name)) + except Exception as e: + raise CLIError("The containerapp {} has no assigned registries.".format(name)) from e def set_registry(cmd, name, resource_group_name, server, username=None, password=None, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1741,7 +1677,7 @@ def set_registry(cmd, name, resource_group_name, server, username=None, password try: username, password = _get_acr_cred(cmd.cli_ctx, registry_name) except Exception as ex: - raise RequiredArgumentMissingError('Failed to retrieve credentials for container registry. Please provide the registry username and password') + raise RequiredArgumentMissingError('Failed to retrieve credentials for container registry. Please provide the registry username and password') from ex # Check if updating existing registry updating_existing_registry = False @@ -1773,7 +1709,7 @@ def set_registry(cmd, name, resource_group_name, server, username=None, password # Should this be false? ^ registries_def.append(registry) - + try: r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) @@ -1797,18 +1733,17 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) registries_def = None - registry = None try: containerapp_def["properties"]["configuration"]["registries"] - except: - raise CLIError("The containerapp {} has no assigned registries.".format(name)) + except Exception as e: + raise CLIError("The containerapp {} has no assigned registries.".format(name)) from e registries_def = containerapp_def["properties"]["configuration"]["registries"] wasRemoved = False - for i in range(0, len(registries_def)): - r = registries_def[i] + for i, value in enumerate(registries_def): + r = value if r['server'].lower() == server.lower(): registries_def.pop(i) _remove_registry_secret(containerapp_def=containerapp_def, server=server, username=r["username"]) @@ -1827,8 +1762,8 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): logger.warning("Registry successfully removed.") return r["properties"]["configuration"]["registries"] # No registries to return, so return nothing - except Exception as e: - return + except Exception: + pass def list_secrets(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1844,8 +1779,8 @@ def list_secrets(cmd, name, resource_group_name): try: return ContainerAppClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name)["value"] - except: - raise CLIError("The containerapp {} has no assigned secrets.".format(name)) + except Exception as e: + raise CLIError("The containerapp {} has no assigned secrets.".format(name)) from e def show_secret(cmd, name, resource_group_name, secret_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -2012,7 +1947,7 @@ def create_or_update_dapr_component(cmd, resource_group_name, environment_name, _validate_subscription_registered(cmd, "Microsoft.App") yaml_containerapp = load_yaml_file(yaml) - if type(yaml_containerapp) != dict: + if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') # Deserialize the yaml into a DaprComponent object. Need this since we're not using SDK @@ -2022,7 +1957,7 @@ def create_or_update_dapr_component(cmd, resource_group_name, environment_name, daprcomponent_def = deserializer('DaprComponent', yaml_containerapp) except DeserializationError as ex: - raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') + raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') from ex #daprcomponent_def = _object_to_dict(daprcomponent_def) daprcomponent_def = _convert_object_from_snake_to_camel_case(_object_to_dict(daprcomponent_def)) @@ -2049,8 +1984,8 @@ def remove_dapr_component(cmd, resource_group_name, dapr_component_name, environ try: DaprComponentClient.show(cmd, resource_group_name, environment_name, name=dapr_component_name) - except: - raise CLIError("Dapr component not found.") + except Exception as e: + raise CLIError("Dapr component not found.") from e try: r = DaprComponentClient.delete(cmd, resource_group_name, environment_name, name=dapr_component_name) From 2893b559a86444f595105012aa9cae155442b1e4 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Thu, 17 Mar 2022 16:03:31 -0400 Subject: [PATCH 14/24] Fixed flake8 commands and custom. --- .../azext_containerapp/commands.py | 4 +- src/containerapp/azext_containerapp/custom.py | 124 +++++++++++------- 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 5f75b937bbe..73008caaf8d 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -70,8 +70,8 @@ def load_command_table(self, _): with self.command_group('containerapp github-action') as g: g.custom_command('add', 'create_or_update_github_action', exception_handler=ex_handler_factory()) - g.custom_command('show', 'show_github_action', exception_handler=ex_handler_factory()) - g.custom_command('delete', 'delete_github_action', exception_handler=ex_handler_factory()) + g.custom_command('show', 'show_github_action', exception_handler=ex_handler_factory()) + g.custom_command('delete', 'delete_github_action', exception_handler=ex_handler_factory()) with self.command_group('containerapp revision') as g: g.custom_command('activate', 'activate_revision') diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index ea67aa4274d..0c55c8dc01c 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -2,7 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -# pylint: disable=line-too-long, consider-using-f-string, multiple-imports, logging-format-interpolation, inconsistent-return-statements, broad-except, bare-except, too-many-statements, too-many-locals, too-many-boolean-expressions, too-many-branches, too-many-nested-blocks, pointless-statement +# pylint: disable=line-too-long, consider-using-f-string, logging-format-interpolation, inconsistent-return-statements, broad-except, bare-except, too-many-statements, too-many-locals, too-many-boolean-expressions, too-many-branches, too-many-nested-blocks, pointless-statement from urllib.parse import urlparse from azure.cli.command_modules.appservice.custom import (_get_acr_cred) @@ -16,7 +16,7 @@ from ._client_factory import handle_raw_exception from ._clients import ManagedEnvironmentClient, ContainerAppClient, GitHubActionClient, DaprComponentClient -from ._sdk_models import * # pylint: disable=wildcard-import, unused-wildcard-import +# from ._sdk_models import * # pylint: disable=wildcard-import, unused-wildcard-import from ._github_oauth import get_github_access_token from ._models import ( ManagedEnvironment as ManagedEnvironmentModel, @@ -38,12 +38,12 @@ SourceControl as SourceControlModel, ManagedServiceIdentity as ManagedServiceIdentityModel) from ._utils import (_validate_subscription_registered, _get_location_from_resource_group, _ensure_location_allowed, - parse_secret_flags, store_as_secret_and_return_secret_ref, parse_env_var_flags, - _generate_log_analytics_if_not_provided, _get_existing_secrets, _convert_object_from_snake_to_camel_case, - _object_to_dict, _add_or_update_secrets, _remove_additional_attributes, _remove_readonly_attributes, - _add_or_update_env_vars, _add_or_update_tags, update_nested_dictionary, _update_traffic_weights, - _get_app_from_revision, raise_missing_token_suggestion, _infer_acr_credentials, _remove_registry_secret, _remove_secret, - _ensure_identity_resource_id, _remove_dapr_readonly_attributes, _registry_exists, _remove_env_vars, _update_revision_env_secretrefs) + parse_secret_flags, store_as_secret_and_return_secret_ref, parse_env_var_flags, + _generate_log_analytics_if_not_provided, _get_existing_secrets, _convert_object_from_snake_to_camel_case, + _object_to_dict, _add_or_update_secrets, _remove_additional_attributes, _remove_readonly_attributes, + _add_or_update_env_vars, _add_or_update_tags, update_nested_dictionary, _update_traffic_weights, + _get_app_from_revision, raise_missing_token_suggestion, _infer_acr_credentials, _remove_registry_secret, _remove_secret, + _ensure_identity_resource_id, _remove_dapr_readonly_attributes, _registry_exists, _remove_env_vars, _update_revision_env_secretrefs) logger = get_logger(__name__) @@ -68,7 +68,7 @@ def load_yaml_file(file_name): import errno try: - with open(file_name) as stream: # pylint: disable=unspecified-encoding + with open(file_name) as stream: # pylint: disable=unspecified-encoding return yaml.safe_load(stream) except (IOError, OSError) as ex: if getattr(ex, 'errno', 0) == errno.ENOENT: @@ -80,7 +80,8 @@ def load_yaml_file(file_name): def create_deserializer(): from msrest import Deserializer - import sys, inspect + import sys + import inspect sdkClasses = inspect.getmembers(sys.modules["azext_containerapp._sdk_models"]) deserializer = {} @@ -93,7 +94,7 @@ def create_deserializer(): def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_revision=None, no_wait=False): yaml_containerapp = process_loaded_yaml(load_yaml_file(file_name)) - if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck + if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') if not yaml_containerapp.get('name'): @@ -172,7 +173,7 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev def create_containerapp_yaml(cmd, name, resource_group_name, file_name, no_wait=False): yaml_containerapp = process_loaded_yaml(load_yaml_file(file_name)) - if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck + if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') if not yaml_containerapp.get('name'): @@ -291,7 +292,7 @@ def create_containerapp(cmd, if image or managed_env or min_replicas or max_replicas or target_port or ingress or\ revisions_mode or secrets or env_vars or cpu or memory or registry_server or\ registry_user or registry_pass or dapr_enabled or dapr_app_port or dapr_app_id or\ - startup_command or args or tags: + startup_command or args or tags: logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') return create_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, no_wait=no_wait) @@ -380,7 +381,7 @@ def create_containerapp(cmd, for r in assign_user_identities: r = _ensure_identity_resource_id(subscription_id, resource_group_name, r) - identity_def["userAssignedIdentities"][r] = {} # pylint: disable=unsupported-assignment-operation + identity_def["userAssignedIdentities"][r] = {} # pylint: disable=unsupported-assignment-operation scale_def = None if min_replicas is not None or max_replicas is not None: @@ -470,9 +471,9 @@ def update_containerapp(cmd, if yaml: if image or min_replicas or max_replicas or\ - revisions_mode or secrets or set_env_vars or remove_env_vars or replace_env_vars or remove_all_env_vars or cpu or memory or registry_server or\ - registry_user or registry_pass or\ - startup_command or args or tags: + revisions_mode or secrets or set_env_vars or remove_env_vars or replace_env_vars or remove_all_env_vars or cpu or memory or registry_server or\ + registry_user or registry_pass or\ + startup_command or args or tags: logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') return update_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, no_wait=no_wait) @@ -724,20 +725,20 @@ def delete_containerapp(cmd, name, resource_group_name): def create_managed_environment(cmd, - name, - resource_group_name, - logs_customer_id=None, - logs_key=None, - location=None, - instrumentation_key=None, - infrastructure_subnet_resource_id=None, - app_subnet_resource_id=None, - docker_bridge_cidr=None, - platform_reserved_cidr=None, - platform_reserved_dns_ip=None, - internal_only=False, - tags=None, - no_wait=False): + name, + resource_group_name, + logs_customer_id=None, + logs_key=None, + location=None, + instrumentation_key=None, + infrastructure_subnet_resource_id=None, + app_subnet_resource_id=None, + docker_bridge_cidr=None, + platform_reserved_cidr=None, + platform_reserved_dns_ip=None, + internal_only=False, + tags=None, + no_wait=False): location = location or _get_location_from_resource_group(cmd.cli_ctx, resource_group_name) @@ -815,12 +816,13 @@ def create_managed_environment(cmd, def update_managed_environment(cmd, - name, - resource_group_name, - tags=None, - no_wait=False): + name, + resource_group_name, + tags=None, + no_wait=False): raise CLIError('Containerapp env update is not yet supported.') + def show_managed_environment(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1020,6 +1022,8 @@ def show_managed_identity(cmd, name, resource_group_name): r["identity"] = {} r["identity"]["type"] = "None" return r["identity"] + + def create_or_update_github_action(cmd, name, resource_group_name, @@ -1069,8 +1073,8 @@ def create_or_update_github_action(cmd, logger.warning('Verified GitHub repo and branch') except BadCredentialsException as e: raise CLIError("Could not authenticate to the repository. Please create a Personal Access Token and use " - "the --token argument. Run 'az webapp deployment github-actions add --help' " - "for more information.") from e + "the --token argument. Run 'az webapp deployment github-actions add --help' " + "for more information.") from e except GithubException as e: error_msg = "Encountered GitHub error when accessing {} repo".format(repo) if e.data and e.data['message']: @@ -1085,7 +1089,6 @@ def create_or_update_github_action(cmd, source_control_info = None try: - #source_control_info = client.get_source_control_info(resource_group_name, name).properties source_control_info = GitHubActionClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) except Exception as ex: @@ -1138,7 +1141,7 @@ def create_or_update_github_action(cmd, headers = ["x-ms-github-auxiliary={}".format(token)] try: - r = GitHubActionClient.create_or_update(cmd = cmd, resource_group_name=resource_group_name, name=name, github_action_envelope=source_control_info, headers = headers) + r = GitHubActionClient.create_or_update(cmd=cmd, resource_group_name=resource_group_name, name=name, github_action_envelope=source_control_info, headers=headers) return r except Exception as e: handle_raw_exception(e) @@ -1188,8 +1191,8 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_ raise CLIError("The token does not have appropriate access rights to repository {}.".format(repo)) except BadCredentialsException as e: raise CLIError("Could not authenticate to the repository. Please create a Personal Access Token and use " - "the --token argument. Run 'az webapp deployment github-actions add --help' " - "for more information.") from e + "the --token argument. Run 'az webapp deployment github-actions add --help' " + "for more information.") from e except GithubException as e: error_msg = "Encountered GitHub error when accessing {} repo".format(repo) if e.data and e.data['message']: @@ -1245,6 +1248,7 @@ def activate_revision(cmd, resource_group_name, revision_name, name=None): except CLIError as e: handle_raw_exception(e) + def deactivate_revision(cmd, resource_group_name, revision_name, name=None): if not name: name = _get_app_from_revision(revision_name) @@ -1254,10 +1258,11 @@ def deactivate_revision(cmd, resource_group_name, revision_name, name=None): except CLIError as e: handle_raw_exception(e) + def copy_revision(cmd, resource_group_name, from_revision=None, - #label=None, + # label=None, name=None, yaml=None, image=None, @@ -1287,7 +1292,7 @@ def copy_revision(cmd, if image or min_replicas or max_replicas or\ set_env_vars or replace_env_vars or remove_env_vars or \ remove_all_env_vars or cpu or memory or \ - startup_command or args or tags: + startup_command or args or tags: logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') return update_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, from_revision=from_revision, no_wait=no_wait) @@ -1455,6 +1460,7 @@ def copy_revision(cmd, except Exception as e: handle_raw_exception(e) + def set_revision_mode(cmd, resource_group_name, name, mode, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1478,6 +1484,7 @@ def set_revision_mode(cmd, resource_group_name, name, mode, no_wait=False): except Exception as e: handle_raw_exception(e) + def show_ingress(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1495,7 +1502,8 @@ def show_ingress(cmd, name, resource_group_name): except Exception as e: raise CLIError("The containerapp '{}' does not have ingress enabled.".format(name)) from e -def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, allow_insecure=False, no_wait=False): # pylint: disable=redefined-builtin + +def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, allow_insecure=False, no_wait=False): # pylint: disable=redefined-builtin _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None @@ -1533,6 +1541,7 @@ def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, except Exception as e: handle_raw_exception(e) + def disable_ingress(cmd, name, resource_group_name, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1557,6 +1566,7 @@ def disable_ingress(cmd, name, resource_group_name, no_wait=False): except Exception as e: handle_raw_exception(e) + def set_ingress_traffic(cmd, name, resource_group_name, traffic_weights, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1586,6 +1596,7 @@ def set_ingress_traffic(cmd, name, resource_group_name, traffic_weights, no_wait except Exception as e: handle_raw_exception(e) + def show_ingress_traffic(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1603,6 +1614,7 @@ def show_ingress_traffic(cmd, name, resource_group_name): except Exception as e: raise CLIError("Ingress must be enabled to show ingress traffic. Try running `az containerapp ingress -h` for more info.") from e + def show_registry(cmd, name, resource_group_name, server): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1627,6 +1639,7 @@ def show_registry(cmd, name, resource_group_name, server): return r raise CLIError("The containerapp {} does not have specified registry assigned.".format(name)) + def list_registry(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1644,6 +1657,7 @@ def list_registry(cmd, name, resource_group_name): except Exception as e: raise CLIError("The containerapp {} has no assigned registries.".format(name)) from e + def set_registry(cmd, name, resource_group_name, server, username=None, password=None, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1706,7 +1720,6 @@ def set_registry(cmd, name, resource_group_name, server, username=None, password server, password, update_existing_secret=True) - # Should this be false? ^ registries_def.append(registry) @@ -1718,6 +1731,7 @@ def set_registry(cmd, name, resource_group_name, server, username=None, password except Exception as e: handle_raw_exception(e) + def remove_registry(cmd, name, resource_group_name, server, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1765,6 +1779,7 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): except Exception: pass + def list_secrets(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1782,6 +1797,7 @@ def list_secrets(cmd, name, resource_group_name): except Exception as e: raise CLIError("The containerapp {} has no assigned secrets.".format(name)) from e + def show_secret(cmd, name, resource_group_name, secret_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1800,7 +1816,8 @@ def show_secret(cmd, name, resource_group_name, secret_name): return secret raise CLIError("The containerapp {} does not have a secret assigned with name {}.".format(name, secret_name)) -def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait = False): + +def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None @@ -1835,10 +1852,10 @@ def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait = False except Exception as e: handle_raw_exception(e) + def set_secrets(cmd, name, resource_group_name, secrets, - #secrets=None, - #yaml=None, - no_wait = False): + # yaml=None, + no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") # if not yaml and not secrets: @@ -1875,6 +1892,7 @@ def set_secrets(cmd, name, resource_group_name, secrets, except Exception as e: handle_raw_exception(e) + def enable_dapr(cmd, name, resource_group_name, dapr_app_id=None, dapr_app_port=None, dapr_app_protocol=None, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1910,6 +1928,7 @@ def enable_dapr(cmd, name, resource_group_name, dapr_app_id=None, dapr_app_port= except Exception as e: handle_raw_exception(e) + def disable_dapr(cmd, name, resource_group_name, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1933,21 +1952,24 @@ def disable_dapr(cmd, name, resource_group_name, no_wait=False): except Exception as e: handle_raw_exception(e) + def list_dapr_components(cmd, resource_group_name, environment_name): _validate_subscription_registered(cmd, "Microsoft.App") return DaprComponentClient.list(cmd, resource_group_name, environment_name) + def show_dapr_component(cmd, resource_group_name, dapr_component_name, environment_name): _validate_subscription_registered(cmd, "Microsoft.App") return DaprComponentClient.show(cmd, resource_group_name, environment_name, name=dapr_component_name) + def create_or_update_dapr_component(cmd, resource_group_name, environment_name, dapr_component_name, yaml): _validate_subscription_registered(cmd, "Microsoft.App") yaml_containerapp = load_yaml_file(yaml) - if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck + if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') # Deserialize the yaml into a DaprComponent object. Need this since we're not using SDK @@ -1959,7 +1981,6 @@ def create_or_update_dapr_component(cmd, resource_group_name, environment_name, except DeserializationError as ex: raise ValidationError('Invalid YAML provided. Please see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples for a valid containerapps YAML spec.') from ex - #daprcomponent_def = _object_to_dict(daprcomponent_def) daprcomponent_def = _convert_object_from_snake_to_camel_case(_object_to_dict(daprcomponent_def)) # Remove "additionalProperties" and read-only attributes that are introduced in the deserialization. Need this since we're not using SDK @@ -1979,6 +2000,7 @@ def create_or_update_dapr_component(cmd, resource_group_name, environment_name, except Exception as e: handle_raw_exception(e) + def remove_dapr_component(cmd, resource_group_name, dapr_component_name, environment_name): _validate_subscription_registered(cmd, "Microsoft.App") From a63dbc6855f263ffd97090a6e07c3d374cfd7552 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Thu, 17 Mar 2022 17:52:46 -0400 Subject: [PATCH 15/24] Fixed flake issues in src. --- .../azext_containerapp/__init__.py | 2 +- .../azext_containerapp/_client_factory.py | 2 + .../azext_containerapp/_clients.py | 24 ++--- .../azext_containerapp/_github_oauth.py | 1 + src/containerapp/azext_containerapp/_help.py | 11 +-- .../azext_containerapp/_models.py | 94 +++++++++---------- .../azext_containerapp/_params.py | 7 +- src/containerapp/azext_containerapp/_utils.py | 43 ++++++--- .../azext_containerapp/_validators.py | 9 ++ 9 files changed, 111 insertions(+), 82 deletions(-) diff --git a/src/containerapp/azext_containerapp/__init__.py b/src/containerapp/azext_containerapp/__init__.py index ad67435e53e..dcff6d86def 100644 --- a/src/containerapp/azext_containerapp/__init__.py +++ b/src/containerapp/azext_containerapp/__init__.py @@ -17,7 +17,7 @@ def __init__(self, cli_ctx=None): operations_tmpl='azext_containerapp.custom#{}', client_factory=None) super(ContainerappCommandsLoader, self).__init__(cli_ctx=cli_ctx, - custom_command_type=containerapp_custom) + custom_command_type=containerapp_custom) def load_command_table(self, args): from azext_containerapp.commands import load_command_table diff --git a/src/containerapp/azext_containerapp/_client_factory.py b/src/containerapp/azext_containerapp/_client_factory.py index 0d0f61ad5a6..9a249cdbe7e 100644 --- a/src/containerapp/azext_containerapp/_client_factory.py +++ b/src/containerapp/azext_containerapp/_client_factory.py @@ -63,11 +63,13 @@ def cf_resource_groups(cli_ctx, subscription_id=None): return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES, subscription_id=subscription_id).resource_groups + def log_analytics_client_factory(cli_ctx): from azure.mgmt.loganalytics import LogAnalyticsManagementClient return get_mgmt_service_client(cli_ctx, LogAnalyticsManagementClient).workspaces + def log_analytics_shared_key_client_factory(cli_ctx): from azure.mgmt.loganalytics import LogAnalyticsManagementClient diff --git a/src/containerapp/azext_containerapp/_clients.py b/src/containerapp/azext_containerapp/_clients.py index 0945a67b0e7..2dc138a6031 100644 --- a/src/containerapp/azext_containerapp/_clients.py +++ b/src/containerapp/azext_containerapp/_clients.py @@ -16,8 +16,8 @@ API_VERSION = "2021-03-01" NEW_API_VERSION = "2022-01-01-preview" -POLLING_TIMEOUT = 60 # how many seconds before exiting -POLLING_SECONDS = 2 # how many seconds between requests +POLLING_TIMEOUT = 60 # how many seconds before exiting +POLLING_SECONDS = 2 # how many seconds between requests class PollingAnimation(): @@ -38,7 +38,7 @@ def flush(self): sys.stdout.write("\033[K") -def poll(cmd, request_url, poll_if_status): # pylint: disable=inconsistent-return-statements +def poll(cmd, request_url, poll_if_status): # pylint: disable=inconsistent-return-statements try: start = time.time() end = time.time() + POLLING_TIMEOUT @@ -54,18 +54,19 @@ def poll(cmd, request_url, poll_if_status): # pylint: disable=inconsistent-retur r = send_raw_request(cmd.cli_ctx, "GET", request_url) r2 = r.json() - if not "properties" in r2 or not "provisioningState" in r2["properties"] or not r2["properties"]["provisioningState"].lower() == poll_if_status: + if "properties" not in r2 or "provisioningState" not in r2["properties"] or not r2["properties"]["provisioningState"].lower() == poll_if_status: break start = time.time() animation.flush() return r.json() - except Exception as e: # pylint: disable=broad-except + except Exception as e: # pylint: disable=broad-except animation.flush() - if not poll_if_status == "scheduledfordelete": # Catch "not found" errors if polling for delete + if not poll_if_status == "scheduledfordelete": # Catch "not found" errors if polling for delete raise e + class ContainerAppClient(): @classmethod def create_or_update(cls, cmd, resource_group_name, name, container_app_envelope, no_wait=False): @@ -334,6 +335,7 @@ def deactivate_revision(cls, cmd, resource_group_name, container_app_name, name) r = send_raw_request(cmd.cli_ctx, "POST", request_url) return r.json() + class ManagedEnvironmentClient(): @classmethod def create(cls, cmd, resource_group_name, name, managed_environment_envelope, no_wait=False): @@ -409,7 +411,7 @@ def delete(cls, cmd, resource_group_name, name, no_wait=False): r = send_raw_request(cmd.cli_ctx, "DELETE", request_url) if no_wait: - return # API doesn't return JSON (it returns no content) + return # API doesn't return JSON (it returns no content) elif r.status_code in [200, 201, 202, 204]: url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}?api-version={}" request_url = url_fmt.format( @@ -502,6 +504,7 @@ def list_by_resource_group(cls, cmd, resource_group_name, formatter=lambda x: x) return env_list + class GitHubActionClient(): @classmethod def create_or_update(cls, cmd, resource_group_name, name, github_action_envelope, headers, no_wait=False): @@ -548,7 +551,6 @@ def show(cls, cmd, resource_group_name, name): r = send_raw_request(cmd.cli_ctx, "GET", request_url) return r.json() - #TODO @classmethod def delete(cls, cmd, resource_group_name, name, headers, no_wait=False): management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager @@ -565,7 +567,7 @@ def delete(cls, cmd, resource_group_name, name, headers, no_wait=False): r = send_raw_request(cmd.cli_ctx, "DELETE", request_url, headers=headers) if no_wait: - return # API doesn't return JSON (it returns no content) + return # API doesn't return JSON (it returns no content) elif r.status_code in [200, 201, 202, 204]: url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/sourcecontrols/current?api-version={}" request_url = url_fmt.format( @@ -584,10 +586,10 @@ def delete(cls, cmd, resource_group_name, name, headers, no_wait=False): logger.warning('Containerapp github action successfully deleted') return + class DaprComponentClient(): @classmethod def create_or_update(cls, cmd, resource_group_name, environment_name, name, dapr_component_envelope, no_wait=False): - #create_or_update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.App/managedEnvironments/{environmentName}/daprComponents/{name}'} management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager api_version = NEW_API_VERSION @@ -635,7 +637,7 @@ def delete(cls, cmd, resource_group_name, environment_name, name, no_wait=False) r = send_raw_request(cmd.cli_ctx, "DELETE", request_url) if no_wait: - return # API doesn't return JSON (it returns no content) + return # API doesn't return JSON (it returns no content) elif r.status_code in [200, 201, 202, 204]: url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}/daprComponents/{}?api-version={}" request_url = url_fmt.format( diff --git a/src/containerapp/azext_containerapp/_github_oauth.py b/src/containerapp/azext_containerapp/_github_oauth.py index fa058863369..659d43afc39 100644 --- a/src/containerapp/azext_containerapp/_github_oauth.py +++ b/src/containerapp/azext_containerapp/_github_oauth.py @@ -23,6 +23,7 @@ "workflow" ] + def get_github_access_token(cmd, scope_list=None): # pylint: disable=unused-argument if scope_list: for scope in scope_list: diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index cb5126c3f61..a4a71960f02 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -151,7 +151,7 @@ examples: - name: Set a container app to single revision mode. text: | - az containerapp revision set-mode -n MyContainerapp -g MyResourceGroup --mode Single + az containerapp revision set-mode -n MyContainerapp -g MyResourceGroup --mode Single """ helps['containerapp revision copy'] = """ @@ -368,7 +368,7 @@ examples: - name: Show a container app's ingress traffic configuration. text: | - az containerapp ingress traffic show -n MyContainerapp -g MyResourceGroup + az containerapp ingress traffic show -n MyContainerapp -g MyResourceGroup """ # Registry Commands @@ -392,7 +392,7 @@ examples: - name: List container registries configured in a container app. text: | - az containerapp registry list -n MyContainerapp -g MyResourceGroup + az containerapp registry list -n MyContainerapp -g MyResourceGroup """ helps['containerapp registry set'] = """ @@ -403,7 +403,6 @@ text: | az containerapp registry set -n MyContainerapp -g MyResourceGroup \\ --server MyExistingContainerappRegistry.azurecr.io --username MyRegistryUsername --password MyRegistryPassword - """ helps['containerapp registry remove'] = """ @@ -454,10 +453,10 @@ examples: - name: Add secrets to a container app. text: | - az containerapp secret set -n MyContainerapp -g MyResourceGroup --secrets MySecretName1=MySecretValue1 MySecretName2=MySecretValue2 + az containerapp secret set -n MyContainerapp -g MyResourceGroup --secrets MySecretName1=MySecretValue1 MySecretName2=MySecretValue2 - name: Update a secret. text: | - az containerapp secret set -n MyContainerapp -g MyResourceGroup --secrets MyExistingSecretName=MyNewSecretValue + az containerapp secret set -n MyContainerapp -g MyResourceGroup --secrets MyExistingSecretName=MyNewSecretValue """ helps['containerapp github-action'] = """ diff --git a/src/containerapp/azext_containerapp/_models.py b/src/containerapp/azext_containerapp/_models.py index 3f6b5c60c18..d00798765c5 100644 --- a/src/containerapp/azext_containerapp/_models.py +++ b/src/containerapp/azext_containerapp/_models.py @@ -18,7 +18,7 @@ "tags": None, "properties": { "daprAIInstrumentationKey": None, - "vnetConfiguration": None, # VnetConfiguration + "vnetConfiguration": None, # VnetConfiguration "internalLoadBalancerEnabled": None, "appLogsConfiguration": None } @@ -64,15 +64,15 @@ "name": None, "command": None, "args": None, - "env": None, # [EnvironmentVar] - "resources": None, # ContainerResources - "volumeMounts": None, # [VolumeMount] + "env": None, # [EnvironmentVar] + "resources": None, # ContainerResources + "volumeMounts": None, # [VolumeMount] } Volume = { "name": None, - "storageType": "EmptyDir", # AzureFile or EmptyDir - "storageName": None # None for EmptyDir, otherwise name of storage resource + "storageType": "EmptyDir", # AzureFile or EmptyDir + "storageName": None # None for EmptyDir, otherwise name of storage resource } ScaleRuleAuth = { @@ -83,25 +83,25 @@ QueueScaleRule = { "queueName": None, "queueLength": None, - "auth": None # ScaleRuleAuth + "auth": None # ScaleRuleAuth } CustomScaleRule = { "type": None, "metadata": {}, - "auth": None # ScaleRuleAuth + "auth": None # ScaleRuleAuth } HttpScaleRule = { "metadata": {}, - "auth": None # ScaleRuleAuth + "auth": None # ScaleRuleAuth } ScaleRule = { "name": None, - "azureQueue": None, # QueueScaleRule - "customScaleRule": None, # CustomScaleRule - "httpScaleRule": None, # HttpScaleRule + "azureQueue": None, # QueueScaleRule + "customScaleRule": None, # CustomScaleRule + "httpScaleRule": None, # HttpScaleRule } Secret = { @@ -112,7 +112,7 @@ Scale = { "minReplicas": None, "maxReplicas": None, - "rules": [] # list of ScaleRule + "rules": [] # list of ScaleRule } TrafficWeight = { @@ -127,7 +127,7 @@ CustomDomain = { "name": None, - "bindingType": None, # BindingType + "bindingType": None, # BindingType "certificateId": None } @@ -135,9 +135,9 @@ "fqdn": None, "external": False, "targetPort": None, - "transport": None, # 'auto', 'http', 'http2' - "traffic": None, # TrafficWeight - "customDomains": None # [CustomDomain] + "transport": None, # 'auto', 'http', 'http2' + "traffic": None, # TrafficWeight + "customDomains": None # [CustomDomain] } RegistryCredentials = { @@ -148,17 +148,17 @@ Template = { "revisionSuffix": None, - "containers": None, # [Container] + "containers": None, # [Container] "scale": Scale, "dapr": Dapr, - "volumes": None # [Volume] + "volumes": None # [Volume] } Configuration = { - "secrets": None, # [Secret] - "activeRevisionsMode": None, # 'multiple' or 'single' - "ingress": None, # Ingress - "registries": None # [RegistryCredentials] + "secrets": None, # [Secret] + "activeRevisionsMode": None, # 'multiple' or 'single' + "ingress": None, # Ingress + "registries": None # [RegistryCredentials] } UserAssignedIdentity = { @@ -166,24 +166,24 @@ } ManagedServiceIdentity = { - "type": None, # 'None', 'SystemAssigned', 'UserAssigned', 'SystemAssigned,UserAssigned' - "userAssignedIdentities": None # {string: UserAssignedIdentity} + "type": None, # 'None', 'SystemAssigned', 'UserAssigned', 'SystemAssigned,UserAssigned' + "userAssignedIdentities": None # {string: UserAssignedIdentity} } ContainerApp = { "location": None, - "identity": None, # ManagedServiceIdentity + "identity": None, # ManagedServiceIdentity "properties": { "managedEnvironmentId": None, - "configuration": None, # Configuration - "template": None # Template + "configuration": None, # Configuration + "template": None # Template }, "tags": None } DaprComponent = { "properties": { - "componentType": None, #String + "componentType": None, # String "version": None, "ignoreErrors": None, "initTimeout": None, @@ -194,39 +194,39 @@ } DaprMetadata = { - "key": None, #str - "value": None, #str - "secret_ref": None #str + "key": None, # str + "value": None, # str + "secret_ref": None # str } SourceControl = { "properties": { "repoUrl": None, "branch": None, - "githubActionConfiguration": None # [GitHubActionConfiguration] + "githubActionConfiguration": None # [GitHubActionConfiguration] } } GitHubActionConfiguration = { - "registryInfo": None, # [RegistryInfo] - "azureCredentials": None, # [AzureCredentials] - "dockerfilePath": None, # str - "publishType": None, # str - "os": None, # str - "runtimeStack": None, # str - "runtimeVersion": None # str + "registryInfo": None, # [RegistryInfo] + "azureCredentials": None, # [AzureCredentials] + "dockerfilePath": None, # str + "publishType": None, # str + "os": None, # str + "runtimeStack": None, # str + "runtimeVersion": None # str } RegistryInfo = { - "registryUrl": None, # str - "registryUserName": None, # str - "registryPassword": None # str + "registryUrl": None, # str + "registryUserName": None, # str + "registryPassword": None # str } AzureCredentials = { - "clientId": None, # str - "clientSecret": None, # str - "tenantId": None, #str - "subscriptionId": None #str + "clientId": None, # str + "clientSecret": None, # str + "tenantId": None, # str + "subscriptionId": None # str } diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index a85e0d375fb..169b65edbe5 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -9,11 +9,12 @@ from azure.cli.core.commands.parameters import (resource_group_name_type, get_location_type, file_type, get_three_state_flag, get_enum_type, tags_type) -#from azure.cli.core.commands.validators import get_default_location_from_resource_group +# from azure.cli.core.commands.validators import get_default_location_from_resource_group from ._validators import (validate_memory, validate_cpu, validate_managed_env_name_or_id, validate_registry_server, validate_registry_user, validate_registry_pass, validate_target_port, validate_ingress) + def load_arguments(self, _): name_type = CLIArgumentType(options_list=['--name', '-n']) @@ -101,7 +102,7 @@ def load_arguments(self, _): c.argument('docker_bridge_cidr', type=str, options_list=['--docker-bridge-cidr'], help='CIDR notation IP range assigned to the Docker bridge. It must not overlap with any Subnet IP ranges or the IP range defined in Platform Reserved CIDR, if defined') c.argument('platform_reserved_cidr', type=str, options_list=['--platform-reserved-cidr'], help='IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other Subnet IP ranges') c.argument('platform_reserved_dns_ip', type=str, options_list=['--platform-reserved-dns-ip'], help='An IP address from the IP range defined by Platform Reserved CIDR that will be reserved for the internal DNS server.') - c.argument('internal_only', arg_type=get_three_state_flag(), options_list=['--internal-only'], help='Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource, therefore must provide infrastructureSubnetResourceId and appSubnetResourceId if enabling this property') + c.argument('internal_only', arg_type=get_three_state_flag(), options_list=['--internal-only'], help='Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource, therefore must provide infrastructureSubnetResourceId and appSubnetResourceId if enabling this property') with self.argument_context('containerapp env update') as c: c.argument('name', name_type, help='Name of the Container Apps environment.') @@ -162,7 +163,7 @@ def load_arguments(self, _): c.argument('dapr_app_port', help="The port of your app.") c.argument('dapr_app_protocol', help="Tells Dapr which protocol your application is using. Allowed values: grpc, http.") c.argument('dapr_component_name', help="The dapr component name.") - c.argument('environment_name', options_list=['--name','-n'], help="The environment name.") + c.argument('environment_name', options_list=['--name', '-n'], help="The environment name.") with self.argument_context('containerapp revision set-mode') as c: c.argument('mode', arg_type=get_enum_type(['single', 'multiple']), help="The active revisions mode for the container app.") diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 730b0aa8394..c963008fc7c 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -37,7 +37,7 @@ def _validate_subscription_registered(cmd, resource_provider, subscription_id=No subscription_id, resource_provider, resource_provider)) except ValidationError as ex: raise ex - except Exception: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except pass @@ -61,7 +61,7 @@ def _ensure_location_allowed(cmd, location, resource_provider, resource_type): location, resource_provider, resource_type)) except ValidationError as ex: raise ex - except Exception: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except pass @@ -75,7 +75,7 @@ def parse_env_var_flags(env_list, is_update_containerapp=False): raise ValidationError("Environment variables must be in the format \"=\" \"=secretref:\" ...\".") raise ValidationError("Environment variables must be in the format \"=\" \"=secretref:\" ...\".") if key_val[0] in env_pairs: - raise ValidationError("Duplicate environment variable {env} found, environment variable names must be unique.".format(env = key_val[0])) + raise ValidationError("Duplicate environment variable {env} found, environment variable names must be unique.".format(env=key_val[0])) value = key_val[1].split('secretref:') env_pairs[key_val[0]] = value @@ -103,7 +103,7 @@ def parse_secret_flags(secret_list): if len(key_val) != 2: raise ValidationError("--secrets: must be in format \"=,=,...\"") if key_val[0] in secret_pairs: - raise ValidationError("--secrets: duplicate secret {secret} found, secret names must be unique.".format(secret = key_val[0])) + raise ValidationError("--secrets: duplicate secret {secret} found, secret names must be unique.".format(secret=key_val[0])) secret_pairs[key_val[0]] = key_val[1] secret_var_def = [] @@ -115,6 +115,7 @@ def parse_secret_flags(secret_list): return secret_var_def + def _update_revision_env_secretrefs(containers, name): for container in containers: if "env" in container: @@ -122,6 +123,7 @@ def _update_revision_env_secretrefs(containers, name): if "secretRef" in var: var["secretRef"] = var["secretRef"].replace("{}-".format(name), "") + def store_as_secret_and_return_secret_ref(secrets_list, registry_user, registry_server, registry_pass, update_existing_secret=False): if registry_pass.startswith("secretref:"): # If user passed in registry password using a secret @@ -152,7 +154,7 @@ def store_as_secret_and_return_secret_ref(secrets_list, registry_user, registry_ raise ValidationError('Found secret with name \"{}\" but value does not equal the supplied registry password.'.format(registry_secret_name)) return registry_secret_name - logger.warning('Adding registry password as a secret with name \"{}\"'.format(registry_secret_name)) # pylint: disable=logging-format-interpolation + logger.warning('Adding registry password as a secret with name \"{}\"'.format(registry_secret_name)) # pylint: disable=logging-format-interpolation secrets_list.append({ "name": registry_secret_name, "value": registry_pass @@ -165,6 +167,7 @@ def parse_list_of_strings(comma_separated_string): comma_separated = comma_separated_string.split(',') return [s.strip() for s in comma_separated] + def raise_missing_token_suggestion(): pat_documentation = "https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line" raise RequiredArgumentMissingError("GitHub access token is required to authenticate to your repositories. " @@ -172,6 +175,7 @@ def raise_missing_token_suggestion(): "please run with the '--login-with-github' flag or follow " "the steps found at the following link:\n{0}".format(pat_documentation)) + def _get_default_log_analytics_location(cmd): default_location = "eastus" providers_client = None @@ -188,16 +192,18 @@ def _get_default_log_analytics_location(cmd): if location: return location - except Exception: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except return default_location return default_location + # Generate random 4 character string def _new_tiny_guid(): import random import string return ''.join(random.choices(string.ascii_letters + string.digits, k=4)) + # Follow same naming convention as Portal def _generate_log_analytics_workspace_name(resource_group_name): import re @@ -229,7 +235,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc log_analytics_location = location try: _ensure_location_allowed(cmd, log_analytics_location, "Microsoft.OperationalInsights", "workspaces") - except Exception: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except log_analytics_location = _get_default_log_analytics_location(cmd) from azure.cli.core.commands import LongRunningOperation @@ -237,7 +243,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc workspace_name = _generate_log_analytics_workspace_name(resource_group_name) workspace_instance = Workspace(location=log_analytics_location) - logger.warning("Generating a Log Analytics workspace with name \"{}\"".format(workspace_name)) # pylint: disable=logging-format-interpolation + logger.warning("Generating a Log Analytics workspace with name \"{}\"".format(workspace_name)) # pylint: disable=logging-format-interpolation poller = log_analytics_client.begin_create_or_update(resource_group_name, workspace_name, workspace_instance) log_analytics_workspace = LongRunningOperation(cmd.cli_ctx)(poller) @@ -251,7 +257,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc raise ValidationError("Unable to generate a Log Analytics workspace. You can use \"az monitor log-analytics workspace create\" to create one and supply --logs-customer-id and --logs-key") from ex elif logs_customer_id is None: raise ValidationError("Usage error: Supply the --logs-customer-id associated with the --logs-key") - elif logs_key is None: # Try finding the logs-key + elif logs_key is None: # Try finding the logs-key log_analytics_client = log_analytics_client_factory(cmd.cli_ctx) log_analytics_shared_key_client = log_analytics_shared_key_client_factory(cmd.cli_ctx) @@ -285,11 +291,12 @@ def _get_existing_secrets(cmd, resource_group_name, name, containerapp_def): secrets = [] try: secrets = ContainerAppClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name) - except Exception as e: # pylint: disable=broad-except + except Exception as e: # pylint: disable=broad-except handle_raw_exception(e) containerapp_def["properties"]["configuration"]["secrets"] = secrets["value"] + def _ensure_identity_resource_id(subscription_id, resource_group, resource): from msrestazure.tools import resource_id, is_valid_resource_id if is_valid_resource_id(resource): @@ -301,6 +308,7 @@ def _ensure_identity_resource_id(subscription_id, resource_group, resource): type='userAssignedIdentities', name=resource) + def _add_or_update_secrets(containerapp_def, add_secrets): if "secrets" not in containerapp_def["properties"]["configuration"]: containerapp_def["properties"]["configuration"]["secrets"] = [] @@ -316,6 +324,7 @@ def _add_or_update_secrets(containerapp_def, add_secrets): if not is_existing: containerapp_def["properties"]["configuration"]["secrets"].append(new_secret) + def _remove_registry_secret(containerapp_def, server, username): if urlparse(server).hostname is not None: registry_secret_name = "{server}-{user}".format(server=urlparse(server).hostname.replace('.', ''), user=username.lower()) @@ -324,6 +333,7 @@ def _remove_registry_secret(containerapp_def, server, username): _remove_secret(containerapp_def, secret_name=registry_secret_name) + def _remove_secret(containerapp_def, secret_name): if "secrets" not in containerapp_def["properties"]["configuration"]: containerapp_def["properties"]["configuration"]["secrets"] = [] @@ -334,6 +344,7 @@ def _remove_secret(containerapp_def, secret_name): containerapp_def["properties"]["configuration"]["secrets"].pop(index) break + def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): for new_env_var in new_env_vars: @@ -343,7 +354,7 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): if existing_env_var["name"].lower() == new_env_var["name"].lower(): is_existing = True if is_add: - logger.warning("Environment variable {} already exists. Replacing environment variable value.".format(new_env_var["name"])) # pylint: disable=logging-format-interpolation + logger.warning("Environment variable {} already exists. Replacing environment variable value.".format(new_env_var["name"])) # pylint: disable=logging-format-interpolation if "value" in new_env_var: existing_env_var["value"] = new_env_var["value"] @@ -359,9 +370,10 @@ def _add_or_update_env_vars(existing_env_vars, new_env_vars, is_add=False): # If not updating existing env var, add it as a new env var if not is_existing: if not is_add: - logger.warning("Environment variable {} does not exist. Adding as new environment variable.".format(new_env_var["name"])) # pylint: disable=logging-format-interpolation + logger.warning("Environment variable {} does not exist. Adding as new environment variable.".format(new_env_var["name"])) # pylint: disable=logging-format-interpolation existing_env_vars.append(new_env_var) + def _remove_env_vars(existing_env_vars, remove_env_vars): for old_env_var in remove_env_vars: @@ -376,7 +388,8 @@ def _remove_env_vars(existing_env_vars, remove_env_vars): # If not updating existing env var, add it as a new env var if not is_existing: - logger.warning("Environment variable {} does not exist.".format(old_env_var)) # pylint: disable=logging-format-interpolation + logger.warning("Environment variable {} does not exist.".format(old_env_var)) # pylint: disable=logging-format-interpolation + def _add_or_update_tags(containerapp_def, tags): if 'tags' not in containerapp_def: @@ -439,6 +452,7 @@ def _remove_readonly_attributes(containerapp_def): elif unneeded_property in containerapp_def['properties']: del containerapp_def['properties'][unneeded_property] + def _remove_dapr_readonly_attributes(daprcomponent_def): unneeded_properties = [ "id", @@ -457,13 +471,14 @@ def _remove_dapr_readonly_attributes(daprcomponent_def): if unneeded_property in daprcomponent_def: del daprcomponent_def[unneeded_property] + def update_nested_dictionary(orig_dict, new_dict): # Recursively update a nested dictionary. If the value is a list, replace the old list with new list import collections for key, val in new_dict.items(): if isinstance(val, collections.Mapping): - tmp = update_nested_dictionary(orig_dict.get(key, { }), val) + tmp = update_nested_dictionary(orig_dict.get(key, {}), val) orig_dict[key] = tmp elif isinstance(val, list): if new_dict[key]: diff --git a/src/containerapp/azext_containerapp/_validators.py b/src/containerapp/azext_containerapp/_validators.py index 090c355d7af..e7fe0435a11 100644 --- a/src/containerapp/azext_containerapp/_validators.py +++ b/src/containerapp/azext_containerapp/_validators.py @@ -6,6 +6,7 @@ from azure.cli.core.azclierror import (ValidationError) + def _is_number(s): try: float(s) @@ -13,6 +14,7 @@ def _is_number(s): except ValueError: return False + def validate_memory(namespace): memory = namespace.memory @@ -25,6 +27,7 @@ def validate_memory(namespace): if not valid: raise ValidationError("Usage error: --memory must be a number ending with \"Gi\"") + def validate_cpu(namespace): if namespace.cpu: cpu = namespace.cpu @@ -33,6 +36,7 @@ def validate_cpu(namespace): except ValueError as e: raise ValidationError("Usage error: --cpu must be a number eg. \"0.5\"") from e + def validate_managed_env_name_or_id(cmd, namespace): from azure.cli.core.commands.client_factory import get_subscription_id from msrestazure.tools import is_valid_resource_id, resource_id @@ -47,6 +51,7 @@ def validate_managed_env_name_or_id(cmd, namespace): name=namespace.managed_env ) + def validate_registry_server(namespace): if "create" in namespace.command.lower(): if namespace.registry_server: @@ -54,24 +59,28 @@ def validate_registry_server(namespace): if ".azurecr.io" not in namespace.registry_server: raise ValidationError("Usage error: --registry-server, --registry-password and --registry-username are required together if not using Azure Container Registry") + def validate_registry_user(namespace): if "create" in namespace.command.lower(): if namespace.registry_user: if not namespace.registry_server or (not namespace.registry_pass and ".azurecr.io" not in namespace.registry_server): raise ValidationError("Usage error: --registry-server, --registry-password and --registry-username are required together if not using Azure Container Registry") + def validate_registry_pass(namespace): if "create" in namespace.command.lower(): if namespace.registry_pass: if not namespace.registry_server or (not namespace.registry_user and ".azurecr.io" not in namespace.registry_server): raise ValidationError("Usage error: --registry-server, --registry-password and --registry-username are required together if not using Azure Container Registry") + def validate_target_port(namespace): if "create" in namespace.command.lower(): if namespace.target_port: if not namespace.ingress: raise ValidationError("Usage error: must specify --ingress with --target-port") + def validate_ingress(namespace): if "create" in namespace.command.lower(): if namespace.ingress: From 5294ff964ac5a2cffde84f63153ec4a7feb451e0 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Thu, 17 Mar 2022 17:54:24 -0400 Subject: [PATCH 16/24] Added license header to _sdk_models. --- src/containerapp/azext_containerapp/_sdk_models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/containerapp/azext_containerapp/_sdk_models.py b/src/containerapp/azext_containerapp/_sdk_models.py index 473bf5e19f1..b34325cdb9c 100644 --- a/src/containerapp/azext_containerapp/_sdk_models.py +++ b/src/containerapp/azext_containerapp/_sdk_models.py @@ -1,3 +1,8 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + # coding=utf-8 # -------------------------------------------------------------------------- # Code generated by Microsoft (R) AutoRest Code Generator. From 82f389cdc9e838e32ed8d04c3f13af00b56f6207 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Thu, 17 Mar 2022 18:03:02 -0400 Subject: [PATCH 17/24] Added confirmation for containerapp delete. --- src/containerapp/azext_containerapp/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 73008caaf8d..87a892201a8 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -48,7 +48,7 @@ def load_command_table(self, _): g.custom_command('list', 'list_containerapp', table_transformer=transform_containerapp_list_output) g.custom_command('create', 'create_containerapp', supports_no_wait=True, exception_handler=ex_handler_factory()) g.custom_command('update', 'update_containerapp', supports_no_wait=True, exception_handler=ex_handler_factory()) - g.custom_command('delete', 'delete_containerapp', exception_handler=ex_handler_factory()) + g.custom_command('delete', 'delete_containerapp', confirmation=True, exception_handler=ex_handler_factory()) with self.command_group('containerapp env') as g: g.custom_command('show', 'show_managed_environment') From 04b470535d6e51eb4f1e34c57b6faa19ba7ed216 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Fri, 18 Mar 2022 14:16:58 -0400 Subject: [PATCH 18/24] Update helps for identity, revision. Removed env-var alias for set-env-vars. Added name param help. --- src/containerapp/azext_containerapp/_help.py | 18 +++++++++++++----- src/containerapp/azext_containerapp/_params.py | 4 ++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index a4a71960f02..a306f2f6bd7 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -124,7 +124,7 @@ examples: - name: Restart a revision. text: | - az containerapp revision restart -n MyContainerapp -g MyResourceGroup --revision-name MyContainerappRevision + az containerapp revision restart -n MyContainerapp -g MyResourceGroup --revision MyContainerappRevision """ helps['containerapp revision activate'] = """ @@ -133,7 +133,7 @@ examples: - name: Activate a revision. text: | - az containerapp revision activate -n MyContainerapp -g MyResourceGroup --revision-name MyContainerappRevision + az containerapp revision activate -g MyResourceGroup --revision MyContainerappRevision """ helps['containerapp revision deactivate'] = """ @@ -142,7 +142,7 @@ examples: - name: Deactivate a revision. text: | - az containerapp revision deactivate -n MyContainerapp -g MyResourceGroup --revision-name MyContainerappRevision + az containerapp revision deactivate -g MyResourceGroup --revision MyContainerappRevision """ helps['containerapp revision set-mode'] = """ @@ -158,10 +158,15 @@ type: command short-summary: Create a revision based on a previous revision. examples: - - name: Create a revision based on a previous revision. + - name: Create a revision based on the latest revision. text: | az containerapp revision copy -n MyContainerapp -g MyResourceGroup \\ + --cpu 0.75 --memory 1.5Gi + - name: Create a revision based on a previous revision. + text: | + az containerapp revision copy -g MyResourceGroup \\ --from-revision PreviousRevisionName --cpu 0.75 --memory 1.5Gi + """ helps['containerapp revision copy'] = """ @@ -231,7 +236,7 @@ helps['containerapp env dapr-component'] = """ type: group - short-summary: Commands to manage Container App environment dapr components. + short-summary: Commmands to manage dapr components on the Container App environment. """ helps['containerapp env dapr-component list'] = """ @@ -284,6 +289,9 @@ - name: Assign system identity. text: | az containerapp identity assign + - name: Assign user identity. + text: | + az containerapp identity assign --identities myAssignedId - name: Assign system and user identity. text: | az containerapp identity assign --identities [system] myAssignedId diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 169b65edbe5..60605b399bd 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -21,7 +21,7 @@ def load_arguments(self, _): with self.argument_context('containerapp') as c: # Base arguments - c.argument('name', name_type, metavar='NAME', id_part='name') + c.argument('name', name_type, metavar='NAME', id_part='name', help="The name of the Containerapp.") c.argument('resource_group_name', arg_type=resource_group_name_type) c.argument('location', arg_type=get_location_type(self.cli_ctx)) @@ -43,7 +43,7 @@ def load_arguments(self, _): # Env vars with self.argument_context('containerapp', arg_group='Environment variables (Creates new revision)') as c: - c.argument('set_env_vars', options_list=['--set-env-vars, --env-vars'], nargs='*', help="A list of environment variable(s) to add to the container. Space-separated values in 'key=value' format. If stored as a secret, value must start with \'secretref:\' followed by the secret name.") + c.argument('set_env_vars', nargs='*', help="A list of environment variable(s) to add to the container. Space-separated values in 'key=value' format. If stored as a secret, value must start with \'secretref:\' followed by the secret name.") c.argument('remove_env_vars', nargs='*', help="A list of environment variable(s) to remove from container. Space-separated env var name values.") c.argument('replace_env_vars', nargs='*', help="A list of environment variable(s) to replace from the container. Space-separated values in 'key=value' format. If stored as a secret, value must start with \'secretref:\' followed by the secret name.") c.argument('remove_all_env_vars', help="Option to remove all environment variable(s) from the container.") From 10037278b6698f169d7fc7826ec02472e1aaadf1 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Fri, 18 Mar 2022 14:31:27 -0400 Subject: [PATCH 19/24] Removed app-subnet-resource-id. --- src/containerapp/azext_containerapp/custom.py | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index d19ff49ea69..8970362ba0c 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -731,7 +731,7 @@ def create_managed_environment(cmd, location=None, instrumentation_key=None, infrastructure_subnet_resource_id=None, - app_subnet_resource_id=None, + # app_subnet_resource_id=None, docker_bridge_cidr=None, platform_reserved_cidr=None, platform_reserved_dns_ip=None, @@ -745,13 +745,13 @@ def create_managed_environment(cmd, _ensure_location_allowed(cmd, location, "Microsoft.App", "managedEnvironments") # Microsoft.ContainerService RP registration is required for vnet enabled environments - if infrastructure_subnet_resource_id is not None or app_subnet_resource_id is not None: - if is_valid_resource_id(app_subnet_resource_id): - parsed_app_subnet_resource_id = parse_resource_id(app_subnet_resource_id) - subnet_subscription = parsed_app_subnet_resource_id["subscription"] - _validate_subscription_registered(cmd, "Microsoft.ContainerService", subnet_subscription) - else: - raise ValidationError('Subnet resource ID is invalid.') + # if infrastructure_subnet_resource_id is not None or app_subnet_resource_id is not None: + # if is_valid_resource_id(app_subnet_resource_id): + # parsed_app_subnet_resource_id = parse_resource_id(app_subnet_resource_id) + # subnet_subscription = parsed_app_subnet_resource_id["subscription"] + # _validate_subscription_registered(cmd, "Microsoft.ContainerService", subnet_subscription) + # else: + # raise ValidationError('Subnet resource ID is invalid.') if logs_customer_id is None or logs_key is None: logs_customer_id, logs_key = _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, location, resource_group_name) @@ -773,19 +773,12 @@ def create_managed_environment(cmd, if instrumentation_key is not None: managed_env_def["properties"]["daprAIInstrumentationKey"] = instrumentation_key - if infrastructure_subnet_resource_id or app_subnet_resource_id or docker_bridge_cidr or platform_reserved_cidr or platform_reserved_dns_ip: + if infrastructure_subnet_resource_id or docker_bridge_cidr or platform_reserved_cidr or platform_reserved_dns_ip: vnet_config_def = VnetConfigurationModel if infrastructure_subnet_resource_id is not None: - if not app_subnet_resource_id: - raise ValidationError('App subnet resource ID needs to be supplied with infrastructure subnet resource ID.') vnet_config_def["infrastructureSubnetId"] = infrastructure_subnet_resource_id - if app_subnet_resource_id is not None: - if not infrastructure_subnet_resource_id: - raise ValidationError('Infrastructure subnet resource ID needs to be supplied with app subnet resource ID.') - vnet_config_def["runtimeSubnetId"] = app_subnet_resource_id - if docker_bridge_cidr is not None: vnet_config_def["dockerBridgeCidr"] = docker_bridge_cidr @@ -798,8 +791,8 @@ def create_managed_environment(cmd, managed_env_def["properties"]["vnetConfiguration"] = vnet_config_def if internal_only: - if not infrastructure_subnet_resource_id or not app_subnet_resource_id: - raise ValidationError('Infrastructure subnet resource ID and App subnet resource ID need to be supplied for internal only environments.') + if not infrastructure_subnet_resource_id: + raise ValidationError('Infrastructure subnet resource ID needs to be supplied for internal only environments.') managed_env_def["properties"]["internalLoadBalancerEnabled"] = True try: From ab81c230784d853d6ad5cf869da3f2d23373ecdb Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Fri, 18 Mar 2022 16:06:54 -0400 Subject: [PATCH 20/24] Updated infrastructure subnet param help. --- src/containerapp/azext_containerapp/_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 60605b399bd..0179e1f77f7 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -97,7 +97,7 @@ def load_arguments(self, _): c.argument('instrumentation_key', options_list=['--dapr-instrumentation-key'], help='Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry') with self.argument_context('containerapp env', arg_group='Virtual Network') as c: - c.argument('infrastructure_subnet_resource_id', type=str, options_list=['--infrastructure-subnet-resource-id'], help='Resource ID of a subnet for infrastructure components. This subnet must be in the same VNET as the subnet defined in appSubnetResourceId.') + c.argument('infrastructure_subnet_resource_id', type=str, options_list=['--infrastructure-subnet-resource-id'], help='Resource ID of a subnet for infrastructure components and user app containers.') c.argument('app_subnet_resource_id', type=str, options_list=['--app-subnet-resource-id'], help='Resource ID of a subnet that Container App containers are injected into. This subnet must be in the same VNET as the subnet defined in infrastructureSubnetResourceId.') c.argument('docker_bridge_cidr', type=str, options_list=['--docker-bridge-cidr'], help='CIDR notation IP range assigned to the Docker bridge. It must not overlap with any Subnet IP ranges or the IP range defined in Platform Reserved CIDR, if defined') c.argument('platform_reserved_cidr', type=str, options_list=['--platform-reserved-cidr'], help='IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other Subnet IP ranges') From 8aa4e0af8e41ec8a772b3c6c6dbdb5a78b64e39a Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Fri, 18 Mar 2022 17:45:23 -0400 Subject: [PATCH 21/24] Check if containerapp resource exists before attempting to delete. --- src/containerapp/azext_containerapp/custom.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 8970362ba0c..dd60c2c6f54 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -717,6 +717,11 @@ def list_containerapp(cmd, resource_group_name=None): def delete_containerapp(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") + try: + ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) + except CLIError as e: + handle_raw_exception(e) + try: return ContainerAppClient.delete(cmd=cmd, name=name, resource_group_name=resource_group_name) except CLIError as e: From e3b5ef602f346e12887b62aab66c86fa71866c0a Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Fri, 18 Mar 2022 17:46:40 -0400 Subject: [PATCH 22/24] Added check before deleting managed env. --- src/containerapp/azext_containerapp/custom.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index dd60c2c6f54..877bf076187 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -847,6 +847,11 @@ def list_managed_environments(cmd, resource_group_name=None): def delete_managed_environment(cmd, name, resource_group_name, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") + try: + ManagedEnvironmentClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) + except CLIError as e: + handle_raw_exception(e) + try: return ManagedEnvironmentClient.delete(cmd=cmd, name=name, resource_group_name=resource_group_name, no_wait=no_wait) except CLIError as e: From d83e2763f5d9b36c8d8f973b6a752648030a175b Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Mon, 21 Mar 2022 13:42:59 -0400 Subject: [PATCH 23/24] Changed error types to be more specific. --- .../azext_containerapp/_client_factory.py | 11 +- src/containerapp/azext_containerapp/custom.py | 131 +++++++++--------- 2 files changed, 70 insertions(+), 72 deletions(-) diff --git a/src/containerapp/azext_containerapp/_client_factory.py b/src/containerapp/azext_containerapp/_client_factory.py index 9a249cdbe7e..4e8ad424138 100644 --- a/src/containerapp/azext_containerapp/_client_factory.py +++ b/src/containerapp/azext_containerapp/_client_factory.py @@ -6,8 +6,7 @@ from azure.cli.core.commands.client_factory import get_mgmt_service_client from azure.cli.core.profiles import ResourceType - -from knack.util import CLIError +from azure.cli.core.azclierror import CLIInternalError # pylint: disable=inconsistent-return-statements @@ -21,7 +20,7 @@ def _polish_bad_errors(ex): elif 'Message' in content: detail = content['Message'] - ex = CLIError(detail) + ex = CLIInternalError(detail) except Exception: # pylint: disable=broad-except pass if no_throw: @@ -45,13 +44,13 @@ def handle_raw_exception(e): if 'code' in jsonError and 'message' in jsonError: code = jsonError['code'] message = jsonError['message'] - raise CLIError('({}) {}'.format(code, message)) + raise CLIInternalError('({}) {}'.format(code, message)) elif "Message" in jsonError: message = jsonError["Message"] - raise CLIError(message) + raise CLIInternalError(message) elif "message" in jsonError: message = jsonError["message"] - raise CLIError(message) + raise CLIInternalError(message) raise e diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 877bf076187..ca6003d1a4d 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -6,9 +6,8 @@ from urllib.parse import urlparse from azure.cli.command_modules.appservice.custom import (_get_acr_cred) -from azure.cli.core.azclierror import (RequiredArgumentMissingError, ValidationError) +from azure.cli.core.azclierror import (RequiredArgumentMissingError, ValidationError, ResourceNotFoundError, CLIInternalError, InvalidArgumentValueError) from azure.cli.core.commands.client_factory import get_subscription_id -from knack.util import CLIError from knack.log import get_logger from msrestazure.tools import parse_resource_id, is_valid_resource_id @@ -72,10 +71,10 @@ def load_yaml_file(file_name): return yaml.safe_load(stream) except (IOError, OSError) as ex: if getattr(ex, 'errno', 0) == errno.ENOENT: - raise CLIError('{} does not exist'.format(file_name)) from ex + raise ValidationError('{} does not exist'.format(file_name)) from ex raise except (yaml.parser.ParserError, UnicodeDecodeError) as ex: - raise CLIError('Error parsing {} ({})'.format(file_name, str(ex))) from ex + raise ValidationError('Error parsing {} ({})'.format(file_name, str(ex))) from ex def create_deserializer(): @@ -123,7 +122,7 @@ def update_containerapp_yaml(cmd, name, resource_group_name, file_name, from_rev if from_revision: try: r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) _update_revision_env_secretrefs(r["properties"]["template"]["containers"], name) current_containerapp_def["properties"]["template"] = r["properties"]["template"] @@ -483,7 +482,7 @@ def update_containerapp(cmd, pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) # Doing this while API has bug. If env var is an empty string, API doesn't return "value" even though the "value" should be an empty string if "properties" in containerapp_def and "template" in containerapp_def["properties"] and "containers" in containerapp_def["properties"]["template"]: @@ -695,7 +694,7 @@ def show_containerapp(cmd, name, resource_group_name): try: return ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -710,7 +709,7 @@ def list_containerapp(cmd, resource_group_name=None): containerapps = ContainerAppClient.list_by_resource_group(cmd=cmd, resource_group_name=resource_group_name) return containerapps - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -719,12 +718,12 @@ def delete_containerapp(cmd, name, resource_group_name): try: ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) try: return ContainerAppClient.delete(cmd=cmd, name=name, resource_group_name=resource_group_name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -817,7 +816,7 @@ def update_managed_environment(cmd, resource_group_name, tags=None, no_wait=False): - raise CLIError('Containerapp env update is not yet supported.') + raise CLIInternalError('Containerapp env update is not yet supported.') def show_managed_environment(cmd, name, resource_group_name): @@ -825,7 +824,7 @@ def show_managed_environment(cmd, name, resource_group_name): try: return ManagedEnvironmentClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -840,7 +839,7 @@ def list_managed_environments(cmd, resource_group_name=None): managed_envs = ManagedEnvironmentClient.list_by_resource_group(cmd=cmd, resource_group_name=resource_group_name) return managed_envs - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -849,12 +848,12 @@ def delete_managed_environment(cmd, name, resource_group_name, no_wait=False): try: ManagedEnvironmentClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) try: return ManagedEnvironmentClient.delete(cmd=cmd, name=name, resource_group_name=resource_group_name, no_wait=no_wait) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -879,7 +878,7 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -959,7 +958,7 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -972,11 +971,11 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= containerapp_def["identity"]["type"] = "None" if containerapp_def["identity"]["type"] == "None": - raise CLIError("The containerapp {} has no system or user assigned identities.".format(name)) + raise InvalidArgumentValueError("The containerapp {} has no system or user assigned identities.".format(name)) if remove_system_identity: if containerapp_def["identity"]["type"] == "UserAssigned": - raise CLIError("The containerapp {} has no system assigned identities.".format(name)) + raise InvalidArgumentValueError("The containerapp {} has no system assigned identities.".format(name)) containerapp_def["identity"]["type"] = ("None" if containerapp_def["identity"]["type"] == "SystemAssigned" else "UserAssigned") if remove_user_identities: @@ -997,7 +996,7 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= break if not wasRemoved: - raise CLIError("The containerapp does not have specified user identity '{}' assigned, so it cannot be removed.".format(given_id)) + raise InvalidArgumentValueError("The containerapp does not have specified user identity '{}' assigned, so it cannot be removed.".format(given_id)) if containerapp_def["identity"]["userAssignedIdentities"] == {}: containerapp_def["identity"]["userAssignedIdentities"] = None @@ -1015,7 +1014,7 @@ def show_managed_identity(cmd, name, resource_group_name): try: r = ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) try: @@ -1064,25 +1063,25 @@ def create_or_update_github_action(cmd, try: github_repo = g.get_repo(repo) if not github_repo.permissions.push or not github_repo.permissions.maintain: - raise CLIError("The token does not have appropriate access rights to repository {}.".format(repo)) + raise ValidationError("The token does not have appropriate access rights to repository {}.".format(repo)) try: github_repo.get_branch(branch=branch) except GithubException as e: error_msg = "Encountered GitHub error when accessing {} branch in {} repo.".format(branch, repo) if e.data and e.data['message']: error_msg += " Error: {}".format(e.data['message']) - raise CLIError(error_msg) from e + raise CLIInternalError(error_msg) from e logger.warning('Verified GitHub repo and branch') except BadCredentialsException as e: - raise CLIError("Could not authenticate to the repository. Please create a Personal Access Token and use " + raise ValidationError("Could not authenticate to the repository. Please create a Personal Access Token and use " "the --token argument. Run 'az webapp deployment github-actions add --help' " "for more information.") from e except GithubException as e: error_msg = "Encountered GitHub error when accessing {} repo".format(repo) if e.data and e.data['message']: error_msg += " Error: {}".format(e.data['message']) - raise CLIError(error_msg) from e - except CLIError as clierror: + raise CLIInternalError(error_msg) from e + except CLIInternalError as clierror: raise clierror except Exception: # If exception due to github package missing, etc just continue without validating the repo and rely on api validation @@ -1190,17 +1189,17 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_ try: github_repo = g.get_repo(repo) if not github_repo.permissions.push or not github_repo.permissions.maintain: - raise CLIError("The token does not have appropriate access rights to repository {}.".format(repo)) + raise ValidationError("The token does not have appropriate access rights to repository {}.".format(repo)) except BadCredentialsException as e: - raise CLIError("Could not authenticate to the repository. Please create a Personal Access Token and use " + raise CLIInternalError("Could not authenticate to the repository. Please create a Personal Access Token and use " "the --token argument. Run 'az webapp deployment github-actions add --help' " "for more information.") from e except GithubException as e: error_msg = "Encountered GitHub error when accessing {} repo".format(repo) if e.data and e.data['message']: error_msg += " Error: {}".format(e.data['message']) - raise CLIError(error_msg) from e - except CLIError as clierror: + raise CLIInternalError(error_msg) from e + except CLIInternalError as clierror: raise clierror except Exception: # If exception due to github package missing, etc just continue without validating the repo and rely on api validation @@ -1217,7 +1216,7 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_ def list_revisions(cmd, name, resource_group_name): try: return ContainerAppClient.list_revisions(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -1227,7 +1226,7 @@ def show_revision(cmd, resource_group_name, revision_name, name=None): try: return ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -1237,7 +1236,7 @@ def restart_revision(cmd, resource_group_name, revision_name, name=None): try: return ContainerAppClient.restart_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -1247,7 +1246,7 @@ def activate_revision(cmd, resource_group_name, revision_name, name=None): try: return ContainerAppClient.activate_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -1257,7 +1256,7 @@ def deactivate_revision(cmd, resource_group_name, revision_name, name=None): try: return ContainerAppClient.deactivate_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) - except CLIError as e: + except CLIInternalError as e: handle_raw_exception(e) @@ -1305,12 +1304,12 @@ def copy_revision(cmd, pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) if from_revision: try: r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) - except CLIError as e: + except CLIInternalError as e: # Error handle the case where revision not found? handle_raw_exception(e) @@ -1473,7 +1472,7 @@ def set_revision_mode(cmd, resource_group_name, name, mode, no_wait=False): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) containerapp_def["properties"]["configuration"]["activeRevisionsMode"] = mode.lower() @@ -1497,12 +1496,12 @@ def show_ingress(cmd, name, resource_group_name): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) try: return containerapp_def["properties"]["configuration"]["ingress"] except Exception as e: - raise CLIError("The containerapp '{}' does not have ingress enabled.".format(name)) from e + raise ValidationError("The containerapp '{}' does not have ingress enabled.".format(name)) from e def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, allow_insecure=False, no_wait=False): # pylint: disable=redefined-builtin @@ -1515,7 +1514,7 @@ def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) external_ingress = None if type is not None: @@ -1554,7 +1553,7 @@ def disable_ingress(cmd, name, resource_group_name, no_wait=False): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) containerapp_def["properties"]["configuration"]["ingress"] = None @@ -1579,12 +1578,12 @@ def set_ingress_traffic(cmd, name, resource_group_name, traffic_weights, no_wait pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) try: containerapp_def["properties"]["configuration"]["ingress"] except Exception as e: - raise CLIError("Ingress must be enabled to set ingress traffic. Try running `az containerapp ingress -h` for more info.") from e + raise ValidationError("Ingress must be enabled to set ingress traffic. Try running `az containerapp ingress -h` for more info.") from e if traffic_weights is not None: _update_traffic_weights(containerapp_def, traffic_weights) @@ -1609,12 +1608,12 @@ def show_ingress_traffic(cmd, name, resource_group_name): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) try: return containerapp_def["properties"]["configuration"]["ingress"]["traffic"] except Exception as e: - raise CLIError("Ingress must be enabled to show ingress traffic. Try running `az containerapp ingress -h` for more info.") from e + raise ValidationError("Ingress must be enabled to show ingress traffic. Try running `az containerapp ingress -h` for more info.") from e def show_registry(cmd, name, resource_group_name, server): @@ -1627,19 +1626,19 @@ def show_registry(cmd, name, resource_group_name, server): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) try: containerapp_def["properties"]["configuration"]["registries"] except Exception as e: - raise CLIError("The containerapp {} has no assigned registries.".format(name)) from e + raise ValidationError("The containerapp {} has no assigned registries.".format(name)) from e registries_def = containerapp_def["properties"]["configuration"]["registries"] for r in registries_def: if r['server'].lower() == server.lower(): return r - raise CLIError("The containerapp {} does not have specified registry assigned.".format(name)) + raise InvalidArgumentValueError("The containerapp {} does not have specified registry assigned.".format(name)) def list_registry(cmd, name, resource_group_name): @@ -1652,12 +1651,12 @@ def list_registry(cmd, name, resource_group_name): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) try: return containerapp_def["properties"]["configuration"]["registries"] except Exception as e: - raise CLIError("The containerapp {} has no assigned registries.".format(name)) from e + raise ValidationError("The containerapp {} has no assigned registries.".format(name)) from e def set_registry(cmd, name, resource_group_name, server, username=None, password=None, no_wait=False): @@ -1670,7 +1669,7 @@ def set_registry(cmd, name, resource_group_name, server, username=None, password pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -1744,7 +1743,7 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -1753,7 +1752,7 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): try: containerapp_def["properties"]["configuration"]["registries"] except Exception as e: - raise CLIError("The containerapp {} has no assigned registries.".format(name)) from e + raise ValidationError("The containerapp {} has no assigned registries.".format(name)) from e registries_def = containerapp_def["properties"]["configuration"]["registries"] @@ -1767,7 +1766,7 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): break if not wasRemoved: - raise CLIError("Containerapp does not have registry server {} assigned.".format(server)) + raise ValidationError("Containerapp does not have registry server {} assigned.".format(server)) if len(containerapp_def["properties"]["configuration"]["registries"]) == 0: containerapp_def["properties"]["configuration"].pop("registries") @@ -1792,12 +1791,12 @@ def list_secrets(cmd, name, resource_group_name): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) try: return ContainerAppClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name)["value"] except Exception as e: - raise CLIError("The containerapp {} has no assigned secrets.".format(name)) from e + raise ValidationError("The containerapp {} has no assigned secrets.".format(name)) from e def show_secret(cmd, name, resource_group_name, secret_name): @@ -1810,13 +1809,13 @@ def show_secret(cmd, name, resource_group_name, secret_name): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) r = ContainerAppClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name) for secret in r["value"]: if secret["name"].lower() == secret_name.lower(): return secret - raise CLIError("The containerapp {} does not have a secret assigned with name {}.".format(name, secret_name)) + raise ValidationError("The containerapp {} does not have a secret assigned with name {}.".format(name, secret_name)) def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait=False): @@ -1829,7 +1828,7 @@ def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait=False): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -1841,7 +1840,7 @@ def remove_secrets(cmd, name, resource_group_name, secret_names, no_wait=False): wasRemoved = True break if not wasRemoved: - raise CLIError("The containerapp {} does not have a secret assigned with name {}.".format(name, secret_name)) + raise ValidationError("The containerapp {} does not have a secret assigned with name {}.".format(name, secret_name)) try: r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) @@ -1871,7 +1870,7 @@ def set_secrets(cmd, name, resource_group_name, secrets, # try: # parse_secret_flags(yaml_secrets) # except: - # raise CLIError("YAML secrets must be a list of secrets in key=value format, delimited by new line.") + # raise ValidationError("YAML secrets must be a list of secrets in key=value format, delimited by new line.") # for secret in yaml_secrets: # secrets.append(secret.strip()) @@ -1882,7 +1881,7 @@ def set_secrets(cmd, name, resource_group_name, secrets, pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) _add_or_update_secrets(containerapp_def, parse_secret_flags(secrets)) @@ -1905,7 +1904,7 @@ def enable_dapr(cmd, name, resource_group_name, dapr_app_id=None, dapr_app_port= pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -1941,7 +1940,7 @@ def disable_dapr(cmd, name, resource_group_name, no_wait=False): pass if not containerapp_def: - raise CLIError("The containerapp '{}' does not exist".format(name)) + raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) @@ -2009,7 +2008,7 @@ def remove_dapr_component(cmd, resource_group_name, dapr_component_name, environ try: DaprComponentClient.show(cmd, resource_group_name, environment_name, name=dapr_component_name) except Exception as e: - raise CLIError("Dapr component not found.") from e + raise ResourceNotFoundError("Dapr component not found.") from e try: r = DaprComponentClient.delete(cmd, resource_group_name, environment_name, name=dapr_component_name) From f9e3d964e063943c0150d2a369c2a611b695abe0 Mon Sep 17 00:00:00 2001 From: Haroon Feisal Date: Mon, 21 Mar 2022 14:03:45 -0400 Subject: [PATCH 24/24] Removed check before deletion. Removed comments. --- src/containerapp/azext_containerapp/custom.py | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index ca6003d1a4d..657b5995e03 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -716,11 +716,6 @@ def list_containerapp(cmd, resource_group_name=None): def delete_containerapp(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") - try: - ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIInternalError as e: - handle_raw_exception(e) - try: return ContainerAppClient.delete(cmd=cmd, name=name, resource_group_name=resource_group_name) except CLIInternalError as e: @@ -735,7 +730,6 @@ def create_managed_environment(cmd, location=None, instrumentation_key=None, infrastructure_subnet_resource_id=None, - # app_subnet_resource_id=None, docker_bridge_cidr=None, platform_reserved_cidr=None, platform_reserved_dns_ip=None, @@ -748,15 +742,6 @@ def create_managed_environment(cmd, _validate_subscription_registered(cmd, "Microsoft.App") _ensure_location_allowed(cmd, location, "Microsoft.App", "managedEnvironments") - # Microsoft.ContainerService RP registration is required for vnet enabled environments - # if infrastructure_subnet_resource_id is not None or app_subnet_resource_id is not None: - # if is_valid_resource_id(app_subnet_resource_id): - # parsed_app_subnet_resource_id = parse_resource_id(app_subnet_resource_id) - # subnet_subscription = parsed_app_subnet_resource_id["subscription"] - # _validate_subscription_registered(cmd, "Microsoft.ContainerService", subnet_subscription) - # else: - # raise ValidationError('Subnet resource ID is invalid.') - if logs_customer_id is None or logs_key is None: logs_customer_id, logs_key = _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, location, resource_group_name) @@ -846,11 +831,6 @@ def list_managed_environments(cmd, resource_group_name=None): def delete_managed_environment(cmd, name, resource_group_name, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") - try: - ManagedEnvironmentClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except CLIInternalError as e: - handle_raw_exception(e) - try: return ManagedEnvironmentClient.delete(cmd=cmd, name=name, resource_group_name=resource_group_name, no_wait=no_wait) except CLIInternalError as e: