Skip to content

Commit

Permalink
containerapp refactor decorator (Azure#6511)
Browse files Browse the repository at this point in the history
  • Loading branch information
Greedygre authored Jul 13, 2023
1 parent 1ddb047 commit 457e8a7
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,21 @@ def __init__(
self.azext_default_utils = _get_azext_containerapp_module("azext_containerapp._utils")

def construct_containerapp(self):
containerapp_def = super().construct_containerapp()
containerapp_def = self.set_up_extended_location(containerapp_def)
return containerapp_def
super().construct_containerapp()
self.set_up_extended_location()

def set_up_extended_location(self, containerapp_def):
def set_up_extended_location(self):
if self.get_argument_environment_type() == CONNECTED_ENVIRONMENT_TYPE:
parsed_env = parse_resource_id(self.get_argument_managed_env()) # custom_location check here perhaps
env_name = parsed_env['name']
env_rg = parsed_env['resource_group']
env_info = self.get_environment_client().show(cmd=self.cmd, resource_group_name=env_rg, name=env_name)
if not containerapp_def.get('extendedLocation'):
containerapp_def["extendedLocation"] = env_info["extendedLocation"]
return containerapp_def
if not self.containerapp_def.get('extendedLocation'):
parsed_env = parse_resource_id(self.get_argument_managed_env()) # custom_location check here perhaps
env_name = parsed_env['name']
env_rg = parsed_env['resource_group']
env_info = self.get_environment_client().show(cmd=self.cmd, resource_group_name=env_rg, name=env_name)
self.containerapp_def["extendedLocation"] = env_info["extendedLocation"]

def get_environment_client(self):
if self.get_argument_yaml():
yaml_containerapp = self.get_yaml_containerapp()
if type(yaml_containerapp) != dict: # pylint: disable=unidiomatic-typecheck
raise ValidationError('Invalid YAML provided. Please see https://aka.ms/azure-container-apps-yaml for a valid containerapps YAML spec.')
env = self.azext_default_utils.safe_get(yaml_containerapp, "properties", "environmentId")
if not env:
raise RequiredArgumentMissingError(
'environmentId is required. This can be retrieved using the `az containerapp env show -g MyResourceGroup -n MyContainerappEnvironment --query id` command. Please see https://aka.ms/azure-container-apps-yaml for a valid containerapps YAML spec.')
env = self.azext_default_utils.safe_get(self.containerapp_def, "properties", "environmentId")
else:
env = self.get_argument_managed_env()

Expand Down
8 changes: 4 additions & 4 deletions src/containerapp-preview/azext_containerapp_preview/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ def create_containerapp(cmd,
)
containerapp_preview_create_decorator.register_provider()
containerapp_preview_create_decorator.validate_arguments()
containerapp_def = containerapp_preview_create_decorator.construct_containerapp()
r = containerapp_preview_create_decorator.create_containerapp(containerapp_def)
containerapp_def = containerapp_preview_create_decorator.construct_containerapp_for_post_process(containerapp_def, r)
r = containerapp_preview_create_decorator.post_process_containerapp(containerapp_def, r)
containerapp_preview_create_decorator.construct_containerapp()
r = containerapp_preview_create_decorator.create_containerapp()
containerapp_preview_create_decorator.construct_containerapp_for_post_process(r)
r = containerapp_preview_create_decorator.post_process_containerapp(r)
return r
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os
import time
from time import sleep
from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, JMESPathCheck, live_only)
from subprocess import run

Expand All @@ -15,14 +16,20 @@


class ContainerappScenarioTest(ScenarioTest):
def setUp(self):
super(ContainerappScenarioTest, self).setUp()
def __init__(self, method_name, config_file=None, recording_name=None, recording_processors=None,
replay_processors=None, recording_patches=None, replay_patches=None, random_config_dir=False):

super().__init__(method_name, config_file, recording_name, recording_processors, replay_processors,
recording_patches, replay_patches, random_config_dir)
cmd = ['azdev', 'extension', 'add', 'containerapp']
run(cmd, check=True)
cmd = ['azdev', 'extension', 'add', 'connectedk8s']
run(cmd, check=True)
cmd = ['azdev', 'extension', 'add', 'k8s-extension']
run(cmd, check=True)
# Wait for extensions to be installed
# We mock time.sleep in azure-sdk-tools, that's why we need to use sleep here.
sleep(120)

@ResourceGroupPreparer(location="eastus", random_name_length=15)
def test_containerapp_preview_environment_type(self, resource_group):
Expand Down
65 changes: 30 additions & 35 deletions src/containerapp/azext_containerapp/containerapp_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ def __init__(
self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str
):
super().__init__(cmd, client, raw_parameters, models)
self.containerapp_def = ContainerAppModel

def validate_arguments(self):
validate_container_app_name(self.get_argument_name(), AppType.ContainerApp.name)
Expand Down Expand Up @@ -506,58 +507,55 @@ def construct_containerapp(self):
if self.get_argument_termination_grace_period() is not None:
template_def["terminationGracePeriodSeconds"] = self.get_argument_termination_grace_period()

containerapp_def = ContainerAppModel
containerapp_def["location"] = location
containerapp_def["identity"] = identity_def
containerapp_def["properties"]["environmentId"] = self.get_argument_managed_env()
containerapp_def["properties"]["configuration"] = config_def
containerapp_def["properties"]["template"] = template_def
containerapp_def["tags"] = self.get_argument_tags()
self.containerapp_def["location"] = location
self.containerapp_def["identity"] = identity_def
self.containerapp_def["properties"]["environmentId"] = self.get_argument_managed_env()
self.containerapp_def["properties"]["configuration"] = config_def
self.containerapp_def["properties"]["template"] = template_def
self.containerapp_def["tags"] = self.get_argument_tags()

if self.get_argument_workload_profile_name():
containerapp_def["properties"]["workloadProfileName"] = self.get_argument_workload_profile_name()
self.containerapp_def["properties"]["workloadProfileName"] = self.get_argument_workload_profile_name()
ensure_workload_profile_supported(self.cmd, managed_env_name, managed_env_rg, self.get_argument_workload_profile_name(),
managed_env_info)

if self.get_argument_registry_identity():
if is_registry_msi_system(self.get_argument_registry_identity()):
set_managed_identity(self.cmd, self.get_argument_resource_group_name(), containerapp_def, system_assigned=True)
set_managed_identity(self.cmd, self.get_argument_resource_group_name(), self.containerapp_def, system_assigned=True)
else:
set_managed_identity(self.cmd, self.get_argument_resource_group_name(), containerapp_def, user_assigned=[self.get_argument_registry_identity()])
return containerapp_def
set_managed_identity(self.cmd, self.get_argument_resource_group_name(), self.containerapp_def, user_assigned=[self.get_argument_registry_identity()])

def create_containerapp(self, containerapp_def):
def create_containerapp(self):
try:
r = self.client.create_or_update(
cmd=self.cmd, resource_group_name=self.get_argument_resource_group_name(), name=self.get_argument_name(), container_app_envelope=containerapp_def,
cmd=self.cmd, resource_group_name=self.get_argument_resource_group_name(), name=self.get_argument_name(), container_app_envelope=self.containerapp_def,
no_wait=self.get_argument_no_wait())

return r
except Exception as e:
handle_raw_exception(e)

def construct_containerapp_for_post_process(self, containerapp_def, r):
def construct_containerapp_for_post_process(self, r):
if is_registry_msi_system(self.get_argument_registry_identity()):
while r["properties"]["provisioningState"] == "InProgress":
r = self.client.show(self.cmd, self.get_argument_resource_group_name(), self.get_argument_name())
time.sleep(10)
logger.info("Creating an acrpull role assignment for the system identity")
system_sp = r["identity"]["principalId"]
create_acrpull_role_assignment(self.cmd, self.get_argument_registry_server(), registry_identity=None, service_principal=system_sp)
containers_def = safe_get(containerapp_def, "properties", "template", "containers")
containers_def = safe_get(self.containerapp_def, "properties", "template", "containers")
containers_def[0]["image"] = self.get_argument_image()

safe_set(containerapp_def, "properties", "template", "revisionSuffix", value=self.get_argument_revision_suffix())
safe_set(self.containerapp_def, "properties", "template", "revisionSuffix", value=self.get_argument_revision_suffix())

registries_def = RegistryCredentialsModel
registries_def["server"] = self.get_argument_registry_server()
registries_def["identity"] = self.get_argument_registry_identity()
safe_set(containerapp_def, "properties", "configuration", "registries", value=[registries_def])
return containerapp_def
safe_set(self.containerapp_def, "properties", "configuration", "registries", value=[registries_def])

def post_process_containerapp(self, containerapp_def, r):
def post_process_containerapp(self, r):
if is_registry_msi_system(self.get_argument_registry_identity()):
r = self.create_containerapp(containerapp_def)
r = self.create_containerapp()

if "properties" in r and "provisioningState" in r["properties"] and r["properties"]["provisioningState"].lower() == "waiting" and not self.get_argument_no_wait():
not self.get_argument_disable_warnings() and logger.warning('Containerapp creation in progress. Please monitor the creation using `az containerapp show -n {} -g {}`'.format(self.get_argument_name(), self.get_argument_resource_group_name()))
Expand Down Expand Up @@ -609,11 +607,10 @@ def set_up_create_containerapp_yaml(self, name, file_name):
raise ValidationError('Containerapp type must be \"Microsoft.App/ContainerApps\"')

# Deserialize the yaml into a ContainerApp object. Need this since we're not using SDK
containerapp_def = None
try:
deserializer = create_deserializer(self.models)

containerapp_def = deserializer('ContainerApp', yaml_containerapp)
self.containerapp_def = deserializer('ContainerApp', yaml_containerapp)
except DeserializationError as ex:
raise ValidationError(
'Invalid YAML provided. Please see https://aka.ms/azure-container-apps-yaml for a valid containerapps YAML spec.') from ex
Expand All @@ -624,26 +621,26 @@ def set_up_create_containerapp_yaml(self, name, file_name):
tags = yaml_containerapp.get('tags')
del yaml_containerapp['tags']

containerapp_def = _convert_object_from_snake_to_camel_case(_object_to_dict(containerapp_def))
containerapp_def['tags'] = tags
self.containerapp_def = _convert_object_from_snake_to_camel_case(_object_to_dict(self.containerapp_def))
self.containerapp_def['tags'] = tags

# After deserializing, some properties may need to be moved under the "properties" attribute. Need this since we're not using SDK
containerapp_def = process_loaded_yaml(containerapp_def)
self.containerapp_def = process_loaded_yaml(self.containerapp_def)

# Remove "additionalProperties" and read-only attributes that are introduced in the deserialization. Need this since we're not using SDK
_remove_additional_attributes(containerapp_def)
_remove_readonly_attributes(containerapp_def)
_remove_additional_attributes(self.containerapp_def)
_remove_readonly_attributes(self.containerapp_def)

# Remove extra workloadProfileName introduced in deserialization
if "workloadProfileName" in containerapp_def:
del containerapp_def["workloadProfileName"]
if "workloadProfileName" in self.containerapp_def:
del self.containerapp_def["workloadProfileName"]

# Validate managed environment
if not containerapp_def["properties"].get('environmentId'):
if not self.containerapp_def["properties"].get('environmentId'):
raise RequiredArgumentMissingError(
'environmentId is required. This can be retrieved using the `az containerapp env show -g MyResourceGroup -n MyContainerappEnvironment --query id` command. Please see https://aka.ms/azure-container-apps-yaml for a valid containerapps YAML spec.')

env_id = containerapp_def["properties"]['environmentId']
env_id = self.containerapp_def["properties"]['environmentId']
env_name = None
env_rg = None
env_info = None
Expand All @@ -664,10 +661,8 @@ def set_up_create_containerapp_yaml(self, name, file_name):
raise ValidationError("The environment '{}' in resource group '{}' was not found".format(env_name, env_rg))

# Validate location
if not containerapp_def.get('location'):
containerapp_def['location'] = env_info['location']

return containerapp_def
if not self.containerapp_def.get('location'):
self.containerapp_def['location'] = env_info['location']

def set_up_scale_rule(self):
scale_def = None
Expand Down
8 changes: 4 additions & 4 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,10 +461,10 @@ def create_containerapp(cmd,
containerapp_create_decorator.register_provider(CONTAINER_APPS_RP)
containerapp_create_decorator.validate_arguments()

containerapp_def = containerapp_create_decorator.construct_containerapp()
r = containerapp_create_decorator.create_containerapp(containerapp_def)
containerapp_def = containerapp_create_decorator.construct_containerapp_for_post_process(containerapp_def, r)
r = containerapp_create_decorator.post_process_containerapp(containerapp_def, r)
containerapp_create_decorator.construct_containerapp()
r = containerapp_create_decorator.create_containerapp()
containerapp_create_decorator.construct_containerapp_for_post_process(r)
r = containerapp_create_decorator.post_process_containerapp(r)
return r


Expand Down

0 comments on commit 457e8a7

Please sign in to comment.