sigs.k8s.io/cluster-api-provider-azure@v1.14.3/test/e2e/e2e_suite_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 "bytes" 24 "context" 25 "encoding/base64" 26 "encoding/gob" 27 "flag" 28 "os" 29 "path/filepath" 30 "strings" 31 "testing" 32 33 . "github.com/onsi/ginkgo/v2" 34 . "github.com/onsi/gomega" 35 "k8s.io/klog/v2" 36 capi_e2e "sigs.k8s.io/cluster-api/test/e2e" 37 "sigs.k8s.io/cluster-api/test/framework" 38 "sigs.k8s.io/cluster-api/test/framework/bootstrap" 39 "sigs.k8s.io/cluster-api/test/framework/clusterctl" 40 ctrl "sigs.k8s.io/controller-runtime" 41 ) 42 43 func init() { 44 flag.StringVar(&configPath, "e2e.config", "", "path to the e2e config file") 45 flag.StringVar(&artifactFolder, "e2e.artifacts-folder", "", "folder where e2e test artifact should be stored") 46 flag.BoolVar(&useCIArtifacts, "kubetest.use-ci-artifacts", false, "use the latest build from the main branch of the Kubernetes repository. Set KUBERNETES_VERSION environment variable to latest-1.xx to use the build from 1.xx release branch.") 47 flag.BoolVar(&usePRArtifacts, "kubetest.use-pr-artifacts", false, "use the build from a PR of the Kubernetes repository") 48 flag.BoolVar(&skipCleanup, "e2e.skip-resource-cleanup", false, "if true, the resource cleanup after tests will be skipped") 49 flag.BoolVar(&skipLogCollection, "e2e.skip-log-collection", false, "if true, the log collection after tests will be skipped") 50 flag.BoolVar(&useExistingCluster, "e2e.use-existing-cluster", false, "if true, the test uses the current cluster instead of creating a new one (default discovery rules apply)") 51 flag.StringVar(&kubetestConfigFilePath, "kubetest.config-file", "", "path to the kubetest configuration file") 52 flag.StringVar(&kubetestRepoListPath, "kubetest.repo-list-path", "", "path to the kubetest repo-list path") 53 } 54 55 func TestE2E(t *testing.T) { 56 ctrl.SetLogger(klog.Background()) 57 RegisterFailHandler(Fail) 58 RunSpecs(t, "capz-e2e") 59 } 60 61 // Using a SynchronizedBeforeSuite for controlling how to create resources shared across ParallelNodes (~ginkgo threads). 62 // The local clusterctl repository & the bootstrap cluster are created once and shared across all the tests. 63 var _ = SynchronizedBeforeSuite(func() []byte { 64 // Before all ParallelNodes. 65 66 Expect(configPath).To(BeAnExistingFile(), "Invalid test suite argument. e2e.config should be an existing file.") 67 Expect(os.MkdirAll(artifactFolder, 0o755)).To(Succeed(), "Invalid test suite argument. Can't create e2e.artifacts-folder %q", artifactFolder) 68 69 Byf("Loading the e2e test configuration from %q", configPath) 70 e2eConfig = loadE2EConfig(configPath) 71 72 Byf("Creating a clusterctl local repository into %q", artifactFolder) 73 clusterctlConfigPath = createClusterctlLocalRepository(e2eConfig, filepath.Join(artifactFolder, "repository")) 74 75 By("Setting up the bootstrap cluster") 76 bootstrapClusterProvider, bootstrapClusterProxy = setupBootstrapCluster(e2eConfig, useExistingCluster) 77 78 By("Initializing the bootstrap cluster") 79 initBootstrapCluster(bootstrapClusterProxy, e2eConfig, clusterctlConfigPath, artifactFolder) 80 81 // encode the e2e config into the byte array. 82 var configBuf bytes.Buffer 83 enc := gob.NewEncoder(&configBuf) 84 Expect(enc.Encode(e2eConfig)).To(Succeed()) 85 configStr := base64.StdEncoding.EncodeToString(configBuf.Bytes()) 86 87 return []byte( 88 strings.Join([]string{ 89 artifactFolder, 90 clusterctlConfigPath, 91 configStr, 92 bootstrapClusterProxy.GetKubeconfigPath(), 93 }, ","), 94 ) 95 }, func(data []byte) { 96 // Before each ParallelNode. 97 98 parts := strings.Split(string(data), ",") 99 Expect(parts).To(HaveLen(4)) 100 101 artifactFolder = parts[0] 102 clusterctlConfigPath = parts[1] 103 104 // Decode the e2e config 105 configBytes, err := base64.StdEncoding.DecodeString(parts[2]) 106 Expect(err).NotTo(HaveOccurred()) 107 buf := bytes.NewBuffer(configBytes) 108 dec := gob.NewDecoder(buf) 109 Expect(dec.Decode(&e2eConfig)).To(Succeed()) 110 111 // we unset Kubernetes version variables to make sure we use the ones resolved from the first Ginkgo ParallelNode in the e2e config. 112 os.Unsetenv(capi_e2e.KubernetesVersion) 113 os.Unsetenv(capi_e2e.KubernetesVersionUpgradeFrom) 114 os.Unsetenv(capi_e2e.KubernetesVersionUpgradeTo) 115 116 kubeconfigPath := parts[3] 117 bootstrapClusterProxy = NewAzureClusterProxy("bootstrap", kubeconfigPath, framework.WithMachineLogCollector(AzureLogCollector{})) 118 }) 119 120 // Using a SynchronizedAfterSuite for controlling how to delete resources shared across ParallelNodes (~ginkgo threads). 121 // The bootstrap cluster is shared across all the tests, so it should be deleted only after all ParallelNodes completes. 122 // The local clusterctl repository is preserved like everything else created into the artifact folder. 123 var _ = SynchronizedAfterSuite(func() { 124 // After each ParallelNode. 125 }, func() { 126 // After all ParallelNodes. 127 128 By("Tearing down the management cluster") 129 if !skipCleanup { 130 tearDown(bootstrapClusterProvider, bootstrapClusterProxy) 131 } 132 }) 133 134 func loadE2EConfig(configPath string) *clusterctl.E2EConfig { 135 config := clusterctl.LoadE2EConfig(context.TODO(), clusterctl.LoadE2EConfigInput{ConfigPath: configPath}) 136 Expect(config).NotTo(BeNil(), "Failed to load E2E config from %s", configPath) 137 138 resolveKubernetesVersions(config) 139 140 return config 141 } 142 143 func createClusterctlLocalRepository(config *clusterctl.E2EConfig, repositoryFolder string) string { 144 createRepositoryInput := clusterctl.CreateRepositoryInput{ 145 E2EConfig: config, 146 RepositoryFolder: repositoryFolder, 147 } 148 149 // Ensuring a CNI file is defined in the config and register a FileTransformation to inject the referenced file as in place of the CNI_RESOURCES envSubst variable. 150 Expect(config.Variables).To(HaveKey(capi_e2e.CNIPath), "Missing %s variable in the config", capi_e2e.CNIPath) 151 cniPath := config.GetVariable(capi_e2e.CNIPath) 152 Expect(cniPath).To(BeAnExistingFile(), "The %s variable should resolve to an existing file", capi_e2e.CNIPath) 153 createRepositoryInput.RegisterClusterResourceSetConfigMapTransformation(cniPath, capi_e2e.CNIResources) 154 155 clusterctlConfig := clusterctl.CreateRepository(context.TODO(), createRepositoryInput) 156 Expect(clusterctlConfig).To(BeAnExistingFile(), "The clusterctl config file does not exists in the local repository %s", repositoryFolder) 157 return clusterctlConfig 158 } 159 160 func setupBootstrapCluster(config *clusterctl.E2EConfig, useExistingCluster bool) (bootstrap.ClusterProvider, framework.ClusterProxy) { 161 var clusterProvider bootstrap.ClusterProvider 162 kubeconfigPath := "" 163 if !useExistingCluster { 164 clusterProvider = bootstrap.CreateKindBootstrapClusterAndLoadImages(context.TODO(), bootstrap.CreateKindBootstrapClusterAndLoadImagesInput{ 165 Name: config.ManagementClusterName, 166 RequiresDockerSock: config.HasDockerProvider(), 167 Images: config.Images, 168 }) 169 Expect(clusterProvider).NotTo(BeNil(), "Failed to create a bootstrap cluster") 170 171 kubeconfigPath = clusterProvider.GetKubeconfigPath() 172 Expect(kubeconfigPath).To(BeAnExistingFile(), "Failed to get the kubeconfig file for the bootstrap cluster") 173 } else { 174 // @sonasingh46: Workaround for testing workload identity. 175 // Loading image for already created cluster 176 imagesInput := bootstrap.LoadImagesToKindClusterInput{ 177 Name: "capz-e2e", 178 Images: config.Images, 179 } 180 err := bootstrap.LoadImagesToKindCluster(context.TODO(), imagesInput) 181 Expect(err).NotTo(HaveOccurred(), "Failed to load images to the bootstrap cluster: %s", err) 182 } 183 clusterProxy := NewAzureClusterProxy("bootstrap", kubeconfigPath) 184 Expect(clusterProxy).NotTo(BeNil(), "Failed to get a bootstrap cluster proxy") 185 return clusterProvider, clusterProxy 186 } 187 188 func initBootstrapCluster(bootstrapClusterProxy framework.ClusterProxy, config *clusterctl.E2EConfig, clusterctlConfig, artifactFolder string) { 189 clusterctl.InitManagementClusterAndWatchControllerLogs(context.TODO(), clusterctl.InitManagementClusterAndWatchControllerLogsInput{ 190 ClusterProxy: bootstrapClusterProxy, 191 ClusterctlConfigPath: clusterctlConfig, 192 InfrastructureProviders: config.InfrastructureProviders(), 193 AddonProviders: config.AddonProviders(), 194 LogFolder: filepath.Join(artifactFolder, "clusters", bootstrapClusterProxy.GetName()), 195 }, config.GetIntervals(bootstrapClusterProxy.GetName(), "wait-controllers")...) 196 } 197 198 func tearDown(bootstrapClusterProvider bootstrap.ClusterProvider, bootstrapClusterProxy framework.ClusterProxy) { 199 if bootstrapClusterProxy != nil { 200 bootstrapClusterProxy.Dispose(context.TODO()) 201 } 202 if bootstrapClusterProvider != nil { 203 bootstrapClusterProvider.Dispose(context.TODO()) 204 } 205 }