Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 4 additions & 16 deletions src/containerapp/azext_containerapp/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
11 changes: 3 additions & 8 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
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')
4 changes: 0 additions & 4 deletions src/containerapp/azext_containerapp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1722,7 +1722,6 @@ def format_location(location=None):
return location.lower().replace(" ", "").replace("(", "").replace(")", "")
return location


def is_docker_running():
# check to see if docker is running
client = None
Expand Down Expand Up @@ -1776,7 +1775,6 @@ def get_pack_exec_path():

return ""


def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom):
tagProp = parseOryxMarinerTag(repoTagSplit)
if tagProp is None:
Expand Down Expand Up @@ -1839,7 +1837,6 @@ def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom):
}
return result


def getCurrentMarinerTags() -> list(OryxMarinerRunImgTagProperty):
r = requests.get("https://mcr.microsoft.com/v2/oryx/builder/tags/list")
tags = r.json()
Expand Down Expand Up @@ -1870,7 +1867,6 @@ def getCurrentMarinerTags() -> list(OryxMarinerRunImgTagProperty):
tagList[framework] = {majorMinorVer: {support: {marinerVer: [tagObj]}}}
return tagList


def parseOryxMarinerTag(tag: str) -> OryxMarinerRunImgTagProperty:
tagSplit = tag.split("-")
if tagSplit[0] == "run" and tagSplit[1] == "dotnet":
Expand Down
5 changes: 1 addition & 4 deletions src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
45 changes: 27 additions & 18 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -4301,16 +4301,26 @@ 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)
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"], targetContainerName=container["name"], targetContainerAppName=ca["name"], revisionMode=ca["properties"]["configuration"]["activeRevisionsMode"])
imgs.append(result)

result = dict(imageName=container["image"], targetContainerName=container["name"], targetContainerAppName=ca["name"], targetContainerAppEnvironmentName = managedEnvName, revisionMode=ca["properties"]["configuration"]["activeRevisionsMode"])
imgs.append(result)
# Get the BOM of the images
results = []
boms = []
Expand All @@ -4319,7 +4329,7 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False):
#
for img in imgs:
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"], "revisionMode": img["revisionMode"] }
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:
Expand All @@ -4329,11 +4339,11 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False):
bom = dict(remote_info=401, image_name=img["imageName"])
else:
bom = json.loads(lines)
bom.update({ "targetContainerName": img["targetContainerName"], "targetContainerAppName": img["targetContainerAppName"], "revisionMode": img["revisionMode"] })
bom.update({ "targetContainerName": img["targetContainerName"], "targetContainerAppName": img["targetContainerAppName"], "targetContainerAppEnvironmentName":img["targetContainerAppEnvironmentName"],"revisionMode": img["revisionMode"] })

boms.append(bom)

# # For testing

# 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:
Expand All @@ -4352,13 +4362,12 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False):
# Start checking if the images are based on Mariner
for bom in boms:
if bom["remote_info"] == 401:
results["NotPatchable"].append(dict(targetContainerName=bom["targetContainerName"], targetContainerAppName=bom["targetContainerAppName"], revisionMode=bom["revisionMode"], targetImageName=bom["image_name"], oldRunImage=None, 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=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["NotPatchable"].append(dict(targetContainerName=bom["targetContainerName"], targetContainerAppName=bom["targetContainerAppName"], revisionMode=bom["revisionMode"], targetImageName=bom["image_name"], oldRunImage=None, newRunImage=None, id=None, reason=notBasedMarinerReason))
results["NotPatchable"].append(dict(targetContainerName=bom["targetContainerName"], targetContainerAppName=bom["targetContainerAppName"],targetContainerAppEnvironmentName=bom["targetContainerAppEnvironmentName"], revisionMode=bom["revisionMode"], targetImageName=bom["image_name"], oldRunImage=None, newRunImage=None, id=None, reason=notBasedMarinerReason))
else:
for runImagesProp in runImagesProps:
# result = None
Expand All @@ -4373,21 +4382,22 @@ def patch_list(cmd, resource_group_name, managed_env, show_all=False):
else:
results[checkResult["id"]] = checkResult
else:
results["NotPatchable"].append(dict(targetContainerName=bom["targetContainerName"], targetContainerAppName=bom["targetContainerAppName"], revisionMode=bom["revisionMode"], targetImageName=bom["image_name"], oldRunImage=bom["remote_info"]["run_images"]["name"], 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["NotPatchable"].append(dict(targetContainerName=bom["targetContainerName"], targetContainerAppName=bom["targetContainerAppName"], revisionMode=bom["revisionMode"], targetImageName=bom["image_name"], oldRunImage=bom["remote_info"]["run_images"]["name"], 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 = {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)
Expand All @@ -4410,7 +4420,7 @@ def patch_apply(cmd, patchCheckList, method, resource_group_name):
patchCheckList[key]["newRunImage"],
patchCheckList[key]["revisionMode"]))
elif m == "n":
print("No patch applied.")
print("No patch applied."); return
else:
if method in patchCheckList.keys():
results.append(patch_cli_call(cmd,
Expand All @@ -4421,7 +4431,7 @@ def patch_apply(cmd, patchCheckList, method, resource_group_name):
patchCheckList[method]["newRunImage"],
patchCheckList[method]["revisionMode"]))
else:
print("Invalid patch method or id.")
print("Invalid patch method or id."); return
return results

def patch_cli_call(cmd, resource_group, container_app_name, container_name, target_image_name, new_run_image, revision_mode):
Expand Down Expand Up @@ -4450,4 +4460,3 @@ def patch_cli_call(cmd, resource_group, container_app_name, container_name, targ
except Exception:
print("Error: Failed to create new revision with the container app.")
raise