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  }