Skip to content

Commit 8c484bc

Browse files
authored
[Container App] az containerapp env: Add http-route-config and premium-ingress (#32240)
1 parent 15f25f5 commit 8c484bc

File tree

10 files changed

+30838
-1
lines changed

10 files changed

+30838
-1
lines changed

src/azure-cli/azure/cli/command_modules/containerapp/_clients.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,3 +1458,91 @@ def list_usages(cls, cmd, location):
14581458

14591459
r = send_raw_request(cmd.cli_ctx, "GET", request_url)
14601460
return r.json()
1461+
1462+
1463+
class HttpRouteConfigClient:
1464+
api_version = CURRENT_API_VERSION
1465+
1466+
@classmethod
1467+
def create(cls, cmd, resource_group_name, name, http_route_config_name, http_route_config_envelope):
1468+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1469+
sub_id = get_subscription_id(cmd.cli_ctx)
1470+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}/httpRouteConfigs/{}?api-version={}"
1471+
request_url = url_fmt.format(
1472+
management_hostname.strip('/'),
1473+
sub_id,
1474+
resource_group_name,
1475+
name,
1476+
http_route_config_name,
1477+
cls.api_version)
1478+
1479+
r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(http_route_config_envelope))
1480+
return r.json()
1481+
1482+
@classmethod
1483+
def update(cls, cmd, resource_group_name, name, http_route_config_name, http_route_config_envelope):
1484+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1485+
sub_id = get_subscription_id(cmd.cli_ctx)
1486+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}/httpRouteConfigs/{}?api-version={}"
1487+
request_url = url_fmt.format(
1488+
management_hostname.strip('/'),
1489+
sub_id,
1490+
resource_group_name,
1491+
name,
1492+
http_route_config_name,
1493+
cls.api_version)
1494+
1495+
r = send_raw_request(cmd.cli_ctx, "PATCH", request_url, body=json.dumps(http_route_config_envelope))
1496+
return r.json()
1497+
1498+
@classmethod
1499+
def list(cls, cmd, resource_group_name, name):
1500+
route_list = []
1501+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1502+
sub_id = get_subscription_id(cmd.cli_ctx)
1503+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}/httpRouteConfigs?api-version={}"
1504+
request_url = url_fmt.format(
1505+
management_hostname.strip('/'),
1506+
sub_id,
1507+
resource_group_name,
1508+
name,
1509+
cls.api_version)
1510+
1511+
r = send_raw_request(cmd.cli_ctx, "GET", request_url, body=None)
1512+
j = r.json()
1513+
for route in j["value"]:
1514+
route_list.append(route)
1515+
return route_list
1516+
1517+
@classmethod
1518+
def show(cls, cmd, resource_group_name, name, http_route_config_name):
1519+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1520+
sub_id = get_subscription_id(cmd.cli_ctx)
1521+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}/httpRouteConfigs/{}?api-version={}"
1522+
request_url = url_fmt.format(
1523+
management_hostname.strip('/'),
1524+
sub_id,
1525+
resource_group_name,
1526+
name,
1527+
http_route_config_name,
1528+
cls.api_version)
1529+
1530+
r = send_raw_request(cmd.cli_ctx, "GET", request_url, body=None)
1531+
return r.json()
1532+
1533+
@classmethod
1534+
def delete(cls, cmd, resource_group_name, name, http_route_config_name):
1535+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1536+
sub_id = get_subscription_id(cmd.cli_ctx)
1537+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}/httpRouteConfigs/{}?api-version={}"
1538+
request_url = url_fmt.format(
1539+
management_hostname.strip('/'),
1540+
sub_id,
1541+
resource_group_name,
1542+
name,
1543+
http_route_config_name,
1544+
cls.api_version)
1545+
1546+
send_raw_request(cmd.cli_ctx, "DELETE", request_url, body=None)
1547+
# API doesn't return JSON (it returns no content)
1548+
return

src/azure-cli/azure/cli/command_modules/containerapp/_help.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,3 +1641,104 @@
16411641
--environment MyContainerappEnv \\
16421642
--compose-file-path "path/to/docker-compose.yml"
16431643
"""
1644+
1645+
# Routing
1646+
helps['containerapp env http-route-config'] = """
1647+
type: group
1648+
short-summary: Commands to manage environment level http routing.
1649+
"""
1650+
1651+
helps['containerapp env http-route-config list'] = """
1652+
type: command
1653+
short-summary: List the http route configs in the environment.
1654+
examples:
1655+
- name: List the http route configs in the environment.
1656+
text: |
1657+
az containerapp env http-route-config list -g MyResourceGroup -n MyEnvironment
1658+
"""
1659+
1660+
helps['containerapp env http-route-config create'] = """
1661+
type: command
1662+
short-summary: Create a new http route config.
1663+
examples:
1664+
- name: Create a new route from a yaml file.
1665+
text: |
1666+
az containerapp env http-route-config create -g MyResourceGroup -n MyEnvironment -r configname --yaml config.yaml
1667+
"""
1668+
1669+
helps['containerapp env http-route-config update'] = """
1670+
type: command
1671+
short-summary: Update a http route config.
1672+
examples:
1673+
- name: Update a route in the environment from a yaml file.
1674+
text: |
1675+
az containerapp env http-route-config update -g MyResourceGroup -n MyEnvironment -r configname --yaml config.yaml
1676+
"""
1677+
1678+
helps['containerapp env http-route-config show'] = """
1679+
type: command
1680+
short-summary: Show a http route config.
1681+
examples:
1682+
- name: Show a route in the environment.
1683+
text: |
1684+
az containerapp env http-route-config show -g MyResourceGroup -n MyEnvironment -r configname
1685+
"""
1686+
1687+
helps['containerapp env http-route-config delete'] = """
1688+
type: command
1689+
short-summary: Delete a http route config.
1690+
examples:
1691+
- name: Delete a route from the environment.
1692+
text: |
1693+
az containerapp env http-route-config delete -g MyResourceGroup -n MyEnvironment -r configname
1694+
"""
1695+
1696+
# Ingress
1697+
helps['containerapp env premium-ingress show'] = """
1698+
type: command
1699+
short-summary: Show the premium ingress settings for the environment.
1700+
examples:
1701+
- name: Show the premium ingress settings for the environment.
1702+
text: |
1703+
az containerapp env premium-ingress show -g MyResourceGroup -n MyEnvironment
1704+
"""
1705+
1706+
helps['containerapp env premium-ingress'] = """
1707+
type: group
1708+
short-summary: Configure premium ingress settings for the environment.
1709+
long-summary: |
1710+
Premium ingress settings apply to all applications in the environment. They allow moving the ingress instances to a workload profile and scaling them beyond the system defaults to enable high traffic workloads. Other settings include request idle timeouts, header count limits, and the termination grace period.
1711+
examples:
1712+
- name: Enable premium ingress for the environment.
1713+
text: |
1714+
az containerapp env premium-ingress add -g MyResourceGroup -n MyEnvironment -w WorkloadProfileName
1715+
"""
1716+
1717+
helps['containerapp env premium-ingress add'] = """
1718+
type: command
1719+
short-summary: Enable the premium ingress settings for the environment.
1720+
long-summary: |
1721+
Unspecified optional parameters will be cleared from any existing configuration.
1722+
examples:
1723+
- name: Add the premium ingress settings for the environment.
1724+
text: |
1725+
az containerapp env premium-ingress add -g MyResourceGroup -n MyEnvironment -w WorkloadProfileName
1726+
"""
1727+
1728+
helps['containerapp env premium-ingress update'] = """
1729+
type: command
1730+
short-summary: Update the premium ingress settings for the environment.
1731+
examples:
1732+
- name: Update the workload profile used for premium ingress.
1733+
text: |
1734+
az containerapp env premium-ingress update -g MyResourceGroup -n MyEnvironment -w WorkloadProfileName
1735+
"""
1736+
1737+
helps['containerapp env premium-ingress remove'] = """
1738+
type: command
1739+
short-summary: Remove the ingress settings and restores the system to default values.
1740+
examples:
1741+
- name: Reset the ingress settings for the environment to its default values
1742+
text: |
1743+
az containerapp env premium-ingress remove -g MyResourceGroup -n MyEnvironment
1744+
"""

src/azure-cli/azure/cli/command_modules/containerapp/_params.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,3 +500,16 @@ def load_arguments(self, _):
500500

501501
with self.argument_context('containerapp job registry list') as c:
502502
c.argument('name', id_part=None)
503+
504+
with self.argument_context('containerapp env http-route-config') as c:
505+
c.argument('http_route_config_name', options_list=['--http-route-config-name', '-r'], help="The name of the http route configuration.")
506+
c.argument('yaml', help="The path to the YAML input file.")
507+
c.argument('name', id_part=None)
508+
509+
with self.argument_context('containerapp env premium-ingress') as c:
510+
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
511+
c.argument('name', options_list=['--name', '-n'], help="The name of the managed environment.")
512+
c.argument('workload_profile_name', options_list=['--workload-profile-name', '-w'], help="The workload profile to run ingress replicas on. This profile must not be shared with any container app or job.")
513+
c.argument('termination_grace_period', options_list=['--termination-grace-period', '-t'], type=int, help="Time in seconds to drain requests during ingress shutdown. Default 500, minimum 0, maximum 3600.")
514+
c.argument('request_idle_timeout', type=int, help="Timeout in minutes for idle requests. Default 4, minimum 4, maximum 30.")
515+
c.argument('header_count_limit', type=int, help="Limit of http headers per request. Default 100, minimum 1.")

src/azure-cli/azure/cli/command_modules/containerapp/commands.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,16 @@ def load_command_table(self, _):
217217
g.custom_command('add', 'add_workload_profile')
218218
g.custom_command('update', 'update_workload_profile')
219219
g.custom_command('delete', 'delete_workload_profile')
220+
221+
with self.command_group('containerapp env http-route-config') as g:
222+
g.custom_show_command('show', 'show_http_route_config')
223+
g.custom_command('list', 'list_http_route_configs')
224+
g.custom_command('create', 'create_http_route_config', exception_handler=ex_handler_factory())
225+
g.custom_command('update', 'update_http_route_config', exception_handler=ex_handler_factory())
226+
g.custom_command('delete', 'delete_http_route_config', confirmation=True, exception_handler=ex_handler_factory())
227+
228+
with self.command_group('containerapp env premium-ingress') as g:
229+
g.custom_show_command('show', 'show_environment_premium_ingress')
230+
g.custom_command('add', 'add_environment_premium_ingress')
231+
g.custom_command('update', 'update_environment_premium_ingress')
232+
g.custom_command('remove', 'remove_environment_premium_ingress', confirmation=True)

src/azure-cli/azure/cli/command_modules/containerapp/custom.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
AuthClient,
4646
WorkloadProfileClient,
4747
ContainerAppsJobClient,
48+
HttpRouteConfigClient,
4849
SubscriptionClient
4950
)
5051
from ._github_oauth import get_github_access_token
@@ -5088,3 +5089,160 @@ def delete_workload_profile(cmd, resource_group_name, env_name, workload_profile
50885089
return r
50895090
except Exception as e:
50905091
handle_raw_exception(e)
5092+
5093+
5094+
def create_http_route_config(cmd, resource_group_name, name, http_route_config_name, yaml):
5095+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5096+
yaml_http_route_config = load_yaml_file(yaml)
5097+
# check if the type is dict
5098+
if not isinstance(yaml_http_route_config, dict):
5099+
raise ValidationError('Invalid YAML provided. Please see https://aka.ms/azure-container-apps-yaml for a valid YAML spec.')
5100+
5101+
http_route_config_envelope = {"properties": yaml_http_route_config}
5102+
5103+
try:
5104+
return HttpRouteConfigClient.create(cmd, resource_group_name, name, http_route_config_name, http_route_config_envelope)
5105+
except Exception as e:
5106+
handle_raw_exception(e)
5107+
5108+
5109+
def update_http_route_config(cmd, resource_group_name, name, http_route_config_name, yaml):
5110+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5111+
yaml_http_route_config = load_yaml_file(yaml)
5112+
# check if the type is dict
5113+
if not isinstance(yaml_http_route_config, dict):
5114+
raise ValidationError('Invalid YAML provided. Please see https://aka.ms/azure-container-apps-yaml for a valid YAML spec.')
5115+
5116+
http_route_config_envelope = {"properties": yaml_http_route_config}
5117+
5118+
try:
5119+
return HttpRouteConfigClient.update(cmd, resource_group_name, name, http_route_config_name, http_route_config_envelope)
5120+
except Exception as e:
5121+
handle_raw_exception(e)
5122+
5123+
5124+
def list_http_route_configs(cmd, resource_group_name, name):
5125+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5126+
try:
5127+
return HttpRouteConfigClient.list(cmd, resource_group_name, name)
5128+
except Exception as e:
5129+
handle_raw_exception(e)
5130+
5131+
5132+
def show_http_route_config(cmd, resource_group_name, name, http_route_config_name):
5133+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5134+
try:
5135+
return HttpRouteConfigClient.show(cmd, resource_group_name, name, http_route_config_name)
5136+
except Exception as e:
5137+
handle_raw_exception(e)
5138+
5139+
5140+
def delete_http_route_config(cmd, resource_group_name, name, http_route_config_name):
5141+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5142+
try:
5143+
return HttpRouteConfigClient.delete(cmd, resource_group_name, name, http_route_config_name)
5144+
except Exception as e:
5145+
handle_raw_exception(e)
5146+
5147+
5148+
def show_environment_premium_ingress(cmd, name, resource_group_name):
5149+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5150+
5151+
try:
5152+
env = ManagedEnvironmentClient.show(cmd, resource_group_name, name)
5153+
ingress_config = safe_get(env, "properties", "ingressConfiguration")
5154+
if not ingress_config:
5155+
return {"message": "No premium ingress configuration found for this environment, using default values."}
5156+
5157+
return ingress_config
5158+
except Exception as e:
5159+
handle_raw_exception(e)
5160+
5161+
5162+
def add_environment_premium_ingress(cmd, name, resource_group_name, workload_profile_name, min_replicas=None, max_replicas=None, termination_grace_period=None, request_idle_timeout=None, header_count_limit=None, no_wait=False):
5163+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5164+
5165+
try:
5166+
ManagedEnvironmentClient.show(cmd, resource_group_name, name)
5167+
env_patch = {}
5168+
ingress_config = {}
5169+
safe_set(env_patch, "properties", "ingressConfiguration", value=ingress_config)
5170+
5171+
# Required
5172+
ingress_config["workloadProfileName"] = workload_profile_name
5173+
# Optional, remove if None
5174+
ingress_config["terminationGracePeriodSeconds"] = termination_grace_period
5175+
ingress_config["requestIdleTimeout"] = request_idle_timeout
5176+
ingress_config["headerCountLimit"] = header_count_limit
5177+
5178+
result = ManagedEnvironmentClient.update(
5179+
cmd=cmd,
5180+
resource_group_name=resource_group_name,
5181+
name=name,
5182+
managed_environment_envelope=env_patch,
5183+
no_wait=no_wait
5184+
)
5185+
5186+
return safe_get(result, "properties", "ingressConfiguration")
5187+
5188+
except Exception as e:
5189+
handle_raw_exception(e)
5190+
5191+
5192+
def update_environment_premium_ingress(cmd, name, resource_group_name, workload_profile_name=None, min_replicas=None, max_replicas=None, termination_grace_period=None, request_idle_timeout=None, header_count_limit=None, no_wait=False):
5193+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5194+
5195+
try:
5196+
ManagedEnvironmentClient.show(cmd, resource_group_name, name)
5197+
env_patch = {}
5198+
ingress_config = {}
5199+
5200+
if workload_profile_name is not None:
5201+
ingress_config["workloadProfileName"] = workload_profile_name
5202+
if termination_grace_period is not None:
5203+
ingress_config["terminationGracePeriodSeconds"] = termination_grace_period
5204+
if request_idle_timeout is not None:
5205+
ingress_config["requestIdleTimeout"] = request_idle_timeout
5206+
if header_count_limit is not None:
5207+
ingress_config["headerCountLimit"] = header_count_limit
5208+
5209+
# Only add ingressConfiguration to the patch if any values were specified
5210+
if ingress_config:
5211+
safe_set(env_patch, "properties", "ingressConfiguration", value=ingress_config)
5212+
else:
5213+
return {"message": "No changes specified for premium ingress configuration"}
5214+
5215+
# Update the environment with the patched ingress configuration
5216+
result = ManagedEnvironmentClient.update(
5217+
cmd=cmd,
5218+
resource_group_name=resource_group_name,
5219+
name=name,
5220+
managed_environment_envelope=env_patch,
5221+
no_wait=no_wait
5222+
)
5223+
5224+
return safe_get(result, "properties", "ingressConfiguration")
5225+
5226+
except Exception as e:
5227+
handle_raw_exception(e)
5228+
5229+
5230+
def remove_environment_premium_ingress(cmd, name, resource_group_name, no_wait=False):
5231+
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)
5232+
5233+
try:
5234+
ManagedEnvironmentClient.show(cmd, resource_group_name, name)
5235+
env_patch = {}
5236+
# Remove the whole section to restore defaults
5237+
safe_set(env_patch, "properties", "ingressConfiguration", value=None)
5238+
5239+
ManagedEnvironmentClient.update(
5240+
cmd=cmd,
5241+
resource_group_name=resource_group_name,
5242+
name=name,
5243+
managed_environment_envelope=env_patch,
5244+
no_wait=no_wait
5245+
)
5246+
5247+
except Exception as e:
5248+
handle_raw_exception(e)

0 commit comments

Comments
 (0)