github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/build/multi-platform.go (about) 1 package build 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "log" 8 "os" 9 "strings" 10 "time" 11 12 "github.com/redhat-appstudio/e2e-tests/pkg/clients/has" 13 "github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton" 14 "golang.org/x/crypto/ssh" 15 v1 "k8s.io/api/core/v1" 16 17 "github.com/devfile/library/v2/pkg/util" 18 . "github.com/onsi/ginkgo/v2" 19 . "github.com/onsi/gomega" 20 appservice "github.com/redhat-appstudio/application-api/api/v1alpha1" 21 buildservice "github.com/redhat-appstudio/build-service/api/v1alpha1" 22 "github.com/redhat-appstudio/e2e-tests/pkg/constants" 23 "github.com/redhat-appstudio/e2e-tests/pkg/framework" 24 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 25 pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/types" 28 29 "github.com/aws/aws-sdk-go-v2/aws" 30 "github.com/aws/aws-sdk-go-v2/config" 31 "github.com/aws/aws-sdk-go-v2/service/ec2" 32 ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" 33 34 "github.com/IBM/go-sdk-core/v5/core" 35 "github.com/IBM/vpc-go-sdk/vpcv1" 36 ) 37 38 const ( 39 Ec2ArmTag = "multi-platform-e2e-arm64" 40 HostConfig = "host-config" 41 ControllerNamespace = "multi-platform-controller" 42 AwsSecretName = "awskeys" 43 IbmSecretName = "ibmkey" 44 IbmKey = "multi-platform-tests" 45 SshSecretName = "sshkeys" 46 Ec2User = "ec2-user" 47 AwsRegion = "us-east-1" 48 AwsPlatform = "linux/arm64" 49 DynamicMaxInstances = "1" 50 IbmZUrl = "https://us-east.iaas.cloud.ibm.com/v1" 51 IbmPUrl = "https://us-south.power-iaas.cloud.ibm.com" 52 CRN = "crn:v1:bluemix:public:power-iaas:dal10:a/934e118c399b4a28a70afdf2210d708f:8c9ef568-16a5-4aa2-bfd5-946349c9aeac::" 53 MultiPlatformSecretKey = "build.appstudio.redhat.com/multi-platform-secret" 54 MultiPlatformConfigKey = "build.appstudio.redhat.com/multi-platform-config" 55 ) 56 57 var ( 58 IbmVpc = "us-east-default-vpc" 59 multiPlatformProjectGitUrl = utils.GetEnv("MULTI_PLATFORM_TEST_REPO_URL", "https://github.com/devfile-samples/devfile-sample-go-basic") 60 multiPlatformProjectRevision = utils.GetEnv("MULTI_PLATFORM_TEST_REPO_REVISION", "c713067b0e65fb3de50d1f7c457eb51c2ab0dbb0") 61 timeout = 20 * time.Minute 62 interval = 10 * time.Second 63 ) 64 65 var _ = framework.MultiPlatformBuildSuiteDescribe("Multi Platform Controller E2E tests", Pending, Label("multi-platform"), func() { 66 var f *framework.Framework 67 AfterEach(framework.ReportFailure(&f)) 68 var err error 69 70 defer GinkgoRecover() 71 72 Describe("aws host-pool allocation", Label("aws-host-pool"), func() { 73 74 var testNamespace, applicationName, componentName, multiPlatformSecretName, host, userDir string 75 var component *appservice.Component 76 77 AfterAll(func() { 78 // Cleanup aws secet and host-config 79 Expect(f.AsKubeAdmin.CommonController.DeleteSecret(ControllerNamespace, AwsSecretName)).To(Succeed()) 80 Expect(f.AsKubeAdmin.CommonController.DeleteConfigMap(HostConfig, ControllerNamespace, true)).To(Succeed()) 81 82 if !CurrentSpecReport().Failed() { 83 Expect(f.AsKubeAdmin.HasController.DeleteComponent(componentName, testNamespace, false)).To(Succeed()) 84 Expect(f.AsKubeAdmin.HasController.DeleteApplication(applicationName, testNamespace, false)).To(Succeed()) 85 Expect(f.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(testNamespace)).To(Succeed()) 86 } else { 87 Expect(f.AsKubeAdmin.CommonController.StoreAllPods(testNamespace)).To(Succeed()) 88 Expect(f.AsKubeAdmin.TektonController.StoreAllPipelineRuns(testNamespace)).To(Succeed()) 89 } 90 }) 91 92 BeforeAll(func() { 93 94 f, err = framework.NewFramework(utils.GetGeneratedNamespace("multi-platform-host")) 95 Expect(err).NotTo(HaveOccurred()) 96 testNamespace = f.UserNamespace 97 Expect(testNamespace).NotTo(BeNil(), "failed to create sandbox user namespace") 98 99 Expect(err).ShouldNot(HaveOccurred()) 100 101 err = createConfigMapForHostPool(f) 102 Expect(err).ShouldNot(HaveOccurred()) 103 104 err = createSecretForHostPool(f) 105 Expect(err).ShouldNot(HaveOccurred()) 106 107 err = createBuildPipelineSelector(f, testNamespace, "ARM64") 108 Expect(err).ShouldNot(HaveOccurred()) 109 110 component, applicationName, componentName = createApplicationAndComponent(f, testNamespace) 111 }) 112 113 When("the Component with multi-platform-build is created", func() { 114 It("a PipelineRun is triggered", func() { 115 validatePipelineRunIsRunning(f, componentName, applicationName, testNamespace) 116 }) 117 118 It("the build-container task from component pipelinerun is buildah-remote", func() { 119 _, multiPlatformSecretName = validateBuildContainerTaskIsBuildahRemote(f, componentName, applicationName, testNamespace) 120 }) 121 It("The multi platform secret is populated", func() { 122 var secret *v1.Secret 123 Eventually(func() error { 124 secret, err = f.AsKubeAdmin.CommonController.GetSecret(testNamespace, multiPlatformSecretName) 125 if err != nil { 126 return err 127 } 128 return nil 129 }, timeout, interval).Should(Succeed(), "timed out when verifying the secret is created") 130 131 // Get the host and the user directory so we can verify they are cleaned up at the end of the run 132 fullHost, present := secret.Data["host"] 133 Expect(present).To(BeTrue()) 134 135 userDirTmp, present := secret.Data["user-dir"] 136 Expect(present).To(BeTrue()) 137 userDir = string(userDirTmp) 138 hostParts := strings.Split(string(fullHost), "@") 139 host = strings.TrimSpace(hostParts[1]) 140 }) 141 142 It("that PipelineRun completes successfully", func() { 143 Expect(f.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component, "", f.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, nil)).To(Succeed()) 144 }) 145 146 It("test that cleanup happened successfully", func() { 147 148 // Parse the private key 149 signer, err := ssh.ParsePrivateKey([]byte(os.Getenv("MULTI_PLATFORM_AWS_SSH_KEY"))) 150 if err != nil { 151 log.Fatalf("Unable to parse private key: %v", err) 152 } 153 // SSH configuration using public key authentication 154 config := &ssh.ClientConfig{ 155 User: Ec2User, 156 Auth: []ssh.AuthMethod{ 157 ssh.PublicKeys(signer), 158 }, 159 HostKeyCallback: ssh.InsecureIgnoreHostKey(), // #nosec 160 } 161 Eventually(func() error { 162 client, err := ssh.Dial("tcp", host+":22", config) 163 if err != nil { 164 return err 165 } 166 defer client.Close() 167 168 // Create a new session 169 session, err := client.NewSession() 170 if err != nil { 171 return err 172 } 173 defer session.Close() 174 175 // Check if the file exists 176 if dirExists(session, userDir) { 177 return fmt.Errorf("cleanup not successful, user dir still exists") 178 } 179 return nil 180 }, timeout, interval).Should(Succeed(), "timed out when verifying that the remote host was cleaned up correctly") 181 }) 182 }) 183 }) 184 Describe("aws dynamic allocation", Label("aws-dynamic"), func() { 185 var testNamespace, applicationName, componentName, multiPlatformSecretName, multiPlatformTaskName, dynamicInstanceTag, instanceId string 186 var component *appservice.Component 187 188 AfterAll(func() { 189 // Cleanup aws&ssh secrets and host-config 190 Expect(f.AsKubeAdmin.CommonController.DeleteSecret(ControllerNamespace, AwsSecretName)).To(Succeed()) 191 Expect(f.AsKubeAdmin.CommonController.DeleteSecret(ControllerNamespace, SshSecretName)).To(Succeed()) 192 Expect(f.AsKubeAdmin.CommonController.DeleteConfigMap(HostConfig, ControllerNamespace, true)).To(Succeed()) 193 194 //Forcefully remove instance incase it is not removed by multi-platform-controller 195 err = terminateAwsInstance(instanceId) 196 if err != nil { 197 GinkgoWriter.Printf("error terminating instance again: %v", err) 198 } 199 200 if !CurrentSpecReport().Failed() { 201 Expect(f.AsKubeAdmin.HasController.DeleteComponent(componentName, testNamespace, false)).To(Succeed()) 202 Expect(f.AsKubeAdmin.HasController.DeleteApplication(applicationName, testNamespace, false)).To(Succeed()) 203 Expect(f.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(testNamespace)).To(Succeed()) 204 } else { 205 Expect(f.AsKubeAdmin.CommonController.StoreAllPods(testNamespace)).To(Succeed()) 206 Expect(f.AsKubeAdmin.TektonController.StoreAllPipelineRuns(testNamespace)).To(Succeed()) 207 } 208 }) 209 210 BeforeAll(func() { 211 212 f, err = framework.NewFramework(utils.GetGeneratedNamespace("multi-platform-dynamic")) 213 Expect(err).NotTo(HaveOccurred()) 214 testNamespace = f.UserNamespace 215 Expect(testNamespace).NotTo(BeNil(), "failed to create sandbox user namespace") 216 Expect(err).ShouldNot(HaveOccurred()) 217 218 dynamicInstanceTag := "dynamic-instance-" + util.GenerateRandomString(4) 219 GinkgoWriter.Printf("Generated dynamic instance tag: %q\n", dynamicInstanceTag) 220 221 // Restart multi-platform-controller pod to reload configMap again 222 restartMultiPlatformControllerPod(f) 223 224 err = createConfigMapForDynamicInstance(f, dynamicInstanceTag) 225 Expect(err).ShouldNot(HaveOccurred()) 226 227 err = createSecretsForDynamicInstance(f) 228 Expect(err).ShouldNot(HaveOccurred()) 229 230 err = createBuildPipelineSelector(f, testNamespace, "ARM64") 231 Expect(err).ShouldNot(HaveOccurred()) 232 233 component, applicationName, componentName = createApplicationAndComponent(f, testNamespace) 234 }) 235 236 When("the Component with multi-platform-build is created", func() { 237 It("a PipelineRun is triggered", func() { 238 validatePipelineRunIsRunning(f, componentName, applicationName, testNamespace) 239 }) 240 241 It("the build-container task from component pipelinerun is buildah-remote", func() { 242 multiPlatformTaskName, multiPlatformSecretName = validateBuildContainerTaskIsBuildahRemote(f, componentName, applicationName, testNamespace) 243 }) 244 245 It("The multi platform secret is populated", func() { 246 instanceId = validateMultiPlatformSecretIsPopulated(f, testNamespace, multiPlatformTaskName, multiPlatformSecretName) 247 }) 248 249 It("that PipelineRun completes successfully", func() { 250 Expect(f.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component, "", f.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, nil)).To(Succeed()) 251 }) 252 253 It("check cleanup happened successfully", func() { 254 Eventually(func() error { 255 instances, err := getDynamicAwsInstance(dynamicInstanceTag) 256 if err != nil { 257 return err 258 } 259 if len(instances) != 0 { 260 return fmt.Errorf("instance is not cleaned up properly, current running instances: %v", instances) 261 } 262 return nil 263 }, timeout, interval).Should(Succeed(), "timed out when verifying that the remote host was cleaned up correctly") 264 }) 265 266 }) 267 }) 268 // TODO: Enable the test after https://issues.redhat.com/browse/KFLUXBUGS-1179 is fixed 269 Describe("ibm system z dynamic allocation", Label("ibmz-dynamic"), Pending, func() { 270 var testNamespace, applicationName, componentName, multiPlatformSecretName, multiPlatformTaskName, dynamicInstanceTag, instanceId string 271 var component *appservice.Component 272 273 AfterAll(func() { 274 //Cleanup ibm&ssh secrets and host-config 275 Expect(f.AsKubeAdmin.CommonController.DeleteSecret(ControllerNamespace, IbmSecretName)).To(Succeed()) 276 Expect(f.AsKubeAdmin.CommonController.DeleteSecret(ControllerNamespace, SshSecretName)).To(Succeed()) 277 Expect(f.AsKubeAdmin.CommonController.DeleteConfigMap(HostConfig, ControllerNamespace, true)).To(Succeed()) 278 279 //Forcefully remove instance incase it is not removed by multi-platform-controller 280 err = terminateIbmZInstance(instanceId) 281 if err != nil { 282 GinkgoWriter.Printf("error terminating instance again: %v", err) 283 } 284 285 if !CurrentSpecReport().Failed() { 286 Expect(f.AsKubeAdmin.HasController.DeleteComponent(componentName, testNamespace, false)).To(Succeed()) 287 Expect(f.AsKubeAdmin.HasController.DeleteApplication(applicationName, testNamespace, false)).To(Succeed()) 288 Expect(f.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(testNamespace)).To(Succeed()) 289 } else { 290 Expect(f.AsKubeAdmin.CommonController.StoreAllPods(testNamespace)).To(Succeed()) 291 Expect(f.AsKubeAdmin.TektonController.StoreAllPipelineRuns(testNamespace)).To(Succeed()) 292 } 293 }) 294 295 BeforeAll(func() { 296 297 f, err = framework.NewFramework(utils.GetGeneratedNamespace("multi-platform-ibmz")) 298 Expect(err).NotTo(HaveOccurred()) 299 testNamespace = f.UserNamespace 300 Expect(testNamespace).NotTo(BeNil(), "failed to create sandbox user namespace") 301 Expect(err).ShouldNot(HaveOccurred()) 302 303 restartMultiPlatformControllerPod(f) 304 305 dynamicInstanceTag = "ibmz-instance-" + util.GenerateRandomString(4) 306 err = createConfigMapForIbmZDynamicInstance(f, dynamicInstanceTag) 307 Expect(err).ShouldNot(HaveOccurred()) 308 309 err = createSecretsForIbmDynamicInstance(f) 310 Expect(err).ShouldNot(HaveOccurred()) 311 312 err = createBuildPipelineSelector(f, testNamespace, "S390X") 313 Expect(err).ShouldNot(HaveOccurred()) 314 315 component, applicationName, componentName = createApplicationAndComponent(f, testNamespace) 316 }) 317 318 When("the Component with multi-platform-build is created", func() { 319 It("a PipelineRun is triggered", func() { 320 validatePipelineRunIsRunning(f, componentName, applicationName, testNamespace) 321 }) 322 323 It("the build-container task from component pipelinerun is buildah-remote", func() { 324 multiPlatformTaskName, multiPlatformSecretName = validateBuildContainerTaskIsBuildahRemote(f, componentName, applicationName, testNamespace) 325 }) 326 327 It("The multi platform secret is populated", func() { 328 instanceId = validateMultiPlatformSecretIsPopulated(f, testNamespace, multiPlatformTaskName, multiPlatformSecretName) 329 }) 330 331 It("that PipelineRun completes successfully", func() { 332 Expect(f.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component, "", f.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, nil)).To(Succeed()) 333 }) 334 335 It("check cleanup happened successfully", func() { 336 Eventually(func() error { 337 instances, err := getIbmZDynamicInstances(dynamicInstanceTag) 338 if err != nil { 339 return err 340 } 341 if len(instances) != 0 { 342 return fmt.Errorf("instance is not cleaned up properly, current running instances: %v", instances) 343 } 344 return nil 345 }, timeout, interval).Should(Succeed(), "timed out when verifying that the remote host was cleaned up correctly") 346 }) 347 348 }) 349 }) 350 // TODO: Enable the test after https://issues.redhat.com/browse/KFLUXBUGS-1179 is fixed 351 Describe("ibm power pc dynamic allocation", Label("ibmp-dynamic"), Pending, func() { 352 var testNamespace, applicationName, componentName, multiPlatformSecretName, multiPlatformTaskName, dynamicInstanceTag, instanceId string 353 var component *appservice.Component 354 355 AfterAll(func() { 356 // Cleanup ibm key & ssh secrets and host-config 357 Expect(f.AsKubeAdmin.CommonController.DeleteSecret(ControllerNamespace, IbmSecretName)).To(Succeed()) 358 Expect(f.AsKubeAdmin.CommonController.DeleteSecret(ControllerNamespace, SshSecretName)).To(Succeed()) 359 Expect(f.AsKubeAdmin.CommonController.DeleteConfigMap(HostConfig, ControllerNamespace, true)).To(Succeed()) 360 361 //Forcefully remove instance incase it is not removed by multi-platform-controller 362 err = terminateIbmPInstance(instanceId) 363 if err != nil { 364 GinkgoWriter.Printf("error terminating instance again: %v", err) 365 } 366 367 if !CurrentSpecReport().Failed() { 368 Expect(f.AsKubeAdmin.HasController.DeleteComponent(componentName, testNamespace, false)).To(Succeed()) 369 Expect(f.AsKubeAdmin.HasController.DeleteApplication(applicationName, testNamespace, false)).To(Succeed()) 370 Expect(f.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(testNamespace)).To(Succeed()) 371 } else { 372 Expect(f.AsKubeAdmin.CommonController.StoreAllPods(testNamespace)).To(Succeed()) 373 Expect(f.AsKubeAdmin.TektonController.StoreAllPipelineRuns(testNamespace)).To(Succeed()) 374 } 375 }) 376 377 BeforeAll(func() { 378 379 f, err = framework.NewFramework(utils.GetGeneratedNamespace("multi-platform-ibmp")) 380 Expect(err).NotTo(HaveOccurred()) 381 testNamespace = f.UserNamespace 382 Expect(testNamespace).NotTo(BeNil(), "failed to create sandbox user namespace") 383 Expect(err).ShouldNot(HaveOccurred()) 384 385 // Restart multi-platform-controller pod to reload configMap again 386 restartMultiPlatformControllerPod(f) 387 388 dynamicInstanceTag = "ibmp-instance-" + util.GenerateRandomString(4) 389 err = createConfigMapForIbmPDynamicInstance(f, dynamicInstanceTag) 390 Expect(err).ShouldNot(HaveOccurred()) 391 392 err = createSecretsForIbmDynamicInstance(f) 393 Expect(err).ShouldNot(HaveOccurred()) 394 395 err = createBuildPipelineSelector(f, testNamespace, "PPC64LE") 396 Expect(err).ShouldNot(HaveOccurred()) 397 398 component, applicationName, componentName = createApplicationAndComponent(f, testNamespace) 399 }) 400 401 When("the Component with multi-platform-build is created", func() { 402 It("a PipelineRun is triggered", func() { 403 validatePipelineRunIsRunning(f, componentName, applicationName, testNamespace) 404 }) 405 406 It("the build-container task from component pipelinerun is buildah-remote", func() { 407 multiPlatformTaskName, multiPlatformSecretName = validateBuildContainerTaskIsBuildahRemote(f, componentName, applicationName, testNamespace) 408 }) 409 410 It("The multi platform secret is populated", func() { 411 instanceId = validateMultiPlatformSecretIsPopulated(f, testNamespace, multiPlatformTaskName, multiPlatformSecretName) 412 }) 413 414 It("that PipelineRun completes successfully", func() { 415 Expect(f.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component, "", f.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, nil)).To(Succeed()) 416 }) 417 418 It("check cleanup happened successfully", func() { 419 Eventually(func() error { 420 count, err := getIbmPDynamicInstanceCount(dynamicInstanceTag) 421 if err != nil { 422 return err 423 } 424 if count != 0 { 425 return fmt.Errorf("instance is not cleaned up properly, running instances count: %d", count) 426 } 427 return nil 428 }, timeout, interval).Should(Succeed(), "timed out when verifying that the remote host was cleaned up correctly") 429 }) 430 431 }) 432 }) 433 }) 434 435 func createApplicationAndComponent(f *framework.Framework, testNamespace string) (component *appservice.Component, applicationName, componentName string) { 436 applicationName = fmt.Sprintf("multi-platform-suite-application-%s", util.GenerateRandomString(4)) 437 _, err := f.AsKubeAdmin.HasController.CreateApplication(applicationName, testNamespace) 438 Expect(err).NotTo(HaveOccurred()) 439 440 componentName = fmt.Sprintf("multi-platform-suite-component-%s", util.GenerateRandomString(4)) 441 442 // Create a component with Git Source URL being defined 443 componentObj := appservice.ComponentSpec{ 444 ComponentName: componentName, 445 Source: appservice.ComponentSource{ 446 ComponentSourceUnion: appservice.ComponentSourceUnion{ 447 GitSource: &appservice.GitSource{ 448 URL: multiPlatformProjectGitUrl, 449 Revision: multiPlatformProjectRevision, 450 }, 451 }, 452 }, 453 } 454 component, err = f.AsKubeAdmin.HasController.CreateComponent(componentObj, testNamespace, "", "", applicationName, true, map[string]string{}) 455 Expect(err).ShouldNot(HaveOccurred()) 456 return 457 } 458 459 func validateMultiPlatformSecretIsPopulated(f *framework.Framework, testNamespace, multiPlatformTaskName, multiPlatformSecretName string) (instanceId string) { 460 Eventually(func() error { 461 _, err := f.AsKubeAdmin.CommonController.GetSecret(testNamespace, multiPlatformSecretName) 462 if err != nil { 463 return err 464 } 465 return nil 466 }, timeout, interval).Should(Succeed(), "timed out when verifying the secret is created") 467 468 // Get the instance id from the task so that we can check during cleanup 469 taskRun, err := f.AsKubeDeveloper.TektonController.GetTaskRun(multiPlatformTaskName, testNamespace) 470 Expect(err).ShouldNot(HaveOccurred()) 471 instanceId = taskRun.Annotations["build.appstudio.redhat.com/cloud-instance-id"] 472 GinkgoWriter.Printf("INSTANCE ID: %s\n", instanceId) 473 Expect(instanceId).ShouldNot(BeEmpty()) 474 return 475 } 476 477 func validateBuildContainerTaskIsBuildahRemote(f *framework.Framework, componentName, applicationName, testNamespace string) (multiPlatformTaskName, multiPlatformSecretName string) { 478 Eventually(func() error { 479 pr, err := f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "") 480 Expect(err).ShouldNot(HaveOccurred()) 481 482 for _, chr := range pr.Status.ChildReferences { 483 taskRun := &pipeline.TaskRun{} 484 taskRunKey := types.NamespacedName{Namespace: pr.Namespace, Name: chr.Name} 485 err := f.AsKubeAdmin.CommonController.KubeRest().Get(context.TODO(), taskRunKey, taskRun) 486 Expect(err).ShouldNot(HaveOccurred()) 487 488 prTrStatus := &pipeline.PipelineRunTaskRunStatus{ 489 PipelineTaskName: chr.PipelineTaskName, 490 Status: &taskRun.Status, 491 } 492 493 if chr.PipelineTaskName == constants.BuildTaskRunName && prTrStatus.Status != nil && prTrStatus.Status.TaskSpec != nil && prTrStatus.Status.TaskSpec.Volumes != nil { 494 multiPlatformTaskName = chr.Name 495 for _, vol := range prTrStatus.Status.TaskSpec.Volumes { 496 if vol.Secret != nil && strings.HasPrefix(vol.Secret.SecretName, "multi-platform-ssh-") { 497 multiPlatformSecretName = vol.Secret.SecretName 498 return nil 499 } 500 } 501 } 502 } 503 return fmt.Errorf("couldn't find a matching step buildah-remote or ssh secret attached as a volume in the task %s in PipelineRun %s/%s", constants.BuildTaskRunName, testNamespace, pr.GetName()) 504 }, timeout, interval).Should(Succeed(), "timed out when verifying the buildah-remote image reference in pipelinerun") 505 return 506 } 507 508 func validatePipelineRunIsRunning(f *framework.Framework, componentName, applicationName, testNamespace string) { 509 Eventually(func() error { 510 pr, err := f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "") 511 if err != nil { 512 GinkgoWriter.Printf("PipelineRun has not been created yet for the component %s/%s", testNamespace, componentName) 513 return err 514 } 515 if !pr.HasStarted() { 516 return fmt.Errorf("pipelinerun %s/%s hasn't started yet", pr.GetNamespace(), pr.GetName()) 517 } 518 return nil 519 }, timeout, constants.PipelineRunPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for the PipelineRun to start for the component %s/%s", testNamespace, componentName)) 520 } 521 522 func restartMultiPlatformControllerPod(f *framework.Framework) { 523 // Restart multi-platform-controller pod to reload configMap again 524 podList, err := f.AsKubeAdmin.CommonController.ListAllPods(ControllerNamespace) 525 Expect(err).ShouldNot(HaveOccurred()) 526 for i := range podList.Items { 527 podName := podList.Items[i].Name 528 if strings.HasPrefix(podName, ControllerNamespace) { 529 err := f.AsKubeAdmin.CommonController.DeletePod(podName, ControllerNamespace) 530 Expect(err).ShouldNot(HaveOccurred()) 531 } 532 } 533 time.Sleep(10 * time.Second) 534 //check that multi-platform-controller pod is running 535 Eventually(func() (bool, error) { 536 podList, err := f.AsKubeAdmin.CommonController.ListAllPods(ControllerNamespace) 537 if err != nil { 538 return false, err 539 } 540 for i := range podList.Items { 541 podName := podList.Items[i].Name 542 if strings.HasPrefix(podName, ControllerNamespace) { 543 pod, err := f.AsKubeAdmin.CommonController.GetPod(ControllerNamespace, podName) 544 if err != nil { 545 return false, err 546 } 547 if pod.Status.Phase == v1.PodRunning { 548 return true, nil 549 } 550 } 551 } 552 return false, nil 553 }, 1*time.Minute, 5*time.Second).Should(BeTrue(), "timed out while checking if the pod is running") 554 } 555 556 func pCloudId() string { 557 return strings.Split(strings.Split(CRN, "/")[1], ":")[1] 558 } 559 560 func getIbmPDynamicInstanceCount(instanceTag string) (int, error) { 561 apiKey := os.Getenv("MULTI_PLATFORM_IBM_API_KEY") 562 serviceOptions := &core.ServiceOptions{ 563 URL: IbmPUrl, 564 Authenticator: &core.IamAuthenticator{ 565 ApiKey: apiKey, 566 }, 567 } 568 baseService, err := core.NewBaseService(serviceOptions) 569 if err != nil { 570 return 0, err 571 } 572 573 builder := core.NewRequestBuilder(core.GET) 574 builder = builder.WithContext(context.Background()) 575 builder.EnableGzipCompression = baseService.GetEnableGzipCompression() 576 577 pathParamsMap := map[string]string{ 578 "cloud": pCloudId(), 579 } 580 _, err = builder.ResolveRequestURL(IbmPUrl, `/pcloud/v1/cloud-instances/{cloud}/pvm-instances`, pathParamsMap) 581 if err != nil { 582 return 0, err 583 } 584 builder.AddHeader("CRN", CRN) 585 builder.AddHeader("Accept", "application/json") 586 587 request, err := builder.Build() 588 if err != nil { 589 return 0, err 590 } 591 592 var rawResponse map[string]json.RawMessage 593 _, err = baseService.Request(request, &rawResponse) 594 if err != nil { 595 return 0, err 596 } 597 instancesData := rawResponse["pvmInstances"] 598 instances := []json.RawMessage{} 599 err = json.Unmarshal(instancesData, &instances) 600 if err != nil { 601 return 0, err 602 } 603 count := 0 604 type Instance struct { 605 ServerName string 606 } 607 singleInstance := &Instance{} 608 for i := range instances { 609 err = json.Unmarshal(instances[i], singleInstance) 610 if err != nil { 611 return 0, err 612 } 613 if strings.HasPrefix(singleInstance.ServerName, instanceTag) { 614 count++ 615 } 616 } 617 return count, nil 618 } 619 620 func terminateIbmPInstance(instanceId string) error { 621 apiKey := os.Getenv("MULTI_PLATFORM_IBM_API_KEY") 622 serviceOptions := &core.ServiceOptions{ 623 URL: IbmPUrl, 624 Authenticator: &core.IamAuthenticator{ 625 ApiKey: apiKey, 626 }, 627 } 628 baseService, err := core.NewBaseService(serviceOptions) 629 if err != nil { 630 return err 631 } 632 633 builder := core.NewRequestBuilder(core.DELETE) 634 builder = builder.WithContext(context.Background()) 635 builder.EnableGzipCompression = baseService.GetEnableGzipCompression() 636 637 pathParamsMap := map[string]string{ 638 "cloud": pCloudId(), 639 "pvm_instance_id": instanceId, 640 } 641 _, err = builder.ResolveRequestURL(IbmPUrl, `/pcloud/v1/cloud-instances/{cloud}/pvm-instances/{pvm_instance_id}`, pathParamsMap) 642 if err != nil { 643 return err 644 } 645 builder.AddQuery("delete_data_volumes", "true") 646 builder.AddHeader("CRN", CRN) 647 builder.AddHeader("Accept", "application/json") 648 649 request, err := builder.Build() 650 if err != nil { 651 return err 652 } 653 654 var rawResponse map[string]json.RawMessage 655 _, err = baseService.Request(request, &rawResponse) 656 if err != nil { 657 if err.Error() == "pvm-instance not found" { 658 return nil 659 } 660 return err 661 } 662 return nil 663 } 664 func getIbmZDynamicInstances(instanceTag string) ([]string, error) { 665 apiKey := os.Getenv("MULTI_PLATFORM_IBM_API_KEY") 666 if apiKey == "" { 667 return nil, fmt.Errorf("ibm api key is not set") 668 } 669 // Instantiate the service with an API key based IAM authenticator 670 vpcService, err := vpcv1.NewVpcV1(&vpcv1.VpcV1Options{ 671 URL: IbmZUrl, 672 Authenticator: &core.IamAuthenticator{ 673 ApiKey: apiKey, 674 }, 675 }) 676 if err != nil { 677 return nil, err 678 } 679 // Lookup VPC 680 vpcs, _, err := vpcService.ListVpcs(&vpcv1.ListVpcsOptions{}) 681 if err != nil { 682 return nil, err 683 } 684 var vpc *vpcv1.VPC 685 for i := range vpcs.Vpcs { 686 //GinkgoWriter.Println("VPC: " + *vpcs.Vpcs[i].Name) 687 if *vpcs.Vpcs[i].Name == IbmVpc { 688 vpc = &vpcs.Vpcs[i] 689 break 690 } 691 } 692 if vpc == nil { 693 return nil, fmt.Errorf("failed to find VPC %s", IbmVpc) 694 } 695 696 instances, _, err := vpcService.ListInstances(&vpcv1.ListInstancesOptions{ResourceGroupID: vpc.ResourceGroup.ID, VPCName: &IbmVpc}) 697 if err != nil { 698 return nil, err 699 } 700 var instanceIds []string 701 for _, instance := range instances.Instances { 702 if strings.HasPrefix(*instance.Name, instanceTag) { 703 instanceIds = append(instanceIds, *instance.ID) 704 } 705 } 706 return instanceIds, nil 707 } 708 709 func terminateIbmZInstance(instanceId string) error { 710 apiKey := os.Getenv("MULTI_PLATFORM_IBM_API_KEY") 711 if apiKey == "" { 712 return fmt.Errorf("ibm api key is not set correctly") 713 } 714 vpcService, err := vpcv1.NewVpcV1(&vpcv1.VpcV1Options{ 715 URL: IbmZUrl, 716 Authenticator: &core.IamAuthenticator{ 717 ApiKey: apiKey, 718 }, 719 }) 720 if err != nil { 721 return err 722 } 723 instance, _, err := vpcService.GetInstance(&vpcv1.GetInstanceOptions{ID: &instanceId}) 724 if err != nil { 725 if err.Error() == "Instance not found" { 726 return nil 727 } 728 GinkgoWriter.Printf("failed to delete system z instance, unable to get instance with error: %v\n", err) 729 return err 730 } 731 _, err = vpcService.DeleteInstance(&vpcv1.DeleteInstanceOptions{ID: instance.ID}) 732 if err != nil { 733 GinkgoWriter.Printf("failed to delete system z instance: %v\n", err) 734 return err 735 } 736 return nil 737 } 738 739 func createConfigMapForIbmZDynamicInstance(f *framework.Framework, instanceTag string) error { 740 hostConfig := &v1.ConfigMap{} 741 hostConfig.Name = HostConfig 742 hostConfig.Namespace = ControllerNamespace 743 hostConfig.Labels = map[string]string{MultiPlatformConfigKey: "hosts"} 744 745 hostConfig.Data = map[string]string{} 746 hostConfig.Data["dynamic-platforms"] = "linux/s390x" 747 hostConfig.Data["instance-tag"] = instanceTag 748 hostConfig.Data["dynamic.linux-s390x.type"] = "ibmz" 749 hostConfig.Data["dynamic.linux-s390x.ssh-secret"] = SshSecretName 750 hostConfig.Data["dynamic.linux-s390x.secret"] = IbmSecretName 751 hostConfig.Data["dynamic.linux-s390x.vpc"] = IbmVpc 752 hostConfig.Data["dynamic.linux-s390x.key"] = IbmKey 753 hostConfig.Data["dynamic.linux-s390x.subnet"] = "us-east-2-default-subnet" 754 hostConfig.Data["dynamic.linux-s390x.image-id"] = "r014-17c957e0-01a1-4f7f-bc24-191f5f10eba8" 755 hostConfig.Data["dynamic.linux-s390x.region"] = "us-east-2" 756 hostConfig.Data["dynamic.linux-s390x.url"] = IbmZUrl 757 hostConfig.Data["dynamic.linux-s390x.profile"] = "bz2-1x4" 758 hostConfig.Data["dynamic.linux-s390x.max-instances"] = "1" 759 760 _, err := f.AsKubeAdmin.CommonController.CreateConfigMap(hostConfig, ControllerNamespace) 761 if err != nil { 762 return fmt.Errorf("error while creating config map for dynamic instance: %v", err) 763 } 764 return nil 765 } 766 767 func createSecretsForIbmDynamicInstance(f *framework.Framework) error { 768 ibmKey := v1.Secret{} 769 ibmKey.Name = "ibmkey" 770 ibmKey.Namespace = ControllerNamespace 771 ibmKey.Labels = map[string]string{MultiPlatformSecretKey: "true"} 772 ibmKey.StringData = map[string]string{ 773 "api-key": os.Getenv("MULTI_PLATFORM_IBM_API_KEY"), 774 } 775 _, err := f.AsKubeAdmin.CommonController.CreateSecret(ControllerNamespace, &ibmKey) 776 if err != nil { 777 return fmt.Errorf("error creating secret with api_key: %v", err) 778 } 779 780 sshKeys := v1.Secret{} 781 sshKeys.Name = SshSecretName 782 sshKeys.Namespace = ControllerNamespace 783 sshKeys.Labels = map[string]string{MultiPlatformSecretKey: "true"} 784 sshKeys.StringData = map[string]string{"id_rsa": os.Getenv("MULTI_PLATFORM_AWS_SSH_KEY")} 785 _, err = f.AsKubeAdmin.CommonController.CreateSecret(ControllerNamespace, &sshKeys) 786 if err != nil { 787 return fmt.Errorf("error creating secret with ssh private key: %v", err) 788 } 789 return nil 790 } 791 792 func createConfigMapForIbmPDynamicInstance(f *framework.Framework, instanceTag string) error { 793 hostConfig := &v1.ConfigMap{} 794 hostConfig.Name = HostConfig 795 hostConfig.Namespace = ControllerNamespace 796 hostConfig.Labels = map[string]string{MultiPlatformConfigKey: "hosts"} 797 798 hostConfig.Data = map[string]string{} 799 hostConfig.Data["dynamic-platforms"] = "linux/ppc64le" 800 hostConfig.Data["instance-tag"] = instanceTag 801 hostConfig.Data["dynamic.linux-ppc64le.type"] = "ibmp" 802 hostConfig.Data["dynamic.linux-ppc64le.ssh-secret"] = SshSecretName 803 hostConfig.Data["dynamic.linux-ppc64le.secret"] = "ibmkey" 804 hostConfig.Data["dynamic.linux-ppc64le.key"] = IbmKey 805 hostConfig.Data["dynamic.linux-ppc64le.image"] = "sdouglas-rhel-test" 806 hostConfig.Data["dynamic.linux-ppc64le.crn"] = CRN 807 hostConfig.Data["dynamic.linux-ppc64le.url"] = IbmPUrl 808 hostConfig.Data["dynamic.linux-ppc64le.network"] = "dff71085-73da-49f5-9bf2-5ea60c66c99b" 809 hostConfig.Data["dynamic.linux-ppc64le.system"] = "e980" 810 hostConfig.Data["dynamic.linux-ppc64le.cores"] = "0.25" 811 hostConfig.Data["dynamic.linux-ppc64le.memory"] = "2" 812 hostConfig.Data["dynamic.linux-ppc64le.max-instances"] = "2" 813 814 _, err := f.AsKubeAdmin.CommonController.CreateConfigMap(hostConfig, ControllerNamespace) 815 if err != nil { 816 return fmt.Errorf("error while creating config map for dynamic instance: %v", err) 817 } 818 return nil 819 } 820 821 // Function to check if a file exists on the remote host 822 func dirExists(session *ssh.Session, dirPath string) bool { 823 cmd := fmt.Sprintf("[ -d %s ] && echo 'exists'", dirPath) 824 output, err := session.CombinedOutput(cmd) 825 if err != nil { 826 fmt.Fprintf(os.Stderr, "Error running command: %s\n", err) 827 return false 828 } 829 return string(output) == "exists\n" 830 } 831 832 // Get AWS instances that are running 833 // These are identified by tag 834 func getHostPoolAwsInstances() ([]string, error) { 835 cfg, err := config.LoadDefaultConfig(context.TODO(), 836 config.WithCredentialsProvider(EnvCredentialsProvider{}), 837 config.WithRegion(AwsRegion)) 838 if err != nil { 839 return nil, err 840 } 841 842 // Create an EC2 client 843 ec2Client := ec2.NewFromConfig(cfg) 844 res, err := ec2Client.DescribeInstances(context.TODO(), &ec2.DescribeInstancesInput{Filters: []ec2types.Filter{{Name: aws.String("tag:" + Ec2ArmTag), Values: []string{"true"}}}}) 845 if err != nil { 846 return nil, err 847 } 848 ret := []string{} 849 for _, res := range res.Reservations { 850 for _, inst := range res.Instances { 851 if inst.State.Name != ec2types.InstanceStateNameTerminated { 852 ret = append(ret, *inst.PublicDnsName) 853 } 854 } 855 } 856 return ret, nil 857 } 858 859 func createConfigMapForHostPool(f *framework.Framework) error { 860 armInstances, err := getHostPoolAwsInstances() 861 if err != nil { 862 return fmt.Errorf("error getting aws host pool instances: %v", err) 863 } 864 hostConfig := &v1.ConfigMap{} 865 hostConfig.Name = HostConfig 866 hostConfig.Namespace = ControllerNamespace 867 hostConfig.Labels = map[string]string{MultiPlatformConfigKey: "hosts"} 868 869 hostConfig.Data = map[string]string{} 870 count := 0 871 for _, instance := range armInstances { 872 hostConfig.Data[fmt.Sprintf("host.aws-arm64-%d.address", count)] = instance 873 hostConfig.Data[fmt.Sprintf("host.aws-arm64-%d.platform", count)] = AwsPlatform 874 hostConfig.Data[fmt.Sprintf("host.aws-arm64-%d.user", count)] = Ec2User 875 hostConfig.Data[fmt.Sprintf("host.aws-arm64-%d.secret", count)] = AwsSecretName 876 hostConfig.Data[fmt.Sprintf("host.aws-arm64-%d.concurrency", count)] = "4" 877 count++ 878 } 879 880 _, err = f.AsKubeAdmin.CommonController.CreateConfigMap(hostConfig, ControllerNamespace) 881 if err != nil { 882 return fmt.Errorf("error creating host-pool config map: %v", err) 883 } 884 return nil 885 } 886 887 func createConfigMapForDynamicInstance(f *framework.Framework, instanceTag string) error { 888 hostConfig := &v1.ConfigMap{} 889 hostConfig.Name = HostConfig 890 hostConfig.Namespace = ControllerNamespace 891 hostConfig.Labels = map[string]string{MultiPlatformConfigKey: "hosts"} 892 893 hostConfig.Data = map[string]string{} 894 hostConfig.Data["dynamic-platforms"] = AwsPlatform 895 hostConfig.Data["instance-tag"] = instanceTag 896 hostConfig.Data["dynamic.linux-arm64.type"] = "aws" 897 hostConfig.Data["dynamic.linux-arm64.region"] = AwsRegion 898 hostConfig.Data["dynamic.linux-arm64.ami"] = "ami-09d5d0912f52f9514" 899 hostConfig.Data["dynamic.linux-arm64.instance-type"] = "t4g.micro" 900 hostConfig.Data["dynamic.linux-arm64.key-name"] = "multi-platform-e2e" 901 hostConfig.Data["dynamic.linux-arm64.aws-secret"] = AwsSecretName 902 hostConfig.Data["dynamic.linux-arm64.ssh-secret"] = SshSecretName 903 hostConfig.Data["dynamic.linux-arm64.security-group"] = "launch-wizard-7" 904 hostConfig.Data["dynamic.linux-arm64.max-instances"] = DynamicMaxInstances 905 906 _, err := f.AsKubeAdmin.CommonController.CreateConfigMap(hostConfig, ControllerNamespace) 907 if err != nil { 908 return fmt.Errorf("error while creating config map for dynamic instance: %v", err) 909 } 910 return nil 911 } 912 913 func createSecretForHostPool(f *framework.Framework) error { 914 keys := v1.Secret{} 915 keys.Name = AwsSecretName 916 keys.Namespace = ControllerNamespace 917 keys.Labels = map[string]string{MultiPlatformSecretKey: "true"} 918 keys.StringData = map[string]string{"id_rsa": os.Getenv("MULTI_PLATFORM_AWS_SSH_KEY")} 919 _, err := f.AsKubeAdmin.CommonController.CreateSecret(ControllerNamespace, &keys) 920 if err != nil { 921 return fmt.Errorf("error while creating host-pool secret: %v", err) 922 } 923 return nil 924 } 925 926 func createSecretsForDynamicInstance(f *framework.Framework) error { 927 awsKeys := v1.Secret{} 928 awsKeys.Name = AwsSecretName 929 awsKeys.Namespace = ControllerNamespace 930 awsKeys.Labels = map[string]string{MultiPlatformSecretKey: "true"} 931 awsKeys.StringData = map[string]string{ 932 "access-key-id": os.Getenv("MULTI_PLATFORM_AWS_ACCESS_KEY"), 933 "secret-access-key": os.Getenv("MULTI_PLATFORM_AWS_SECRET_ACCESS_KEY"), 934 } 935 _, err := f.AsKubeAdmin.CommonController.CreateSecret(ControllerNamespace, &awsKeys) 936 if err != nil { 937 return fmt.Errorf("error creating secret with access_key and secret_key: %v", err) 938 } 939 940 sshKeys := v1.Secret{} 941 sshKeys.Name = SshSecretName 942 sshKeys.Namespace = ControllerNamespace 943 sshKeys.Labels = map[string]string{MultiPlatformSecretKey: "true"} 944 sshKeys.StringData = map[string]string{"id_rsa": os.Getenv("MULTI_PLATFORM_AWS_SSH_KEY")} 945 _, err = f.AsKubeAdmin.CommonController.CreateSecret(ControllerNamespace, &sshKeys) 946 if err != nil { 947 return fmt.Errorf("error creating secret with ssh private key: %v", err) 948 } 949 return nil 950 } 951 952 func createBuildPipelineSelector(f *framework.Framework, namespace string, platform string) error { 953 trueBool := true 954 customBuildahRemotePipeline := os.Getenv(constants.CUSTOM_BUILDAH_REMOTE_PIPELINE_BUILD_BUNDLE_ENV + "_" + platform) 955 Expect(customBuildahRemotePipeline).ShouldNot(BeEmpty()) 956 if customBuildahRemotePipeline == "" { 957 return fmt.Errorf("remote build pipeline bundle is empty") 958 } 959 ps := &buildservice.BuildPipelineSelector{ 960 ObjectMeta: metav1.ObjectMeta{ 961 Name: "build-pipeline-selector", 962 Namespace: namespace, 963 }, 964 Spec: buildservice.BuildPipelineSelectorSpec{Selectors: []buildservice.PipelineSelector{ 965 { 966 Name: "custom remote-buildah selector", 967 PipelineRef: *tekton.NewBundleResolverPipelineRef("buildah-remote-pipeline", customBuildahRemotePipeline), 968 WhenConditions: buildservice.WhenCondition{DockerfileRequired: &trueBool}, 969 }, 970 }}, 971 } 972 err := f.AsKubeAdmin.CommonController.KubeRest().Create(context.TODO(), ps) 973 if err != nil { 974 return fmt.Errorf("error creating build pipeline selector: %v", err) 975 } 976 return nil 977 } 978 979 func terminateAwsInstance(instanceId string) error { 980 cfg, err := config.LoadDefaultConfig(context.TODO(), 981 config.WithCredentialsProvider(EnvCredentialsProvider{}), 982 config.WithRegion(AwsRegion)) 983 if err != nil { 984 return err 985 } 986 // Create an EC2 client 987 ec2Client := ec2.NewFromConfig(cfg) 988 //Terminate Instance 989 _, err = ec2Client.TerminateInstances(context.TODO(), &ec2.TerminateInstancesInput{InstanceIds: []string{string(instanceId)}}) 990 return err 991 } 992 993 func getDynamicAwsInstance(tagName string) ([]string, error) { 994 cfg, err := config.LoadDefaultConfig(context.TODO(), 995 config.WithCredentialsProvider(EnvCredentialsProvider{}), 996 config.WithRegion(AwsRegion)) 997 if err != nil { 998 return nil, err 999 } 1000 1001 // Create an EC2 client 1002 ec2Client := ec2.NewFromConfig(cfg) 1003 res, err := ec2Client.DescribeInstances(context.TODO(), &ec2.DescribeInstancesInput{Filters: []ec2types.Filter{{Name: aws.String("tag:" + "multi-platform-instance"), Values: []string{tagName}}}}) 1004 if err != nil { 1005 return nil, err 1006 } 1007 ret := []string{} 1008 for _, res := range res.Reservations { 1009 for _, inst := range res.Instances { 1010 if inst.State.Name != ec2types.InstanceStateNameTerminated { 1011 ret = append(ret, *inst.PublicDnsName) 1012 } 1013 } 1014 } 1015 return ret, nil 1016 } 1017 1018 type EnvCredentialsProvider struct { 1019 } 1020 1021 func (r EnvCredentialsProvider) Retrieve(ctx context.Context) (aws.Credentials, error) { 1022 return aws.Credentials{AccessKeyID: os.Getenv("MULTI_PLATFORM_AWS_ACCESS_KEY"), SecretAccessKey: os.Getenv("MULTI_PLATFORM_AWS_SECRET_ACCESS_KEY")}, nil 1023 }