Skip to content
Merged
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
7 changes: 6 additions & 1 deletion src/fleet/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,9 @@ Release History
1.7.0
++++++
* Removed is_preview flag from `enable_vnet_integration` and `assign_identity` parameters.
* Updated Private Fleet V2 to require user assigned identity.
* Updated Private Fleet V2 to require user assigned identity.

1.8.0
++++++
* Upgrade SDK version to 2025-08-01-preview
* Add Fleet Managed Namespace support
2 changes: 1 addition & 1 deletion src/fleet/azext_fleet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def register_fleet_resource_type():
register_resource_type(
"latest",
CUSTOM_MGMT_FLEET,
SDKProfile("2025-04-01-preview"),
SDKProfile("2025-08-01-preview"),
)


Expand Down
4 changes: 4 additions & 0 deletions src/fleet/azext_fleet/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def cf_gates(cli_ctx, *_):
return get_container_service_client(cli_ctx).gates


def cf_fleet_managed_namespaces(cli_ctx, *_):
return get_container_service_client(cli_ctx).fleet_managed_namespaces


def cf_update_runs(cli_ctx, *_):
return get_container_service_client(cli_ctx).update_runs

Expand Down
91 changes: 90 additions & 1 deletion src/fleet/azext_fleet/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,11 @@

helps['fleet get-credentials'] = """
type: command
short-summary: For hubful fleets, gets the kubeconfig for the fleet's hub cluster.
short-summary: For hubful fleets, gets the kubeconfig for the fleet's hub cluster. For fleet members, gets kubeconfig from the member's managed cluster.
parameters:
- name: --member -m
type: string
short-summary: Specify the fleet member name to get credentials from its associated managed cluster.
- name: --overwrite-existing
type: bool
short-summary: Overwrite any existing cluster entry with the same name.
Expand All @@ -84,6 +87,8 @@
text: az fleet get-credentials -g MyFleetResourceGroup -n MyFleetName
- name: Get a fleet's hub cluster kubeconfig, and save it to a specific file.
text: az fleet get-credentials -g MyFleetResourceGroup -n MyFleetName -f ~/mykubeconfigfile.txt
- name: Get kubeconfig from a specific fleet member's managed cluster.
text: az fleet get-credentials -g MyFleetResourceGroup -n MyFleetName --member MyFleetMember
"""

helps['fleet reconcile'] = """
Expand Down Expand Up @@ -434,3 +439,87 @@
- name: Approves a gate.
text: az fleet gate approve -g MyFleetResourceGroup --fleet-name MyFleetName --gate-name 3fa85f64-5717-4562-b3fc-2c963f66afa6
"""

helps['fleet namespace'] = """
type: group
short-summary: Commands to manage fleet managed namespaces.
"""

helps['fleet namespace create'] = """
type: command
short-summary: Creates a fleet managed namespace.
examples:
- name: Create a basic fleet managed namespace.
text: az fleet namespace create -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace
- name: Create a fleet managed namespace with tags.
text: az fleet namespace create -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace --tags environment=production key=value
- name: Create a fleet managed namespace with resource limits and policies.
text: az fleet namespace create -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace --annotations key=value --labels key=value --cpu-requests 1m --cpu-limits 4m --memory-requests 1Mi --memory-limits 4Mi --ingress-policy AllowAll --egress-policy DenyAll --delete-policy Keep --adoption-policy Never
- name: Create a fleet managed namespace on specific member clusters.
text: az fleet namespace create -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace --member-cluster-names team-01 team-02 team-03 team-04
"""

helps['fleet namespace update'] = """
type: command
short-summary: Updates a fleet managed namespace.
examples:
- name: Updates a fleet managed namespace.
text: az fleet namespace update -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace
- name: Update tags for a fleet managed namespace.
text: az fleet namespace update -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace --tags environment=production
"""

helps['fleet namespace list'] = """
type: command
short-summary: Lists a fleet's managed namespaces.
examples:
- name: List all managed namespaces for a given fleet.
text: az fleet namespace list -g MyFleetResourceGroup -f MyFleetName
"""

helps['fleet namespace show'] = """
type: command
short-summary: Gets a fleet managed namespace.
examples:
- name: Show the details of a specific managed namespace.
text: az fleet namespace show -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace
"""

helps['fleet namespace delete'] = """
type: command
short-summary: Deletes a fleet managed namespace.
examples:
- name: Delete a specific managed namespace.
text: az fleet namespace delete -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace
"""

helps['fleet namespace wait'] = """
type: command
short-summary: Wait for a fleet managed namespace to reach a desired state.
long-summary: If an operation on a fleet managed namespace was interrupted or was started with `--no-wait`, use this command to wait for it to complete.
"""

helps['fleet namespace get-credentials'] = """
type: command
short-summary: Get kubeconfig for a fleet namespace, with the namespace context pre-configured.
parameters:
- name: --member -m
type: string
short-summary: Specify the fleet member name to get credentials from its associated managed cluster.
- name: --context
type: string
short-summary: If specified, overwrite the default context name.
- name: --overwrite-existing
type: bool
short-summary: Overwrite any existing cluster entry with the same name.
- name: --file
type: string
short-summary: Kubernetes configuration file to update. Use "-" to print YAML to stdout instead.
examples:
- name: Get kubeconfig for a fleet namespace from the hub cluster.
text: az fleet namespace get-credentials -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace
- name: Get kubeconfig for a fleet namespace from a specific fleet member.
text: az fleet namespace get-credentials -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace --member MyFleetMember
- name: Save kubeconfig to a specific file.
text: az fleet namespace get-credentials -g MyFleetResourceGroup -f MyFleetName -n MyManagedNamespace --file ~/my-namespace-config
"""
33 changes: 33 additions & 0 deletions src/fleet/azext_fleet/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from azure.cli.core.commands.validators import get_default_location_from_resource_group
from azext_fleet._validators import (
validate_member_cluster_id,
validate_member_cluster_names,
validate_kubernetes_version,
validate_apiserver_subnet_id,
validate_agent_subnet_id,
Expand Down Expand Up @@ -60,6 +61,7 @@ def load_arguments(self, _):
help='With --enable-managed-identity, enable user assigned managed identity (MSI) on the Fleet resource. Specify the existing user assigned identity resource.')

with self.argument_context('fleet get-credentials') as c:
c.argument('member_name', options_list=['--member', '-m'], help='Specify the fleet member name to get credentials from its associated managed cluster.')
c.argument('context_name', options_list=['--context'], help='If specified, overwrite the default context name.')
c.argument('path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(),
default=os.path.join(os.path.expanduser('~'), '.kube', 'config'))
Expand Down Expand Up @@ -173,3 +175,34 @@ def load_arguments(self, _):
c.argument('resource_group_name', options_list=['--resource-group', '-g'], help='Name of the resource group.')
c.argument('fleet_name', options_list=['--fleet-name', '-f'], help='Name of the fleet.')
c.argument('gate_name', options_list=['--gate-name', '--gate', '-n'], help='Name of the gate.')

with self.argument_context('fleet namespace') as c:
c.argument('resource_group_name', options_list=['--resource-group', '-g'], help='Name of the resource group.')
c.argument('fleet_name', options_list=['--fleet-name', '-f'], help='Name of the fleet.')
c.argument('managed_namespace_name', options_list=['--name', '-n'], help='Name of the managed namespace.')

with self.argument_context('fleet namespace create') as c:
c.argument('managed_namespace_name', options_list=['--name', '-n'], help='The name of the Kubernetes namespace to be created on member clusters.')
c.argument('tags', tags_type)
c.argument('labels', labels_type, help='Space-separated labels in key=value format. Example: env=production region=us-west team=devops')
c.argument('annotations', labels_type, help='Space-separated annotations in key=value format. Example: env=production region=us-west team=devops')
c.argument('cpu_requests', help='CPU requests for the namespace. Example: 1000m')
c.argument('cpu_limits', help='CPU limits for the namespace. Example: 1000m')
c.argument('memory_requests', help='Memory requests for the namespace. Example: 500Mi')
c.argument('memory_limits', help='Memory limits for the namespace. Example: 500Mi')
c.argument('ingress_policy', help='Ingress policy for the namespace', arg_type=get_enum_type(['DenyAll', 'AllowAll', 'AllowSameNamespace']))
c.argument('egress_policy', help='Egress policy for the namespace', arg_type=get_enum_type(['DenyAll', 'AllowAll', 'AllowSameNamespace']))
c.argument('delete_policy', help='Delete policy for the namespace.', arg_type=get_enum_type(['Keep', 'Delete']), default='Keep')
c.argument('adoption_policy', help='Adoption policy for the namespace.', arg_type=get_enum_type(['Always', 'IfIdentical', 'Never']), default='Never')
c.argument('member_cluster_names', nargs='*', validator=validate_member_cluster_names, help='Space-separated list of member cluster names to apply the namespace to.')

with self.argument_context('fleet namespace update') as c:
c.argument('tags', tags_type)

with self.argument_context('fleet namespace get-credentials') as c:
c.argument('managed_namespace_name', options_list=['--name', '-n'], help='Specify the managed namespace name.')
c.argument('member_name', options_list=['--member', '-m'], help='Specify the fleet member name to get credentials from its associated managed cluster.')
c.argument('context_name', options_list=['--context'], help='If specified, overwrite the default context name.')
c.argument('overwrite_existing', help='Overwrite any existing cluster entry with the same name.')
c.argument('path', options_list=['--file'], type=file_type, completer=FilesCompleter(),
default=os.path.join(os.path.expanduser('~'), '.kube', 'config'))
9 changes: 9 additions & 0 deletions src/fleet/azext_fleet/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ def validate_member_cluster_id(namespace):
"--member-cluster-id is not a valid Azure resource ID.")


def validate_member_cluster_names(namespace):
if namespace.member_cluster_names:
valid_subresource = re.compile(r'^[a-z0-9]$|^[a-z0-9][-a-z0-9]{0,48}[a-z0-9]$')
for name in namespace.member_cluster_names:
if not valid_subresource.match(name):
raise InvalidArgumentValueError(
f"--member-cluster-names {name} is not a valid member cluster name.")


def validate_kubernetes_version(namespace):
if namespace.kubernetes_version:
k8s_release_regex = re.compile(r'^[v|V]?(\d+\.\d+(?:\.\d+)?)$')
Expand Down
3 changes: 2 additions & 1 deletion src/fleet/azext_fleet/azext_metadata.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"azext.isPreview": false,
"azext.minCliCoreVersion": "2.61.0"
"azext.minCliCoreVersion": "2.61.0",
"version": "1.8.0"
}
19 changes: 18 additions & 1 deletion src/fleet/azext_fleet/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
cf_fleet_update_strategies,
cf_auto_upgrade_profiles,
cf_auto_upgrade_profile_operations,
cf_gates
cf_gates,
cf_fleet_managed_namespaces
)


Expand All @@ -30,6 +31,12 @@ def load_command_table(self, _):
client_factory=cf_fleet_members
)

managed_namespaces_sdk = CliCommandType(
operations_tmpl="azext_fleet.vendored_sdks.operations._fleet_managed_namespaces_operations#FleetManagedNamespacesOperations.{}",
operation_group="fleet_managed_namespaces",
client_factory=cf_fleet_managed_namespaces
)

update_runs_sdk = CliCommandType(
operations_tmpl="azext_fleet.vendored_sdks.operations._update_runs_operations#UpdateRunsOperations.{}",
operation_group="update_runs",
Expand Down Expand Up @@ -118,3 +125,13 @@ def load_command_table(self, _):
g.custom_show_command("show", "show_gate")
g.custom_command("update", "update_gate")
g.custom_command("approve", "approve_gate")

# fleet namespaces command group
with self.command_group("fleet namespace", managed_namespaces_sdk, client_factory=cf_fleet_managed_namespaces, is_preview=True) as g:
g.custom_command("create", "create_managed_namespace", supports_no_wait=True)
g.custom_command("update", "update_managed_namespace", supports_no_wait=True)
g.custom_command("delete", "delete_managed_namespace", supports_no_wait=True, confirmation=True)
g.custom_command("list", "list_managed_namespaces")
g.custom_show_command("show", "show_managed_namespace")
g.custom_command("get-credentials", "get_namespace_credentials")
g.wait_command("wait")
Loading
Loading