sigs.k8s.io/cluster-api-provider-azure@v1.14.3/test/e2e/azure_machinepools.go (about) 1 //go:build e2e 2 // +build e2e 3 4 /* 5 Copyright 2023 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package e2e 21 22 import ( 23 "context" 24 "sync" 25 26 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5" 27 . "github.com/onsi/ginkgo/v2" 28 . "github.com/onsi/gomega" 29 corev1 "k8s.io/api/core/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/labels" 32 "k8s.io/apimachinery/pkg/selection" 33 "k8s.io/utils/ptr" 34 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 35 infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1" 36 azureutil "sigs.k8s.io/cluster-api-provider-azure/util/azure" 37 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 38 expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 39 "sigs.k8s.io/cluster-api/test/framework" 40 "sigs.k8s.io/controller-runtime/pkg/client" 41 ) 42 43 const ( 44 AzureMachinePoolsSpecName = "azure-machinepools" 45 regexpFlexibleVM = `^azure:\/\/\/subscriptions\/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\/resourceGroups\/.+\/providers\/Microsoft.Compute\/virtualMachines\/.+$` 46 regexpUniformInstance = `^azure:\/\/\/subscriptions\/[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\/resourceGroups\/.+\/providers\/Microsoft.Compute\/virtualMachineScaleSets\/.+\/virtualMachines\/\d+$` 47 ) 48 49 // AzureMachinePoolsSpecInput is the input for AzureMachinePoolsSpec. 50 type ( 51 AzureMachinePoolsSpecInput struct { 52 Cluster *clusterv1.Cluster 53 BootstrapClusterProxy framework.ClusterProxy 54 Namespace *corev1.Namespace 55 ClusterName string 56 WaitIntervals []interface{} 57 } 58 ) 59 60 // AzureMachinePoolsSpec tests that the expected machinepool resources exist. 61 func AzureMachinePoolsSpec(ctx context.Context, inputGetter func() AzureMachinePoolsSpecInput) { 62 input := inputGetter() 63 Expect(input.Cluster).NotTo(BeNil(), "Invalid argument. input.Cluster can't be nil when calling %s spec", AzureMachinePoolsSpecName) 64 Expect(input.BootstrapClusterProxy).NotTo(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", AzureMachinePoolsSpecName) 65 Expect(input.Namespace).NotTo(BeNil(), "Invalid argument. input.Namespace can't be nil when calling %s spec", AzureMachinePoolsSpecName) 66 Expect(input.ClusterName).NotTo(BeEmpty(), "Invalid argument. input.ClusterName can't be empty when calling %s spec", AzureMachinePoolsSpecName) 67 Expect(input.WaitIntervals).NotTo(BeEmpty(), "Invalid argument. input.WaitIntervals can't be empty when calling %s spec", AzureMachinePoolsSpecName) 68 69 var ( 70 bootstrapClusterProxy = input.BootstrapClusterProxy 71 workloadClusterProxy = bootstrapClusterProxy.GetWorkloadCluster(ctx, input.Namespace.Name, input.ClusterName) 72 clusterLabels = map[string]string{clusterv1.ClusterNameLabel: workloadClusterProxy.GetName()} 73 ) 74 75 Expect(workloadClusterProxy).NotTo(BeNil()) 76 mgmtClient := bootstrapClusterProxy.GetClient() 77 Expect(mgmtClient).NotTo(BeNil()) 78 79 Byf("listing AzureMachinePools for cluster %s in namespace %s", input.ClusterName, input.Namespace.Name) 80 ampList := &infrav1exp.AzureMachinePoolList{} 81 Expect(mgmtClient.List(ctx, ampList, client.InNamespace(input.Namespace.Name), client.MatchingLabels(clusterLabels))).To(Succeed()) 82 Expect(ampList.Items).NotTo(BeEmpty()) 83 machinepools := []*expv1.MachinePool{} 84 for _, amp := range ampList.Items { 85 Byf("checking AzureMachinePool %s in %s orchestration mode", amp.Name, amp.Spec.OrchestrationMode) 86 Expect(amp.Status.Replicas).To(BeNumerically("==", len(amp.Spec.ProviderIDList))) 87 for _, providerID := range amp.Spec.ProviderIDList { 88 switch amp.Spec.OrchestrationMode { 89 case infrav1.OrchestrationModeType(armcompute.OrchestrationModeFlexible): 90 Expect(providerID).To(MatchRegexp(regexpFlexibleVM)) 91 default: 92 Expect(providerID).To(MatchRegexp(regexpUniformInstance)) 93 } 94 } 95 mp, err := azureutil.FindParentMachinePool(amp.Name, bootstrapClusterProxy.GetClient()) 96 Expect(err).NotTo(HaveOccurred()) 97 Expect(mp).NotTo(BeNil()) 98 machinepools = append(machinepools, mp) 99 } 100 101 var wg sync.WaitGroup 102 for _, mp := range machinepools { 103 goalReplicas := ptr.Deref[int32](mp.Spec.Replicas, 0) + 1 104 Byf("Scaling machine pool %s out from %d to %d", mp.Name, *mp.Spec.Replicas, goalReplicas) 105 wg.Add(1) 106 go func(mp *expv1.MachinePool) { 107 defer GinkgoRecover() 108 defer wg.Done() 109 framework.ScaleMachinePoolAndWait(ctx, framework.ScaleMachinePoolAndWaitInput{ 110 ClusterProxy: bootstrapClusterProxy, 111 Cluster: input.Cluster, 112 Replicas: goalReplicas, 113 MachinePools: []*expv1.MachinePool{mp}, 114 WaitForMachinePoolToScale: input.WaitIntervals, 115 }) 116 }(mp) 117 } 118 wg.Wait() 119 120 for _, mp := range machinepools { 121 goalReplicas := ptr.Deref[int32](mp.Spec.Replicas, 0) - 1 122 Byf("Scaling machine pool %s in from %d to %d", mp.Name, *mp.Spec.Replicas, goalReplicas) 123 wg.Add(1) 124 go func(mp *expv1.MachinePool) { 125 defer GinkgoRecover() 126 defer wg.Done() 127 framework.ScaleMachinePoolAndWait(ctx, framework.ScaleMachinePoolAndWaitInput{ 128 ClusterProxy: bootstrapClusterProxy, 129 Cluster: input.Cluster, 130 Replicas: goalReplicas, 131 MachinePools: []*expv1.MachinePool{mp}, 132 WaitForMachinePoolToScale: input.WaitIntervals, 133 }) 134 }(mp) 135 } 136 wg.Wait() 137 138 By("verifying that workload nodes are schedulable") 139 clientset := workloadClusterProxy.GetClientSet() 140 Expect(clientset).NotTo(BeNil()) 141 workloadNodeRequirement, err := labels.NewRequirement("node-role.kubernetes.io/control-plane", selection.DoesNotExist, nil) 142 Expect(err).NotTo(HaveOccurred()) 143 selector := labels.NewSelector().Add(*workloadNodeRequirement) 144 nodeList, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{LabelSelector: selector.String()}) 145 Expect(err).NotTo(HaveOccurred()) 146 Expect(nodeList.Items).NotTo(BeEmpty()) 147 for _, node := range nodeList.Items { 148 for _, taint := range node.Spec.Taints { 149 Expect(taint.Effect).NotTo(BeElementOf( 150 corev1.TaintEffectNoSchedule, corev1.TaintEffectPreferNoSchedule, corev1.TaintEffectNoExecute), 151 "node %s has %s taint", node.Name, taint.Effect) 152 } 153 } 154 }