From bbaee5fad6bf75fc01e510abbdbf9c6e548a477e Mon Sep 17 00:00:00 2001 From: harrli Date: Thu, 4 May 2023 09:32:28 -0700 Subject: [PATCH 01/17] Adding patch list --- .../azext_containerapp/_models.py | 23 ++++++ src/containerapp/azext_containerapp/_utils.py | 73 +++++++++++++++++++ .../azext_containerapp/commands.py | 3 + src/containerapp/azext_containerapp/custom.py | 66 ++++++++++++++++- 4 files changed, 164 insertions(+), 1 deletion(-) diff --git a/src/containerapp/azext_containerapp/_models.py b/src/containerapp/azext_containerapp/_models.py index ae9720c8e33..fb750498314 100644 --- a/src/containerapp/azext_containerapp/_models.py +++ b/src/containerapp/azext_containerapp/_models.py @@ -293,3 +293,26 @@ "validationMethod": None # str } } + +# ContainerApp Patch +ImageProperties = { + "imageName": None, + "targetContainerAppName": None +} + +ImagePatchableCheck = { + "targetContainerAppName": None, + "oldRunImage": None, + "newRunImage": None, + "id": None, + "reason": None +} + +OryxMarinerRunImgTagProperty = { + "fullTag": None, + "framework": None, + "version": None, + "marinerVersion": None, + "architectures": None, + "support": None, +} \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 8bcc296a234..378101414b4 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -7,6 +7,9 @@ import time import json import platform +import hashlib +import requests +import packaging.version as SemVer from urllib.parse import urlparse from datetime import datetime @@ -31,6 +34,7 @@ LOG_ANALYTICS_RP, CONTAINER_APPS_RP, CHECK_CERTIFICATE_NAME_AVAILABILITY_TYPE, ACR_IMAGE_SUFFIX, LOGS_STRING, PENDING_STATUS, SUCCEEDED_STATUS, UPDATING_STATUS) from ._models import (ContainerAppCustomDomainEnvelope as ContainerAppCustomDomainEnvelopeModel, ManagedCertificateEnvelop as ManagedCertificateEnvelopModel) +from ._models import ImagePatchableCheck, OryxMarinerRunImgTagProperty logger = get_logger(__name__) @@ -1714,3 +1718,72 @@ def format_location(location=None): if location: return location.lower().replace(" ", "").replace("(", "").replace(")", "") return location + +def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom): + tagProp = parseOryxMarinerTag(repoTagSplit) + repoTagSplit = repoTagSplit.split("-") + if repoTagSplit[1] == "dotnet": + matchingVersionInfo = oryxBuilderRunImgTags[repoTagSplit[2]][str(tagProp["version"].major) + "." + str(tagProp["version"].minor)][tagProp["support"]][tagProp["marinerVersion"]] + + # Check if the image minor version is four less than the latest minor version + if tagProp["version"] < matchingVersionInfo[0]["version"]: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["oldRunImage"] = tagProp["fullTag"] + if (tagProp["version"].minor == matchingVersionInfo[0]["version"].minor) and (tagProp["version"].micro < matchingVersionInfo[0]["version"].micro): + # Patchable + result["newRunImage"] = "mcr.microsoft.com/oryx/builder:" + matchingVersionInfo[0]["fullTag"] + result["id"] = hashlib.md5(str(result["oldRunImage"] + result["targetContainerAppName"]).encode()).hexdigest() + result["reason"] = "New security patch released for your current run image." + else: + # Not patchable + result["newRunImage"] = "mcr.microsoft.com/oryx/builder:" + matchingVersionInfo[0]["fullTag"] + result["reason"] = "The image is not pachable Please check for major or minor version upgrade." + else: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["oldRunImage"] = tagProp["fullTag"] + result["reason"] = "You're already up to date!" + return result + +def getCurrentMarinerTags() -> list(OryxMarinerRunImgTagProperty): + r = requests.get("https://mcr.microsoft.com/v2/oryx/builder/tags/list") + tags = r.json() + tagList = {} + # only keep entries that container keyword "mariner" + tags = [tag for tag in tags["tags"] if "mariner" in tag] + + for tag in tags: + tagObj = parseOryxMarinerTag(tag) + if tagObj: + majorMinorVer = str(tagObj["version"].major) + "." + str(tagObj["version"].minor) + support = tagObj["support"] + framework = tagObj["framework"] + marinerVer = tagObj["marinerVersion"] + if framework in tagList.keys(): + if majorMinorVer in tagList.keys(): + # preview + if support in tagList[majorMinorVer].keys(): + if marinerVer in tagList[majorMinorVer][support].keys(): + (tagList[framework][majorMinorVer])[support][marinerVer].append(tagObj) + (tagList[framework][majorMinorVer])[support][marinerVer].sort(reverse=True, key=lambda x: x["version"]) + else: + (tagList[framework][majorMinorVer])[support][marinerVer] = [tagObj] + else: + tagList[framework][majorMinorVer][support] = {marinerVer: [tagObj]} + else: + tagList[framework][majorMinorVer] = {support: {marinerVer: [tagObj]}} + else: + tagList[framework] = {majorMinorVer: {support: {marinerVer: [tagObj]}}} + return tagList + +def parseOryxMarinerTag(tag: str) -> OryxMarinerRunImgTagProperty: + + tagSplit = tag.split("-") + if tagSplit[0] == "run" and tagSplit[1] == "dotnet": + versionRE = r"(\d+\.\d+(\.\d+)?).*?(cbl-mariner(\d+\.\d+))" + REmatch = re.findall(versionRE, tag)[0] + tagObj = dict(fullTag=tag, version=SemVer.parse(REmatch[0]), framework=tagSplit[2], marinerVersion=REmatch[2], architectures=None, support="lts") + else: + tagObj = None + return tagObj \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 0a4a359d77b..7a311631b47 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -199,3 +199,6 @@ 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') diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index a269de1da5c..23c90816500 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -9,6 +9,8 @@ import time from urllib.parse import urlparse import requests +import json +import subprocess from azure.cli.core.azclierror import ( RequiredArgumentMissingError, @@ -57,6 +59,7 @@ ScaleRule as ScaleRuleModel, Volume as VolumeModel, VolumeMount as VolumeMountModel,) +from ._models import DotnetTagProperty, ImagePatchableCheck, ImageProperties from ._utils import (_validate_subscription_registered, _ensure_location_allowed, parse_secret_flags, store_as_secret_and_return_secret_ref, parse_env_var_flags, @@ -73,7 +76,8 @@ validate_environment_location, safe_set, parse_metadata_flags, parse_auth_flags, _azure_monitor_quickstart, set_ip_restrictions, certificate_location_matches, certificate_matches, generate_randomized_managed_cert_name, check_managed_cert_name_availability, prepare_managed_certificate_envelop, - get_default_workload_profile_name_from_env, get_default_workload_profiles, ensure_workload_profile_supported, _generate_secret_volume_name) + get_default_workload_profile_name_from_env, get_default_workload_profiles, ensure_workload_profile_supported, _generate_secret_volume_name, + getCurrentMarinerTags, patchableCheck) from ._validators import validate_create, validate_revision_suffix from ._ssh_utils import (SSH_DEFAULT_ENCODING, WebSocketConnection, read_ssh, get_stdin_writer, SSH_CTRL_C_MSG, SSH_BACKUP_ENCODING) @@ -4297,3 +4301,63 @@ def delete_workload_profile(cmd, resource_group_name, env_name, workload_profile return r except Exception as e: handle_raw_exception(e) + +def patch_list(cmd, resource_group_name=None, managed_env=None): + caList = list_containerapp(cmd, resource_group_name, managed_env) + imgs = [] + if caList: + for ca in caList: + containers = ca["properties"]["template"]["containers"] + for container in containers: + result = dict(imageName=container["image"], targetContainerAppName=container["name"]) + imgs.append(result) + + # Get the BOM of the images + results = [] + boms = [] + + ## For production + # + for img in imgs: + subprocess.run("pack inspect-image " + img["imageName"] + " --output json > /tmp/bom.json", shell=True) + with open("/tmp/bom.json", "rb") as f: + bom = json.load(f) + bom.update('{ "targetContainerAppName": img["targetContainerAppName"] }') + boms.append(bom) + + ## For testing + # + # with open("./bom.json", "rb") as f: + # lines = f.read() + # bom = json.loads(lines) + + # bom.update({ "targetContainerAppName": "test-containerapp-1" }) + # boms.append(bom) + + # Get the current tags of Dotnet Mariners + oryxRunImgTags = getCurrentMarinerTags() + + # Start checking if the images are based on Mariner + for bom in boms: + # devide run-images into different parts by "/" + runImagesProps = bom["remote_info"]["run_images"] + for runImagesProp in runImagesProps: + if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1): + runImagesProp = runImagesProp["name"].split(":") + runImagesTag = runImagesProp[1] + # Based on Mariners + if runImagesTag.find('mariner') != -1: + result = patchableCheck(runImagesTag, oryxRunImgTags, bom=bom) + else: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["oldRunImage"] = bom["remote_info"]["run_images"] + result["reason"] = "Image not based on Mariners" + else: + # Not based on image from mcr.microsoft.com/dotnet + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["oldRunImage"] = bom["remote_info"]["run_images"] + result["reason"] = "Image not from mcr.microsoft.com/oryx/builder" + results.append(result) + return results \ No newline at end of file From 5419d8eac6025943be19e1bcfe73eae2e74d0750 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Thu, 4 May 2023 11:10:34 -0700 Subject: [PATCH 02/17] Added patch list command --- .../azext_containerapp/_params.py | 7 ++++- .../azext_containerapp/commands.py | 3 +++ src/containerapp/azext_containerapp/custom.py | 27 ++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 0fe3852be6b..1ec908e23e0 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -384,7 +384,7 @@ def load_arguments(self, _): with self.argument_context('containerapp hostname bind') as c: c.argument('hostname', help='The custom domain name.') - c.argument('thumbprint', options_list=['--thumbprint', '-t'], help='Thumbprint of the certificate.') + c.argument('thumbprint', 'resource_group_name', configured_default='resource_group_name', id_part=None, help='Thumbprint of the certificate.') c.argument('certificate', options_list=['--certificate', '-c'], help='Name or resource id of the certificate.') c.argument('environment', options_list=['--environment', '-e'], help='Name or resource id of the Container App environment.') c.argument('validation_method', options_list=['--validation-method', '-v'], help='Validation method of custom domain ownership.', is_preview=True) @@ -414,3 +414,8 @@ def load_arguments(self, _): c.argument('workload_profile_type', help="The type of workload profile to add or update. Run 'az containerapp env workload-profile list-supported -l ' to check the options for your region.") 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 list') as c: + c.argument('resource_group_name', options_list=['--rg'], configured_default='resource_group_name', id_part=None) + c.argument('env_name', options_list=['--env','--e'], help='Name or resource id of the Container App environment.') + c.argument('show_all', options_list=['--show-all'],help='Show all patchable and non-patchable containerapps') \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 0a4a359d77b..653fbf016f7 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -199,3 +199,6 @@ 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') as g: + g.custom_command('list','list_dummy_values',exception_handler=ex_handler_factory()) \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index a269de1da5c..21c425b4d56 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -9,6 +9,7 @@ import time from urllib.parse import urlparse import requests +import json from azure.cli.core.azclierror import ( RequiredArgumentMissingError, @@ -4128,9 +4129,33 @@ def show_auth_config(cmd, resource_group_name, name): pass return auth_settings -# Compose +def list_dummy_values(cmd, resource_group_name, env_name, show_all=False): + print(show_all) + dummy_json = { + "children":[ + { + "targetContainerAppName": "test-rg-1", + "oldRunImage": "mcr.microsoft.com/dotnet/sdk:6.0.100-cbl-mariner1.0", + "newRunImage": "mcr.microsoft.com/dotnet/sdk6.0.400-cbl-mariner1.0", + "id": "014eef45-350d-4f94-9e31-7a088d932354", + "reason": "The image is based on Mariner and the minor version is within safe update range." + }, + { + "targetContainerAppName": "test-rg-1", + "oldRunImage": "mcr.microsoft.com/dotnet/sdk:7.0.101-cbl-mariner2.0", + "newRunImage": None, + "id": None, + "reason": "You're already up to date!" + + } + + ] + } + return json.dumps(dummy_json) +# Compose + def create_containerapps_from_compose(cmd, # pylint: disable=R0914 resource_group_name, managed_env, From 9f425d84c0373f00d55b3cf472ee39481d91953a Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Thu, 4 May 2023 11:29:33 -0700 Subject: [PATCH 03/17] Revert thumbprint arg --- 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 1ec908e23e0..1ee5647e9a4 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -384,7 +384,7 @@ def load_arguments(self, _): with self.argument_context('containerapp hostname bind') as c: c.argument('hostname', help='The custom domain name.') - c.argument('thumbprint', 'resource_group_name', configured_default='resource_group_name', id_part=None, help='Thumbprint of the certificate.') + c.argument('thumbprint', options_list=['--thumbprint', '-t'], help='Thumbprint of the certificate.') c.argument('certificate', options_list=['--certificate', '-c'], help='Name or resource id of the certificate.') c.argument('environment', options_list=['--environment', '-e'], help='Name or resource id of the Container App environment.') c.argument('validation_method', options_list=['--validation-method', '-v'], help='Validation method of custom domain ownership.', is_preview=True) From 1d8078ac8b878611fc9965d726367688a1bca7a7 Mon Sep 17 00:00:00 2001 From: harrli Date: Thu, 4 May 2023 13:26:10 -0700 Subject: [PATCH 04/17] Fix bugs --- src/containerapp/azext_containerapp/_utils.py | 26 +++++----- src/containerapp/azext_containerapp/custom.py | 50 +++++++++++-------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 378101414b4..bf2d86264ae 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -10,6 +10,7 @@ import hashlib import requests import packaging.version as SemVer +import re from urllib.parse import urlparse from datetime import datetime @@ -1733,7 +1734,7 @@ def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom): if (tagProp["version"].minor == matchingVersionInfo[0]["version"].minor) and (tagProp["version"].micro < matchingVersionInfo[0]["version"].micro): # Patchable result["newRunImage"] = "mcr.microsoft.com/oryx/builder:" + matchingVersionInfo[0]["fullTag"] - result["id"] = hashlib.md5(str(result["oldRunImage"] + result["targetContainerAppName"]).encode()).hexdigest() + result["id"] = hashlib.md5(str(result["oldRunImage"] + result["targetContainerAppName"] + result["newRunImage"]).encode()).hexdigest() result["reason"] = "New security patch released for your current run image." else: # Not patchable @@ -1749,10 +1750,10 @@ def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom): def getCurrentMarinerTags() -> list(OryxMarinerRunImgTagProperty): r = requests.get("https://mcr.microsoft.com/v2/oryx/builder/tags/list") tags = r.json() + # tags = dict(tags=["run-dotnet-aspnet-7.0.1-cbl-mariner2.0", "run-dotnet-aspnet-7.0.1-cbl-mariner1.0", "run-dotnet-aspnet-7.1.0-cbl-mariner2.0"]) tagList = {} # only keep entries that container keyword "mariner" tags = [tag for tag in tags["tags"] if "mariner" in tag] - for tag in tags: tagObj = parseOryxMarinerTag(tag) if tagObj: @@ -1761,14 +1762,13 @@ def getCurrentMarinerTags() -> list(OryxMarinerRunImgTagProperty): framework = tagObj["framework"] marinerVer = tagObj["marinerVersion"] if framework in tagList.keys(): - if majorMinorVer in tagList.keys(): - # preview - if support in tagList[majorMinorVer].keys(): - if marinerVer in tagList[majorMinorVer][support].keys(): - (tagList[framework][majorMinorVer])[support][marinerVer].append(tagObj) - (tagList[framework][majorMinorVer])[support][marinerVer].sort(reverse=True, key=lambda x: x["version"]) + if majorMinorVer in tagList[framework].keys(): + if support in tagList[framework][majorMinorVer].keys(): + if marinerVer in tagList[framework][majorMinorVer][support].keys(): + tagList[framework][majorMinorVer][support][marinerVer].append(tagObj) + tagList[framework][majorMinorVer][support][marinerVer].sort(reverse=True, key=lambda x: x["version"]) else: - (tagList[framework][majorMinorVer])[support][marinerVer] = [tagObj] + tagList[framework][majorMinorVer][support][marinerVer] = [tagObj] else: tagList[framework][majorMinorVer][support] = {marinerVer: [tagObj]} else: @@ -1778,12 +1778,14 @@ def getCurrentMarinerTags() -> list(OryxMarinerRunImgTagProperty): return tagList def parseOryxMarinerTag(tag: str) -> OryxMarinerRunImgTagProperty: - tagSplit = tag.split("-") if tagSplit[0] == "run" and tagSplit[1] == "dotnet": versionRE = r"(\d+\.\d+(\.\d+)?).*?(cbl-mariner(\d+\.\d+))" - REmatch = re.findall(versionRE, tag)[0] - tagObj = dict(fullTag=tag, version=SemVer.parse(REmatch[0]), framework=tagSplit[2], marinerVersion=REmatch[2], architectures=None, support="lts") + REmatches = re.findall(versionRE, tag) + if REmatches.count == 0: + tagObj = None + else: + tagObj = dict(fullTag=tag, version=SemVer.parse(REmatches[0][0]), framework=tagSplit[2], marinerVersion=REmatches[0][2], architectures=None, support="lts") else: tagObj = None return tagObj \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 23c90816500..851b2d9345a 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4312,14 +4312,14 @@ def patch_list(cmd, resource_group_name=None, managed_env=None): result = dict(imageName=container["image"], targetContainerAppName=container["name"]) imgs.append(result) - # Get the BOM of the images + # Get the BOM of the images results = [] boms = [] ## For production # for img in imgs: - subprocess.run("pack inspect-image " + img["imageName"] + " --output json > /tmp/bom.json", shell=True) + subprocess.run("pack inspect-image " + img["imageName"] + " --output json > /tmp/bom.json 2>&1", shell=True) with open("/tmp/bom.json", "rb") as f: bom = json.load(f) bom.update('{ "targetContainerAppName": img["targetContainerAppName"] }') @@ -4329,8 +4329,10 @@ def patch_list(cmd, resource_group_name=None, managed_env=None): # # with open("./bom.json", "rb") as f: # lines = f.read() + # # if lines.find(b"status code 401 Unauthorized") == -1 or lines.find(b"unable to find image") == -1: + # # bom = dict(remote_info=401) + # # else: # bom = json.loads(lines) - # bom.update({ "targetContainerAppName": "test-containerapp-1" }) # boms.append(bom) @@ -4339,25 +4341,31 @@ def patch_list(cmd, resource_group_name=None, managed_env=None): # Start checking if the images are based on Mariner for bom in boms: - # devide run-images into different parts by "/" - runImagesProps = bom["remote_info"]["run_images"] - for runImagesProp in runImagesProps: - if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1): - runImagesProp = runImagesProp["name"].split(":") - runImagesTag = runImagesProp[1] - # Based on Mariners - if runImagesTag.find('mariner') != -1: - result = patchableCheck(runImagesTag, oryxRunImgTags, bom=bom) - else: + if bom["remote_info"] == 401: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["reason"] = "Failed to get BOM of the image. Please check if the image exists or you have the permission to access the image." + results.append(result) + else: + # devide run-images into different parts by "/" + runImagesProps = bom["remote_info"]["run_images"] + for runImagesProp in runImagesProps: + if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1): + runImagesProp = runImagesProp["name"].split(":") + runImagesTag = runImagesProp[1] + # Based on Mariners + if runImagesTag.find('mariner') != -1: + result = patchableCheck(runImagesTag, oryxRunImgTags, bom=bom) + else: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["oldRunImage"] = bom["remote_info"]["run_images"] + result["reason"] = "Image not based on Mariners" + else: + # Not based on image from mcr.microsoft.com/dotnet result = ImagePatchableCheck result["targetContainerAppName"] = bom["targetContainerAppName"] result["oldRunImage"] = bom["remote_info"]["run_images"] - result["reason"] = "Image not based on Mariners" - else: - # Not based on image from mcr.microsoft.com/dotnet - result = ImagePatchableCheck - result["targetContainerAppName"] = bom["targetContainerAppName"] - result["oldRunImage"] = bom["remote_info"]["run_images"] - result["reason"] = "Image not from mcr.microsoft.com/oryx/builder" - results.append(result) + result["reason"] = "Image not from mcr.microsoft.com/oryx/builder" + results.append(result) return results \ No newline at end of file From 1821286528740359cf30906fc3df5bcff08d2ba6 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Thu, 4 May 2023 16:36:25 -0700 Subject: [PATCH 05/17] Integrated with Harry --- src/containerapp/azext_containerapp/_help.py | 12 +++ .../azext_containerapp/_params.py | 4 +- src/containerapp/azext_containerapp/custom.py | 80 ++++++++----------- 3 files changed, 46 insertions(+), 50 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index ac20bfd0383..03069c554c6 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -1271,3 +1271,15 @@ --environment MyContainerappEnv \\ --compose-file-path "path/to/docker-compose.yml" """ + +helps['containerapp patch list'] = """ + type: command + short-summary: List patchable and unpatchable container apps. + examples: + - name: List patchable container apps. + text: | + az containerapp list -g MyResourceGroup --environment MyContainerappEnv + - name: List patchable and non-patchable container apps. + text: | + az containerapp list -g MyResourceGroup --environment MyContainerappEnv --showAll +""" \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 1ee5647e9a4..a64f3e31bc5 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -416,6 +416,6 @@ def load_arguments(self, _): c.argument('max_nodes', help="The maximum node count for the workload profile") with self.argument_context('containerapp patch list') as c: - c.argument('resource_group_name', options_list=['--rg'], configured_default='resource_group_name', id_part=None) - c.argument('env_name', options_list=['--env','--e'], help='Name or resource id of the Container App environment.') + c.argument('resource_group_name', options_list=['--rg','-g'], configured_default='resource_group_name', id_part=None) + c.argument('environment', options_list=['--environment'], help='Name or resource id of the Container App environment.') c.argument('show_all', options_list=['--show-all'],help='Show all patchable and non-patchable containerapps') \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 5da3a9fa92d..14d5c8ec06c 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -59,7 +59,7 @@ ScaleRule as ScaleRuleModel, Volume as VolumeModel, VolumeMount as VolumeMountModel,) -from ._models import DotnetTagProperty, ImagePatchableCheck, ImageProperties +from ._models import OryxMarinerRunImgTagProperty, ImagePatchableCheck, ImageProperties from ._utils import (_validate_subscription_registered, _ensure_location_allowed, parse_secret_flags, store_as_secret_and_return_secret_ref, parse_env_var_flags, @@ -4132,31 +4132,6 @@ def show_auth_config(cmd, resource_group_name, name): pass return auth_settings - -def list_dummy_values(cmd, resource_group_name, env_name, show_all=False): - print(show_all) - dummy_json = { - "children":[ - { - "targetContainerAppName": "test-rg-1", - "oldRunImage": "mcr.microsoft.com/dotnet/sdk:6.0.100-cbl-mariner1.0", - "newRunImage": "mcr.microsoft.com/dotnet/sdk6.0.400-cbl-mariner1.0", - "id": "014eef45-350d-4f94-9e31-7a088d932354", - "reason": "The image is based on Mariner and the minor version is within safe update range." - }, - { - "targetContainerAppName": "test-rg-1", - "oldRunImage": "mcr.microsoft.com/dotnet/sdk:7.0.101-cbl-mariner2.0", - "newRunImage": None, - "id": None, - "reason": "You're already up to date!" - - } - - ] - } - return json.dumps(dummy_json) - # Compose def create_containerapps_from_compose(cmd, # pylint: disable=R0914 @@ -4326,8 +4301,8 @@ def delete_workload_profile(cmd, resource_group_name, env_name, workload_profile except Exception as e: handle_raw_exception(e) -def patch_list(cmd, resource_group_name=None, managed_env=None): - caList = list_containerapp(cmd, resource_group_name, managed_env) +def patch_list(cmd, resource_group_name, managed_env, show_all=False): + caList = list_containerapp(cmd, resource_group_name, managed_env) imgs = [] if caList: for ca in caList: @@ -4343,10 +4318,11 @@ def patch_list(cmd, resource_group_name=None, managed_env=None): ## For production # for img in imgs: - subprocess.run("pack inspect-image " + img["imageName"] + " --output json > /tmp/bom.json 2>&1", shell=True) - with open("/tmp/bom.json", "rb") as f: - bom = json.load(f) - bom.update('{ "targetContainerAppName": img["targetContainerAppName"] }') + subprocess.run("pack inspect-image " + img["imageName"] + " --output json > ./bom.json 2>&1", shell=True) + with open("./bom.json", "rb") as f: + lines = f.read() + bom = json.loads(lines) + bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) boms.append(bom) ## For testing @@ -4373,23 +4349,31 @@ def patch_list(cmd, resource_group_name=None, managed_env=None): else: # devide run-images into different parts by "/" runImagesProps = bom["remote_info"]["run_images"] - for runImagesProp in runImagesProps: - if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1): - runImagesProp = runImagesProp["name"].split(":") - runImagesTag = runImagesProp[1] - # Based on Mariners - if runImagesTag.find('mariner') != -1: - result = patchableCheck(runImagesTag, oryxRunImgTags, bom=bom) - else: + if runImagesProps is None: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["reason"] = "Image not based on Mariners" + results.append(result) + else: + for runImagesProp in runImagesProps: + if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1): + runImagesProp = runImagesProp["name"].split(":") + runImagesTag = runImagesProp[1] + # Based on Mariners + if runImagesTag.find('mariner') != -1: + result = patchableCheck(runImagesTag, oryxRunImgTags, bom=bom) + else: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["oldRunImage"] = bom["remote_info"]["run_images"] + result["reason"] = "Image not based on Mariners" + else: + # Not based on image from mcr.microsoft.com/dotnet result = ImagePatchableCheck result["targetContainerAppName"] = bom["targetContainerAppName"] result["oldRunImage"] = bom["remote_info"]["run_images"] - result["reason"] = "Image not based on Mariners" - else: - # Not based on image from mcr.microsoft.com/dotnet - result = ImagePatchableCheck - result["targetContainerAppName"] = bom["targetContainerAppName"] - result["oldRunImage"] = bom["remote_info"]["run_images"] - result["reason"] = "Image not from mcr.microsoft.com/oryx/builder" - results.append(result) + result["reason"] = "Image not from mcr.microsoft.com/oryx/builder" + results.append(result) + if show_all == False : + results = [x for x in results if x["newRunImage"] != None] return results \ No newline at end of file From e373062366bb200f25077bce4e732e7e9cc9081c Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Thu, 4 May 2023 19:04:46 -0700 Subject: [PATCH 06/17] Fixed bug --- src/containerapp/azext_containerapp/_utils.py | 6 ++++ src/containerapp/azext_containerapp/custom.py | 36 +++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index bf2d86264ae..833ad19e7e9 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -1722,6 +1722,12 @@ def format_location(location=None): def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom): tagProp = parseOryxMarinerTag(repoTagSplit) + if tagProp is None: + result = ImagePatchableCheck + result["targetContainerAppName"] = bom["targetContainerAppName"] + result["oldRunImage"] = repoTagSplit + result["reason"] = "Image not based on dotnet Mariner." + return result repoTagSplit = repoTagSplit.split("-") if repoTagSplit[1] == "dotnet": matchingVersionInfo = oryxBuilderRunImgTags[repoTagSplit[2]][str(tagProp["version"].major) + "." + str(tagProp["version"].minor)][tagProp["support"]][tagProp["marinerVersion"]] diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 14d5c8ec06c..7565266ba5b 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4320,8 +4320,12 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False): for img in imgs: subprocess.run("pack inspect-image " + img["imageName"] + " --output json > ./bom.json 2>&1", shell=True) with open("./bom.json", "rb") as f: + bom = None lines = f.read() - bom = json.loads(lines) + if lines.find(b"status code 401 Unauthorized") != -1 or lines.find(b"unable to find image") != -1: + bom = dict(remote_info=401) + else: + bom = json.loads(lines) bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) boms.append(bom) @@ -4338,42 +4342,34 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False): # Get the current tags of Dotnet Mariners oryxRunImgTags = getCurrentMarinerTags() - + failedReason = "Failed to get BOM of the image. Please check if the image exists or you have the permission to access the image." + notBasedMarinerReason = "Image not based on Mariner" + mcrCheckReason = "Image not from mcr.microsoft.com/oryx/builder" + results = [] # Start checking if the images are based on Mariner for bom in boms: if bom["remote_info"] == 401: - result = ImagePatchableCheck - result["targetContainerAppName"] = bom["targetContainerAppName"] - result["reason"] = "Failed to get BOM of the image. Please check if the image exists or you have the permission to access the image." - results.append(result) + results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=None, newRunImage=None, id=None, reason=failedReason)) else: # devide run-images into different parts by "/" runImagesProps = bom["remote_info"]["run_images"] if runImagesProps is None: - result = ImagePatchableCheck - result["targetContainerAppName"] = bom["targetContainerAppName"] - result["reason"] = "Image not based on Mariners" - results.append(result) + + results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=None, newRunImage=None, id=None, reason=notBasedMarinerReason)) else: for runImagesProp in runImagesProps: + # result = None if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1): runImagesProp = runImagesProp["name"].split(":") runImagesTag = runImagesProp[1] # Based on Mariners if runImagesTag.find('mariner') != -1: - result = patchableCheck(runImagesTag, oryxRunImgTags, bom=bom) + results.append(patchableCheck(runImagesTag, oryxRunImgTags, bom=bom)) else: - result = ImagePatchableCheck - result["targetContainerAppName"] = bom["targetContainerAppName"] - result["oldRunImage"] = bom["remote_info"]["run_images"] - result["reason"] = "Image not based on Mariners" + results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=failedReason)) else: # Not based on image from mcr.microsoft.com/dotnet - result = ImagePatchableCheck - result["targetContainerAppName"] = bom["targetContainerAppName"] - result["oldRunImage"] = bom["remote_info"]["run_images"] - result["reason"] = "Image not from mcr.microsoft.com/oryx/builder" - results.append(result) + results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=mcrCheckReason)) if show_all == False : results = [x for x in results if x["newRunImage"] != None] return results \ No newline at end of file From eae2a49c15949a27f306d7450c20c87d942215b1 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Thu, 4 May 2023 19:25:50 -0700 Subject: [PATCH 07/17] Fixed command usage text --- src/containerapp/azext_containerapp/_help.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 03069c554c6..7b1050d5b75 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -1281,5 +1281,5 @@ az containerapp list -g MyResourceGroup --environment MyContainerappEnv - name: List patchable and non-patchable container apps. text: | - az containerapp list -g MyResourceGroup --environment MyContainerappEnv --showAll + az containerapp list -g MyResourceGroup --environment MyContainerappEnv --show-all """ \ No newline at end of file From 44371f1717a886ec4416417bab11db8aa9811f1b Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Mon, 8 May 2023 16:10:10 -0700 Subject: [PATCH 08/17] Made managed env optional --- src/containerapp/azext_containerapp/custom.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 7565266ba5b..181af5ea003 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4301,8 +4301,18 @@ def delete_workload_profile(cmd, resource_group_name, env_name, workload_profile except Exception as e: handle_raw_exception(e) -def patch_list(cmd, resource_group_name, managed_env, show_all=False): - caList = list_containerapp(cmd, resource_group_name, managed_env) +def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): + if(managed_env): + caList = list_containerapp(cmd, resource_group_name, managed_env) + else: + envList = list_managed_environments(cmd, resource_group_name) + envNames = [] + for env in envList: + envNames.append(env["name"]) + caList = [] + for envName in envNames: + caList += list_containerapp(cmd, resource_group_name, envName) + print(caList) imgs = [] if caList: for ca in caList: @@ -4311,7 +4321,7 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False): result = dict(imageName=container["image"], targetContainerAppName=container["name"]) imgs.append(result) - # Get the BOM of the images + # Get the BOM of the images results = [] boms = [] @@ -4325,7 +4335,7 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False): if lines.find(b"status code 401 Unauthorized") != -1 or lines.find(b"unable to find image") != -1: bom = dict(remote_info=401) else: - bom = json.loads(lines) + bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) boms.append(bom) @@ -4372,4 +4382,6 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False): results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=mcrCheckReason)) if show_all == False : results = [x for x in results if x["newRunImage"] != None] + if results == []: + return "No containerapps available to patch at this time." return results \ No newline at end of file From 9ba04df858c779ff000ed004609e22aefeb1b148 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Mon, 8 May 2023 16:17:52 -0700 Subject: [PATCH 09/17] Fixed identation --- src/containerapp/azext_containerapp/custom.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 181af5ea003..8b7175e3b99 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4334,9 +4334,8 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): lines = f.read() if lines.find(b"status code 401 Unauthorized") != -1 or lines.find(b"unable to find image") != -1: bom = dict(remote_info=401) - else: - - bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) + else: + bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) boms.append(bom) ## For testing From fd55c595da64bc7f0a8514d339f0566da9e5be35 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Mon, 8 May 2023 16:18:34 -0700 Subject: [PATCH 10/17] Fixed identation --- src/containerapp/azext_containerapp/custom.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 181af5ea003..11e985cd19e 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4335,8 +4335,7 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): if lines.find(b"status code 401 Unauthorized") != -1 or lines.find(b"unable to find image") != -1: bom = dict(remote_info=401) else: - - bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) + bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) boms.append(bom) ## For testing From 01ec69aa4dcd692148a0c0d2dd0ad934dfb1a2eb Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Tue, 9 May 2023 15:36:32 -0700 Subject: [PATCH 11/17] Made env optional --- src/containerapp/azext_containerapp/custom.py | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 8b7175e3b99..e5c750bb54c 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4308,19 +4308,29 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): envList = list_managed_environments(cmd, resource_group_name) envNames = [] for env in envList: - envNames.append(env["name"]) + envNames.append(env["name"]) + # if(envNames): + # envNames.append("Select all managed environments in my subscription.") + # user_input = [] + # input_message = "Enter managed environment:\n" + # for index, item in enumerate(envNames): + # input_message += f'{index+1}) {item}\n' + # input_message += 'Your choice: ' + # while user_input not in envNames: + # user_input = [x for x in input(input_message).split()] + # print('You picked: ' + user_input) + # userInput = input("Do you want to apply all the patch or specify by id? \n" + envNames) caList = [] for envName in envNames: - caList += list_containerapp(cmd, resource_group_name, envName) - print(caList) + caList+= list_containerapp(cmd,resource_group_name, envName) imgs = [] if caList: for ca in caList: + managedEnvName = ca["properties"]["managedEnvironmentId"].split('/')[-1] containers = ca["properties"]["template"]["containers"] for container in containers: - result = dict(imageName=container["image"], targetContainerAppName=container["name"]) - imgs.append(result) - + result = dict(imageName=container["image"], targetContainerName=container["name"], targetContainerAppName=ca["name"], targetContainerAppEnvironmentName = managedEnvName.split('/')[-1], revisionMode=ca["properties"]["configuration"]["activeRevisionsMode"]) + imgs.append(result) # Get the BOM of the images results = [] boms = [] @@ -4328,14 +4338,18 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): ## For production # for img in imgs: - subprocess.run("pack inspect-image " + img["imageName"] + " --output json > ./bom.json 2>&1", shell=True) - with open("./bom.json", "rb") as f: - bom = None - lines = f.read() - if lines.find(b"status code 401 Unauthorized") != -1 or lines.find(b"unable to find image") != -1: - bom = dict(remote_info=401) - else: - bom.update({ "targetContainerAppName": img["targetContainerAppName"] }) + if (img["imageName"].find("run-dotnet") != -1) and (img["imageName"].find("cbl-mariner") != -1): + bom = { "remote_info": { "run_images": [{ "name": "mcr.microsoft.com/oryx/builder:" + img["imageName"].split(":")[-1] }] }, "image_name": img["imageName"], "targetContainerName": img["targetContainerName"], "targetContainerAppName": img["targetContainerAppName"], "targetContainerAppEnvironmentName": img["targetContainerAppEnvironmentName"], "revisionMode": img["revisionMode"] } + else: + subprocess.run("pack inspect-image " + img["imageName"] + " --output json > ./bom.json 2>&1", shell=True) + with open("./bom.json", "rb") as f: + bom = None + lines = f.read() + if lines.find(b"status code 401 Unauthorized") != -1 or lines.find(b"unable to find image") != -1: + bom = dict(remote_info=401, image_name=img["imageName"]) + else: + bom = json.loads(lines) + bom.update({ "targetContainerName": img["targetContainerName"], "targetContainerAppName": img["targetContainerAppName"], "targetContainerAppEnvironmentName":img["targetContainerAppEnvironmentName"],"revisionMode": img["revisionMode"] }) boms.append(bom) ## For testing @@ -4358,13 +4372,12 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): # Start checking if the images are based on Mariner for bom in boms: if bom["remote_info"] == 401: - results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=None, newRunImage=None, id=None, reason=failedReason)) + results.append(dict(targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName=bom["targetContainerAppEnvironmentName"], oldRunImage=None, newRunImage=None, id=None, reason=failedReason)) else: # devide run-images into different parts by "/" runImagesProps = bom["remote_info"]["run_images"] if runImagesProps is None: - - results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=None, newRunImage=None, id=None, reason=notBasedMarinerReason)) + results.append(dict(targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName=bom["targetContainerAppEnvironmentName"], oldRunImage=None, newRunImage=None, id=None, reason=notBasedMarinerReason)) else: for runImagesProp in runImagesProps: # result = None @@ -4375,10 +4388,10 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): if runImagesTag.find('mariner') != -1: results.append(patchableCheck(runImagesTag, oryxRunImgTags, bom=bom)) else: - results.append(dict(targetContainerAppName=bom["targetContainerAppName"], oldRunImage=bom["remote_info"]["run_images"], 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"], oldRunImage=bom["remote_info"]["run_images"], newRunImage=None, id=None, reason=mcrCheckReason)) + 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 = [x for x in results if x["newRunImage"] != None] if results == []: From cfb5fe2fb207f6dc89990a856600689019243ec8 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Wed, 10 May 2023 14:32:10 -0700 Subject: [PATCH 12/17] Remove patch list --- src/containerapp/azext_containerapp/_help.py | 20 ++++--------------- .../azext_containerapp/_params.py | 11 +++------- .../azext_containerapp/commands.py | 5 +---- src/containerapp/azext_containerapp/custom.py | 17 +++------------- 4 files changed, 11 insertions(+), 42 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 92f583e4f92..27cdcbfcb14 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -1272,26 +1272,14 @@ --compose-file-path "path/to/docker-compose.yml" """ -helps['containerapp patch list'] = """ +helps['containerapp patch'] = """ type: command - short-summary: List patchable and unpatchable container apps. - examples: - - name: List patchable container apps. - text: | - az containerapp list -g MyResourceGroup --environment MyContainerappEnv - - name: List patchable and non-patchable container apps. - text: | - az containerapp list -g MyResourceGroup --environment MyContainerappEnv --show-all -""" - -helps['containerapp patch run'] = """ - type: command - short-summary: List and choose container apps to be patched. + short-summary: List and select container apps to be patched. exmaples: - - name: Patch a container app. + - name: Show containerapps that can be patched and apply patch. text: | az containerapp patch -g MyResourceGroup --environment MyContainerappEnv - - name: Patch a container app and show scan results of all container apps. + - name: Show patchable and unpatchable containerapps 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 dcd6b8bf744..6833161c505 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -415,12 +415,7 @@ 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 list') as c: + 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'], help='Name or resource id of the Container App environment.') - c.argument('show_all', options_list=['--show-all'],help='Show all patchable and non-patchable containerapps') - - with self.argument_context('containerapp patch run') 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'], help='Name or resource id of the Container App environment.') - c.argument('show_all', options_list=['--show-all'],help='Show all patchable and non-patchable containerapps') \ No newline at end of file + 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 diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index 4ed96005fd1..2fb296cbb58 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -53,6 +53,7 @@ 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 @@ -199,7 +200,3 @@ 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') - g.custom_command('run', 'patch_run') diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index e99dadeb373..2943faf731a 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4309,18 +4309,7 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): envList = list_managed_environments(cmd, resource_group_name) envNames = [] for env in envList: - envNames.append(env["name"]) - # if(envNames): - # envNames.append("Select all managed environments in my subscription.") - # user_input = [] - # input_message = "Enter managed environment:\n" - # for index, item in enumerate(envNames): - # input_message += f'{index+1}) {item}\n' - # input_message += 'Your choice: ' - # while user_input not in envNames: - # user_input = [x for x in input(input_message).split()] - # print('You picked: ' + user_input) - # userInput = input("Do you want to apply all the patch or specify by id? \n" + envNames) + envNames.append(env["name"]) caList = [] for envName in envNames: caList+= list_containerapp(cmd,resource_group_name, envName) @@ -4402,13 +4391,13 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): results = {k: v for k, v in results.items() if k != "NotPatchable"} return results -def patch_run(cmd, resource_group_name, managed_env, show_all=False): +def patch_run(cmd, resource_group_name, 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"]: print("No patchable image found.") if not show_all: - print("Use --show-all to show all the images' patchable check.") + print("Use --show-all to show all the patchable and unpatchable images.") return else: patchable_check_results_json = json.dumps(patchable_check_results, indent=4) From eca488e19fadb9e93b238fe489d185db66a89f81 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Wed, 10 May 2023 16:24:41 -0700 Subject: [PATCH 13/17] Update help text for patch list and patch run --- src/containerapp/azext_containerapp/_help.py | 13 ++++++++++++- src/containerapp/azext_containerapp/_params.py | 11 ++++++++--- src/containerapp/azext_containerapp/_utils.py | 1 + src/containerapp/azext_containerapp/commands.py | 5 ++++- src/containerapp/azext_containerapp/custom.py | 8 +++++--- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 27cdcbfcb14..f0196ed2590 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -1272,7 +1272,18 @@ --compose-file-path "path/to/docker-compose.yml" """ -helps['containerapp patch'] = """ +helps['containerapp patch list'] = """ + type: command + short-summary: List Container Apps to be patched. + exmaples: + - name: Show Container Apps that can be patched. + text: | + az containerapp patch -g MyResourceGroup --environment MyContainerappEnv + - name: Show 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. exmaples: diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 6833161c505..5d62bf065de 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -415,7 +415,12 @@ 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) + with self.argument_context('containerapp patch list') as c: + c.argument('resource_group_name', arg_type=resource_group_name_type) 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 + c.argument('show_all', options_list=['--show-all'],help='Show all patchable and unpatchable containerapps') + + 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'], help="Name or resource ID of the container app's environment.") + c.argument('show_all', options_list=['--show-all'],help='Show all patchable and unpatchable containerapps') \ 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..c1e839112b3 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,7 @@ 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') + g.custom_command('run','patch_run') \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 2943faf731a..0a9d1706804 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4382,13 +4382,15 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): 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)) + 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 : + if not show_all : 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): From f75be55d4411eb87c531365c9f27778ed4c3c9a5 Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Wed, 10 May 2023 18:02:09 -0700 Subject: [PATCH 14/17] Modified help text --- src/containerapp/azext_containerapp/_help.py | 12 ++++++------ src/containerapp/azext_containerapp/commands.py | 4 ++-- src/containerapp/azext_containerapp/custom.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index f0196ed2590..7ee22feb35f 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -1274,23 +1274,23 @@ helps['containerapp patch list'] = """ type: command - short-summary: List Container Apps to be patched. + short-summary: List Container Apps to be patched.Patching is only available for the apps built using the source to cloud feature. exmaples: - - name: Show Container Apps that can be patched. + - name: List Container Apps that can be patched. text: | az containerapp patch -g MyResourceGroup --environment MyContainerappEnv - - name: Show patchable and unpatchable Container Apps. + - 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/commands.py b/src/containerapp/azext_containerapp/commands.py index c1e839112b3..c5721258689 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -201,5 +201,5 @@ def load_command_table(self, _): g.custom_command('delete', 'delete_workload_profile') with self.command_group('containerapp patch', is_preview=True) as g: - g.custom_command('list','patch_list') - g.custom_command('run','patch_run') \ No newline at end of file + 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 0a9d1706804..0c5c1c502ff 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4302,7 +4302,7 @@ def delete_workload_profile(cmd, resource_group_name, env_name, workload_profile handle_raw_exception(e) -def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): +def patch_list(cmd, resource_group_name=None, managed_env=None, show_all=False): if(managed_env): caList = list_containerapp(cmd, resource_group_name, managed_env) else: From d1f890b7151e872dcfb170ef68f04a7bc765426a Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Wed, 10 May 2023 23:34:51 -0700 Subject: [PATCH 15/17] Made resouce group optional --- src/containerapp/azext_containerapp/_params.py | 8 ++++---- src/containerapp/azext_containerapp/custom.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 5d62bf065de..35972ae9397 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -417,10 +417,10 @@ def load_arguments(self, _): with self.argument_context('containerapp patch list') as c: c.argument('resource_group_name', arg_type=resource_group_name_type) - 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') + 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'], help="Name or resource ID of the container app's environment.") - c.argument('show_all', options_list=['--show-all'],help='Show all patchable and unpatchable containerapps') \ No newline at end of file + 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') \ No newline at end of file diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 0c5c1c502ff..49f06070dcc 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -4393,7 +4393,7 @@ def patch_list(cmd, resource_group_name=None, managed_env=None, show_all=False): 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"]: From 9b3e38109abd5c437a62b4e25c4a04710f2090cb Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Thu, 11 May 2023 00:04:23 -0700 Subject: [PATCH 16/17] Added custom telemetry --- src/containerapp/azext_containerapp/custom.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 8b841be4465..b9338573291 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 +import telemetry from azure.cli.core.azclierror import ( RequiredArgumentMissingError, @@ -4405,6 +4406,8 @@ def patch_run(cmd, resource_group_name=None, 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.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): From 398f7ced824a069e4e7b9f23df30bfb7866d029a Mon Sep 17 00:00:00 2001 From: snehapar9 Date: Thu, 11 May 2023 00:39:20 -0700 Subject: [PATCH 17/17] Fixed linting errors --- src/containerapp/azext_containerapp/_help.py | 6 +++ .../azext_containerapp/_params.py | 3 +- .../azext_containerapp/commands.py | 3 +- src/containerapp/azext_containerapp/custom.py | 47 ++++++++++--------- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index c07f45e68da..a4ff31af6ad 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -1272,6 +1272,12 @@ --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. diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index def04f65577..53b844386e7 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -423,4 +423,5 @@ def load_arguments(self, _): 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 + 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/commands.py b/src/containerapp/azext_containerapp/commands.py index c5721258689..432400888c0 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -202,4 +202,5 @@ def load_command_table(self, _): 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 + 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 b9338573291..b387e14746f 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -11,7 +11,7 @@ import requests import json import subprocess -import telemetry +from azure.cli.core import telemetry as telemetry_core from azure.cli.core.azclierror import ( RequiredArgumentMissingError, @@ -31,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 @@ -4134,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, @@ -4304,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) @@ -4321,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 = [] @@ -4372,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 @@ -4382,12 +4383,12 @@ def patch_list(cmd, resource_group_name, managed_env=None, show_all=False): results["NotPatchable"].append(checkResult) else: results[checkResult["id"]] = checkResult - else: + 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)) + 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 : @@ -4406,8 +4407,8 @@ def patch_run(cmd, resource_group_name=None, 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.add_extension_event('patch-run') + 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): @@ -4417,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 @@ -4452,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