From 5cbaa2f1d022b7e9e9e0c8e5d6f8a7e2ebea362d Mon Sep 17 00:00:00 2001 From: snehapar9 <108305436+snehapar9@users.noreply.github.com> Date: Thu, 11 May 2023 09:27:07 -0700 Subject: [PATCH] Modified help text (#4) * Adding patch list * Added patch list command * Revert thumbprint arg * Fix bugs * Integrated with Harry * Fixed bug * Fixed command usage text * Made managed env optional * Fixed identation * Fixed identation * Made env optional * Remove patch list * Update help text for patch list and patch run * Modified help text * Made resouce group optional * Added custom telemetry * Fixed linting errors --------- Co-authored-by: harrli Co-authored-by: Harry Li <110055355+harryli0108@users.noreply.github.com> --- src/containerapp/azext_containerapp/_help.py | 24 +++++++-- .../azext_containerapp/_params.py | 14 +++-- src/containerapp/azext_containerapp/_utils.py | 1 + .../azext_containerapp/commands.py | 6 ++- src/containerapp/azext_containerapp/custom.py | 54 ++++++++++--------- 5 files changed, 67 insertions(+), 32 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 27cdcbfcb14..a4ff31af6ad 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -1272,14 +1272,32 @@ --compose-file-path "path/to/docker-compose.yml" """ +#Patch commands helps['containerapp patch'] = """ + type: group + short-summary: Patch Azure Container Apps. +""" + +helps['containerapp patch list'] = """ + type: command + short-summary: List Container Apps to be patched.Patching is only available for the apps built using the source to cloud feature. + exmaples: + - name: List Container Apps that can be patched. + text: | + az containerapp patch -g MyResourceGroup --environment MyContainerappEnv + - name: List patchable and unpatchable Container Apps. + text: | + az containerapp patch -g MyResourceGroup --environment MyContainerappEnv --show-all +""" + +helps['containerapp patch run'] = """ type: command - short-summary: List and select container apps to be patched. + short-summary: List and select Container Apps to be patched.Patching is only available for the apps built using the source to cloud feature. exmaples: - - name: Show containerapps that can be patched and apply patch. + - name: List Container Apps that can be patched and apply patch. text: | az containerapp patch -g MyResourceGroup --environment MyContainerappEnv - - name: Show patchable and unpatchable containerapps and apply patch. + - name: List patchable and unpatchable Container Apps and apply patch. text: | az containerapp patch -g MyResourceGroup --environment MyContainerappEnv --show-all """ \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 6833161c505..53b844386e7 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -415,7 +415,13 @@ def load_arguments(self, _): c.argument('min_nodes', help="The minimum node count for the workload profile") c.argument('max_nodes', help="The maximum node count for the workload profile") - with self.argument_context('containerapp patch') as c: - c.argument('resource_group_name', options_list=['--rg','-g'], configured_default='resource_group_name', id_part=None) - c.argument('environment', options_list=['--environment', '-e'], help='Name or resource id of the Container App environment.') - c.argument('show_all', options_list=['--show-all'],help='Show all patchable and unpatchable containerapps') \ No newline at end of file + with self.argument_context('containerapp patch list') as c: + c.argument('resource_group_name', arg_type=resource_group_name_type) + c.argument('managed_env',options_list=['--environment','-e'],help='Name or resource id of the Container App environment.') + c.argument('show_all', options_list=['--show-all'],help='Show all patchable and unpatchable Container Apps') + + with self.argument_context('containerapp patch run') as c: + c.argument('resource_group_name', arg_type=resource_group_name_type) + c.argument('managed_env',validator=validate_managed_env_name_or_id, options_list=['--environment', '-e'], help='Name or resource id of the Container App environment.') + c.argument('show_all', options_list=['--show-all'],help='Show all patchable and unpatchable Container Apps') + \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index b58135b1783..afc43d3d4e4 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -1781,6 +1781,7 @@ def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom): result = { "targetContainerAppName": bom["targetContainerAppName"], "targetContainerName": bom["targetContainerName"], + "targetContainerAppEnvironmentName": bom["targetContainerAppEnvironmentName"], "revisionMode": bom["revisionMode"], "targetImageName": bom["image_name"], "oldRunImage": repoTagSplit, diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 2fb296cbb58..432400888c0 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -53,7 +53,6 @@ def load_command_table(self, _): g.custom_command('exec', 'containerapp_ssh', validator=validate_ssh) g.custom_command('up', 'containerapp_up', supports_no_wait=False, exception_handler=ex_handler_factory()) g.custom_command('browse', 'open_containerapp_in_browser') - g.custom_command('patch','patch_run', isPreview=True) with self.command_group('containerapp replica') as g: g.custom_show_command('show', 'get_replica') # TODO implement the table transformer @@ -200,3 +199,8 @@ def load_command_table(self, _): g.custom_show_command('show', 'show_workload_profile') g.custom_command('set', 'set_workload_profile') g.custom_command('delete', 'delete_workload_profile') + + with self.command_group('containerapp patch', is_preview=True) as g: + g.custom_command('list','patch_list',is_preview=True) + g.custom_command('run','patch_run',is_preview=True) + \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 2943faf731a..b387e14746f 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -11,6 +11,7 @@ import requests import json import subprocess +from azure.cli.core import telemetry as telemetry_core from azure.cli.core.azclierror import ( RequiredArgumentMissingError, @@ -30,6 +31,7 @@ from msrestazure.tools import parse_resource_id, is_valid_resource_id from msrest.exceptions import DeserializationError + from ._client_factory import handle_raw_exception, handle_non_404_exception from ._clients import ManagedEnvironmentClient, ContainerAppClient, GitHubActionClient, DaprComponentClient, StorageClient, AuthClient, WorkloadProfileClient from ._github_oauth import get_github_access_token @@ -4133,7 +4135,7 @@ def show_auth_config(cmd, resource_group_name, name): return auth_settings # Compose - + def create_containerapps_from_compose(cmd, # pylint: disable=R0914 resource_group_name, managed_env, @@ -4303,7 +4305,7 @@ def delete_workload_profile(cmd, resource_group_name, env_name, workload_profile def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): - if(managed_env): + if managed_env: caList = list_containerapp(cmd, resource_group_name, managed_env) else: envList = list_managed_environments(cmd, resource_group_name) @@ -4320,7 +4322,7 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): containers = ca["properties"]["template"]["containers"] for container in containers: result = dict(imageName=container["image"], targetContainerName=container["name"], targetContainerAppName=ca["name"], targetContainerAppEnvironmentName = managedEnvName, revisionMode=ca["properties"]["configuration"]["activeRevisionsMode"]) - imgs.append(result) + imgs.append(result) # Get the BOM of the images results = [] boms = [] @@ -4371,7 +4373,7 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): else: for runImagesProp in runImagesProps: # result = None - if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1): + if runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1: runImagesProp = runImagesProp["name"].split(":") runImagesTag = runImagesProp[1] # Based on Mariners @@ -4381,17 +4383,19 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): results["NotPatchable"].append(checkResult) else: results[checkResult["id"]] = checkResult - else: - results["NotPatchable"].append(dict(targetContainerName=bom["targetContainerName"], targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName=bom["targetContainerAppEnvironmentName"], revisionMode=bom["revisionMode"], targetImageName=bom["image_name"], oldRunImage=bom["remote_info"]["run_images"]["name"], newRunImage=None, id=None, reason=failedReason)) - results.append(dict(targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName=bom["targetContainerAppEnvironmentName"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=failedReason)) + else: + results["NotPatchable"].append(dict(targetContainerName=bom["targetContainerName"], targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName = bom["targetContainerAppEnvironmentName"], revisionMode=bom["revisionMode"], targetImageName=bom["image_name"], oldRunImage=bom["remote_info"]["run_images"]["name"], newRunImage=None, id=None, reason=failedReason)) + results.append(dict(targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName = bom["targetContainerAppEnvironmentName"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=failedReason)) else: # Not based on image from mcr.microsoft.com/dotnet - results.append(dict(targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName=bom["targetContainerAppEnvironmentName"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=mcrCheckReason)) - if show_all == False : + results.append(dict(targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName=bom["targetContainerAppEnvironmentName"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=mcrCheckReason)) + if not show_all == False : results = {k: v for k, v in results.items() if k != "NotPatchable"} + if not results : + print("No Container App available to patch at this time.");return return results -def patch_run(cmd, resource_group_name, managed_env=None, show_all=False): +def patch_run(cmd, resource_group_name=None, managed_env=None, show_all=False): patchable_check_results = patch_list(cmd, resource_group_name, managed_env, show_all=show_all) patchable_result_key_list = list(patchable_check_results.keys()) if len(patchable_result_key_list) == 0 or patchable_result_key_list == ["NotPatchable"]: @@ -4403,6 +4407,8 @@ def patch_run(cmd, resource_group_name, managed_env=None, show_all=False): patchable_check_results_json = json.dumps(patchable_check_results, indent=4) print(patchable_check_results_json) user_input=input("Do you want to apply all the patch or specify by id? (y/n/id)\n") + if user_input == "y": + telemetry_core.add_extension_event('patch-run') return patch_apply(cmd, patchable_check_results, user_input, resource_group_name) def patch_apply(cmd, patchCheckList, method, resource_group_name): @@ -4412,23 +4418,23 @@ def patch_apply(cmd, patchCheckList, method, resource_group_name): for key in patchCheckList.keys(): if key != "NotPatchable": if patchCheckList[key]["newRunImage"]: - results.append(patch_cli_call(cmd, + results.append(patch_cli_call(cmd, resource_group_name, - patchCheckList[key]["targetContainerAppName"], - patchCheckList[key]["targetContainerName"], - patchCheckList[key]["targetImageName"], - patchCheckList[key]["newRunImage"], + patchCheckList[key]["targetContainerAppName"], + patchCheckList[key]["targetContainerName"], + patchCheckList[key]["targetImageName"], + patchCheckList[key]["newRunImage"], patchCheckList[key]["revisionMode"])) elif m == "n": print("No patch applied."); return else: if method in patchCheckList.keys(): - results.append(patch_cli_call(cmd, + results.append(patch_cli_call(cmd, resource_group_name, - patchCheckList[method]["targetContainerAppName"], - patchCheckList[method]["targetContainerName"], - patchCheckList[method]["targetImageName"], - patchCheckList[method]["newRunImage"], + patchCheckList[method]["targetContainerAppName"], + patchCheckList[method]["targetContainerName"], + patchCheckList[method]["targetImageName"], + patchCheckList[method]["newRunImage"], patchCheckList[method]["revisionMode"])) else: print("Invalid patch method or id."); return @@ -4447,10 +4453,10 @@ def patch_cli_call(cmd, resource_group, container_app_name, container_name, targ raise try: print("Patching container app: " + container_app_name + " container: " + container_name + " with image: " + new_target_image_name) - update_info_json = update_containerapp(cmd, - name=container_app_name, - resource_group_name=resource_group, - container_name=container_name, + update_info_json = update_containerapp(cmd, + name=container_app_name, + resource_group_name=resource_group, + container_name=container_name, image=new_target_image_name) print("Container app revision created successfully.") ## TODO: activate revision, fix the error when running the following two lines