-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ContainerApp] Added support for Cloud Patching #7571
base: main
Are you sure you want to change the base?
Changes from all commits
7cdadb6
812b5d6
8f806d1
a209144
50c303a
5426685
ebcf4e0
f068700
0aef23b
670b342
b7544e2
07de3ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -809,6 +809,9 @@ | |
- name: List patchable and unpatchable container apps by managed environment with the show-all option. | ||
text: | | ||
az containerapp patch list -g MyResourceGroup --environment MyContainerAppEnv --show-all | ||
- name: List available patches for a container app. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
text: | | ||
az containerapp patch list -g MyResourceGroup --container-app-name MyContainerApp | ||
""" | ||
|
||
helps['containerapp patch apply'] = """ | ||
|
@@ -827,6 +830,27 @@ | |
- name: List patchable and unpatchable container apps by managed environment with the show-all option and apply patch for patchable container apps. | ||
text: | | ||
az containerapp patch apply -g MyResourceGroup --environment MyContainerAppEnv --show-all | ||
- name: Apply a patch for a container app by patch name using Cloud Patching. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
text: | | ||
az containerapp patch apply -g MyResourceGroup --container-app-name MyContainerApp --patch-name patch-12345 | ||
""" | ||
|
||
helps['containerapp patch delete'] = """ | ||
type: command | ||
short-summary: Delete a Container App Cloud Patch from a Container App. | ||
examples: | ||
- name: Delete a Cloud Patch. | ||
text: | | ||
az containerapp patch delete -g MyResourceGroup --container-app-name MyContainerApp --patch-name patch-12345 | ||
""" | ||
|
||
helps['containerapp patch show'] = """ | ||
type: command | ||
short-summary: Show the details of a Container App Cloud Patch. | ||
examples: | ||
- name: Show the details of a Cloud Patch. | ||
text: | | ||
az containerapp patch show -g MyResourceGroup --container-app-name MyContainerApp --patch-name patch-12345 | ||
""" | ||
|
||
helps['containerapp patch interactive'] = """ | ||
|
@@ -847,6 +871,20 @@ | |
az containerapp patch interactive -g MyResourceGroup --environment MyContainerAppEnv --show-all | ||
""" | ||
|
||
helps["containerapp patch configure"] = """ | ||
type: group | ||
short-summary: Commands to configure the patching settings for a container app. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace container app by |
||
""" | ||
|
||
helps["containerapp patch configure mode"] = """ | ||
type: command | ||
short-summary: Configure the patching mode for a container app. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
examples: | ||
- name: Configure the patching mode for a container app to be manually patched. | ||
text: | | ||
az containerapp patch configure mode -g MyResourceGroup --container-app-name MyContainerApp --patch-mode Manual | ||
""" | ||
|
||
# containerapp create for preview | ||
helps['containerapp create'] = """ | ||
type: command | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,6 +127,7 @@ | |
|
||
logger = get_logger(__name__) | ||
|
||
|
||
def list_all_services(cmd, environment_name, resource_group_name): | ||
services = list_containerapp(cmd, resource_group_name=resource_group_name, managed_env=environment_name) | ||
dev_service_list = [] | ||
|
@@ -1496,7 +1497,25 @@ def set_workload_profile(cmd, resource_group_name, env_name, workload_profile_na | |
return update_managed_environment(cmd, env_name, resource_group_name, workload_profile_type=workload_profile_type, workload_profile_name=workload_profile_name, min_nodes=min_nodes, max_nodes=max_nodes) | ||
|
||
|
||
def patch_list(cmd, resource_group_name=None, managed_env=None, show_all=False): | ||
def patch_list(cmd, resource_group_name=None, managed_env=None, container_app_name=None, show_all=False): | ||
from azure.cli.command_modules.containerapp._utils import format_location | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this import statement to the top of this file |
||
if container_app_name: | ||
# Use cloud patching if container app name is provided | ||
logger.warning("Container app name is provided. List cloud patches available for the container app.") | ||
logger.warning("Cloud patching is only supported in North Central US (Stage) now.") | ||
app = show_containerapp(cmd, container_app_name, resource_group_name) | ||
if app is None: | ||
logger.error("Container app {0} not found in resource group {1}.".format(container_app_name, resource_group_name)) | ||
return | ||
if format_location(app["location"]) != format_location("North Central US (Stage)"): | ||
logger.warning("Cloud patching is not supported in the location of the container app. Defaulted back to use local patching.") | ||
logger.warning("Container App name will not be used in local patching.") | ||
else: | ||
from ._clients import PatchClient | ||
patches = PatchClient.list(cmd, resource_group_name, container_app_name) | ||
if patches: | ||
return patches["value"] | ||
|
||
# Ensure that Docker is running locally before attempting to use the pack CLI | ||
if is_docker_running() is False: | ||
logger.error("Please install or start Docker and try again.") | ||
|
@@ -1552,6 +1571,81 @@ def patch_list(cmd, resource_group_name=None, managed_env=None, show_all=False): | |
return results | ||
|
||
|
||
def patch_show(cmd, resource_group_name=None, container_app_name=None, patch_name=None): | ||
from azure.cli.command_modules.containerapp._utils import format_location | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this import statement to the top and re-use across methods. |
||
logger.warning("This command is currently only available for container app cloud patches in North Central US (Stage).") | ||
if patch_name is None: | ||
logger.error("Please provide the name of the patch to show.") | ||
return | ||
try: | ||
app = show_containerapp(cmd, container_app_name, resource_group_name) | ||
except Exception as e: | ||
logger.error("Failed to fetch container app {0} in resource group {1}. Error: {2}".format(container_app_name, resource_group_name, str(e))) | ||
return | ||
if app is None: | ||
logger.error("Container app {0} not found in resource group {1}.".format(container_app_name, resource_group_name)) | ||
return | ||
if format_location(app["location"]) != format_location("North Central US (Stage)"): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use safe_get() to get app's location |
||
logger.warning("Cloud patching is not supported in the location of the container app.") | ||
return | ||
from ._clients import PatchClient | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment - Move all imports to the top. |
||
patch_client = PatchClient() | ||
return patch_client.show(cmd, resource_group_name, container_app_name, patch_name) | ||
|
||
|
||
def patch_delete(cmd, resource_group_name=None, container_app_name=None, patch_name=None): | ||
from azure.cli.command_modules.containerapp._utils import format_location | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment about moving this import statement to the top |
||
logger.warning("This command is currently only available for container app cloud patches in North Central US (Stage).") | ||
if patch_name is None: | ||
logger.error("Please provide the name of the patch to delete.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should throw a validation error instead of just logging as an error? |
||
return | ||
try: | ||
app = show_containerapp(cmd, container_app_name, resource_group_name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could throw a not found exception if container app does not exist. |
||
except Exception as e: | ||
logger.error("Failed to fetch container app {0} in resource group {1}. Error: {2}".format(container_app_name, resource_group_name, str(e))) | ||
return | ||
if app is None: | ||
logger.error("Container app {0} not found in resource group {1}.".format(container_app_name, resource_group_name)) | ||
return | ||
if format_location(app["location"]) != format_location("North Central US (Stage)"): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use safe_get() |
||
logger.warning("Cloud patching is not supported in the location of the container app.") | ||
return | ||
from ._clients import PatchClient | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same comment about import statement |
||
patch_client = PatchClient() | ||
is_patch_deleted = patch_client.delete(cmd, resource_group_name, container_app_name, patch_name) | ||
if is_patch_deleted: | ||
print("Patch {0} for container app {1} is deleted successfully.".format(patch_name, container_app_name)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logger.warning instead of print |
||
else: | ||
logger.error("Failed to delete patch {0} for container app {1}.".format(patch_name, container_app_name)) | ||
|
||
|
||
def patch_mode_configure(cmd, resource_group_name=None, container_app_name=None, patch_mode=None): | ||
from azure.cli.command_modules.containerapp._utils import format_location | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move import |
||
logger.warning("This command is currently only available for container app Cloud Patches in North Central US (Stage).") | ||
try: | ||
app = show_containerapp(cmd, container_app_name, resource_group_name) | ||
except Exception as e: | ||
logger.error("Failed to fetch container app {0} in resource group {1}. Error: {2}".format(container_app_name, resource_group_name, str(e))) | ||
return | ||
if app is None: | ||
logger.error("Container app {0} not found in resource group {1}.".format(container_app_name, resource_group_name)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change to |
||
return | ||
if format_location(app["location"]) != format_location("North Central US (Stage)"): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use safe_get instead of just app["location"] |
||
logger.warning("Cloud patching is not supported in the location of the container app.") | ||
return | ||
if patch_mode not in ["Automatic", "Manual", "Disabled"]: | ||
logger.error("Invalid patch mode provided. Please provide 'Automatic', 'Manual', or 'Disabled'.") | ||
return | ||
from ._clients import PatchClient | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move imort |
||
patch_client = PatchClient() | ||
is_patch_mode_configured = patch_client.patch_mode_configure(cmd, resource_group_name, container_app_name, patch_mode) | ||
if is_patch_mode_configured: | ||
print("Patch mode for container app {0} is set to {1}.".format(container_app_name, patch_mode)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logger.warning if we want this to show up on the console. |
||
else: | ||
logger.error("Failed to set patch mode for container app {}.".format(container_app_name)) | ||
return | ||
|
||
|
||
def _get_patchable_check_result(inspect_result, oryx_run_images): | ||
# Define reasons for patchable check failure | ||
failed_reason = "Failed to inspect the image. Please make sure that you are authenticated to the container registry and that the image exists." | ||
|
@@ -1642,7 +1736,10 @@ def patch_interactive(cmd, resource_group_name=None, managed_env=None, show_all= | |
patch_apply_handle_input(cmd, patchable_check_results, user_input, pack_exec_path) | ||
|
||
|
||
def patch_apply(cmd, resource_group_name=None, managed_env=None, show_all=False): | ||
def patch_apply(cmd, resource_group_name=None, managed_env=None, container_app_name=None, patch_name=None, show_all=False): | ||
if container_app_name: | ||
# Use Cloud Patching if container app name is provided | ||
return use_cloud_patch(cmd, container_app_name, resource_group_name, patch_name) | ||
if is_docker_running() is False: | ||
logger.error("Please install or start Docker and try again.") | ||
return | ||
|
@@ -1663,6 +1760,28 @@ def patch_apply(cmd, resource_group_name=None, managed_env=None, show_all=False) | |
patch_apply_handle_input(cmd, patchable_check_results, "y", pack_exec_path) | ||
|
||
|
||
def use_cloud_patch(cmd, container_app_name, resource_group_name, patch_name): | ||
try: | ||
app = show_containerapp(cmd, container_app_name, resource_group_name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this throw a not found exception if container app does not exist? Probably need to handle it separately? by check if not found exception is thrown in a separate method and return |
||
except Exception as e: | ||
logger.error("Failed to fetch container app {0} in resource group {1}. Error: {2}".format(container_app_name, resource_group_name, str(e))) | ||
return | ||
if app is None: | ||
logger.error("Container app {0} not found in resource group {1}.".format(container_app_name, resource_group_name)) | ||
return | ||
if app["location"] == "North Central US (Stage)": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use safe_get() here for getting the app's location and let's use |
||
logger.warning("Using cloud patching...") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's combine this into a single line. |
||
logger.warning("Cloud patching is only supported in North Central US (Stage) now.") | ||
from ._clients import PatchClient | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move import. |
||
patch_client = PatchClient() | ||
is_patch_success = patch_client.apply(cmd, resource_group_name, container_app_name, patch_name) | ||
if is_patch_success: | ||
print("Patch {0} for container app {1} is queued successfully to be applied.".format(patch_name, container_app_name)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logger.warning if we want this to show up on cx console? |
||
else: | ||
logger.error("Failed to apply patch {0} for container app {1}.".format(patch_name, container_app_name)) | ||
return | ||
|
||
|
||
def patch_apply_handle_input(cmd, patch_check_list, method, pack_exec_path): | ||
input_method = method.strip().lower() | ||
# Track number of times patches were applied successfully. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be
PatchPreviewClient
?