Skip to content

Commit 0cf0be6

Browse files
Merge pull request #1 from khushishah513/functionaca-keys
[Containerapp] `az containerapp function`: Add function key management commands
2 parents 8c68569 + b9c8a4b commit 0cf0be6

File tree

7 files changed

+401
-2
lines changed

7 files changed

+401
-2
lines changed

src/containerapp/HISTORY.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Release History
44
===============
55
upcoming
66
++++++
7+
* 'az containerapp function list-keys': List function keys for a specific function in a container app
8+
* 'az containerapp function update-keys': Update specific function key for a specific function in a container app
9+
* 'az containerapp function list-hostkeys': List host keys for a container app
10+
* 'az containerapp function update-hostkeys': Update specific host key for a container app
711
* 'az containerapp update/up': Disallow changing `--revisions-mode` to Labels.
812
* 'az containerapp session code-interpreter': Fix `--path` in examples
913
* 'az containerapp sessionpool create/update': Support `--lifecycle-type` and `--max-alive-period`

src/containerapp/azext_containerapp/_clients.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,3 +1698,79 @@ def remove(cls, cmd, resource_group_name, environment_name):
16981698
if r.status_code == 202:
16991699
operation_url = r.headers.get(HEADER_LOCATION)
17001700
poll_results(cmd, operation_url)
1701+
1702+
class ContainerAppFunctionsPreviewClient:
1703+
api_version = PREVIEW_API_VERSION
1704+
1705+
@classmethod
1706+
def list_function_keys(cls, cmd, resource_group_name, name, function_name):
1707+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1708+
sub_id = get_subscription_id(cmd.cli_ctx)
1709+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/functions/{}/listkeys?api-version={}"
1710+
request_url = url_fmt.format(
1711+
management_hostname.strip('/'),
1712+
sub_id,
1713+
resource_group_name,
1714+
name,
1715+
function_name,
1716+
cls.api_version)
1717+
1718+
r = send_raw_request(cmd.cli_ctx, "POST", request_url)
1719+
return r.json()
1720+
1721+
@classmethod
1722+
def update_function_keys(cls, cmd, resource_group_name, name, function_name, key_name, key_value=None):
1723+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1724+
sub_id = get_subscription_id(cmd.cli_ctx)
1725+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/functions/{}/keys/{}?api-version={}"
1726+
request_url = url_fmt.format(
1727+
management_hostname.strip('/'),
1728+
sub_id,
1729+
resource_group_name,
1730+
name,
1731+
function_name,
1732+
key_name,
1733+
cls.api_version)
1734+
1735+
body = {}
1736+
if key_value:
1737+
body["value"] = key_value
1738+
1739+
r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(body))
1740+
return r.json()
1741+
1742+
@classmethod
1743+
def list_host_keys(cls, cmd, resource_group_name, name):
1744+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1745+
sub_id = get_subscription_id(cmd.cli_ctx)
1746+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/host/default/listkeys?api-version={}"
1747+
request_url = url_fmt.format(
1748+
management_hostname.strip('/'),
1749+
sub_id,
1750+
resource_group_name,
1751+
name,
1752+
cls.api_version)
1753+
1754+
r = send_raw_request(cmd.cli_ctx, "POST", request_url)
1755+
return r.json()
1756+
1757+
@classmethod
1758+
def update_host_keys(cls, cmd, resource_group_name, name, key_type, key_name, key_value=None):
1759+
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
1760+
sub_id = get_subscription_id(cmd.cli_ctx)
1761+
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/host/default/{}/{}?api-version={}"
1762+
request_url = url_fmt.format(
1763+
management_hostname.strip('/'),
1764+
sub_id,
1765+
resource_group_name,
1766+
name,
1767+
key_type,
1768+
key_name,
1769+
cls.api_version)
1770+
1771+
body = {}
1772+
if key_value:
1773+
body["value"] = key_value
1774+
1775+
r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(body))
1776+
return r.json()

src/containerapp/azext_containerapp/_help.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@
166166
az containerapp up -n my-containerapp --image my-app:v1.0 --kind functionapp
167167
"""
168168

169-
170169
helps['containerapp replica count'] = """
171170
type: command
172171
short-summary: Count of a container app's replica(s)
@@ -179,6 +178,53 @@
179178
az containerapp replica count -n my-containerapp -g MyResourceGroup
180179
"""
181180

181+
helps['containerapp function'] = """
182+
type: group
183+
short-summary: Commands to manage function keys in a container app.
184+
"""
185+
186+
helps['containerapp function list-keys'] = """
187+
type: command
188+
short-summary: List function keys for a specific function in a container app.
189+
examples:
190+
- name: List function keys for a specific function
191+
text: |
192+
az containerapp function list-keys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --function-name MyFunctionName
193+
"""
194+
195+
helps['containerapp function update-keys'] = """
196+
type: command
197+
short-summary: Update function keys for a specific function in a container app.
198+
examples:
199+
- name: Update a function key for a specific function
200+
text: |
201+
az containerapp function update-keys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --function-name MyFunctionName --key-name MyKeyName --key-value MyKeyValue
202+
"""
203+
204+
helps['containerapp function list-hostkeys'] = """
205+
type: command
206+
short-summary: List host keys for a container app.
207+
examples:
208+
- name: List host keys for a container app
209+
text: |
210+
az containerapp function list-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision
211+
"""
212+
213+
helps['containerapp function update-hostkeys'] = """
214+
type: command
215+
short-summary: Update host keys for a container app.
216+
examples:
217+
- name: Update a host key for a container app with function key type
218+
text: |
219+
az containerapp function update-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --key-name MyKeyName --key-value MyKeyValue --key-type functionKeys
220+
- name: Update a host key for a container app with master key type
221+
text: |
222+
az containerapp function update-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --key-name MyKeyName --key-value MyKeyValue --key-type masterKey
223+
- name: Update a host key for a container app with system key type
224+
text: |
225+
az containerapp function update-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --key-name MyKeyName --key-value MyKeyValue --key-type systemKeys
226+
"""
227+
182228
# Environment Commands
183229
helps['containerapp env'] = """
184230
type: group

src/containerapp/azext_containerapp/_params.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,3 +530,30 @@ def load_arguments(self, _):
530530
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.")
531531
c.argument('request_idle_timeout', options_list=['--request-idle-timeout'], type=int, help="Timeout in minutes for idle requests. Default 4, minimum 4, maximum 30.")
532532
c.argument('header_count_limit', options_list=['--header-count-limit'], type=int, help="Limit of http headers per request. Default 100, minimum 1.")
533+
534+
with self.argument_context('containerapp function list-keys') as c:
535+
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
536+
c.argument('name', options_list=['--name', '-n'], help="The name of the container app.")
537+
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")
538+
c.argument('function_name', options_list=['--function-name'], help="The name of the function.")
539+
540+
with self.argument_context('containerapp function update-keys') as c:
541+
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
542+
c.argument('name', options_list=['--name', '-n'], help="The name of the container app.")
543+
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")
544+
c.argument('function_name', options_list=['--function-name'], help="The name of the function.")
545+
c.argument('key_name', options_list=['--key-name'], help="The name of the key to update.")
546+
c.argument('key_value', options_list=['--key-value'], help="The value of the key to update.")
547+
548+
with self.argument_context('containerapp function list-hostkeys') as c:
549+
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
550+
c.argument('name', options_list=['--name', '-n'], help="The name of the container app.")
551+
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")
552+
553+
with self.argument_context('containerapp function update-hostkeys') as c:
554+
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
555+
c.argument('name', options_list=['--name', '-n'], help="The name of the container app.")
556+
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")
557+
c.argument('key_name', options_list=['--key-name'], help="The name of the host key to update.")
558+
c.argument('key_value', options_list=['--key-value'], help="The value of the host key to update.")
559+
c.argument('key_type', options_list=['--key-type'], arg_type=get_enum_type(['functionKeys', 'masterKey', 'systemKeys']), help="The type of the host key.")

src/containerapp/azext_containerapp/commands.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,9 @@ def load_command_table(self, args):
297297
g.custom_command('add', 'add_environment_premium_ingress')
298298
g.custom_command('update', 'update_environment_premium_ingress')
299299
g.custom_command('remove', 'remove_environment_premium_ingress', confirmation=True)
300+
301+
with self.command_group('containerapp function') as g:
302+
g.custom_command('list-keys', 'list_containerapp_function_keys')
303+
g.custom_command('update-keys', 'update_containerapp_function_keys')
304+
g.custom_command('list-hostkeys', 'list_containerapp_function_hostkeys')
305+
g.custom_command('update-hostkeys', 'update_containerapp_function_hostkeys')
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# coding=utf-8
2+
# --------------------------------------------------------------------------------------------
3+
# Copyright (c) Microsoft Corporation. All rights reserved.
4+
# Licensed under the MIT License. See License.txt in the project root for license information.
5+
# --------------------------------------------------------------------------------------------
6+
# pylint: disable=line-too-long, broad-except, logging-format-interpolation
7+
from knack.log import get_logger
8+
from typing import Any, Dict
9+
10+
from azure.cli.core.commands import AzCliCommand
11+
from azure.cli.core.azclierror import ValidationError
12+
from azure.cli.command_modules.containerapp.base_resource import BaseResource
13+
14+
from ._clients import ContainerAppFunctionsPreviewClient
15+
from ._client_factory import handle_raw_exception
16+
17+
logger = get_logger(__name__)
18+
19+
20+
class ContainerAppFunctionKeysDecorator(BaseResource):
21+
"""Base decorator for Container App Function Keys operations"""
22+
23+
def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
24+
super().__init__(cmd, client, raw_parameters, models)
25+
26+
def get_argument_function_name(self):
27+
return self.get_param("function_name")
28+
29+
def get_argument_revision_name(self):
30+
return self.get_param("revision_name")
31+
32+
def validate_common_arguments(self):
33+
"""Validate common arguments required for all function operations"""
34+
resource_group_name = self.get_argument_resource_group_name()
35+
name = self.get_argument_name()
36+
revision_name = self.get_argument_revision_name()
37+
38+
if not resource_group_name:
39+
raise ValidationError("Resource group name is required.")
40+
41+
if not name:
42+
raise ValidationError("Container app name is required.")
43+
44+
if not revision_name:
45+
raise ValidationError("Revision name is required.")
46+
47+
return resource_group_name, name, revision_name
48+
49+
def validate_function_arguments(self):
50+
"""Validate arguments required for function-specific operations"""
51+
resource_group_name, name, revision_name = self.validate_common_arguments()
52+
function_name = self.get_argument_function_name()
53+
54+
if not function_name:
55+
raise ValidationError("Function name is required.")
56+
57+
return resource_group_name, name, revision_name, function_name
58+
59+
60+
class ContainerAppFunctionKeysListDecorator(ContainerAppFunctionKeysDecorator):
61+
"""Decorator for listing function keys"""
62+
63+
def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
64+
super().__init__(cmd, client, raw_parameters, models)
65+
66+
def list_keys(self):
67+
"""List keys for a specific function"""
68+
try:
69+
resource_group_name, name, revision_name, function_name = self.validate_function_arguments()
70+
71+
return self.client.list_function_keys(
72+
cmd=self.cmd,
73+
resource_group_name=resource_group_name,
74+
name=name,
75+
function_name=function_name
76+
)
77+
except Exception as e:
78+
handle_raw_exception(e)
79+
80+
81+
class ContainerAppFunctionKeysUpdateDecorator(ContainerAppFunctionKeysDecorator):
82+
"""Decorator for updating function keys"""
83+
84+
def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
85+
super().__init__(cmd, client, raw_parameters, models)
86+
87+
def get_argument_key_name(self):
88+
return self.get_param("key_name")
89+
90+
def get_argument_key_value(self):
91+
return self.get_param("key_value")
92+
93+
def validate_update_arguments(self):
94+
"""Validate arguments required for updating function keys"""
95+
resource_group_name, name, revision_name, function_name = self.validate_function_arguments()
96+
key_name = self.get_argument_key_name()
97+
98+
if not key_name:
99+
raise ValidationError("Key name is required.")
100+
101+
return resource_group_name, name, revision_name, function_name, key_name
102+
103+
def update_keys(self):
104+
"""Update keys for a specific function"""
105+
try:
106+
resource_group_name, name, revision_name, function_name, key_name = self.validate_update_arguments()
107+
key_value = self.get_argument_key_value()
108+
109+
return self.client.update_function_keys(
110+
cmd=self.cmd,
111+
resource_group_name=resource_group_name,
112+
name=name,
113+
function_name=function_name,
114+
key_name=key_name,
115+
key_value=key_value
116+
)
117+
except Exception as e:
118+
handle_raw_exception(e)
119+
120+
121+
class ContainerAppFunctionHostKeysListDecorator(ContainerAppFunctionKeysDecorator):
122+
"""Decorator for listing host keys"""
123+
124+
def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
125+
super().__init__(cmd, client, raw_parameters, models)
126+
127+
def list_host_keys(self):
128+
"""List host keys for the container app function host"""
129+
try:
130+
resource_group_name, name, revision_name = self.validate_common_arguments()
131+
132+
return self.client.list_host_keys(
133+
cmd=self.cmd,
134+
resource_group_name=resource_group_name,
135+
name=name
136+
)
137+
except Exception as e:
138+
handle_raw_exception(e)
139+
140+
141+
class ContainerAppFunctionHostKeysUpdateDecorator(ContainerAppFunctionKeysDecorator):
142+
"""Decorator for updating host keys"""
143+
144+
def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
145+
super().__init__(cmd, client, raw_parameters, models)
146+
147+
def get_argument_key_type(self):
148+
return self.get_param("key_type")
149+
150+
def get_argument_key_name(self):
151+
return self.get_param("key_name")
152+
153+
def get_argument_key_value(self):
154+
return self.get_param("key_value")
155+
156+
def validate_host_key_arguments(self):
157+
"""Validate arguments required for updating host keys"""
158+
resource_group_name, name, revision_name = self.validate_common_arguments()
159+
key_type = self.get_argument_key_type()
160+
key_name = self.get_argument_key_name()
161+
162+
if not key_type:
163+
raise ValidationError("Key type is required.")
164+
165+
if not key_name:
166+
raise ValidationError("Key name is required.")
167+
168+
return resource_group_name, name, revision_name, key_type, key_name
169+
170+
def update_host_keys(self):
171+
"""Update host keys for the container app function host"""
172+
try:
173+
resource_group_name, name, revision_name, key_type, key_name = self.validate_host_key_arguments()
174+
key_value = self.get_argument_key_value()
175+
176+
return self.client.update_host_keys(
177+
cmd=self.cmd,
178+
resource_group_name=resource_group_name,
179+
name=name,
180+
key_type=key_type,
181+
key_name=key_name,
182+
key_value=key_value
183+
)
184+
except Exception as e:
185+
handle_raw_exception(e)

0 commit comments

Comments
 (0)