diff --git a/azure/services/agentpools/agentpools.go b/azure/services/agentpools/agentpools.go index 986ba8b3b60..cf106f1143f 100644 --- a/azure/services/agentpools/agentpools.go +++ b/azure/services/agentpools/agentpools.go @@ -32,15 +32,16 @@ import ( // Spec contains properties to create a agent pool. type Spec struct { - Name string - ResourceGroup string - Cluster string - Version *string - SKU string - Replicas int32 - OSDiskSizeGB int32 - VnetSubnetID string - Mode string + Name string + ResourceGroup string + Cluster string + Version *string + SKU string + Replicas int32 + OSDiskSizeGB int32 + VnetSubnetID string + Mode string + AvailabilityZones *[]string } // Reconcile idempotently creates or updates a agent pool, if possible. @@ -63,6 +64,7 @@ func (s *Service) Reconcile(ctx context.Context, spec interface{}) error { OrchestratorVersion: agentPoolSpec.Version, VnetSubnetID: &agentPoolSpec.VnetSubnetID, Mode: containerservice.AgentPoolMode(agentPoolSpec.Mode), + AvailabilityZones: agentPoolSpec.AvailabilityZones, }, } @@ -107,7 +109,7 @@ func (s *Service) Reconcile(ctx context.Context, spec interface{}) error { } // Diff and check if we require an update - diff := cmp.Diff(existingProfile, normalizedProfile) + diff := cmp.Diff(normalizedProfile, existingProfile) if diff != "" { klog.V(2).Infof("Update required (+new -old):\n%s", diff) err = s.Client.CreateOrUpdate(ctx, agentPoolSpec.ResourceGroup, agentPoolSpec.Cluster, agentPoolSpec.Name, profile) diff --git a/azure/services/managedclusters/managedclusters.go b/azure/services/managedclusters/managedclusters.go index 2d4fb27827f..1e38f3fb137 100644 --- a/azure/services/managedclusters/managedclusters.go +++ b/azure/services/managedclusters/managedclusters.go @@ -154,13 +154,14 @@ func (s *Service) Reconcile(ctx context.Context) error { for i := range managedClusterSpec.AgentPools { pool := managedClusterSpec.AgentPools[i] profile := containerservice.ManagedClusterAgentPoolProfile{ - Name: &pool.Name, - VMSize: &pool.SKU, - OsDiskSizeGB: &pool.OSDiskSizeGB, - Count: &pool.Replicas, - Type: containerservice.AgentPoolTypeVirtualMachineScaleSets, - VnetSubnetID: &managedClusterSpec.VnetSubnetID, - Mode: containerservice.AgentPoolModeSystem, + Name: &pool.Name, + VMSize: &pool.SKU, + OsDiskSizeGB: &pool.OSDiskSizeGB, + Count: &pool.Replicas, + Type: containerservice.AgentPoolTypeVirtualMachineScaleSets, + VnetSubnetID: &managedClusterSpec.VnetSubnetID, + Mode: containerservice.AgentPoolModeSystem, + AvailabilityZones: &pool.AvailabilityZones, } *managedCluster.AgentPoolProfiles = append(*managedCluster.AgentPoolProfiles, profile) } diff --git a/azure/types.go b/azure/types.go index 4920bb21c2c..f846fe8ce32 100644 --- a/azure/types.go +++ b/azure/types.go @@ -358,8 +358,9 @@ type ManagedClusterSpec struct { // AgentPoolSpec contains agent pool specification details. type AgentPoolSpec struct { - Name string - SKU string - Replicas int32 - OSDiskSizeGB int32 + Name string + SKU string + Replicas int32 + OSDiskSizeGB int32 + AvailabilityZones []string } diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedmachinepools.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedmachinepools.yaml index 8c9ab2c64cf..6e338f8cc02 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedmachinepools.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedmachinepools.yaml @@ -42,6 +42,12 @@ spec: description: AzureManagedMachinePoolSpec defines the desired state of AzureManagedMachinePool. properties: + availabilityZones: + description: AvailabilityZones - Availability zones for nodes. Must + use VirtualMachineScaleSets AgentPoolType. + items: + type: string + type: array mode: description: 'Mode - represents mode of an agent pool. Possible values include: System, User.' @@ -117,6 +123,12 @@ spec: description: AzureManagedMachinePoolSpec defines the desired state of AzureManagedMachinePool. properties: + availabilityZones: + description: AvailabilityZones - Availability zones for nodes. Must + use VirtualMachineScaleSets AgentPoolType. + items: + type: string + type: array mode: description: 'Mode - represents mode of an agent pool. Possible values include: System, User.' diff --git a/exp/api/v1alpha3/azuremanagedmachinepool_types.go b/exp/api/v1alpha3/azuremanagedmachinepool_types.go index cfdd986a31f..9df1ae46b4c 100644 --- a/exp/api/v1alpha3/azuremanagedmachinepool_types.go +++ b/exp/api/v1alpha3/azuremanagedmachinepool_types.go @@ -27,6 +27,9 @@ type AzureManagedMachinePoolSpec struct { // +kubebuilder:validation:Enum=System;User Mode string `json:"mode"` + // AvailabilityZones - Availability zones for nodes. Must use VirtualMachineScaleSets AgentPoolType. + AvailabilityZones *[]string `json:"availabilityZones,omitempty"` + // SKU is the size of the VMs in the node pool. SKU string `json:"sku"` diff --git a/exp/api/v1alpha3/zz_generated.conversion.go b/exp/api/v1alpha3/zz_generated.conversion.go index 81713a54995..0b427300f71 100644 --- a/exp/api/v1alpha3/zz_generated.conversion.go +++ b/exp/api/v1alpha3/zz_generated.conversion.go @@ -814,6 +814,7 @@ func Convert_v1alpha4_AzureManagedMachinePoolList_To_v1alpha3_AzureManagedMachin func autoConvert_v1alpha3_AzureManagedMachinePoolSpec_To_v1alpha4_AzureManagedMachinePoolSpec(in *AzureManagedMachinePoolSpec, out *v1alpha4.AzureManagedMachinePoolSpec, s conversion.Scope) error { out.Mode = in.Mode + out.AvailabilityZones = (*[]string)(unsafe.Pointer(in.AvailabilityZones)) out.SKU = in.SKU out.OSDiskSizeGB = (*int32)(unsafe.Pointer(in.OSDiskSizeGB)) out.ProviderIDList = *(*[]string)(unsafe.Pointer(&in.ProviderIDList)) @@ -827,6 +828,7 @@ func Convert_v1alpha3_AzureManagedMachinePoolSpec_To_v1alpha4_AzureManagedMachin func autoConvert_v1alpha4_AzureManagedMachinePoolSpec_To_v1alpha3_AzureManagedMachinePoolSpec(in *v1alpha4.AzureManagedMachinePoolSpec, out *AzureManagedMachinePoolSpec, s conversion.Scope) error { out.Mode = in.Mode + out.AvailabilityZones = (*[]string)(unsafe.Pointer(in.AvailabilityZones)) out.SKU = in.SKU out.OSDiskSizeGB = (*int32)(unsafe.Pointer(in.OSDiskSizeGB)) out.ProviderIDList = *(*[]string)(unsafe.Pointer(&in.ProviderIDList)) diff --git a/exp/api/v1alpha3/zz_generated.deepcopy.go b/exp/api/v1alpha3/zz_generated.deepcopy.go index a74df460670..42ca5b3cb0b 100644 --- a/exp/api/v1alpha3/zz_generated.deepcopy.go +++ b/exp/api/v1alpha3/zz_generated.deepcopy.go @@ -510,6 +510,15 @@ func (in *AzureManagedMachinePoolList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AzureManagedMachinePoolSpec) DeepCopyInto(out *AzureManagedMachinePoolSpec) { *out = *in + if in.AvailabilityZones != nil { + in, out := &in.AvailabilityZones, &out.AvailabilityZones + *out = new([]string) + if **in != nil { + in, out := *in, *out + *out = make([]string, len(*in)) + copy(*out, *in) + } + } if in.OSDiskSizeGB != nil { in, out := &in.OSDiskSizeGB, &out.OSDiskSizeGB *out = new(int32) diff --git a/exp/api/v1alpha4/azuremanagedmachinepool_types.go b/exp/api/v1alpha4/azuremanagedmachinepool_types.go index 68f704307f7..07c2a1b5ea2 100644 --- a/exp/api/v1alpha4/azuremanagedmachinepool_types.go +++ b/exp/api/v1alpha4/azuremanagedmachinepool_types.go @@ -42,6 +42,9 @@ type AzureManagedMachinePoolSpec struct { // +kubebuilder:validation:Enum=System;User Mode string `json:"mode"` + // AvailabilityZones - Availability zones for nodes. Must use VirtualMachineScaleSets AgentPoolType. + AvailabilityZones *[]string `json:"availabilityZones,omitempty"` + // SKU is the size of the VMs in the node pool. SKU string `json:"sku"` diff --git a/exp/api/v1alpha4/azuremanagedmachinepool_webhook.go b/exp/api/v1alpha4/azuremanagedmachinepool_webhook.go index d6514ce1fbd..3b6b65f788d 100644 --- a/exp/api/v1alpha4/azuremanagedmachinepool_webhook.go +++ b/exp/api/v1alpha4/azuremanagedmachinepool_webhook.go @@ -18,6 +18,7 @@ package v1alpha4 import ( "context" + "reflect" "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -86,6 +87,14 @@ func (r *AzureManagedMachinePool) ValidateUpdate(oldRaw runtime.Object, client c } } + if !reflect.DeepEqual(r.Spec.AvailabilityZones, old.Spec.AvailabilityZones) { + allErrs = append(allErrs, + field.Invalid( + field.NewPath("Spec", "AvailabilityZones"), + r.Spec.AvailabilityZones, + "field is immutable")) + } + if r.Spec.Mode != string(NodePoolModeSystem) && old.Spec.Mode == string(NodePoolModeSystem) { // validate for last system node pool if err := r.validateLastSystemNodePool(client); err != nil { diff --git a/exp/api/v1alpha4/azuremanagedmachinepool_webhook_test.go b/exp/api/v1alpha4/azuremanagedmachinepool_webhook_test.go index a05506f8106..7f6de26f5da 100644 --- a/exp/api/v1alpha4/azuremanagedmachinepool_webhook_test.go +++ b/exp/api/v1alpha4/azuremanagedmachinepool_webhook_test.go @@ -94,6 +94,45 @@ func TestAzureManagedMachinePoolUpdatingWebhook(t *testing.T) { }, wantErr: true, }, + { + name: "Cannot add AvailabilityZones after creating agentpool", + new: &AzureManagedMachinePool{ + Spec: AzureManagedMachinePoolSpec{ + Mode: "System", + SKU: "StandardD2S_V3", + OSDiskSizeGB: to.Int32Ptr(512), + }, + }, + old: &AzureManagedMachinePool{ + Spec: AzureManagedMachinePoolSpec{ + Mode: "System", + SKU: "StandardD2S_V3", + OSDiskSizeGB: to.Int32Ptr(512), + AvailabilityZones: to.StringSlicePtr([]string{"1", "2", "3"}), + }, + }, + wantErr: true, + }, + { + name: "Cannot change AvailabilityZones of the agentpool", + new: &AzureManagedMachinePool{ + Spec: AzureManagedMachinePoolSpec{ + Mode: "System", + SKU: "StandardD2S_V3", + OSDiskSizeGB: to.Int32Ptr(512), + AvailabilityZones: to.StringSlicePtr([]string{"1", "2"}), + }, + }, + old: &AzureManagedMachinePool{ + Spec: AzureManagedMachinePoolSpec{ + Mode: "System", + SKU: "StandardD2S_V3", + OSDiskSizeGB: to.Int32Ptr(512), + AvailabilityZones: to.StringSlicePtr([]string{"1", "2", "3"}), + }, + }, + wantErr: true, + }, } var client client.Client for _, tc := range tests { diff --git a/exp/api/v1alpha4/zz_generated.deepcopy.go b/exp/api/v1alpha4/zz_generated.deepcopy.go index 7cbbc076100..a181aca6578 100644 --- a/exp/api/v1alpha4/zz_generated.deepcopy.go +++ b/exp/api/v1alpha4/zz_generated.deepcopy.go @@ -670,6 +670,15 @@ func (in *AzureManagedMachinePoolList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AzureManagedMachinePoolSpec) DeepCopyInto(out *AzureManagedMachinePoolSpec) { *out = *in + if in.AvailabilityZones != nil { + in, out := &in.AvailabilityZones, &out.AvailabilityZones + *out = new([]string) + if **in != nil { + in, out := *in, *out + *out = make([]string, len(*in)) + copy(*out, *in) + } + } if in.OSDiskSizeGB != nil { in, out := &in.OSDiskSizeGB, &out.OSDiskSizeGB *out = new(int32) diff --git a/exp/controllers/azuremanagedmachinepool_reconciler.go b/exp/controllers/azuremanagedmachinepool_reconciler.go index c845a83395a..72a5ad7fb0f 100644 --- a/exp/controllers/azuremanagedmachinepool_reconciler.go +++ b/exp/controllers/azuremanagedmachinepool_reconciler.go @@ -117,7 +117,8 @@ func (s *azureManagedMachinePoolService) Reconcile(ctx context.Context, scope *s scope.ControlPlane.Spec.VirtualNetwork.Name, scope.ControlPlane.Spec.VirtualNetwork.Subnet.Name, ), - Mode: scope.InfraMachinePool.Spec.Mode, + Mode: scope.InfraMachinePool.Spec.Mode, + AvailabilityZones: scope.InfraMachinePool.Spec.AvailabilityZones, } if scope.InfraMachinePool.Spec.OSDiskSizeGB != nil {