-
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
Add Maintenance Config Support #8190
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -8,6 +8,7 @@ upcoming | |
* 'az containerapp containerapp create/up': `--registry-server` and `--source` use managed identity for image pull by default | ||
* 'az containerapp containerapp create': `--registry-server` use managed identity for image pull by default. `--no-wait` will not take effect with system registry identity. | ||
* 'az containerapp env java-component gateway-for-spring': Support create/update/show/delete Gateway for spring. | ||
* 'az containerapp env maintenance-config': Support Add, Update, Show, 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. version |
||
|
||
1.0.0b3 | ||
++++++ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,7 @@ | |
HEADER_AZURE_ASYNC_OPERATION = "azure-asyncoperation" | ||
HEADER_LOCATION = "location" | ||
SESSION_RESOURCE = "https://dynamicsessions.io" | ||
MAINTENANCE_CONFIG_DEFAULT_NAME = "default" | ||
|
||
|
||
class GitHubActionPreviewClient(GitHubActionClient): | ||
|
@@ -1382,3 +1383,98 @@ def list(cls, cmd, resource_group_name, environment_name): | |
dotNet_component_list.append(component) | ||
|
||
return dotNet_component_list | ||
|
||
|
||
class MaintenanceConfigPreviewClient(): | ||
api_version = "2024-10-02-preview" | ||
maintenance_config_name = MAINTENANCE_CONFIG_DEFAULT_NAME | ||
|
||
@classmethod | ||
def show(cls, cmd, resource_group_name, environment_name): | ||
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager | ||
sub_id = get_subscription_id(cmd.cli_ctx) | ||
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/microsoft.app/managedenvironments/{}/maintenanceConfigurations/{}?api-version={}" | ||
request_url = url_fmt.format( | ||
management_hostname.strip('/'), | ||
sub_id, | ||
resource_group_name, | ||
environment_name, | ||
cls.maintenance_config_name, | ||
cls.api_version) | ||
|
||
r = send_raw_request(cmd.cli_ctx, "GET", request_url) | ||
|
||
return r.json() | ||
|
||
@classmethod | ||
def add(cls, cmd, resource_group_name, environment_name, maintenance_config_envelope, no_wait=False): | ||
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager | ||
sub_id = get_subscription_id(cmd.cli_ctx) | ||
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/microsoft.app/managedenvironments/{}/maintenanceConfigurations/{}?api-version={}" | ||
request_url = url_fmt.format( | ||
management_hostname.strip('/'), | ||
sub_id, | ||
resource_group_name, | ||
environment_name, | ||
cls.maintenance_config_name, | ||
cls.api_version) | ||
|
||
r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(maintenance_config_envelope)) | ||
|
||
if no_wait: | ||
return r.json() | ||
elif r.status_code == 201: | ||
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. JFYI - There is no impact. |
||
operation_url = r.headers.get(HEADER_AZURE_ASYNC_OPERATION) | ||
poll_status(cmd, operation_url) | ||
r = send_raw_request(cmd.cli_ctx, "GET", request_url) | ||
|
||
return r.json() | ||
|
||
@classmethod | ||
def update(cls, cmd, resource_group_name, environment_name, maintenance_config_envelope, no_wait=False): | ||
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager | ||
sub_id = get_subscription_id(cmd.cli_ctx) | ||
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/microsoft.app/managedenvironments/{}/maintenanceConfigurations/{}?api-version={}" | ||
request_url = url_fmt.format( | ||
management_hostname.strip('/'), | ||
sub_id, | ||
resource_group_name, | ||
environment_name, | ||
cls.maintenance_config_name, | ||
cls.api_version) | ||
|
||
r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(maintenance_config_envelope)) | ||
|
||
if no_wait: | ||
return | ||
elif r.status_code == 202: | ||
operation_url = r.headers.get(HEADER_LOCATION) | ||
response = poll_results(cmd, operation_url) | ||
if response is None: | ||
raise ResourceNotFoundError("Could not find the maintenance config") | ||
else: | ||
return response | ||
|
||
return r.json() | ||
|
||
@classmethod | ||
def delete(cls, cmd, resource_group_name, environment_name, no_wait=False): | ||
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager | ||
sub_id = get_subscription_id(cmd.cli_ctx) | ||
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/microsoft.app/managedenvironments/{}/maintenanceConfigurations/{}?api-version={}" | ||
request_url = url_fmt.format( | ||
management_hostname.strip('/'), | ||
sub_id, | ||
resource_group_name, | ||
environment_name, | ||
cls.maintenance_config_name, | ||
cls.api_version) | ||
|
||
r = send_raw_request(cmd.cli_ctx, "DELETE", request_url) | ||
|
||
if no_wait: | ||
return # API doesn't return JSON (it returns no content) | ||
elif r.status_code in [200, 201, 202, 204]: | ||
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. 201 polling is not handled and also invalid for delete operations. Refer https://github.com/cloud-and-ai-microsoft/resource-provider-contract/blob/master/v1.0/async-api-reference.md#delete-resource-asynchronously For this api, we only return 200. Still future proofing is good. |
||
if r.status_code == 202: | ||
operation_url = r.headers.get(HEADER_LOCATION) | ||
poll_results(cmd, operation_url) |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2131,3 +2131,42 @@ | |||||
az containerapp job registry set -n my-containerapp-job -g MyResourceGroup \\ | ||||||
--server MyContainerappJobRegistry.azurecr.io --identity system-environment | ||||||
""" | ||||||
|
||||||
# Maintenance Config Commands | ||||||
helps['containerapp env maintenance-config add'] = """ | ||||||
type: command | ||||||
short-summary: Add a Maintenance Configuration to a Container App Environment | ||||||
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. May need to update description based PM final decision. Example:
Suggested change
|
||||||
examples: | ||||||
- name: Configure an Env to use a maintenance config | ||||||
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. nit: To have full name for descriptions. PM can contribute here
Suggested change
|
||||||
text: | | ||||||
az containerapp env maintenance-config add --env-name myEnv -g MyResourceGroup \\ | ||||||
--duration 10 --start-hour-utc 11 --weekday Sunday | ||||||
""" | ||||||
|
||||||
helps['containerapp env maintenance-config update'] = """ | ||||||
type: command | ||||||
short-summary: Update a Maintenance Configuration in a Container App Environment | ||||||
examples: | ||||||
- name: Configure an Env to use a maintenance config | ||||||
text: | | ||||||
az containerapp env maintenance-config update --env-name myEnv -g MyResourceGroup \\ | ||||||
--duration 8 --start-hour-utc 12 --weekday Thursday | ||||||
""" | ||||||
|
||||||
helps['containerapp env maintenance-config show'] = """ | ||||||
type: command | ||||||
short-summary: Show a Maintenance Configuration in a Container App Environment | ||||||
examples: | ||||||
- name: Show a maintenance config | ||||||
text: | | ||||||
az containerapp env maintenance-config show --env-name myEnv -g MyResourceGroup | ||||||
""" | ||||||
|
||||||
helps['containerapp env maintenance-config delete'] = """ | ||||||
type: command | ||||||
short-summary: Delete a Maintenance Configuration in a Container App Environment | ||||||
examples: | ||||||
- name: Delete a Maintenance Config | ||||||
text: | | ||||||
az containerapp env maintenance-config add --env-name myEnv -g MyResourceGroup | ||||||
""" |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -370,6 +370,11 @@ def load_arguments(self, _): | |||||
c.argument('max_replicas', type=int, help="Maximum number of replicas to run for the Java component.") | ||||||
c.argument('route_yaml', options_list=['--route-yaml', '--yaml'], help="Path to a .yaml file with the configuration of a Spring Cloud Gateway route. For an example, see https://aka.ms/gateway-for-spring-routes-yaml") | ||||||
|
||||||
with self.argument_context('containerapp env maintenance-config') as c: | ||||||
c.argument('weekday', options_list=['--weekday', '-w'], arg_type=get_enum_type(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ]), help="The weekday to schedule the maintenance configuration.") | ||||||
c.argument('start_hour_utc', options_list=['--start-hour-utc', '-s'], help="The hour to start the maintenance configuration.") | ||||||
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.
Suggested change
|
||||||
c.argument('duration', options_list=['--duration', '-d'], help="The duration in hours of the maintenance configuration.") | ||||||
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. Since, you have it in validation
Suggested change
|
||||||
|
||||||
with self.argument_context('containerapp job logs show') as c: | ||||||
c.argument('follow', help="Print logs in real time if present.", arg_type=get_three_state_flag()) | ||||||
c.argument('tail', help="The number of past logs to print (0-300)", type=int, default=20) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# coding=utf-8 | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
# pylint: disable=line-too-long, broad-except, logging-format-interpolation | ||
|
||
from azure.cli.core.azclierror import (ValidationError) | ||
from copy import deepcopy | ||
from knack.log import get_logger | ||
from typing import Any, Dict | ||
|
||
from azure.cli.core.commands import AzCliCommand | ||
from azure.cli.command_modules.containerapp.base_resource import BaseResource | ||
|
||
from ._models import MaintenanceConfiguration as MaintenanceConfigurationModel | ||
from ._client_factory import handle_raw_exception, handle_non_404_status_code_exception | ||
|
||
logger = get_logger(__name__) | ||
|
||
|
||
class MaintenanceConfigDecorator(BaseResource): | ||
def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str): | ||
super().__init__(cmd, client, raw_parameters, models) | ||
self.maintenance_config_def = deepcopy(MaintenanceConfigurationModel) | ||
self.existing_maintenance_config_def = None | ||
|
||
def get_argument_environment_name(self): | ||
return self.get_param('env_name') | ||
|
||
def get_argument_resource_group_name(self): | ||
return self.get_param('resource_group_name') | ||
|
||
def get_argument_weekday(self): | ||
return self.get_param('weekday') | ||
|
||
def get_argument_start_hour_utc(self): | ||
return self.get_param('start_hour_utc') | ||
|
||
def get_argument_duration(self): | ||
return self.get_param('duration') | ||
|
||
|
||
class MaintenanceConfigPreviewDecorator(MaintenanceConfigDecorator): | ||
def validate_arguments(self): | ||
if self.get_argument_start_hour_utc() is not None: | ||
if not (0 <= int(self.get_argument_start_hour_utc()) <= 23): | ||
raise ValidationError("Start hour must be an integer from 0 to 23") | ||
|
||
if self.get_argument_duration() is not None: | ||
if not (8 <= int(self.get_argument_duration()) <= 24): | ||
raise ValidationError("Duration must be an integer from 8 to 24") | ||
|
||
if self.get_argument_weekday is not None: | ||
if self.get_argument_weekday().lower() not in ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]: | ||
raise ValidationError("Weekday must be a day of the week") | ||
|
||
def construct_payload(self, forUpdate=False): | ||
if forUpdate: | ||
self.existing_maintenance_config_def = self.client.show( | ||
cmd=self.cmd, | ||
resource_group_name=self.get_argument_resource_group_name(), | ||
environment_name=self.get_argument_environment_name()) | ||
|
||
self.maintenance_config_def = deepcopy(self.existing_maintenance_config_def) | ||
|
||
if self.get_argument_start_hour_utc() is not None: | ||
self.maintenance_config_def["properties"]["scheduledEntries"][0]["startHourUtc"] = self.get_argument_start_hour_utc() | ||
if self.get_argument_duration() is not None: | ||
self.maintenance_config_def["properties"]["scheduledEntries"][0]["durationHours"] = self.get_argument_duration() | ||
if self.get_argument_weekday() is not None: | ||
self.maintenance_config_def["properties"]["scheduledEntries"][0]["weekDay"] = self.get_argument_weekday() | ||
|
||
def add(self): | ||
try: | ||
return self.client.add( | ||
cmd=self.cmd, | ||
resource_group_name=self.get_argument_resource_group_name(), | ||
environment_name=self.get_argument_environment_name(), | ||
maintenance_config_envelope=self.maintenance_config_def, | ||
no_wait=self.get_argument_no_wait()) | ||
except Exception as e: | ||
handle_raw_exception(e) | ||
|
||
def update(self): | ||
try: | ||
return self.client.update( | ||
cmd=self.cmd, | ||
resource_group_name=self.get_argument_resource_group_name(), | ||
environment_name=self.get_argument_environment_name(), | ||
maintenance_config_envelope=self.maintenance_config_def, | ||
no_wait=self.get_argument_no_wait()) | ||
except Exception as e: | ||
handle_raw_exception(e) | ||
|
||
def delete(self): | ||
try: | ||
return self.client.delete( | ||
cmd=self.cmd, | ||
resource_group_name=self.get_argument_resource_group_name(), | ||
environment_name=self.get_argument_environment_name(), | ||
no_wait=self.get_argument_no_wait()) | ||
except Exception as e: | ||
handle_raw_exception(e) | ||
|
||
def show(self): | ||
try: | ||
return self.client.show( | ||
cmd=self.cmd, | ||
resource_group_name=self.get_argument_resource_group_name(), | ||
environment_name=self.get_argument_environment_name()) | ||
except Exception as e: | ||
handle_non_404_status_code_exception(e) | ||
return "" |
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.
Could you get ack from PMs with
maintenance-config
change since doc has different values