From 5bb3f977b17e698167c9b3377a9b726079c4b376 Mon Sep 17 00:00:00 2001 From: Gavin Shaw Date: Tue, 19 Dec 2023 18:28:31 -0500 Subject: [PATCH 1/3] Add support for managed identity to be specified during terraform apply for aks clusters Signed-off-by: Gavin Shaw --- internal/models/akscluster/cluster_config.go | 3 + .../akscluster/managed_identity_config.go | 38 +++++++++++ .../akscluster/managed_identity_type.go | 50 ++++++++++++++ .../user_assigned_identity_config.go | 37 ++++++++++ .../resources/akscluster/akscluster_mapper.go | 67 +++++++++++++++++++ internal/resources/akscluster/constants.go | 2 + internal/resources/akscluster/helpers_test.go | 1 + internal/resources/akscluster/schema.go | 39 +++++++++++ 8 files changed, 237 insertions(+) create mode 100644 internal/models/akscluster/managed_identity_config.go create mode 100644 internal/models/akscluster/managed_identity_type.go create mode 100644 internal/models/akscluster/user_assigned_identity_config.go diff --git a/internal/models/akscluster/cluster_config.go b/internal/models/akscluster/cluster_config.go index 9574eca70..579f0235e 100644 --- a/internal/models/akscluster/cluster_config.go +++ b/internal/models/akscluster/cluster_config.go @@ -50,6 +50,9 @@ type VmwareTanzuManageV1alpha1AksclusterClusterConfig struct { // The metadata to apply to the cluster to assist with categorization and organization. Tags map[string]string `json:"tags,omitempty"` + // The managed identity to apply to the cluster. + IdentityConfig *VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig `json:"identityConfig,omitempty"` + // Kubernetes version of the cluster. Version string `json:"version,omitempty"` } diff --git a/internal/models/akscluster/managed_identity_config.go b/internal/models/akscluster/managed_identity_config.go new file mode 100644 index 000000000..c49600c47 --- /dev/null +++ b/internal/models/akscluster/managed_identity_config.go @@ -0,0 +1,38 @@ +/* +Copyright 2023 VMware, Inc. All Rights Reserved. +SPDX-License-Identifier: MPL-2.0 +*/ + +package models + +import "github.com/go-openapi/swag" + +// VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig The managed identity config. +// +// swagger:model vmware.tanzu.manage.v1alpha1.akscluster.ManagedIdentityConfig +type VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig struct { + Type *VmwareTanzuManageV1alpha1AksclusterManagedIdentityType `json:"type,omitempty"` + + UserAssignedIdentityType *VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig `json:"userAssigned,omitempty"` +} + +// MarshalBinary interface implementation. +func (m *VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation. +func (m *VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig) UnmarshalBinary(b []byte) error { + var res VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + + *m = res + + return nil +} diff --git a/internal/models/akscluster/managed_identity_type.go b/internal/models/akscluster/managed_identity_type.go new file mode 100644 index 000000000..6dbecea8d --- /dev/null +++ b/internal/models/akscluster/managed_identity_type.go @@ -0,0 +1,50 @@ +/* +Copyright 2023 VMware, Inc. All Rights Reserved. +SPDX-License-Identifier: MPL-2.0 +*/ + +package models + +import ( + "encoding/json" +) + +// VmwareTanzuManageV1alpha1AksclusterManagedIdentityType Managed identity type options of identity config. +// +// - IDENTITY_TYPE_SYSTEM_ASSIGNED: Indicates that a system assigned managed identity should be used by the cluster. +// - IDENTITY_TYPE_USER_ASSIGNED: Indicates that a user assigned managed identity should be used by the cluster. +// +// swagger:model vmware.tanzu.manage.v1alpha1.akscluster.ManagedIdentityType +type VmwareTanzuManageV1alpha1AksclusterManagedIdentityType string + +func NewVmwareTanzuManageV1alpha1AksclusterManagedIdentityType(value VmwareTanzuManageV1alpha1AksclusterManagedIdentityType) *VmwareTanzuManageV1alpha1AksclusterManagedIdentityType { + return &value +} + +// Pointer returns a pointer to a freshly-allocated VmwareTanzuManageV1alpha1AksclusterManagedIdentityType. +func (m VmwareTanzuManageV1alpha1AksclusterManagedIdentityType) Pointer() *VmwareTanzuManageV1alpha1AksclusterManagedIdentityType { + return &m +} + +const ( + + // VmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeSYSTEMASSIGNED captures enum value "IDENTITY_TYPE_SYSTEM_ASSIGNED". + VmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeSYSTEMASSIGNED VmwareTanzuManageV1alpha1AksclusterManagedIdentityType = "IDENTITY_TYPE_SYSTEM_ASSIGNED" + + // VmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeUSERASSIGNED captures enum value "IDENTITY_TYPE_USER_ASSIGNED". + VmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeUSERASSIGNED VmwareTanzuManageV1alpha1AksclusterManagedIdentityType = "IDENTITY_TYPE_USER_ASSIGNED" +) + +// for schema. +var vmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeEnum []interface{} + +func init() { + var res []VmwareTanzuManageV1alpha1AksclusterManagedIdentityType + if err := json.Unmarshal([]byte(`["IDENTITY_TYPE_SYSTEM_ASSIGNED","IDENTITY_TYPE_USER_ASSIGNED"]`), &res); err != nil { + panic(err) + } + + for _, v := range res { + vmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeEnum = append(vmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeEnum, v) + } +} diff --git a/internal/models/akscluster/user_assigned_identity_config.go b/internal/models/akscluster/user_assigned_identity_config.go new file mode 100644 index 000000000..1263a9bfa --- /dev/null +++ b/internal/models/akscluster/user_assigned_identity_config.go @@ -0,0 +1,37 @@ +/* +Copyright 2023 VMware, Inc. All Rights Reserved. +SPDX-License-Identifier: MPL-2.0 +*/ + +package models + +import "github.com/go-openapi/swag" + +// VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig The managed identity config. +// +// swagger:model vmware.tanzu.manage.v1alpha1.akscluster.UserAssignedIdentityTypeConfig + +type VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig struct { + ManagedResourceID string `json:"resourceId,omitempty"` +} + +// MarshalBinary interface implementation. +func (m *VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation. +func (m *VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig) UnmarshalBinary(b []byte) error { + var res VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + + *m = res + + return nil +} diff --git a/internal/resources/akscluster/akscluster_mapper.go b/internal/resources/akscluster/akscluster_mapper.go index ac06123f4..089e61c34 100644 --- a/internal/resources/akscluster/akscluster_mapper.go +++ b/internal/resources/akscluster/akscluster_mapper.go @@ -140,6 +140,11 @@ func constructConfig(data []any) *models.VmwareTanzuManageV1alpha1AksclusterClus helper.SetPrimitiveValue(v, &config.NodeResourceGroupName, nodeResourceGroupNameKey) } + if v, ok := configData[identityConfigKey]; ok { + data, _ := v.([]any) + config.IdentityConfig = constructManagedIdentityConfig(data) + } + return config } @@ -433,6 +438,44 @@ func constructAutoUpgradeConfig(data []any) *models.VmwareTanzuManageV1alpha1Aks return autoUpgradeConfig } +func constructManagedIdentityConfig(data []any) *models.VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig { + if len(data) < 1 { + return nil + } + + // ManagedIdentityConfig schema defines max 1 + managedIdentityConfigData, _ := data[0].(map[string]any) + managedIdentityConfig := &models.VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig{} + + if v, ok := managedIdentityConfigData[typeKey]; ok { + identityType := models.VmwareTanzuManageV1alpha1AksclusterManagedIdentityType(v.(string)) + managedIdentityConfig.Type = &identityType + } + + if v, ok := managedIdentityConfigData[userAssignedKey]; ok { + data, _ := v.([]any) + managedIdentityConfig.UserAssignedIdentityType = constructUserAssignedIdentityConfig(data) + } + + return managedIdentityConfig +} + +func constructUserAssignedIdentityConfig(data []any) *models.VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig { + if len(data) < 1 { + return nil + } + + // UserAssignedIdentityConfig schema defines max 1 + userAssignedIdentityConfigData, _ := data[0].(map[string]any) + userAssignedIdentityConfig := &models.VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig{} + + if v, ok := userAssignedIdentityConfigData[resourceIDKey]; ok { + helper.SetPrimitiveValue(v, &userAssignedIdentityConfig.ManagedResourceID, resourceIDKey) + } + + return userAssignedIdentityConfig +} + func ToAKSClusterMap(cluster *models.VmwareTanzuManageV1alpha1AksCluster, nodepools []*models.VmwareTanzuManageV1alpha1AksclusterNodepoolNodepool) any { if cluster == nil { return []any{} @@ -483,6 +526,7 @@ func toConfigMap(config *models.VmwareTanzuManageV1alpha1AksclusterClusterConfig data[storageConfigKey] = toStorageConfigMap(config.StorageConfig) data[addonsConfigKey] = toAddonConfigMap(config.AddonsConfig) data[autoUpgradeConfigKey] = toAutoUpgradeConfigMap(config.AutoUpgradeConfig) + data[identityConfigKey] = toManagedIdentityConfigMap(config.IdentityConfig) return []any{data} } @@ -642,6 +686,29 @@ func toAutoUpgradeConfigMap(config *models.VmwareTanzuManageV1alpha1AksclusterAu return []any{data} } +func toManagedIdentityConfigMap(config *models.VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig) []any { + if config == nil { + return []any{} + } + + data := make(map[string]any) + data[typeKey] = helper.PtrString(config.Type) + data[userAssignedKey] = toUserAssignedIdentityTypeConfigMap(config.UserAssignedIdentityType) + + return []any{data} +} + +func toUserAssignedIdentityTypeConfigMap(config *models.VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig) []any { + if config == nil { + return []any{} + } + + data := make(map[string]any) + data[resourceIDKey] = config.ManagedResourceID + + return []any{data} +} + func toNodePoolList(nodepools []*models.VmwareTanzuManageV1alpha1AksclusterNodepoolNodepool) []any { n := make([]any, 0, len(nodepools)) for _, v := range nodepools { diff --git a/internal/resources/akscluster/constants.go b/internal/resources/akscluster/constants.go index 6a8e262cd..5c8aab4b1 100644 --- a/internal/resources/akscluster/constants.go +++ b/internal/resources/akscluster/constants.go @@ -106,4 +106,6 @@ const ( upgradeConfigKey = "upgrade_config" maxSurgeKey = "max_surge" kubeconfigKey = "kubeconfig" + identityConfigKey = "identity_config" + userAssignedKey = "user_assigned" ) diff --git a/internal/resources/akscluster/helpers_test.go b/internal/resources/akscluster/helpers_test.go index bb517db2a..15422d635 100644 --- a/internal/resources/akscluster/helpers_test.go +++ b/internal/resources/akscluster/helpers_test.go @@ -372,6 +372,7 @@ func aTestClusterDataMap(w ...mapWither) map[string]any { "auto_upgrade_config": []any{map[string]any{ "upgrade_channel": "STABLE", }}, + "identity_config": []any{}, }}, "nodepool": []any{ aTestNodepoolDataMap(), diff --git a/internal/resources/akscluster/schema.go b/internal/resources/akscluster/schema.go index 75d92574d..0b4711a01 100644 --- a/internal/resources/akscluster/schema.go +++ b/internal/resources/akscluster/schema.go @@ -205,6 +205,13 @@ var ClusterConfig = &schema.Resource{ MaxItems: 1, Elem: AutoUpgradeConfig, }, + identityConfigKey: { + Type: schema.TypeList, + Description: "Managed Identity Config", + Optional: true, + MaxItems: 1, + Elem: ManagedIdentityConfig, + }, }, } @@ -514,6 +521,38 @@ var AutoUpgradeConfig = &schema.Resource{ }, } +var ManagedIdentityConfig = &schema.Resource{ + Schema: map[string]*schema.Schema{ + typeKey: { + Type: schema.TypeString, + Description: "Type of managed identity used by the cluster (default IDENTITY_TYPE_SYSTEM_ASSIGNED). Allowed values include: IDENTITY_TYPE_SYSTEM_ASSIGNED or IDENTITY_TYPE_USER_ASSIGNED", + Optional: true, + Default: "IDENTITY_TYPE_SYSTEM_ASSIGNED", + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{ + string(aksmodel.VmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeSYSTEMASSIGNED), + string(aksmodel.VmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeUSERASSIGNED), + }, false)), + }, + userAssignedKey: { + Type: schema.TypeList, + Description: "User Assigned Managed Identity Config", + Optional: true, + MaxItems: 1, + Elem: UserAssignedManagedIdentityConfig, + }, + }, +} + +var UserAssignedManagedIdentityConfig = &schema.Resource{ + Schema: map[string]*schema.Schema{ + resourceIDKey: { + Type: schema.TypeString, + Description: "The ARM resource ID of user assigned identity in the form: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}'", + Required: true, + }, + }, +} + // NodepoolConfig defines the info and nodepool spec for AKS clusters. // // Note: ForceNew is not used in any of the elements because this is a part of From 79e4449a10e5ea73f63bddebfbc63edf2da63db4 Mon Sep 17 00:00:00 2001 From: Gavin Shaw Date: Tue, 19 Dec 2023 18:41:40 -0500 Subject: [PATCH 2/3] Update aks cluster docs Signed-off-by: Gavin Shaw --- docs/data-sources/akscluster.md | 18 ++++++++++++++++++ docs/resources/akscluster.md | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/docs/data-sources/akscluster.md b/docs/data-sources/akscluster.md index 45d33bfaa..f61e9861e 100644 --- a/docs/data-sources/akscluster.md +++ b/docs/data-sources/akscluster.md @@ -109,6 +109,7 @@ Optional: - `api_server_access_config` (Block List, Max: 1) API Server Access Config (see [below for nested schema](#nestedblock--spec--config--api_server_access_config)) - `auto_upgrade_config` (Block List, Max: 1) Auto Upgrade Config (see [below for nested schema](#nestedblock--spec--config--auto_upgrade_config)) - `disk_encryption_set` (String) Resource ID of the disk encryption set to use for enabling +- `identity_config` (Block List, Max: 1) Managed Identity Config (see [below for nested schema](#nestedblock--spec--config--identity_config)) - `linux_config` (Block List, Max: 1) Linux Config (see [below for nested schema](#nestedblock--spec--config--linux_config)) - `node_resource_group_name` (String) Name of the resource group containing nodepools. - `sku` (Block List, Max: 1) Azure Kubernetes Service SKU (see [below for nested schema](#nestedblock--spec--config--sku)) @@ -212,6 +213,23 @@ Optional: - `upgrade_channel` (String) Upgrade Channel. Allowed values include: NONE, PATCH, STABLE, RAPID or NODE_IMAGE + +### Nested Schema for `spec.config.identity_config` + +Optional: + +- `type` (String) Type of managed identity used by the cluster (default IDENTITY_TYPE_SYSTEM_ASSIGNED). Allowed values include: IDENTITY_TYPE_SYSTEM_ASSIGNED or IDENTITY_TYPE_USER_ASSIGNED +- `user_assigned` (Block List, Max: 1) User Assigned Managed Identity Config (see [below for nested schema](#nestedblock--spec--config--identity_config--user_assigned)) + + +### Nested Schema for `spec.config.identity_config.user_assigned` + +Required: + +- `resource_id` (String) The ARM resource ID of user assigned identity in the form: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}' + + + ### Nested Schema for `spec.config.linux_config` diff --git a/docs/resources/akscluster.md b/docs/resources/akscluster.md index 0a62d89c9..02da8c205 100644 --- a/docs/resources/akscluster.md +++ b/docs/resources/akscluster.md @@ -130,6 +130,7 @@ Optional: - `api_server_access_config` (Block List, Max: 1) API Server Access Config (see [below for nested schema](#nestedblock--spec--config--api_server_access_config)) - `auto_upgrade_config` (Block List, Max: 1) Auto Upgrade Config (see [below for nested schema](#nestedblock--spec--config--auto_upgrade_config)) - `disk_encryption_set` (String) Resource ID of the disk encryption set to use for enabling +- `identity_config` (Block List, Max: 1) Managed Identity Config (see [below for nested schema](#nestedblock--spec--config--identity_config)) - `linux_config` (Block List, Max: 1) Linux Config (see [below for nested schema](#nestedblock--spec--config--linux_config)) - `node_resource_group_name` (String) Name of the resource group containing nodepools. - `sku` (Block List, Max: 1) Azure Kubernetes Service SKU (see [below for nested schema](#nestedblock--spec--config--sku)) @@ -233,6 +234,23 @@ Optional: - `upgrade_channel` (String) Upgrade Channel. Allowed values include: NONE, PATCH, STABLE, RAPID or NODE_IMAGE + +### Nested Schema for `spec.config.identity_config` + +Optional: + +- `type` (String) Type of managed identity used by the cluster (default IDENTITY_TYPE_SYSTEM_ASSIGNED). Allowed values include: IDENTITY_TYPE_SYSTEM_ASSIGNED or IDENTITY_TYPE_USER_ASSIGNED +- `user_assigned` (Block List, Max: 1) User Assigned Managed Identity Config (see [below for nested schema](#nestedblock--spec--config--identity_config--user_assigned)) + + +### Nested Schema for `spec.config.identity_config.user_assigned` + +Required: + +- `resource_id` (String) The ARM resource ID of user assigned identity in the form: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}' + + + ### Nested Schema for `spec.config.linux_config` From 92c4692eec1b053cf73878a1f2984d3977dd8379 Mon Sep 17 00:00:00 2001 From: Gavin Shaw Date: Thu, 4 Jan 2024 14:37:21 -0500 Subject: [PATCH 3/3] Improve test coverage of managed identities Signed-off-by: Gavin Shaw --- internal/resources/akscluster/helpers_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/internal/resources/akscluster/helpers_test.go b/internal/resources/akscluster/helpers_test.go index 15422d635..fe59ca9de 100644 --- a/internal/resources/akscluster/helpers_test.go +++ b/internal/resources/akscluster/helpers_test.go @@ -159,6 +159,12 @@ func aTestCluster(w ...clusterWither) *models.VmwareTanzuManageV1alpha1AksCluste AutoUpgradeConfig: &models.VmwareTanzuManageV1alpha1AksclusterAutoUpgradeConfig{ Channel: models.VmwareTanzuManageV1alpha1AksclusterChannelSTABLE.Pointer(), }, + IdentityConfig: &models.VmwareTanzuManageV1alpha1AksclusterManagedIdentityConfig{ + Type: models.VmwareTanzuManageV1alpha1AksclusterManagedIdentityTypeUSERASSIGNED.Pointer(), + UserAssignedIdentityType: &models.VmwareTanzuManageV1alpha1AksclusterUserAssignedIdentityTypeConfig{ + ManagedResourceID: "resource-id-for-a-user-assigned-managed-identity", + }, + }, }, ProxyName: "my-proxy", AgentName: "my-agent-name", @@ -372,7 +378,12 @@ func aTestClusterDataMap(w ...mapWither) map[string]any { "auto_upgrade_config": []any{map[string]any{ "upgrade_channel": "STABLE", }}, - "identity_config": []any{}, + "identity_config": []any{map[string]any{ + "type": "IDENTITY_TYPE_USER_ASSIGNED", + "user_assigned": []any{map[string]any{ + "resource_id": "resource-id-for-a-user-assigned-managed-identity", + }}, + }}, }}, "nodepool": []any{ aTestNodepoolDataMap(),