Skip to content

Commit 837fd53

Browse files
authored
AKS: implement pod identity addons management sub commands (Azure#2644)
* [AKS] vendor 2020-11-01 sdk * aks: enable 2020-1101 api * aks: temporary model fix * aks: implement enable/disable pod identity addon support * aks: add pod identity command group stub * aks: add sub command stubs * aks: add parameters * aks: reimport model * aks: implement add exception sub command * aks: implement remove pod identity exception sub command * aks: implement update pod identity exception sub command * aks: implement list pod identity exception sub command * aks: implement delete pod identity sub command * aks: implement permission granting * aks: compares ignore case * aks: add command helps * aks: add help for update exception * aks: add `--enable-pod-identity` option for `aks create` * aks: sleep more to wait propagation * aks: wait 30s * aks: implement user assigned MI support * aks: typo * aks: remove pod identity from `enable-addons` sub command * aks: enable/disable pod identity with `update` * aks: update help message * aks: fix style * aks: style * aks: style * aks: add test for pod identity resource validator * aks: fix flag check * aks: add pod identity exception test * aks: add pod identity test * aks: remove TODO validations * aks: fix style * aks: fix help command * aks: format * aks: split pod identity add command test * aks: add preview annotation * aks: fix merge error * aks: move to `aks pod-identity exception`
1 parent 3e711c4 commit 837fd53

File tree

10 files changed

+4782
-14
lines changed

10 files changed

+4782
-14
lines changed

src/aks-preview/azext_aks_preview/_format.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,39 @@ def _func_set_preview(self, version): # pylint: disable=no-self-use
140140
except(TypeError, ValueError):
141141
return version
142142

143+
@functions.signature({'types': ['object']})
144+
def _func_pprint_labels(self, labels): # pylint: disable=no-self-use
145+
"""Custom JMESPath `pprint_labels` function that pretty print labels"""
146+
if not labels:
147+
return ''
148+
return ' '.join([
149+
'{}={}'.format(k, labels[k])
150+
for k in sorted(labels.keys())
151+
])
152+
143153
return CustomFunctions()
154+
155+
156+
def aks_pod_identity_exceptions_table_format(result):
157+
"""Format pod identity exceptions results as a summary for display with "-o table"."""
158+
preview = {}
159+
parsed = compile_jmes("""podIdentityProfile.userAssignedIdentityExceptions[].{
160+
name: name,
161+
namespace: namespace,
162+
PodLabels: podLabels | pprint_labels(@)
163+
}""")
164+
# use ordered dicts so headers are predictable
165+
return parsed.search(result, Options(dict_cls=OrderedDict, custom_functions=_custom_functions(preview)))
166+
167+
168+
def aks_pod_identities_table_format(result):
169+
"""Format pod identities results as a summary for display with "-o table"."""
170+
preview = {}
171+
parsed = compile_jmes("""podIdentityProfile.userAssignedIdentities[].{
172+
name: name,
173+
namespace: namespace,
174+
provisioningState: provisioningState
175+
identity: identity.resourceId
176+
}""")
177+
# use ordered dicts so headers are predictable
178+
return parsed.search(result, Options(dict_cls=OrderedDict, custom_functions=_custom_functions(preview)))

src/aks-preview/azext_aks_preview/_help.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@
291291
- name: --linux-os-config
292292
type: string
293293
short-summary: OS configurations for Linux agent nodes.
294+
- name: --enable-pod-identity
295+
type: bool
296+
short-summary: (PREVIEW) Enable pod identity addon.
294297
examples:
295298
- name: Create a Kubernetes cluster with an existing SSH public key.
296299
text: az aks create -g MyResourceGroup -n MyManagedCluster --ssh-key-value /path/to/publickey
@@ -443,6 +446,12 @@
443446
- name: --assign-identity
444447
type: string
445448
short-summary: (PREVIEW) Specify an existing user assigned identity to manage cluster resource group.
449+
- name: --enable-pod-identity
450+
type: bool
451+
short-summary: (PREVIEW) Enable Pod Identity addon for cluster.
452+
- name: --disable-pod-identity
453+
type: bool
454+
short-summary: (PREVIEW) Disable Pod Identity addon for cluster.
446455
examples:
447456
- name: Enable cluster-autoscaler within node count range [1,5]
448457
text: az aks update --enable-cluster-autoscaler --min-count 1 --max-count 5 -g MyResourceGroup -n MyManagedCluster
@@ -480,6 +489,10 @@
480489
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-managed-identity
481490
- name: Update the cluster to use user assigned managed identity in control plane.
482491
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-managed-identity --assign-identity <user_assigned_identity_resource_id>
492+
- name: Enable pod identity addon.
493+
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-pod-identity
494+
- name: Disable pod identity addon.
495+
text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-pod-identity
483496
"""
484497

485498
helps['aks kollect'] = """
@@ -537,7 +550,7 @@
537550

538551
helps['aks nodepool'] = """
539552
type: group
540-
short-summary: Commands to manage node pools in Kubernetes kubernetes cluster.
553+
short-summary: Commands to manage node pools in managed Kubernetes cluster.
541554
"""
542555
helps['aks nodepool show'] = """
543556
type: command
@@ -809,3 +822,51 @@
809822
short-summary: Rotate certificates and keys on a managed Kubernetes cluster
810823
long-summary: Kubernetes will be unavailable during cluster certificate rotation.
811824
"""
825+
826+
helps['aks pod-identity'] = """
827+
type: group
828+
short-summary: Commands to manage pod identities in managed Kubernetes cluster.
829+
"""
830+
831+
helps['aks pod-identity add'] = """
832+
type: command
833+
short-summary: Add a pod identity to a managed Kubernetes cluster
834+
examples:
835+
- name: Add pod identity
836+
text: az aks pod-identity add --cluster-name MyManagedCluster --resource-group MyResourceGroup --namespace my-namespace --name my-identity --identity-resource-id <my-identity-resource-id>
837+
"""
838+
839+
helps['aks pod-identity delete'] = """
840+
type: command
841+
short-summary: Remove a pod identity from a managed Kubernetes cluster
842+
"""
843+
844+
helps['aks pod-identity list'] = """
845+
type: command
846+
short-summary: List pod identities in a managed Kubernetes cluster
847+
"""
848+
849+
helps['aks pod-identity exception'] = """
850+
type: group
851+
short-summary: Commands to manage pod identity exceptions in managed Kubernetes cluster.
852+
"""
853+
854+
helps['aks pod-identity exception add'] = """
855+
type: command
856+
short-summary: Add a pod identity exception to a managed Kubernetes cluster
857+
"""
858+
859+
helps['aks pod-identity exception delete'] = """
860+
type: command
861+
short-summary: Remove a pod identity exception from a managed Kubernetes cluster
862+
"""
863+
864+
helps['aks pod-identity exception update'] = """
865+
type: command
866+
short-summary: Update a pod identity exception in a managed Kubernetes cluster
867+
"""
868+
869+
helps['aks pod-identity exception list'] = """
870+
type: command
871+
short-summary: List pod identity exceptions in a managed Kubernetes cluster
872+
"""

src/aks-preview/azext_aks_preview/_params.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
validate_load_balancer_outbound_ips, validate_load_balancer_outbound_ip_prefixes,
2323
validate_taints, validate_priority, validate_eviction_policy, validate_spot_max_price, validate_acr, validate_user,
2424
validate_load_balancer_outbound_ports, validate_load_balancer_idle_timeout, validate_nodepool_tags,
25-
validate_nodepool_labels, validate_vnet_subnet_id, validate_pod_subnet_id, validate_max_surge, validate_assign_identity, validate_addons)
25+
validate_nodepool_labels, validate_vnet_subnet_id, validate_pod_subnet_id, validate_max_surge, validate_assign_identity, validate_addons,
26+
validate_pod_identity_pod_labels, validate_pod_identity_resource_name, validate_pod_identity_resource_namespace)
2627
from ._consts import CONST_OUTBOUND_TYPE_LOAD_BALANCER, \
2728
CONST_OUTBOUND_TYPE_USER_DEFINED_ROUTING, CONST_SCALE_SET_PRIORITY_REGULAR, CONST_SCALE_SET_PRIORITY_SPOT, \
2829
CONST_SPOT_EVICTION_POLICY_DELETE, CONST_SPOT_EVICTION_POLICY_DEALLOCATE, \
@@ -115,6 +116,7 @@ def load_arguments(self, _):
115116
c.argument('auto_upgrade_channel', arg_type=get_enum_type([CONST_RAPID_UPGRADE_CHANNEL, CONST_STABLE_UPGRADE_CHANNEL, CONST_PATCH_UPGRADE_CHANNEL, CONST_NONE_UPGRADE_CHANNEL]))
116117
c.argument('kubelet_config', type=str)
117118
c.argument('linux_os_config', type=str)
119+
c.argument('enable_pod_identity', action='store_true')
118120

119121
with self.argument_context('aks update') as c:
120122
c.argument('enable_cluster_autoscaler', options_list=["--enable-cluster-autoscaler", "-e"], action='store_true')
@@ -138,6 +140,8 @@ def load_arguments(self, _):
138140
c.argument('auto_upgrade_channel', arg_type=get_enum_type([CONST_RAPID_UPGRADE_CHANNEL, CONST_STABLE_UPGRADE_CHANNEL, CONST_PATCH_UPGRADE_CHANNEL, CONST_NONE_UPGRADE_CHANNEL]))
139141
c.argument('enable_managed_identity', action='store_true')
140142
c.argument('assign_identity', type=str, validator=validate_assign_identity)
143+
c.argument('enable_pod_identity', action='store_true')
144+
c.argument('disable_pod_identity', action='store_true')
141145
c.argument('yes', options_list=['--yes', '-y'], help='Do not prompt for confirmation.', action='store_true')
142146

143147
with self.argument_context('aks scale') as c:
@@ -208,6 +212,52 @@ def load_arguments(self, _):
208212
c.argument('path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(),
209213
default=os.path.join(os.path.expanduser('~'), '.kube', 'config'))
210214

215+
with self.argument_context('aks pod-identity') as c:
216+
c.argument('cluster_name', type=str, help='The cluster name.')
217+
218+
with self.argument_context('aks pod-identity add') as c:
219+
c.argument('identity_name', type=str, options_list=['--name', '-n'], default=None, required=False,
220+
help='The pod identity name. Generate if not specified.',
221+
validator=validate_pod_identity_resource_name('identity_name', required=False))
222+
c.argument('identity_namespace', type=str, options_list=['--namespace'], help='The pod identity namespace.')
223+
c.argument('identity_resource_id', type=str, options_list=['--identity-resource-id'], help='Resource id of the identity to use.')
224+
225+
with self.argument_context('aks pod-identity delete') as c:
226+
c.argument('identity_name', type=str, options_list=['--name', '-n'], default=None, required=True,
227+
help='The pod identity name.',
228+
validator=validate_pod_identity_resource_name('identity_name', required=True))
229+
c.argument('identity_namespace', type=str, options_list=['--namespace'], help='The pod identity namespace.')
230+
231+
with self.argument_context('aks pod-identity exception add') as c:
232+
c.argument('exc_name', type=str, options_list=['--name', '-n'], default=None, required=False,
233+
help='The pod identity exception name. Generate if not specified.',
234+
validator=validate_pod_identity_resource_name('exc_name', required=False))
235+
c.argument('exc_namespace', type=str, options_list=['--namespace'], required=True,
236+
help='The pod identity exception namespace.',
237+
validator=validate_pod_identity_resource_namespace)
238+
c.argument('pod_labels', nargs='*', required=True,
239+
help='space-separated labels: key=value [key=value ...].',
240+
validator=validate_pod_identity_pod_labels)
241+
242+
with self.argument_context('aks pod-identity exception delete') as c:
243+
c.argument('exc_name', type=str, options_list=['--name', '-n'], required=True,
244+
help='The pod identity exception name to remove.',
245+
validator=validate_pod_identity_resource_name('exc_name', required=True))
246+
c.argument('exc_namespace', type=str, options_list=['--namespace'], required=True,
247+
help='The pod identity exception namespace to remove.',
248+
validator=validate_pod_identity_resource_namespace)
249+
250+
with self.argument_context('aks pod-identity exception update') as c:
251+
c.argument('exc_name', type=str, options_list=['--name', '-n'], required=True,
252+
help='The pod identity exception name to remove.',
253+
validator=validate_pod_identity_resource_name('exc_name', required=True))
254+
c.argument('exc_namespace', type=str, options_list=['--namespace'], required=True,
255+
help='The pod identity exception namespace to remove.',
256+
validator=validate_pod_identity_resource_namespace)
257+
c.argument('pod_labels', nargs='*', required=True,
258+
help='pod labels in key=value [key=value ...].',
259+
validator=validate_pod_identity_pod_labels)
260+
211261

212262
def _get_default_install_location(exe_name):
213263
system = platform.system()

src/aks-preview/azext_aks_preview/_validators.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,54 @@ def validate_addons(namespace):
418418

419419
raise CLIError(
420420
f"The addon \"{addon_arg}\" is not a recognized addon option. Did you mean {matches}? Possible options: {all_addons}") # pylint:disable=line-too-long
421+
422+
423+
def validate_pod_identity_pod_labels(namespace):
424+
if not hasattr(namespace, 'pod_labels'):
425+
return
426+
labels = namespace.pod_labels
427+
428+
if labels is None:
429+
# no specify any labels
430+
namespace.pod_labels = {}
431+
return
432+
433+
if isinstance(labels, list):
434+
labels_dict = {}
435+
for item in labels:
436+
labels_dict.update(validate_label(item))
437+
after_validation_labels = labels_dict
438+
else:
439+
after_validation_labels = validate_label(labels)
440+
441+
namespace.pod_labels = after_validation_labels
442+
443+
444+
def validate_pod_identity_resource_name(attr_name, required):
445+
"Validate custom resource name for pod identity addon."
446+
447+
def validator(namespace):
448+
if not hasattr(namespace, attr_name):
449+
return
450+
451+
attr_value = getattr(namespace, attr_name)
452+
if not attr_value:
453+
if required:
454+
raise CLIError('--name is required')
455+
# set empty string for the resource name
456+
attr_value = ''
457+
458+
setattr(namespace, attr_name, attr_value)
459+
460+
return validator
461+
462+
463+
def validate_pod_identity_resource_namespace(namespace):
464+
"Validate custom resource name for pod identity addon."
465+
if not hasattr(namespace, 'namespace'):
466+
return
467+
468+
namespace_value = namespace.namespace
469+
if not namespace_value:
470+
# namespace cannot be empty
471+
raise CLIError('--namespace is required')

src/aks-preview/azext_aks_preview/commands.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from ._format import aks_agentpool_list_table_format
1414
from ._format import aks_versions_table_format
1515
from ._format import aks_upgrades_table_format
16+
from ._format import aks_pod_identities_table_format
17+
from ._format import aks_pod_identity_exceptions_table_format
1618

1719

1820
def load_command_table(self, _):
@@ -70,3 +72,18 @@ def load_command_table(self, _):
7072
g.custom_command('update', 'aks_agentpool_update', supports_no_wait=True)
7173
g.custom_command('delete', 'aks_agentpool_delete', supports_no_wait=True)
7274
g.custom_command('get-upgrades', 'aks_agentpool_get_upgrade_profile')
75+
76+
# AKS pod identity commands
77+
with self.command_group('aks pod-identity', managed_clusters_sdk, client_factory=cf_managed_clusters) as g:
78+
g.custom_command('add', 'aks_pod_identity_add')
79+
g.custom_command('delete', 'aks_pod_identity_delete')
80+
g.custom_command('list', 'aks_pod_identity_list',
81+
table_transformer=aks_pod_identities_table_format)
82+
83+
# AKS pod identity exception commands
84+
with self.command_group('aks pod-identity exception', managed_clusters_sdk, client_factory=cf_managed_clusters) as g:
85+
g.custom_command('add', 'aks_pod_identity_exception_add')
86+
g.custom_command('delete', 'aks_pod_identity_exception_delete')
87+
g.custom_command('update', 'aks_pod_identity_exception_update')
88+
g.custom_command('list', 'aks_pod_identity_exception_list',
89+
table_transformer=aks_pod_identity_exceptions_table_format)

0 commit comments

Comments
 (0)