diff --git a/test/e2e/v2/cmd/create-guests/main.go b/test/e2e/v2/cmd/create-guests/main.go index c3be0061dea6..526c82c8b4ea 100644 --- a/test/e2e/v2/cmd/create-guests/main.go +++ b/test/e2e/v2/cmd/create-guests/main.go @@ -251,6 +251,7 @@ func buildCreateArgs(cfg envConfig, name string, spec lifecycle.ClusterSpec) []s args := []string{ "create", "cluster", cfg.platform.Name(), "--name=" + name, + "--namespace=" + cfg.namespace, "--node-pool-replicas=" + strconv.Itoa(cfg.nodeCount), "--base-domain=" + cfg.baseDomain, "--pull-secret=" + cfg.pullSecret, diff --git a/test/e2e/v2/cmd/destroy-guests/main.go b/test/e2e/v2/cmd/destroy-guests/main.go index 5f317b16ab50..a66d055efb09 100644 --- a/test/e2e/v2/cmd/destroy-guests/main.go +++ b/test/e2e/v2/cmd/destroy-guests/main.go @@ -52,6 +52,11 @@ func main() { hypershiftBin = "hypershift" } + namespace := os.Getenv("HYPERSHIFT_NAMESPACE") + if namespace == "" { + namespace = "clusters" + } + specs := platform.ClusterSpecs("", "") log.Printf("Destroying %d clusters derived from PROW_JOB_ID=%s", len(specs), prowJobID) @@ -67,7 +72,7 @@ func main() { wg.Add(1) go func() { defer wg.Done() - if err := destroyCluster(hypershiftBin, clusterName, spec.Variant, platform); err != nil { + if err := destroyCluster(hypershiftBin, clusterName, namespace, spec.Variant, platform); err != nil { log.Printf("WARNING: Failed to destroy cluster %s (%s): %v", clusterName, spec.Variant, err) log.Printf("ACTION REQUIRED: cloud resources for cluster %s may be orphaned and need manual cleanup (resource group, DNS records, etc.)", clusterName) mu.Lock() @@ -85,12 +90,13 @@ func main() { log.Printf("All clusters destroyed successfully") } -func destroyCluster(hypershiftBin, name, variant string, platform lifecycle.PlatformConfig) error { +func destroyCluster(hypershiftBin, name, namespace, variant string, platform lifecycle.PlatformConfig) error { log.Printf("Destroying cluster %s (%s)", name, variant) args := []string{ "destroy", "cluster", platform.Name(), "--name=" + name, + "--namespace=" + namespace, "--cluster-grace-period=" + clusterGracePeriod, } args = append(args, platform.DestroyArgs()...) diff --git a/test/e2e/v2/cmd/dump-guests/main.go b/test/e2e/v2/cmd/dump-guests/main.go index 3f0fec53999d..af1dd96e2049 100644 --- a/test/e2e/v2/cmd/dump-guests/main.go +++ b/test/e2e/v2/cmd/dump-guests/main.go @@ -52,6 +52,11 @@ func main() { log.Fatalf("Failed to initialize platform config: %v", err) } + namespace := os.Getenv("HYPERSHIFT_NAMESPACE") + if namespace == "" { + namespace = "clusters" + } + specs := platform.ClusterSpecs("", "") log.Printf("Dumping %d clusters derived from PROW_JOB_ID=%s", len(specs), prowJobID) @@ -61,7 +66,7 @@ func main() { wg.Add(1) go func() { defer wg.Done() - dumpCluster(*hypershiftBinary, artifactDir, clusterName) + dumpCluster(*hypershiftBinary, artifactDir, clusterName, namespace) }() } wg.Wait() @@ -69,7 +74,7 @@ func main() { log.Println("All cluster dumps complete") } -func dumpCluster(hypershiftBinary, artifactDir, clusterName string) { +func dumpCluster(hypershiftBinary, artifactDir, clusterName, namespace string) { dumpDir := filepath.Join(artifactDir, clusterName) if err := os.MkdirAll(dumpDir, 0755); err != nil { log.Printf("WARNING: Failed to create artifact directory %s: %v", dumpDir, err) @@ -81,6 +86,7 @@ func dumpCluster(hypershiftBinary, artifactDir, clusterName string) { "--artifact-dir=" + dumpDir, "--dump-guest-cluster=true", "--name=" + clusterName, + "--namespace=" + namespace, } log.Printf("Dumping cluster %s -> %s", clusterName, dumpDir) diff --git a/test/e2e/v2/cmd/run-tests/main.go b/test/e2e/v2/cmd/run-tests/main.go index 260b82a43593..de258128b698 100644 --- a/test/e2e/v2/cmd/run-tests/main.go +++ b/test/e2e/v2/cmd/run-tests/main.go @@ -18,9 +18,9 @@ import ( ) const ( - testBinary = "bin/test-e2e-v2" - clusterNS = "clusters" - defaultVerbose = "false" + defaultTestBinary = "bin/test-e2e-v2" + defaultNamespace = "clusters" + defaultVerbose = "false" defaultGinkgoTimeout = "3h" ) @@ -30,13 +30,27 @@ type testResult struct { err error } +func resolveTestBinary() string { + if binDir := os.Getenv("E2EV2_BIN_DIR"); binDir != "" { + return filepath.Join(binDir, "test-e2e-v2") + } + return defaultTestBinary +} + func main() { log.SetFlags(log.LstdFlags) + testBinary := resolveTestBinary() + sharedDir := requireEnv("SHARED_DIR") artifactDir := requireEnv("ARTIFACT_DIR") releaseImage := os.Getenv("RELEASE_IMAGE_LATEST") + namespace := os.Getenv("HYPERSHIFT_NAMESPACE") + if namespace == "" { + namespace = defaultNamespace + } + eventuallyVerbose := os.Getenv("EVENTUALLY_VERBOSE") if eventuallyVerbose == "" { eventuallyVerbose = defaultVerbose @@ -67,7 +81,7 @@ func main() { defer wg.Done() clusterName := readClusterName(sharedDir, g.ClusterFile) log.Printf("Running %s tests against %s...", g.Name, clusterName) - err := runTestBinary(clusterName, g.LabelFilter, g.Skip, + err := runTestBinary(testBinary, clusterName, namespace, g.LabelFilter, g.Skip, filepath.Join(artifactDir, g.JUnitFile), g.ExtraEnv) mu.Lock() results = append(results, testResult{name: g.Name, err: err}) @@ -90,7 +104,7 @@ func main() { for i, step := range sg.Steps { clusterName := readClusterName(sharedDir, step.ClusterFile) log.Printf("Running %s tests against %s...", step.Name, clusterName) - err := runTestBinary(clusterName, step.LabelFilter, step.Skip, + err := runTestBinary(testBinary, clusterName, namespace, step.LabelFilter, step.Skip, filepath.Join(artifactDir, step.JUnitFile), step.ExtraEnv) mu.Lock() results = append(results, testResult{name: step.Name, err: err}) @@ -126,7 +140,7 @@ func main() { log.Println("All test groups passed") } -func runTestBinary(clusterName, labelFilter, skip, junitPath string, extraEnv []string) error { +func runTestBinary(testBinary, clusterName, namespace, labelFilter, skip, junitPath string, extraEnv []string) error { ginkgoTimeout := os.Getenv("GINKGO_TIMEOUT") if ginkgoTimeout == "" { ginkgoTimeout = defaultGinkgoTimeout @@ -148,7 +162,7 @@ func runTestBinary(clusterName, labelFilter, skip, junitPath string, extraEnv [] cmd.Env = append(os.Environ(), fmt.Sprintf("E2E_HOSTED_CLUSTER_NAME=%s", clusterName), - fmt.Sprintf("E2E_HOSTED_CLUSTER_NAMESPACE=%s", clusterNS), + fmt.Sprintf("E2E_HOSTED_CLUSTER_NAMESPACE=%s", namespace), ) cmd.Env = append(cmd.Env, extraEnv...) diff --git a/test/e2e/v2/lifecycle/aws.go b/test/e2e/v2/lifecycle/aws.go new file mode 100644 index 000000000000..f2547b820c3d --- /dev/null +++ b/test/e2e/v2/lifecycle/aws.go @@ -0,0 +1,122 @@ +//go:build e2ev2 + +package lifecycle + +import ( + "context" + "fmt" + "log" + "os" + "strings" + "time" + + crclient "sigs.k8s.io/controller-runtime/pkg/client" +) + +type AWSPlatformConfig struct { + region string + zones []string + additionalTags []string + sharedDir string +} + +type AWSPlatformOptions struct { + Region string + Zones string +} + +func NewAWSPlatformConfig(opts AWSPlatformOptions, sharedDir string) *AWSPlatformConfig { + zones := strings.Split(opts.Zones, ",") + + // TODO: this is currently just to satisfy an assumption made by EnsureInfrastructureResourceTagsTest + // and should probably be handled another way. That test assumes there is at least one pre-existing + // non-kubernetes-namespaced tag on the infra. + tags := []string{fmt.Sprintf("expirationDate=%s", time.Now().Add(4*time.Hour).UTC().Format(time.RFC3339))} + + cfg := &AWSPlatformConfig{ + region: opts.Region, + sharedDir: sharedDir, + additionalTags: tags, + zones: zones, + } + + log.Printf("AWS platform config: region=%s, zones=%v, additionalTags=%v", cfg.region, cfg.zones, cfg.additionalTags) + return cfg +} + +func (a *AWSPlatformConfig) Name() string { return "aws" } + +func (a *AWSPlatformConfig) DefaultBaseDomain() string { + return "ci.hypershift.devcluster.openshift.com" +} + +func (a *AWSPlatformConfig) ClusterSpecs(releaseImage, n1Image string) []ClusterSpec { + // Parse EXTRA_ARGS from environment if provided + var extraArgs []string + if envArgs := os.Getenv("EXTRA_ARGS"); envArgs != "" { + extraArgs = strings.Fields(envArgs) + } + return []ClusterSpec{ + { + Variant: "public", + OutputFile: "cluster-name-public", + ExtraArgs: extraArgs, + }, + } +} + +func (a *AWSPlatformConfig) CreateArgs() []string { + args := []string{ + "--region=" + a.region, + "--zones=" + strings.Join(a.zones, ","), + "--root-volume-size=64", + "--root-volume-type=gp3", + "--public-only", + "--pods-labels=hypershift-e2e-test-label=test", + "--toleration=key=hypershift-e2e-test-toleration,operator=Equal,value=true,effect=NoSchedule", + "--annotations=hypershift.openshift.io/cleanup-cloud-resources=true", + "--annotations=hypershift.openshift.io/skip-release-image-validation=true", + "--feature-set=TechPreviewNoUpgrade", + } + for _, tag := range a.additionalTags { + args = append(args, "--additional-tags="+tag) + } + return args +} + +func (a *AWSPlatformConfig) PreCreate(ctx context.Context, cl crclient.WithWatch, namespace string) error { + return nil +} + +func (a *AWSPlatformConfig) PostCreate(ctx context.Context, cl crclient.WithWatch, namespace string, clusterNames map[string]string) error { + return nil +} + +func (a *AWSPlatformConfig) PostAvailable(ctx context.Context, cl crclient.WithWatch, namespace string, clusterNames map[string]string) error { + return nil +} + +func (a *AWSPlatformConfig) PostVersionRollout(ctx context.Context, cl crclient.WithWatch, namespace string, clusterNames map[string]string) error { + return nil +} + +func (a *AWSPlatformConfig) TestMatrix(releaseImage string) TestMatrix { + return TestMatrix{ + Parallel: []TestGroup{ + { + Name: "aws-public", + ClusterFile: "cluster-name-public", + LabelFilter: "!lifecycle || hosted-cluster-aws", + JUnitFile: "junit_aws_public.xml", + }, + }, + } +} + +func (a *AWSPlatformConfig) SetupTestEnv(sharedDir string) {} + +func (a *AWSPlatformConfig) DestroyArgs() []string { + return []string{ + "--region=" + a.region, + } +} diff --git a/test/e2e/v2/lifecycle/platform.go b/test/e2e/v2/lifecycle/platform.go index 737ea3f51f32..5e92dbac9a0b 100644 --- a/test/e2e/v2/lifecycle/platform.go +++ b/test/e2e/v2/lifecycle/platform.go @@ -95,7 +95,6 @@ type PlatformConfig interface { // DestroyArgs returns platform-specific args for // "hypershift destroy cluster ". DestroyArgs() []string - } // NewPlatformConfig creates a PlatformConfig for the given platform @@ -105,8 +104,13 @@ func NewPlatformConfig(platform, sharedDir string) (PlatformConfig, error) { switch platform { case "azure", "": return NewAzurePlatformConfig(sharedDir), nil + case "aws": + return NewAWSPlatformConfig(AWSPlatformOptions{ + Region: envOrDefault("HYPERSHIFT_AWS_REGION", "us-east-1"), + Zones: envOrDefault("HYPERSHIFT_AWS_ZONES", "us-east-1a"), + }, sharedDir), nil default: - return nil, fmt.Errorf("unsupported platform %q (supported: azure)", platform) + return nil, fmt.Errorf("unsupported platform %q (supported: azure, aws)", platform) } } @@ -119,4 +123,3 @@ func DeriveClusterName(prowJobID, variant string) string { hash := sha256.Sum256([]byte(prowJobID)) return variant + "-" + fmt.Sprintf("%x", hash)[:10] } -