diff --git a/src/containerapp/azext_containerapp/_clients.py b/src/containerapp/azext_containerapp/_clients.py index 9575a1ced03..4d525bee181 100644 --- a/src/containerapp/azext_containerapp/_clients.py +++ b/src/containerapp/azext_containerapp/_clients.py @@ -256,6 +256,105 @@ def list_secrets(cls, cmd, resource_group_name, name): r = send_raw_request(cmd.cli_ctx, "POST", request_url, body=None) return r.json() + @classmethod + def list_revisions(cls, cmd, resource_group_name, name, formatter=lambda x: x): + + revisions_list = [] + + management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager + api_version = NEW_API_VERSION + sub_id = get_subscription_id(cmd.cli_ctx) + url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/revisions?api-version={}" + request_url = url_fmt.format( + management_hostname.strip('/'), + sub_id, + resource_group_name, + name, + api_version) + + r = send_raw_request(cmd.cli_ctx, "GET", request_url) + j = r.json() + for app in j["value"]: + formatted = formatter(app) + revisions_list.append(formatted) + + while j.get("nextLink") is not None: + request_url = j["nextLink"] + r = send_raw_request(cmd.cli_ctx, "GET", request_url) + j = r.json() + for app in j["value"]: + formatted = formatter(app) + revisions_list.append(formatted) + + return revisions_list + + @classmethod + def show_revision(cls, cmd, resource_group_name, container_app_name, name): + management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager + api_version = NEW_API_VERSION + sub_id = get_subscription_id(cmd.cli_ctx) + url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/revisions/{}?api-version={}" + request_url = url_fmt.format( + management_hostname.strip('/'), + sub_id, + resource_group_name, + container_app_name, + name, + api_version) + + r = send_raw_request(cmd.cli_ctx, "GET", request_url) + return r.json() + + @classmethod + def restart_revision(cls, cmd, resource_group_name, container_app_name, name): + management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager + api_version = NEW_API_VERSION + sub_id = get_subscription_id(cmd.cli_ctx) + url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/revisions/{}/restart?api-version={}" + request_url = url_fmt.format( + management_hostname.strip('/'), + sub_id, + resource_group_name, + container_app_name, + name, + api_version) + + r = send_raw_request(cmd.cli_ctx, "POST", request_url) + return r.json() + + @classmethod + def activate_revision(cls, cmd, resource_group_name, container_app_name, name): + management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager + api_version = NEW_API_VERSION + sub_id = get_subscription_id(cmd.cli_ctx) + url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/revisions/{}/activate?api-version={}" + request_url = url_fmt.format( + management_hostname.strip('/'), + sub_id, + resource_group_name, + container_app_name, + name, + api_version) + + r = send_raw_request(cmd.cli_ctx, "POST", request_url) + return r.json() + + @classmethod + def deactivate_revision(cls, cmd, resource_group_name, container_app_name, name): + management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager + api_version = NEW_API_VERSION + sub_id = get_subscription_id(cmd.cli_ctx) + url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/revisions/{}/deactivate?api-version={}" + request_url = url_fmt.format( + management_hostname.strip('/'), + sub_id, + resource_group_name, + container_app_name, + name, + api_version) + + r = send_raw_request(cmd.cli_ctx, "POST", request_url) + return r.json() class ManagedEnvironmentClient(): @classmethod diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 05c2f63b96e..b32ac7f7b90 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -138,6 +138,57 @@ az containerapp list -g MyResourceGroup """ +# Revision Commands +helps['containerapp revision'] = """ + type: group + short-summary: Commands to manage a Containerapp's revisions. +""" + +helps['containerapp revision show'] = """ + type: command + short-summary: Show details of a Containerapp's revision. + examples: + - name: Show details of a Containerapp's revision. + text: | + az containerapp revision show --revision-name MyContainerappRevision -g MyResourceGroup +""" + +helps['containerapp revision list'] = """ + type: command + short-summary: List details of a Containerapp's revisions. + examples: + - name: List a Containerapp's revisions. + text: | + az containerapp revision list --revision-name MyContainerapp -g MyResourceGroup +""" + +helps['containerapp revision restart'] = """ + type: command + short-summary: Restart a Containerapps's revision. + examples: + - name: Restart a Containerapp's revision. + text: | + az containerapp revision restart --revision-name MyContainerappRevision -g MyResourceGroup +""" + +helps['containerapp revision activate'] = """ + type: command + short-summary: Activates Containerapp's revision. + examples: + - name: Activate a Containerapp's revision. + text: | + az containerapp revision activate --revision-name MyContainerappRevision -g MyResourceGroup +""" + +helps['containerapp revision deactivate'] = """ + type: command + short-summary: Deactivates Containerapp's revision. + examples: + - name: Deactivate a Containerapp's revision. + text: | + az containerapp revision deactivate --revision-name MyContainerappRevision -g MyResourceGroup +""" + # Environment Commands helps['containerapp env'] = """ type: group diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 4662a35bb1f..913b4ee502d 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -102,3 +102,6 @@ def load_arguments(self, _): with self.argument_context('containerapp env show') as c: c.argument('name', name_type, help='Name of the managed Environment.') + + with self.argument_context('containerapp revision') as c: + c.argument('revision_name', type=str, help='Name of the revision') diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 524024589dd..63006d1aae4 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -429,3 +429,13 @@ def _add_or_update_traffic_Weights(containerapp_def, list_weights): "revisionName": key_val[0], "weight": int(key_val[1]) }) + + +def _get_app_from_revision(revision): + if not revision: + raise ValidationError('Invalid revision. Revision must not be empty') + + revision = revision.split('--') + revision.pop() + revision = "--".join(revision) + return revision diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 998e41cf3ae..20d7c332c0d 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -25,6 +25,20 @@ def transform_containerapp_list_output(apps): return [transform_containerapp_output(a) for a in apps] +def transform_revision_output(rev): + props = ['name', 'replicas', 'active', 'createdTime'] + result = {k: rev[k] for k in rev if k in props} + + if 'latestRevisionFqdn' in rev['template']: + result['fqdn'] = rev['template']['latestRevisionFqdn'] + + return result + + +def transform_revision_list_output(revs): + return [transform_revision_output(r) for r in revs] + + def load_command_table(self, _): with self.command_group('containerapp') as g: g.custom_command('show', 'show_containerapp', table_transformer=transform_containerapp_output) @@ -41,3 +55,10 @@ def load_command_table(self, _): g.custom_command('create', 'create_managed_environment', supports_no_wait=True, exception_handler=ex_handler_factory()) # 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 revision') as g: + g.custom_command('activate', 'activate_revision') + g.custom_command('deactivate', 'deactivate_revision') + g.custom_command('list', 'list_revisions', table_transformer=transform_revision_list_output, exception_handler=ex_handler_factory()) + g.custom_command('restart', 'restart_revision') + g.custom_command('show', 'show_revision', table_transformer=transform_revision_output, exception_handler=ex_handler_factory()) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 31510ad80a7..ae27c6474b7 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -34,7 +34,8 @@ parse_secret_flags, store_as_secret_and_return_secret_ref, parse_list_of_strings, 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, _add_or_update_traffic_Weights) + _add_or_update_env_vars, _add_or_update_tags, update_nested_dictionary, _add_or_update_traffic_Weights, + _get_app_from_revision) logger = get_logger(__name__) @@ -891,3 +892,50 @@ def delete_managed_environment(cmd, name, resource_group_name, no_wait=False): return ManagedEnvironmentClient.delete(cmd=cmd, name=name, resource_group_name=resource_group_name, no_wait=no_wait) except CLIError as e: handle_raw_exception(e) + + +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: + handle_raw_exception(e) + + +def show_revision(cmd, resource_group_name, revision_name, name=None): + if not name: + name = _get_app_from_revision(revision_name) + + try: + return ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) + except CLIError as e: + handle_raw_exception(e) + + +def restart_revision(cmd, resource_group_name, revision_name, name=None): + if not name: + name = _get_app_from_revision(revision_name) + + try: + return ContainerAppClient.restart_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) + except CLIError as e: + handle_raw_exception(e) + + +def activate_revision(cmd, resource_group_name, revision_name, name=None): + if not name: + name = _get_app_from_revision(revision_name) + + try: + return ContainerAppClient.activate_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) + 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) + + try: + return ContainerAppClient.deactivate_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=revision_name) + except CLIError as e: + handle_raw_exception(e) +