Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3db6bc5
Add create session pool with oncontainerexit
yitaopan Jul 17, 2025
bb1b284
Merge branch 'main' of https://github.com/yitaopan/azure-cli-extensio…
yitaopan Jul 22, 2025
7dde7ab
fix
yitaopan Jul 22, 2025
51d4e02
fix none reference
yitaopan Jul 29, 2025
281999d
fix
yitaopan Jul 30, 2025
6d0689b
fix test
yitaopan Jul 30, 2025
9176d80
style
yitaopan Jul 30, 2025
d2ca012
add history
yitaopan Jul 30, 2025
964e9fa
Merge branch 'main' of https://github.com/yitaopan/azure-cli-extensio…
yitaopan Aug 4, 2025
5436ff6
fix history
yitaopan Aug 4, 2025
142466b
Auto infer lifecycle type when create
yitaopan Aug 5, 2025
57b98c6
Merge branch 'main' of https://github.com/yitaopan/azure-cli-extensio…
yitaopan Aug 5, 2025
2ed8341
rerun tests
yitaopan Aug 5, 2025
dcb6ccd
style
yitaopan Aug 5, 2025
c809691
add more validation
yitaopan Aug 5, 2025
ec0dd2d
shorter default max alive period
yitaopan Aug 5, 2025
11bd642
rerun tests
yitaopan Aug 5, 2025
4edbfab
fix doc
yitaopan Aug 5, 2025
a8d72b6
fix doc
yitaopan Aug 5, 2025
0989654
fix
yitaopan Aug 6, 2025
7d637b4
fix
yitaopan Aug 6, 2025
05547b1
fully rely on backend logic for patch
yitaopan Aug 6, 2025
7e79b89
refine validation
yitaopan Aug 6, 2025
5119df9
style
yitaopan Aug 6, 2025
f287afd
Update src/containerapp/HISTORY.rst
yitaopan Aug 7, 2025
16ffdc4
validate in update
yitaopan Aug 7, 2025
918343b
Merge branch 'main' of https://github.com/yitaopan/azure-cli-extensio…
yitaopan Aug 7, 2025
71e704f
Validate in update
yitaopan Aug 7, 2025
da4426e
Default lifecycle type to Timed only for creation
yitaopan Aug 7, 2025
010ee8e
merge and resolve conflicts
yitaopan Aug 18, 2025
8ca3b21
style
yitaopan Aug 18, 2025
6ff2d19
Merge branch 'main' of https://github.com/yitaopan/azure-cli-extensio…
yitaopan Aug 20, 2025
1a52db5
Merge branch 'main' of https://github.com/yitaopan/azure-cli-extensio…
yitaopan Aug 21, 2025
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
1 change: 1 addition & 0 deletions src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ upcoming
++++++
* 'az containerapp update/up': Disallow changing `--revisions-mode` to Labels.
* 'az containerapp session code-interpreter': Fix `--path` in examples
* 'az containerapp sessionpool create/update': Support `--lifecycle-type` and `--max-alive-period`
* 'az containerapp env premium-ingress': Deprecate `--min-replicas` and `--max-replicas` parameters, use workload profile scale instead.

1.2.0b3
Expand Down
11 changes: 9 additions & 2 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,11 +443,18 @@ def load_arguments(self, _):
c.argument('mi_user_assigned', nargs='+', help='Space-separated user identities to be assigned.')

with self.argument_context('containerapp sessionpool', arg_group='Configuration') as c:
c.argument('container_type', arg_type=get_enum_type(["CustomContainer", "PythonLTS", "NodeLTS"]), help="The pool type of the Session Pool, default='PythonLTS'")
c.argument('cooldown_period', help="Period (in seconds), after which the session will be deleted, default=300")
c.argument('cooldown_period', help="Period (in seconds), after which the session will be deleted, this is only applicable for lifecycle type 'Timed', default=300")
c.argument('max_alive_period', help="Period (in seconds), before the session be deleted if its container was not exited earlier, this is only applicable for lifecycle type 'OnContainerExit', default=3600")
c.argument('secrets', nargs='*', options_list=['--secrets', '-s'], help="A list of secret(s) for the session pool. Space-separated values in 'key=value' format. Empty string to clear existing values.")
c.argument('network_status', arg_type=get_enum_type(["EgressEnabled", "EgressDisabled"]), help="Egress is enabled for the Sessions or not.")

with self.argument_context('containerapp sessionpool create', arg_group='Configuration') as c:
c.argument('container_type', arg_type=get_enum_type(["CustomContainer", "PythonLTS", "NodeLTS"]), help="The pool type of the Session Pool, default='PythonLTS'")
c.argument('lifecycle_type', arg_type=get_enum_type(["Timed", "OnContainerExit"]), help="The lifecycle type of the Session Pool", default='Timed')

with self.argument_context('containerapp sessionpool update', arg_group='Configuration') as c:
c.argument('lifecycle_type', arg_type=get_enum_type(["Timed", "OnContainerExit"]), help="The lifecycle type of the Session Pool")

with self.argument_context('containerapp sessionpool', arg_group='Scale') as c:
c.argument('max_concurrent_sessions', options_list=['--max-sessions'], help="Max count of sessions can be run at the same time.", type=int)
c.argument('ready_session_instances', options_list=['--ready-sessions'], help="The number of sessions that will be ready in the session pool all the time.", type=int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class ContainerType(Enum):
NodeLTS = 3


class LifecycleType(Enum):
Timed = 0
OnContainerExit = 1


class SessionPoolPreviewDecorator(BaseResource):
def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
super().__init__(cmd, client, raw_parameters, models)
Expand All @@ -70,6 +75,18 @@ def get_argument_container_type(self):
def set_argument_container_type(self, container_type):
return self.set_param('container_type', container_type)

def get_argument_lifecycle_type(self):
return self.get_param('lifecycle_type')

def set_argument_lifecycle_type(self, lifecycle_type):
return self.set_param('lifecycle_type', lifecycle_type)

def get_argument_max_alive_period(self):
return self.get_param('max_alive_period')

def set_argument_max_alive_period(self, max_alive_period):
return self.set_param('max_alive_period', max_alive_period)

def get_argument_cooldown_period_in_seconds(self):
return self.get_param('cooldown_period')

Expand Down Expand Up @@ -160,6 +177,22 @@ def validate_arguments(self):
else:
if environment_name is not None:
raise ValidationError(f"Do not pass environment name when using container type {container_type}")
if self.get_argument_lifecycle_type().lower() != LifecycleType.Timed.name.lower():
raise ValidationError(f"The container type {container_type} only supports lifecycle type '{LifecycleType.Timed.name}'.")
if self.get_argument_max_alive_period() is not None:
raise ValidationError(f"The container type {container_type} does not support --max-alive-period.")

if self.get_argument_max_alive_period() is not None and \
self.get_argument_cooldown_period_in_seconds() is not None:
raise ValidationError("--max-alive-period and --cooldown-period cannot be set at the same time.")

if self.get_argument_max_alive_period() is not None and \
self.get_argument_lifecycle_type().lower() != LifecycleType.OnContainerExit.name.lower():
raise ValidationError(f"--max-alive-period can only be set when --lifecycle-type is '{LifecycleType.OnContainerExit.name}'.")

if self.get_argument_cooldown_period_in_seconds() is not None and \
self.get_argument_lifecycle_type().lower() != LifecycleType.Timed.name.lower():
raise ValidationError(f"--cooldown-period can only be set when --lifecycle-type is '{LifecycleType.Timed.name}'.")

def construct_payload(self):
self.session_pool_def["location"] = self.get_argument_location()
Expand Down Expand Up @@ -253,12 +286,18 @@ def set_up_managed_identity(self):
self.session_pool_def["identity"] = identity_def

def set_up_dynamic_configuration(self):
if self.get_argument_cooldown_period_in_seconds() is None:
if self.get_argument_lifecycle_type().lower() == LifecycleType.Timed.name.lower() and \
self.get_argument_cooldown_period_in_seconds() is None:
self.set_argument_cooldown_period_in_seconds(300)

if self.get_argument_lifecycle_type().lower() == LifecycleType.OnContainerExit.name.lower() and \
self.get_argument_max_alive_period() is None:
self.set_argument_max_alive_period(3600)

dynamic_pool_def = {}
lifecycle_config_def = {}
lifecycle_config_def["lifecycleType"] = "Timed"
lifecycle_config_def["lifecycleType"] = self.get_argument_lifecycle_type()
lifecycle_config_def["maxAlivePeriodInSeconds"] = self.get_argument_max_alive_period()
lifecycle_config_def["cooldownPeriodInSeconds"] = self.get_argument_cooldown_period_in_seconds()
dynamic_pool_def["lifecycleConfiguration"] = lifecycle_config_def

Expand Down Expand Up @@ -401,6 +440,41 @@ def create(self):


class SessionPoolUpdateDecorator(SessionPoolPreviewDecorator):
def validate_arguments(self):
self.existing_pool_def = self.client.show(cmd=self.cmd,
resource_group_name=self.get_argument_resource_group_name(),
name=self.get_argument_name())

if ((self.get_argument_container_type() is not None and safe_get(self.existing_pool_def, "properties", "containerType").lower() == self.get_argument_container_type().lower()) or
(self.get_argument_managed_env() is not None and safe_get(self.existing_pool_def, "properties", "environmentId").lower() == self.get_argument_managed_env().lower())):
raise ValidationError("containerType and environmentId cannot be updated.")

# Validate unsupported arguments with a certain lifecycle type
if self.get_argument_max_alive_period() is not None and \
self.get_argument_lifecycle_type() is not None and \
self.get_argument_lifecycle_type().lower() != LifecycleType.OnContainerExit.name.lower():
raise ValidationError(f"--max-alive-period can only be set when --lifecycle-type is '{LifecycleType.OnContainerExit.name}'.")
if self.get_argument_cooldown_period_in_seconds() is not None and \
self.get_argument_lifecycle_type() is not None and \
self.get_argument_lifecycle_type().lower() != LifecycleType.Timed.name.lower():
raise ValidationError(f"--cooldown-period can only be set when --lifecycle-type is '{LifecycleType.Timed.name}'.")

# Validate that max_alive_period and cooldown_period are not set at the same time
if self.get_argument_max_alive_period() is not None and \
self.get_argument_cooldown_period_in_seconds() is not None:
raise ValidationError("--max-alive-period and --cooldown-period cannot be set at the same time.")

# Validate unsupported arguments with existing lifecycle type
current_lifecycle_type = safe_get(self.existing_pool_def, "properties", "dynamicPoolConfiguration", "lifecycleConfiguration", "lifecycleType")
if self.get_argument_max_alive_period() is not None and \
self.get_argument_lifecycle_type() is None and \
current_lifecycle_type.lower() != LifecycleType.OnContainerExit.name.lower():
raise ValidationError(f"--max-alive-period is not supported for the current --lifecycle-type '{current_lifecycle_type}'.")
if self.get_argument_cooldown_period_in_seconds() is not None and \
self.get_argument_lifecycle_type() is None and \
current_lifecycle_type.lower() != LifecycleType.Timed.name.lower():
raise ValidationError(f"--cooldown-period is not supported for the current --lifecycle-type '{current_lifecycle_type}'.")

def update(self):
try:
return self.client.update(
Expand All @@ -412,13 +486,6 @@ def update(self):

def construct_payload(self):
self.session_pool_def = {}
self.existing_pool_def = self.client.show(cmd=self.cmd,
resource_group_name=self.get_argument_resource_group_name(),
name=self.get_argument_name())

if ((self.get_argument_container_type() is not None and safe_get(self.existing_pool_def, "properties", "containerType").lower() == self.get_argument_container_type().lower()) or
(self.get_argument_managed_env() is not None and safe_get(self.existing_pool_def, "properties", "environmentId").lower() == self.get_argument_managed_env().lower())):
raise ValidationError("containerType and environmentId cannot be updated.")

self.set_up_managed_identity()
self.set_up_dynamic_configuration()
Expand Down Expand Up @@ -483,11 +550,19 @@ def set_up_managed_identity_settings(self):
safe_set(self.session_pool_def, "properties", "managedIdentitySettings", value=managed_identity_settings)

def set_up_dynamic_configuration(self):
lifecycle_config_def = {}

if self.get_argument_lifecycle_type() is not None:
lifecycle_config_def["lifecycleType"] = self.get_argument_lifecycle_type()

if self.get_argument_cooldown_period_in_seconds() is not None:
dynamic_pool_def = {}
lifecycle_config_def = {}
lifecycle_config_def["lifecycleType"] = "Timed"
lifecycle_config_def["cooldownPeriodInSeconds"] = self.get_argument_cooldown_period_in_seconds()

if self.get_argument_max_alive_period() is not None:
lifecycle_config_def["maxAlivePeriodInSeconds"] = self.get_argument_max_alive_period()

if lifecycle_config_def:
dynamic_pool_def = {}
dynamic_pool_def["lifecycleConfiguration"] = lifecycle_config_def
safe_set(self.session_pool_def, "properties", "dynamicPoolConfiguration", value=dynamic_pool_def)

Expand Down
5 changes: 5 additions & 0 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -3096,7 +3096,9 @@ def create_session_pool(cmd,
location=None,
managed_env=None,
container_type=None,
lifecycle_type=None,
cooldown_period=None,
max_alive_period=None,
secrets=None,
network_status=None,
max_concurrent_sessions=None,
Expand Down Expand Up @@ -3135,7 +3137,9 @@ def update_session_pool(cmd,
name,
resource_group_name,
location=None,
lifecycle_type=None,
cooldown_period=None,
max_alive_period=None,
secrets=None,
network_status=None,
max_concurrent_sessions=None,
Expand All @@ -3161,6 +3165,7 @@ def update_session_pool(cmd,
raw_parameters=raw_parameters,
models=CONTAINER_APPS_SDK_MODELS
)
session_pool_decorator.validate_arguments()
session_pool_decorator.construct_payload()
r = session_pool_decorator.update()

Expand Down
Loading
Loading