sigs.k8s.io/cluster-api-provider-azure@v1.14.3/test/e2e/conformance_test.go (about) 1 //go:build e2e 2 // +build e2e 3 4 /* 5 Copyright 2020 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 "fmt" 25 "os" 26 "strconv" 27 "strings" 28 29 "github.com/blang/semver" 30 . "github.com/onsi/ginkgo/v2" 31 . "github.com/onsi/gomega" 32 "github.com/onsi/gomega/gmeasure" 33 corev1 "k8s.io/api/core/v1" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/types" 36 "sigs.k8s.io/cluster-api-provider-azure/test/e2e/kubernetes/node" 37 capi_e2e "sigs.k8s.io/cluster-api/test/e2e" 38 "sigs.k8s.io/cluster-api/test/framework/clusterctl" 39 "sigs.k8s.io/cluster-api/test/framework/kubetest" 40 "sigs.k8s.io/cluster-api/util" 41 ) 42 43 var _ = Describe("Conformance Tests", func() { 44 var ( 45 ctx = context.TODO() 46 cancelWatches context.CancelFunc 47 result *clusterctl.ApplyClusterTemplateAndWaitResult 48 clusterName string 49 namespace *corev1.Namespace 50 specName = "conformance-tests" 51 repoList = "" 52 ) 53 54 BeforeEach(func() { 55 Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName) 56 Expect(bootstrapClusterProxy).NotTo(BeNil(), "Invalid argument. BootstrapClusterProxy can't be nil") 57 Expect(kubetestConfigFilePath).NotTo(BeNil(), "Invalid argument. kubetestConfigFilePath can't be nil") 58 Expect(e2eConfig).NotTo(BeNil(), "Invalid argument. e2eConfig can't be nil when calling %s spec", specName) 59 Expect(clusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. clusterctlConfigPath must be an existing file when calling %s spec", specName) 60 61 Expect(e2eConfig.Variables).To(HaveKey(capi_e2e.KubernetesVersion)) 62 63 clusterName = os.Getenv("CLUSTER_NAME") 64 if clusterName == "" { 65 clusterName = fmt.Sprintf("capz-conf-%s", util.RandomString(6)) 66 } 67 fmt.Fprintf(GinkgoWriter, "INFO: Cluster name is %s\n", clusterName) 68 69 // Setup a Namespace where to host objects for this spec and create a watcher for the namespace events. 70 var err error 71 namespace, cancelWatches, err = setupSpecNamespace(ctx, clusterName, bootstrapClusterProxy, artifactFolder) 72 Expect(err).NotTo(HaveOccurred()) 73 74 Expect(os.Setenv(AzureResourceGroup, clusterName)).To(Succeed()) 75 Expect(os.Setenv(AzureVNetName, fmt.Sprintf("%s-vnet", clusterName))).To(Succeed()) 76 77 result = new(clusterctl.ApplyClusterTemplateAndWaitResult) 78 79 spClientSecret := os.Getenv(AzureClientSecret) 80 secret := &corev1.Secret{ 81 ObjectMeta: metav1.ObjectMeta{ 82 Name: "cluster-identity-secret", 83 Namespace: namespace.Name, 84 }, 85 Type: corev1.SecretTypeOpaque, 86 Data: map[string][]byte{"clientSecret": []byte(spClientSecret)}, 87 } 88 err = bootstrapClusterProxy.GetClient().Create(ctx, secret) 89 Expect(err).NotTo(HaveOccurred()) 90 91 identityName := e2eConfig.GetVariable(ClusterIdentityName) 92 Expect(os.Setenv(ClusterIdentityName, identityName)).To(Succeed()) 93 Expect(os.Setenv(ClusterIdentityNamespace, namespace.Name)).To(Succeed()) 94 Expect(os.Setenv(ClusterIdentitySecretName, "cluster-identity-secret")).To(Succeed()) 95 Expect(os.Setenv(ClusterIdentitySecretNamespace, namespace.Name)).To(Succeed()) 96 }) 97 98 It(specName, func() { 99 experiment := gmeasure.NewExperiment(specName) 100 AddReportEntry(experiment.Name, experiment) 101 102 var err error 103 104 kubernetesVersion := e2eConfig.GetVariable(capi_e2e.KubernetesVersion) 105 flavor := e2eConfig.GetVariable("CONFORMANCE_FLAVOR") 106 107 // clusters with CI artifacts or PR artifacts are based on a known CI version 108 // PR artifacts will replace the CI artifacts during kubeadm init 109 if useCIArtifacts || usePRArtifacts { 110 kubernetesVersion, err = resolveCIVersion(kubernetesVersion) 111 Expect(err).NotTo(HaveOccurred()) 112 Expect(os.Setenv("CI_VERSION", kubernetesVersion)).To(Succeed()) 113 Expect(os.Setenv("CLOUD_PROVIDER_AZURE_LABEL", "azure-ci")).To(Succeed()) 114 115 if useCIArtifacts { 116 flavor = "conformance-ci-artifacts" 117 } else if usePRArtifacts { 118 flavor = "conformance-presubmit-artifacts" 119 } 120 } 121 122 // use the ipv6 flavor if ipv6 IP family is specified. 123 if e2eConfig.GetVariable(capi_e2e.IPFamily) == "IPv6" { 124 flavor += "-ipv6" 125 kubetestConfigFilePath = strings.Replace(kubetestConfigFilePath, ".yaml", "-ipv6.yaml", 1) 126 } else if e2eConfig.GetVariable(capi_e2e.IPFamily) == "dual" { 127 flavor += "-dual-stack" 128 kubetestConfigFilePath = strings.Replace(kubetestConfigFilePath, ".yaml", "-dual-stack.yaml", 1) 129 } 130 131 // Starting with Kubernetes v1.25, the kubetest config file needs to be compatible with Ginkgo V2. 132 v125 := semver.MustParse("1.25.0-alpha.0.0") 133 v, err := semver.ParseTolerant(kubernetesVersion) 134 Expect(err).NotTo(HaveOccurred()) 135 if v.GTE(v125) { 136 // Use the Ginkgo V2 config file. 137 kubetestConfigFilePath = getGinkgoV2ConfigFilePath(kubetestConfigFilePath) 138 } 139 140 // Set the worker counts for conformance tests that use Windows 141 // This is a work around until we can update cluster-api test framework to be aware of windows node counts. 142 conformanceNodeCount := e2eConfig.GetVariable("CONFORMANCE_WORKER_MACHINE_COUNT") 143 numOfConformanceNodes, err := strconv.ParseInt(conformanceNodeCount, 10, 64) 144 Expect(err).NotTo(HaveOccurred()) 145 146 linuxWorkerMachineCount := numOfConformanceNodes 147 if isWindows(kubetestConfigFilePath) { 148 Expect(os.Setenv("WINDOWS_WORKER_MACHINE_COUNT", conformanceNodeCount)).To(Succeed()) 149 150 // Conformance for windows doesn't require any linux worker machines. 151 // The templates use WORKER_MACHINE_COUNT for linux machines for backwards compatibility so clear it 152 linuxWorkerMachineCount = 0 153 } 154 155 controlPlaneMachineCount, err := strconv.ParseInt(e2eConfig.GetVariable("CONFORMANCE_CONTROL_PLANE_MACHINE_COUNT"), 10, 64) 156 Expect(err).NotTo(HaveOccurred()) 157 158 stopwatch := experiment.NewStopwatch() 159 clusterctl.ApplyClusterTemplateAndWait(ctx, createApplyClusterTemplateInput( 160 specName, 161 withFlavor(flavor), 162 withNamespace(namespace.Name), 163 withClusterName(clusterName), 164 withKubernetesVersion(kubernetesVersion), 165 withControlPlaneMachineCount(controlPlaneMachineCount), 166 withWorkerMachineCount(linuxWorkerMachineCount), 167 withControlPlaneWaiters(clusterctl.ControlPlaneWaiters{ 168 WaitForControlPlaneInitialized: EnsureControlPlaneInitializedNoAddons, 169 }), 170 ), result) 171 stopwatch.Record("cluster creation") 172 173 workloadProxy := bootstrapClusterProxy.GetWorkloadCluster(ctx, namespace.Name, clusterName) 174 175 if isWindows(kubetestConfigFilePath) { 176 // Windows requires a taint on control nodes since not all conformance tests have ability to run 177 options := metav1.ListOptions{ 178 LabelSelector: "kubernetes.io/os=linux", 179 } 180 181 noScheduleTaint := &corev1.Taint{ 182 Key: "node-role.kubernetes.io/control-plane", 183 Value: "", 184 Effect: "NoSchedule", 185 } 186 187 if v, err := semver.ParseTolerant(kubernetesVersion); err == nil { 188 if v.LT(semver.MustParse("1.24.0-alpha.0.0")) { 189 noScheduleTaint = &corev1.Taint{ 190 Key: "node-role.kubernetes.io/master", 191 Value: "", 192 Effect: "NoSchedule", 193 } 194 } 195 } 196 197 err = node.TaintNode(workloadProxy.GetClientSet(), options, noScheduleTaint) 198 Expect(err).NotTo(HaveOccurred()) 199 200 // Windows requires a repo-list when running e2e tests against K8s versions prior to v1.25 201 // because some test images published to the k8s gcr do not have Windows flavors. 202 repoList, err = resolveKubetestRepoListPath(kubernetesVersion, kubetestRepoListPath) 203 Expect(err).NotTo(HaveOccurred()) 204 fmt.Fprintf(GinkgoWriter, "INFO: Using repo-list '%s' for version '%s'\n", repoList, kubernetesVersion) 205 } 206 207 ginkgoNodes, err := strconv.Atoi(e2eConfig.GetVariable("CONFORMANCE_NODES")) 208 Expect(err).NotTo(HaveOccurred()) 209 210 stopwatch.Reset() 211 err = kubetest.Run(context.Background(), 212 kubetest.RunInput{ 213 ClusterProxy: workloadProxy, 214 NumberOfNodes: int(numOfConformanceNodes), 215 ConfigFilePath: kubetestConfigFilePath, 216 KubeTestRepoListPath: repoList, 217 ConformanceImage: e2eConfig.GetVariable("CONFORMANCE_IMAGE"), 218 GinkgoNodes: ginkgoNodes, 219 }, 220 ) 221 Expect(err).NotTo(HaveOccurred()) 222 stopwatch.Record("conformance suite") 223 }) 224 225 AfterEach(func() { 226 if result.Cluster == nil { 227 // this means the cluster failed to come up. We make an attempt to find the cluster to be able to fetch logs for the failed bootstrapping. 228 _ = bootstrapClusterProxy.GetClient().Get(ctx, types.NamespacedName{Name: clusterName, Namespace: namespace.Name}, result.Cluster) 229 } 230 231 CheckTestBeforeCleanup() 232 233 cleanInput := cleanupInput{ 234 SpecName: specName, 235 Cluster: result.Cluster, 236 ClusterProxy: bootstrapClusterProxy, 237 Namespace: namespace, 238 CancelWatches: cancelWatches, 239 IntervalsGetter: e2eConfig.GetIntervals, 240 SkipCleanup: skipCleanup, 241 SkipLogCollection: skipLogCollection, 242 ArtifactFolder: artifactFolder, 243 } 244 // Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself. 245 dumpSpecResourcesAndCleanup(ctx, cleanInput) 246 247 Expect(os.Unsetenv(AzureResourceGroup)).To(Succeed()) 248 Expect(os.Unsetenv(AzureVNetName)).To(Succeed()) 249 }) 250 251 }) 252 253 func isWindows(kubetestConfigFilePath string) bool { 254 return strings.Contains(kubetestConfigFilePath, "windows") 255 } 256 257 func getGinkgoV2ConfigFilePath(kubetestConfigFilePath string) string { 258 if !strings.HasSuffix(kubetestConfigFilePath, "-ginkgo-v2.yaml") { 259 return strings.Replace(kubetestConfigFilePath, ".yaml", "-ginkgo-v2.yaml", 1) 260 } 261 return kubetestConfigFilePath 262 }