Skip to content
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

Parameters should override yaml #68

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def load_arguments(self, _):
with self.argument_context('containerapp') as c:
c.argument('tags', arg_type=tags_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('yaml', type=file_type, help='Path to a .yaml file with the configuration of a container app. All other parameters will be ignored. For an example, see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples')
c.argument('yaml', is_preview=True, type=file_type, help='Path to a .yaml file with the configuration of a container app. Parameters passed in as arguments will override same the parameters defined in yaml file. For an example, see https://docs.microsoft.com/azure/container-apps/azure-resource-manager-api-spec#examples')

# Container
with self.argument_context('containerapp', arg_group='Container') as c:
Expand Down
9 changes: 1 addition & 8 deletions src/containerapp/azext_containerapp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,7 @@ def parse_secret_flags(secret_list):
return secret_var_def


def _update_revision_env_secretrefs(containers, name):
for container in containers:
if "env" in container:
for var in container["env"]:
if "secretRef" in var:
var["secretRef"] = var["secretRef"].replace("{}-".format(name), "")


# API for show revisions appends prefix of "containerappname-" to secretrefs. This helper removes that prefix
def _update_revision_env_secretrefs(containers, name):
for container in containers:
if "env" in container:
Expand Down
468 changes: 266 additions & 202 deletions src/containerapp/azext_containerapp/custom.py

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,183 @@
import unittest

from azure.cli.testsdk.scenario_tests import AllowLargeResponse
from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, JMESPathCheck)
from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, JMESPathCheck, live_only)
from knack.util import CLIError


TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..'))


@unittest.skip("Managed environment flaky")
@live_only()
class ContainerappScenarioTest(ScenarioTest):
@AllowLargeResponse(8192)
@ResourceGroupPreparer(location="centraluseuap")
def test_containerapp_e2e(self, resource_group):
containerapp_name = self.create_random_name(prefix='containerapp-e2e', length=24)
env_name = self.create_random_name(prefix='containerapp-e2e-env', length=24)

self.cmd('containerapp env create -g {} -n {}'.format(resource_group, env_name))

# Sleep in case env create takes a while
time.sleep(60)
self.cmd('containerapp env list -g {}'.format(resource_group), checks=[
JMESPathCheck('length(@)', 1),
JMESPathCheck('[0].name', env_name),
])
# Ensure environment is completed
containerapp_env = self.cmd('containerapp env show -g {} -n {}'.format(resource_group, env_name)).get_output_in_json()

while containerapp_env["properties"]["provisioningState"].lower() == "waiting":
time.sleep(5)
containerapp_env = self.cmd('containerapp env show -g {} -n {}'.format(resource_group, env_name)).get_output_in_json()

containerapp_name = self.create_random_name(prefix='containerapp-e2e', length=24)

# Create basic Container App with default image
self.cmd('containerapp create -g {} -n {} --environment {}'.format(resource_group, containerapp_name, env_name), checks=[
JMESPathCheck('name', containerapp_name)
])

# Sleep in case containerapp create takes a while
time.sleep(60)
self.cmd('containerapp show -g {} -n {}'.format(resource_group, containerapp_name), checks=[
JMESPathCheck('name', containerapp_name)
JMESPathCheck('name', containerapp_name),
])

self.cmd('containerapp list -g {}'.format(resource_group), checks=[
JMESPathCheck('length(@)', 1),
JMESPathCheck('[0].name', containerapp_name),
])

# Create Container App with image, resource and replica limits
create_string = "containerapp create -g {} -n {} --environment {} --image nginx --cpu 0.5 --memory 1.0Gi --min-replicas 2 --max-replicas 4".format(resource_group, containerapp_name, env_name)
self.cmd(create_string, checks=[
JMESPathCheck('name', containerapp_name),
JMESPathCheck('properties.template.containers[0].image', 'nginx'),
JMESPathCheck('properties.template.containers[0].resources.cpu', '0.5'),
JMESPathCheck('properties.template.containers[0].resources.memory', '1Gi'),
JMESPathCheck('properties.template.scale.minReplicas', '2'),
JMESPathCheck('properties.template.scale.maxReplicas', '4')
])

self.cmd('containerapp create -g {} -n {} --environment {} --ingress external --target-port 8080'.format(resource_group, containerapp_name, env_name), checks=[
JMESPathCheck('properties.configuration.ingress.external', True),
JMESPathCheck('properties.configuration.ingress.targetPort', 8080)
])

# Container App with ingress should fail unless target port is specified
with self.assertRaises(CLIError):
self.cmd('containerapp create -g {} -n {} --environment {} --ingress external'.format(resource_group, containerapp_name, env_name))

# Create Container App with secrets and environment variables
containerapp_name = self.create_random_name(prefix='containerapp-e2e', length=24)
create_string = 'containerapp create -g {} -n {} --environment {} --secrets mysecret=secretvalue1 anothersecret="secret value 2" --env-vars GREETING="Hello, world" SECRETENV=secretref:anothersecret'.format(
resource_group, containerapp_name, env_name)
self.cmd(create_string, checks=[
JMESPathCheck('name', containerapp_name),
JMESPathCheck('length(properties.template.containers[0].env)', 2),
JMESPathCheck('length(properties.configuration.secrets)', 2)
])


@AllowLargeResponse(8192)
@ResourceGroupPreparer(location="eastus2")
def test_container_acr(self, resource_group):
env_name = self.create_random_name(prefix='containerapp-e2e-env', length=24)

self.cmd('containerapp env create -g {} -n {}'.format(resource_group, env_name))

# Ensure environment is completed
containerapp_env = self.cmd('containerapp env show -g {} -n {}'.format(resource_group, env_name)).get_output_in_json()

while containerapp_env["properties"]["provisioningState"].lower() == "waiting":
time.sleep(5)
containerapp_env = self.cmd('containerapp env show -g {} -n {}'.format(resource_group, env_name)).get_output_in_json()

containerapp_name = self.create_random_name(prefix='containerapp-e2e', length=24)
registry_name = self.create_random_name(prefix='containerapp', length=24)

# Create ACR
acr = self.cmd('acr create -g {} -n {} --sku Basic --admin-enabled'.format(resource_group, registry_name)).get_output_in_json()
registry_server = acr["loginServer"]

acr_credentials = self.cmd('acr credential show -g {} -n {}'.format(resource_group, registry_name)).get_output_in_json()
registry_username = acr_credentials["username"]
registry_password = acr_credentials["passwords"][0]["value"]

# Create Container App with ACR
containerapp_name = self.create_random_name(prefix='containerapp-e2e', length=24)
create_string = 'containerapp create -g {} -n {} --environment {} --registry-username {} --registry-server {} --registry-password {}'.format(
resource_group, containerapp_name, env_name, registry_username, registry_server, registry_password)
self.cmd(create_string, checks=[
JMESPathCheck('name', containerapp_name),
JMESPathCheck('properties.configuration.registries[0].server', registry_server),
JMESPathCheck('properties.configuration.registries[0].username', registry_username),
JMESPathCheck('length(properties.configuration.secrets)', 1),
])


@AllowLargeResponse(8192)
@ResourceGroupPreparer(location="eastus")
def test_containerapp_update(self, resource_group):
env_name = self.create_random_name(prefix='containerapp-e2e-env', length=24)

self.cmd('containerapp env create -g {} -n {}'.format(resource_group, env_name))

# Ensure environment is completed
containerapp_env = self.cmd('containerapp env show -g {} -n {}'.format(resource_group, env_name)).get_output_in_json()

while containerapp_env["properties"]["provisioningState"].lower() == "waiting":
time.sleep(5)
containerapp_env = self.cmd('containerapp env show -g {} -n {}'.format(resource_group, env_name)).get_output_in_json()

# Create basic Container App with default image
containerapp_name = self.create_random_name(prefix='containerapp-update', length=24)
self.cmd('containerapp create -g {} -n {} --environment {}'.format(resource_group, containerapp_name, env_name), checks=[
JMESPathCheck('name', containerapp_name),
JMESPathCheck('length(properties.template.containers)', 1),
JMESPathCheck('properties.template.containers[0].name', containerapp_name)
])

# Update existing Container App that has a single container

update_string = 'containerapp update -g {} -n {} --image {} --cpu 0.5 --memory 1.0Gi --args mycommand mycommand2 --command "mycommand" --revision-suffix suffix --min-replicas 2 --max-replicas 4'.format(
resource_group, containerapp_name, 'nginx')
self.cmd(update_string, checks=[
JMESPathCheck('name', containerapp_name),
JMESPathCheck('length(properties.template.containers)', 1),
JMESPathCheck('properties.template.containers[0].name', containerapp_name),
JMESPathCheck('properties.template.containers[0].image', 'nginx'),
JMESPathCheck('properties.template.containers[0].resources.cpu', '0.5'),
JMESPathCheck('properties.template.containers[0].resources.memory', '1Gi'),
JMESPathCheck('properties.template.scale.minReplicas', '2'),
JMESPathCheck('properties.template.scale.maxReplicas', '4'),
JMESPathCheck('properties.template.containers[0].command[0]', "mycommand"),
JMESPathCheck('length(properties.template.containers[0].args)', 2)
])

# Remove all env vars when Container App doesn't have env vars
update_string = 'containerapp update -g {} -n {} --remove-all-env-vars'.format(
resource_group, containerapp_name)
with self.assertRaises(KeyError):
result = self.cmd(update_string).get_output_in_json()
self.assertEqual(result['properties.template.containers[0].env'], 'None')

# Add new container to existing Container App
update_string = 'containerapp update -g {} -n {} --container-name {} --image {}'.format(
resource_group, containerapp_name, "newcontainer", "nginx")
self.cmd(update_string, checks=[
JMESPathCheck('name', containerapp_name),
JMESPathCheck('length(properties.template.containers)', 2)
])

# Updating container properties in a Container App with multiple containers, without providing container name should error
update_string = 'containerapp update -g {} -n {} --cpu {} --memory {}'.format(
resource_group, containerapp_name, '1.0', '2.0Gi')
with self.assertRaises(CLIError):
self.cmd(update_string)

# Updating container properties in a Container App with multiple containers, should work when container name provided
update_string = 'containerapp update -g {} -n {} --container-name {} --cpu {} --memory {}'.format(
resource_group, containerapp_name, 'newcontainer', '0.75', '1.5Gi')
self.cmd(update_string)

update_string = 'containerapp update -g {} -n {} --container-name {} --cpu {} --memory {}'.format(
resource_group, containerapp_name, containerapp_name, '0.75', '1.5Gi')
self.cmd(update_string, checks=[
JMESPathCheck('properties.template.containers[0].resources.cpu', '0.75'),
JMESPathCheck('properties.template.containers[0].resources.memory', '1.5Gi'),
JMESPathCheck('properties.template.containers[1].resources.cpu', '0.75'),
JMESPathCheck('properties.template.containers[1].resources.memory', '1.5Gi'),
])