sigs.k8s.io/cluster-api-provider-aws@v1.5.5/test/e2e/shared/suite.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 shared 21 22 import ( 23 "context" 24 "flag" 25 "os" 26 "os/exec" 27 "path" 28 "path/filepath" 29 "strconv" 30 "time" 31 32 "github.com/aws/aws-sdk-go/service/iam" 33 "github.com/gofrs/flock" 34 . "github.com/onsi/ginkgo" 35 . "github.com/onsi/gomega" 36 "sigs.k8s.io/yaml" 37 38 "sigs.k8s.io/cluster-api/test/framework" 39 "sigs.k8s.io/cluster-api/test/framework/clusterctl" 40 "sigs.k8s.io/cluster-api/test/framework/kubernetesversions" 41 ) 42 43 type synchronizedBeforeTestSuiteConfig struct { 44 ArtifactFolder string `json:"artifactFolder,omitempty"` 45 ConfigPath string `json:"configPath,omitempty"` 46 ClusterctlConfigPath string `json:"clusterctlConfigPath,omitempty"` 47 KubeconfigPath string `json:"kubeconfigPath,omitempty"` 48 Region string `json:"region,omitempty"` 49 E2EConfig clusterctl.E2EConfig `json:"e2eConfig,omitempty"` 50 BootstrapAccessKey *iam.AccessKey `json:"bootstrapAccessKey,omitempty"` 51 KubetestConfigFilePath string `json:"kubetestConfigFilePath,omitempty"` 52 UseCIArtifacts bool `json:"useCIArtifacts,omitempty"` 53 GinkgoNodes int `json:"ginkgoNodes,omitempty"` 54 GinkgoSlowSpecThreshold int `json:"ginkgoSlowSpecThreshold,omitempty"` 55 Base64EncodedCredentials string `json:"base64EncodedCredentials,omitempty"` 56 } 57 58 // Node1BeforeSuite is the common setup down on the first ginkgo node before the test suite runs. 59 func Node1BeforeSuite(e2eCtx *E2EContext) []byte { 60 flag.Parse() 61 Expect(e2eCtx.Settings.ConfigPath).To(BeAnExistingFile(), "Invalid test suite argument. configPath should be an existing file.") 62 Expect(os.MkdirAll(e2eCtx.Settings.ArtifactFolder, 0o750)).To(Succeed(), "Invalid test suite argument. Can't create artifacts-folder %q", e2eCtx.Settings.ArtifactFolder) 63 Byf("Loading the e2e test configuration from %q", e2eCtx.Settings.ConfigPath) 64 e2eCtx.E2EConfig = LoadE2EConfig(e2eCtx.Settings.ConfigPath) 65 sourceTemplate, err := os.ReadFile(filepath.Join(e2eCtx.Settings.DataFolder, e2eCtx.Settings.SourceTemplate)) 66 Expect(err).NotTo(HaveOccurred()) 67 e2eCtx.StartOfSuite = time.Now() 68 69 var clusterctlCITemplate clusterctl.Files 70 if !e2eCtx.IsManaged { 71 // Create CI manifest for upgrading to Kubernetes main test 72 platformKustomization, err := os.ReadFile(filepath.Join(e2eCtx.Settings.DataFolder, "ci-artifacts-platform-kustomization-for-upgrade.yaml")) 73 Expect(err).NotTo(HaveOccurred()) 74 sourceTemplateForUpgrade, err := os.ReadFile(filepath.Join(e2eCtx.Settings.DataFolder, "infrastructure-aws/generated/cluster-template-upgrade-to-main.yaml")) 75 Expect(err).NotTo(HaveOccurred()) 76 77 ciTemplateForUpgradePath, err := kubernetesversions.GenerateCIArtifactsInjectedTemplateForDebian( 78 kubernetesversions.GenerateCIArtifactsInjectedTemplateForDebianInput{ 79 ArtifactsDirectory: e2eCtx.Settings.ArtifactFolder, 80 SourceTemplate: sourceTemplateForUpgrade, 81 PlatformKustomization: platformKustomization, 82 }, 83 ) 84 Expect(err).NotTo(HaveOccurred()) 85 86 ciTemplateForUpgradeName := "cluster-template-upgrade-ci-artifacts.yaml" 87 templateDir := path.Join(e2eCtx.Settings.ArtifactFolder, "templates") 88 newTemplatePath := templateDir + "/" + ciTemplateForUpgradeName 89 90 err = exec.Command("cp", ciTemplateForUpgradePath, newTemplatePath).Run() //nolint:gosec 91 Expect(err).NotTo(HaveOccurred()) 92 93 clusterctlCITemplateForUpgrade := clusterctl.Files{ 94 SourcePath: newTemplatePath, 95 TargetName: ciTemplateForUpgradeName, 96 } 97 98 // Create CI manifest for conformance test 99 platformKustomization, err = os.ReadFile(filepath.Join(e2eCtx.Settings.DataFolder, "ci-artifacts-platform-kustomization.yaml")) 100 Expect(err).NotTo(HaveOccurred()) 101 ciTemplatePath, err := kubernetesversions.GenerateCIArtifactsInjectedTemplateForDebian( 102 kubernetesversions.GenerateCIArtifactsInjectedTemplateForDebianInput{ 103 ArtifactsDirectory: e2eCtx.Settings.ArtifactFolder, 104 SourceTemplate: sourceTemplate, 105 PlatformKustomization: platformKustomization, 106 }, 107 ) 108 Expect(err).NotTo(HaveOccurred()) 109 110 clusterctlCITemplate = clusterctl.Files{ 111 SourcePath: ciTemplatePath, 112 TargetName: "cluster-template-conformance-ci-artifacts.yaml", 113 } 114 115 providers := e2eCtx.E2EConfig.Providers 116 for i, prov := range providers { 117 if prov.Name != "aws" { 118 continue 119 } 120 e2eCtx.E2EConfig.Providers[i].Files = append(e2eCtx.E2EConfig.Providers[i].Files, clusterctlCITemplate) 121 e2eCtx.E2EConfig.Providers[i].Files = append(e2eCtx.E2EConfig.Providers[i].Files, clusterctlCITemplateForUpgrade) 122 } 123 } 124 125 Expect(err).NotTo(HaveOccurred()) 126 e2eCtx.AWSSession = NewAWSSession() 127 boostrapTemplate := getBootstrapTemplate(e2eCtx) 128 bootstrapTags := map[string]string{"capa-e2e-test": "true"} 129 e2eCtx.CloudFormationTemplate = renderCustomCloudFormation(boostrapTemplate) 130 if !e2eCtx.Settings.SkipCloudFormationCreation { 131 err = createCloudFormationStack(e2eCtx.AWSSession, boostrapTemplate, bootstrapTags) 132 if err != nil { 133 deleteCloudFormationStack(e2eCtx.AWSSession, boostrapTemplate) 134 err = createCloudFormationStack(e2eCtx.AWSSession, boostrapTemplate, bootstrapTags) 135 Expect(err).NotTo(HaveOccurred()) 136 } 137 } 138 ensureStackTags(e2eCtx.AWSSession, boostrapTemplate.Spec.StackName, bootstrapTags) 139 ensureNoServiceLinkedRoles(e2eCtx.AWSSession) 140 ensureSSHKeyPair(e2eCtx.AWSSession, DefaultSSHKeyPairName) 141 e2eCtx.Environment.BootstrapAccessKey = newUserAccessKey(e2eCtx.AWSSession, boostrapTemplate.Spec.BootstrapUser.UserName) 142 e2eCtx.BootstrapUserAWSSession = NewAWSSessionWithKey(e2eCtx.Environment.BootstrapAccessKey) 143 Expect(ensureTestImageUploaded(e2eCtx)).NotTo(HaveOccurred()) 144 145 // Image ID is needed when using a CI Kubernetes version. This is used in conformance test and upgrade to main test. 146 if !e2eCtx.IsManaged { 147 e2eCtx.E2EConfig.Variables["IMAGE_ID"] = conformanceImageID(e2eCtx) 148 } 149 150 Byf("Creating a clusterctl local repository into %q", e2eCtx.Settings.ArtifactFolder) 151 e2eCtx.Environment.ClusterctlConfigPath = createClusterctlLocalRepository(e2eCtx, filepath.Join(e2eCtx.Settings.ArtifactFolder, "repository")) 152 153 By("Setting up the bootstrap cluster") 154 e2eCtx.Environment.BootstrapClusterProvider, e2eCtx.Environment.BootstrapClusterProxy = setupBootstrapCluster(e2eCtx.E2EConfig, e2eCtx.Environment.Scheme, e2eCtx.Settings.UseExistingCluster) 155 156 base64EncodedCredentials := encodeCredentials(e2eCtx.Environment.BootstrapAccessKey, boostrapTemplate.Spec.Region) 157 SetEnvVar("AWS_B64ENCODED_CREDENTIALS", base64EncodedCredentials, true) 158 159 By("Writing AWS service quotas to a file for parallel tests") 160 quotas := EnsureServiceQuotas(e2eCtx.BootstrapUserAWSSession) 161 WriteResourceQuotesToFile(ResourceQuotaFilePath, quotas) 162 WriteResourceQuotesToFile(path.Join(e2eCtx.Settings.ArtifactFolder, "initial-resource-quotas.yaml"), quotas) 163 164 e2eCtx.Settings.InstanceVCPU, err = strconv.Atoi(e2eCtx.E2EConfig.GetVariable(InstanceVcpu)) 165 Expect(err).NotTo(HaveOccurred()) 166 167 By("Initializing the bootstrap cluster") 168 initBootstrapCluster(e2eCtx) 169 170 CreateAWSClusterControllerIdentity(e2eCtx.Environment.BootstrapClusterProxy.GetClient()) 171 172 if e2eCtx.IsManaged { 173 By("Setting up AWS static credentials") 174 SetupStaticCredentials(e2eCtx) 175 } 176 177 conf := synchronizedBeforeTestSuiteConfig{ 178 ArtifactFolder: e2eCtx.Settings.ArtifactFolder, 179 ConfigPath: e2eCtx.Settings.ConfigPath, 180 ClusterctlConfigPath: e2eCtx.Environment.ClusterctlConfigPath, 181 KubeconfigPath: e2eCtx.Environment.BootstrapClusterProxy.GetKubeconfigPath(), 182 Region: getBootstrapTemplate(e2eCtx).Spec.Region, 183 E2EConfig: *e2eCtx.E2EConfig, 184 BootstrapAccessKey: e2eCtx.Environment.BootstrapAccessKey, 185 KubetestConfigFilePath: e2eCtx.Settings.KubetestConfigFilePath, 186 UseCIArtifacts: e2eCtx.Settings.UseCIArtifacts, 187 GinkgoNodes: e2eCtx.Settings.GinkgoNodes, 188 GinkgoSlowSpecThreshold: e2eCtx.Settings.GinkgoSlowSpecThreshold, 189 Base64EncodedCredentials: base64EncodedCredentials, 190 } 191 192 data, err := yaml.Marshal(conf) 193 Expect(err).NotTo(HaveOccurred()) 194 return data 195 } 196 197 // AllNodesBeforeSuite is the common setup down on each ginkgo parallel node before the test suite runs. 198 func AllNodesBeforeSuite(e2eCtx *E2EContext, data []byte) { 199 conf := &synchronizedBeforeTestSuiteConfig{} 200 err := yaml.UnmarshalStrict(data, conf) 201 Expect(err).NotTo(HaveOccurred()) 202 e2eCtx.Settings.ArtifactFolder = conf.ArtifactFolder 203 e2eCtx.Settings.ConfigPath = conf.ConfigPath 204 e2eCtx.Environment.ClusterctlConfigPath = conf.ClusterctlConfigPath 205 e2eCtx.Environment.BootstrapClusterProxy = framework.NewClusterProxy("bootstrap", conf.KubeconfigPath, e2eCtx.Environment.Scheme) 206 e2eCtx.E2EConfig = &conf.E2EConfig 207 e2eCtx.BootstrapUserAWSSession = NewAWSSessionWithKey(conf.BootstrapAccessKey) 208 e2eCtx.Settings.FileLock = flock.New(ResourceQuotaFilePath) 209 e2eCtx.Settings.KubetestConfigFilePath = conf.KubetestConfigFilePath 210 e2eCtx.Settings.UseCIArtifacts = conf.UseCIArtifacts 211 e2eCtx.Settings.GinkgoNodes = conf.GinkgoNodes 212 e2eCtx.Settings.GinkgoSlowSpecThreshold = conf.GinkgoSlowSpecThreshold 213 e2eCtx.AWSSession = NewAWSSession() 214 azs := GetAvailabilityZones(e2eCtx.AWSSession) 215 SetEnvVar(AwsAvailabilityZone1, *azs[0].ZoneName, false) 216 SetEnvVar(AwsAvailabilityZone2, *azs[1].ZoneName, false) 217 SetEnvVar("AWS_REGION", conf.Region, false) 218 SetEnvVar("AWS_SSH_KEY_NAME", DefaultSSHKeyPairName, false) 219 SetEnvVar("AWS_B64ENCODED_CREDENTIALS", conf.Base64EncodedCredentials, true) 220 e2eCtx.Environment.ResourceTicker = time.NewTicker(time.Second * 5) 221 e2eCtx.Environment.ResourceTickerDone = make(chan bool) 222 // Get EC2 logs every minute 223 e2eCtx.Environment.MachineTicker = time.NewTicker(time.Second * 60) 224 e2eCtx.Environment.MachineTickerDone = make(chan bool) 225 resourceCtx, resourceCancel := context.WithCancel(context.Background()) 226 machineCtx, machineCancel := context.WithCancel(context.Background()) 227 // Dump resources every 5 seconds 228 go func() { 229 defer GinkgoRecover() 230 for { 231 select { 232 case <-e2eCtx.Environment.ResourceTickerDone: 233 resourceCancel() 234 return 235 case <-e2eCtx.Environment.ResourceTicker.C: 236 for k := range e2eCtx.Environment.Namespaces { 237 DumpSpecResources(resourceCtx, e2eCtx, k) 238 } 239 } 240 } 241 }() 242 243 // Dump machine logs every 60 seconds 244 go func() { 245 defer GinkgoRecover() 246 for { 247 select { 248 case <-e2eCtx.Environment.MachineTickerDone: 249 machineCancel() 250 return 251 case <-e2eCtx.Environment.MachineTicker.C: 252 for k := range e2eCtx.Environment.Namespaces { 253 DumpMachines(machineCtx, e2eCtx, k) 254 } 255 } 256 } 257 }() 258 } 259 260 // Node1AfterSuite is cleanup that runs on the first ginkgo node after the test suite finishes. 261 func Node1AfterSuite(e2eCtx *E2EContext) { 262 ctx, cancel := context.WithTimeout(context.TODO(), 15*time.Minute) 263 DumpEKSClusters(ctx, e2eCtx) 264 DumpCloudTrailEvents(e2eCtx) 265 266 if e2eCtx.IsManaged { 267 By("Deleting AWS static credentials") 268 CleanupStaticCredentials(ctx, e2eCtx) 269 } 270 271 defer cancel() 272 By("Tearing down the management cluster") 273 if !e2eCtx.Settings.SkipCleanup { 274 tearDown(e2eCtx.Environment.BootstrapClusterProvider, e2eCtx.Environment.BootstrapClusterProxy) 275 if !e2eCtx.Settings.SkipCloudFormationDeletion { 276 deleteCloudFormationStack(e2eCtx.AWSSession, getBootstrapTemplate(e2eCtx)) 277 } 278 } 279 } 280 281 // AllNodesAfterSuite is cleanup that runs on all ginkgo parallel nodes after the test suite finishes. 282 func AllNodesAfterSuite(e2eCtx *E2EContext) { 283 if e2eCtx.Environment.ResourceTickerDone != nil { 284 e2eCtx.Environment.ResourceTickerDone <- true 285 } 286 if e2eCtx.Environment.MachineTickerDone != nil { 287 e2eCtx.Environment.MachineTickerDone <- true 288 } 289 ctx, cancel := context.WithTimeout(context.TODO(), 45*time.Minute) 290 defer cancel() 291 for k := range e2eCtx.Environment.Namespaces { 292 DumpSpecResourcesAndCleanup(ctx, "", k, e2eCtx) 293 DumpMachines(ctx, e2eCtx, k) 294 } 295 }