Skip to content

Containerapp Env Ingress Commands #8670

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

Draft
wants to merge 5 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/_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

logger = get_logger(__name__)

PREVIEW_API_VERSION = "2024-10-02-preview"
PREVIEW_API_VERSION = "2025-02-02-preview"
POLLING_TIMEOUT = 1500 # how many seconds before exiting
POLLING_SECONDS = 2 # how many seconds between requests
POLLING_TIMEOUT_FOR_MANAGED_CERTIFICATE = 1500 # how many seconds before exiting
Expand Down
27 changes: 27 additions & 0 deletions src/containerapp/azext_containerapp/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -2357,3 +2357,30 @@
text: |
az containerapp env http-route-config delete -g MyResourceGroup -n MyEnvironment -r configname
"""

helps['containerapp env ingress show'] = """
type: command
short-summary: Show the ingress settings for the environment.
examples:
- name: Show the ingress settings for the environment.
text: |
az containerapp env ingress show -g MyResourceGroup -n MyEnvironment
"""

helps['containerapp env ingress update'] = """
type: command
short-summary: Update the ingress settings for the environment.
examples:
- name: Update the ingress settings for the environment.
text: |
az containerapp env ingress update -g MyResourceGroup -n MyEnvironment -w WorkloadProfileName
"""

helps['containerapp env ingress restore-defaults'] = """
type: command
short-summary: Reset the ingress settings to default values.
examples:
- name: Reset the ingress settings for the environment to its default values
text: |
az containerapp env ingress restore-defaults -g MyResourceGroup -n MyEnvironment -w WorkloadProfileName
"""
11 changes: 11 additions & 0 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,3 +500,14 @@ def load_arguments(self, _):
with self.argument_context('containerapp revision set-mode') as c:
c.argument('mode', arg_type=get_enum_type(['single', 'multiple', 'labels']), help="The active revisions mode for the container app.")
c.argument('target_label', help="The label to apply to new revisions. Required for revision mode 'labels'.", is_preview=True)

with self.argument_context('containerapp env ingress') as c:
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
c.argument('name', options_list=['--environment'], help="The name of the managed environment.")
c.argument('workload_profile_name', options_list=['--workload-profile-name'], help="The name of the workload profile.")
c.argument('min_replicas', options_list=['--min-replicas'], type=int, help="Minimum number of replicas to run.")
c.argument('max_replicas', options_list=['--max-replicas'], type=int, help="Maximum number of replicas to run.")
c.argument('termination_grace_period', options_list=['--termination-grace-period'], type=int, help="Time given during shutdown to finish requests before cancelling.")
c.argument('request_idle_timeout', options_list=['--request-idle-timeout'], type=int, help="Timeout (in minutes) for idle requests.")
c.argument('header_count_limit', options_list=['--header-count-limit'], type=int, help="Limit for header count.")

5 changes: 5 additions & 0 deletions src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,8 @@ def load_command_table(self, args):
with self.command_group('containerapp revision label') as g:
g.custom_command('add', 'add_revision_label')
g.custom_command('remove', 'remove_revision_label')

with self.command_group('containerapp env ingress', is_preview=True) as g:
g.custom_show_command('show', 'show_environment_ingress')
g.custom_command('update', 'update_environment_ingress')
g.custom_command('restore-defaults', 'reset_environment_ingress_to_defaults')
97 changes: 97 additions & 0 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -3709,3 +3709,100 @@ def remove_revision_label(cmd, resource_group_name, name, label, no_wait=False):
return r['properties']['configuration']['ingress']['traffic']
except Exception as e:
handle_raw_exception(e)


def show_environment_ingress(cmd, environment, resource_group_name):
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)

try:
env = ManagedEnvironmentPreviewClient.show(cmd, resource_group_name, environment)
if not env:
raise ResourceNotFoundError(f"The containerapp environment '{environment}' does not exist")

ingress_config = safe_get(env, "properties", "ingressConfiguration")
if not ingress_config:
return {"message": "No ingress configuration found for this environment"}

return ingress_config
except Exception as e:
handle_raw_exception(e)


def update_environment_ingress(cmd, environment, resource_group_name, workload_profile_name, min_replicas, max_replicas, termination_grace_period, request_idle_timeout, header_count_limit, no_wait=False):
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)

try:
env = ManagedEnvironmentPreviewClient.show(cmd, resource_group_name, environment)
if not env:
raise ResourceNotFoundError(f"The containerapp environment '{environment}' does not exist")

env_patch = {}
ingress_config = {}

# Only set values that were specified
if workload_profile_name is not None:
ingress_config["workloadProfileName"] = workload_profile_name
if min_replicas is not None:
ingress_config["minReplicas"] = min_replicas
if max_replicas is not None:
ingress_config["maxReplicas"] = max_replicas
if termination_grace_period is not None:
ingress_config["terminationGracePeriod"] = termination_grace_period
if request_idle_timeout is not None:
ingress_config["requestIdleTimeout"] = request_idle_timeout
if header_count_limit is not None:
ingress_config["headerCountLimit"] = header_count_limit

# Only add ingressConfiguration to the patch if any values were specified
if ingress_config:
safe_set(env_patch, "properties", "ingressConfiguration", value=ingress_config)
else:
return {"message": "No changes specified for ingress configuration"}

# Update the environment with the patched ingress configuration
result = ManagedEnvironmentPreviewClient.update(
cmd=cmd,
resource_group_name=resource_group_name,
name=environment,
managed_environment_envelope=env_patch,
no_wait=no_wait
)

return safe_get(result, "properties", "ingressConfiguration")

except Exception as e:
handle_raw_exception(e)

def reset_environment_ingress_to_defaults(cmd, environment, resource_group_name, workload_profile_name, no_wait=False):
"""Reset environment ingress configuration to default values.

:param cmd: Command context
:param environment: Name of the Container App environment
:param resource_group_name: Name of resource group
:param no_wait: Do not wait for the long-running operation to finish
:return: The updated ingress configuration
"""
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)

# Default values for environment ingress
default_min_replicas = 2
default_max_replicas = 10
default_termination_grace_period = 8
default_request_idle_timeout = 4
default_header_count_limit = 100

logger.warning("Resetting environment ingress configuration to default values")

# Call existing update method with default values
return update_environment_ingress(
cmd=cmd,
environment=environment,
resource_group_name=resource_group_name,
workload_profile_name=workload_profile_name,
min_replicas=default_min_replicas,
max_replicas=default_max_replicas,
termination_grace_period=default_termination_grace_period,
request_idle_timeout=default_request_idle_timeout,
header_count_limit=default_header_count_limit,
no_wait=no_wait
)
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,73 @@ def test_containerapp_cors_policy(self, resource_group):
JMESPathCheck('corsPolicy', None),
])

@AllowLargeResponse(8192)
@ResourceGroupPreparer(location="westeurope")
def test_containerapp_env_ingress_commands(self, resource_group):
self.cmd('configure --defaults location={}'.format(TEST_LOCATION))

env_name = self.create_random_name(prefix='containerapp-env', length=24)
logs_workspace_name = self.create_random_name(prefix='containerapp-env', length=24)

logs_workspace_id = self.cmd('monitor log-analytics workspace create -g {} -n {} -l eastus'.format(resource_group, logs_workspace_name)).get_output_in_json()["customerId"]
logs_workspace_key = self.cmd('monitor log-analytics workspace get-shared-keys -g {} -n {}'.format(resource_group, logs_workspace_name)).get_output_in_json()["primarySharedKey"]

self.cmd('containerapp env create -g {} -n {} --logs-workspace-id {} --logs-workspace-key {}'.format(resource_group, env_name, logs_workspace_id, logs_workspace_key))

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()

ingress_config = self.cmd('containerapp env ingress show --environment {} -g {}'.format(env_name, resource_group), checks=[
JMESPathCheck('properties', JMESPathCheckExists()),
]).get_output_in_json()

self.cmd('containerapp env ingress update --environment {} -g {} --workload-profile-name Consumption --min-replicas 2 --max-replicas 5'.format(
env_name, resource_group), checks=[
JMESPathCheck('properties.ingressConfiguration.workloadProfileName', 'Consumption'),
JMESPathCheck('properties.ingressConfiguration.minReplicas', 2),
JMESPathCheck('properties.ingressConfiguration.maxReplicas', 5),
])

self.cmd('containerapp env ingress show --environment {} -g {}'.format(env_name, resource_group), checks=[
JMESPathCheck('properties.ingressConfiguration.workloadProfileName', 'Consumption'),
JMESPathCheck('properties.ingressConfiguration.minReplicas', 2),
JMESPathCheck('properties.ingressConfiguration.maxReplicas', 5),
])

self.cmd('containerapp env ingress update --environment {} -g {} --termination-grace-period 45 --request-idle-timeout 180 --header-count-limit 40'.format(
env_name, resource_group), checks=[
JMESPathCheck('properties.ingressConfiguration.workloadProfileName', 'Consumption'),
JMESPathCheck('properties.ingressConfiguration.minReplicas', 2),
JMESPathCheck('properties.ingressConfiguration.maxReplicas', 5),
JMESPathCheck('properties.ingressConfiguration.terminationGracePeriod', 45),
JMESPathCheck('properties.ingressConfiguration.requestIdleTimeout', 180),
JMESPathCheck('properties.ingressConfiguration.headerCountLimit', 40),
])

self.cmd('containerapp env ingress restore-defaults --environment {} -g {}'.format(env_name, resource_group), checks=[
JMESPathCheck('properties.ingressConfiguration.workloadProfileName', 'Consumption'),
JMESPathCheck('properties.ingressConfiguration.minReplicas', 2),
JMESPathCheck('properties.ingressConfiguration.maxReplicas', 10),
JMESPathCheck('properties.ingressConfiguration.terminationGracePeriod', 8),
JMESPathCheck('properties.ingressConfiguration.requestIdleTimeout', 4),
JMESPathCheck('properties.ingressConfiguration.headerCountLimit', 100),
])

self.cmd('containerapp env ingress show --environment {} -g {}'.format(env_name, resource_group), checks=[
JMESPathCheck('properties.ingressConfiguration.workloadProfileName', 'Consumption'),
JMESPathCheck('properties.ingressConfiguration.minReplicas', 2),
JMESPathCheck('properties.ingressConfiguration.maxReplicas', 10),
JMESPathCheck('properties.ingressConfiguration.terminationGracePeriod', 480),
JMESPathCheck('properties.ingressConfiguration.requestIdleTimeout', 240),
JMESPathCheck('properties.ingressConfiguration.headerCountLimit', 100),
])

# Clean up
self.cmd(f'containerapp env delete -g {resource_group} -n {env_name} --yes')


class ContainerappCustomDomainTests(ScenarioTest):
def __init__(self, *arg, **kwargs):
Expand Down
Loading