From ee53410d4e815e64e71dcfc16ef0ebca03f705d8 Mon Sep 17 00:00:00 2001 From: EKS Distro PR Bot <75336432+eks-distro-pr-bot@users.noreply.github.com> Date: Thu, 3 Nov 2022 13:03:29 -0700 Subject: [PATCH] [release-0.12] PR fixing and adding Packages tests to e2e (#3927) * PR fixing and adding Packages tests to e2e Signed-off-by: jonahjon * fixing time tick from go lint Signed-off-by: jonahjon * fixing lint Signed-off-by: jonahjon * adding tests, and fixing stevens suggestions Signed-off-by: jonahjon * adding comments for go linter Signed-off-by: jonahjon * changing region var to use secrets manager Signed-off-by: jonahjon * trying to include errors being tested Signed-off-by: jonahjon * trying different formats for codecov Signed-off-by: jonahjon * trying format for codecov Signed-off-by: jonahjon * trying error tests Signed-off-by: jonahjon * trying error tests Signed-off-by: jonahjon * more error case tests Signed-off-by: jonahjon Signed-off-by: jonahjon Co-authored-by: jonahjon --- .../buildspecs/conformance-eks-a-cli.yml | 5 +- .../build/buildspecs/test-eks-a-cli.yml | 3 + pkg/executables/kubectl.go | 204 ++++++++++++++++- pkg/executables/kubectl_test.go | 205 +++++++++++++++++ test/e2e/README.md | 5 +- test/e2e/curated_packages_test.go | 15 +- test/framework/cluster.go | 211 +++++++++--------- test/framework/curatedpackages.go | 16 ++ 8 files changed, 552 insertions(+), 112 deletions(-) diff --git a/cmd/integration_test/build/buildspecs/conformance-eks-a-cli.yml b/cmd/integration_test/build/buildspecs/conformance-eks-a-cli.yml index 027157ff701e..fe4ed5d94794 100644 --- a/cmd/integration_test/build/buildspecs/conformance-eks-a-cli.yml +++ b/cmd/integration_test/build/buildspecs/conformance-eks-a-cli.yml @@ -13,6 +13,9 @@ env: secrets-manager: EKSA_VSPHERE_USERNAME: "vsphere_ci_beta_connection:vsphere_username" EKSA_VSPHERE_PASSWORD: "vsphere_ci_beta_connection:vsphere_password" + EKSA_AWS_ACCESS_KEY_ID: "packages_ci_beta:aws_access_key_id" + EKSA_AWS_SECRET_ACCESS_KEY: "packages_ci_beta:aws_secret_access_key_id" + EKSA_AWS_REGION: "packages_ci_beta:aws_region" VSPHERE_SERVER: "vsphere_ci_beta_connection:vsphere_url" GOVC_INSECURE: "vsphere_ci_beta_connection:govc_insecure" T_VSPHERE_DATACENTER: "vsphere_ci_beta_connection:vsphere_datacenter" @@ -76,4 +79,4 @@ phases: -m ${INTEGRATION_TEST_MAX_EC2_COUNT} -p ${INTEGRATION_TEST_MAX_CONCURRENT_TEST_COUNT} -r 'Test' - --cleanup-vms=true \ No newline at end of file + --cleanup-vms=true diff --git a/cmd/integration_test/build/buildspecs/test-eks-a-cli.yml b/cmd/integration_test/build/buildspecs/test-eks-a-cli.yml index c45ee7ca10ac..30974c59d91a 100644 --- a/cmd/integration_test/build/buildspecs/test-eks-a-cli.yml +++ b/cmd/integration_test/build/buildspecs/test-eks-a-cli.yml @@ -35,6 +35,9 @@ env: secrets-manager: EKSA_VSPHERE_USERNAME: "vsphere_ci_beta_connection:vsphere_username" EKSA_VSPHERE_PASSWORD: "vsphere_ci_beta_connection:vsphere_password" + EKSA_AWS_ACCESS_KEY_ID: "packages_ci_beta:aws_access_key_id" + EKSA_AWS_SECRET_ACCESS_KEY: "packages_ci_beta:aws_secret_access_key_id" + EKSA_AWS_REGION: "packages_ci_beta:aws_region" VSPHERE_SERVER: "vsphere_ci_beta_connection:vsphere_url" GOVC_INSECURE: "vsphere_ci_beta_connection:govc_insecure" GOVC_DATACENTER: "vsphere_ci_beta_connection:vsphere_datacenter" diff --git a/pkg/executables/kubectl.go b/pkg/executables/kubectl.go index fdfff87c4024..89aa341fccc1 100644 --- a/pkg/executables/kubectl.go +++ b/pkg/executables/kubectl.go @@ -10,6 +10,7 @@ import ( "math" "regexp" "sort" + "strconv" "strings" "time" @@ -21,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,6 +34,7 @@ import ( addons "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1" "sigs.k8s.io/yaml" + packagesv1 "github.com/aws/eks-anywhere-packages/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/clients/kubernetes" "github.com/aws/eks-anywhere/pkg/constants" @@ -71,6 +74,7 @@ var ( clusterResourceSetResourceType = fmt.Sprintf("clusterresourcesets.%s", addons.GroupVersion.Group) kubeadmControlPlaneResourceType = fmt.Sprintf("kubeadmcontrolplanes.controlplane.%s", clusterv1.GroupVersion.Group) eksdReleaseType = fmt.Sprintf("releases.%s", eksdv1alpha1.GroupVersion.Group) + eksaPackagesType = fmt.Sprintf("packages.%s", packagesv1.GroupVersion.Group) kubectlConnectionRefusedRegex = regexp.MustCompile("The connection to the server .* was refused") kubectlIoTimeoutRegex = regexp.MustCompile("Unable to connect to the server.*i/o timeout.*") ) @@ -355,19 +359,39 @@ func (k *Kubectl) WaitForDeployment(ctx context.Context, cluster *types.Cluster, return k.Wait(ctx, cluster.KubeconfigFile, timeout, condition, "deployments/"+target, namespace) } +// WaitForPod waits for a pod resource to reach desired condition before returning. +func (k *Kubectl) WaitForPod(ctx context.Context, cluster *types.Cluster, timeout string, condition string, target string, namespace string) error { + return k.Wait(ctx, cluster.KubeconfigFile, timeout, condition, "pod/"+target, namespace) +} + func (k *Kubectl) WaitForBaseboardManagements(ctx context.Context, cluster *types.Cluster, timeout string, condition string, namespace string) error { return k.Wait(ctx, cluster.KubeconfigFile, timeout, condition, rufioBaseboardManagementResourceType, namespace, WithWaitAll()) } +// WaitForJobCompleted waits for a job resource to reach desired condition before returning. +func (k *Kubectl) WaitForJobCompleted(ctx context.Context, kubeconfig, timeout string, condition string, target string, namespace string) error { + return k.Wait(ctx, kubeconfig, timeout, condition, "job/"+target, namespace) +} + +// WaitForPackagesInstalled waits for a package resource to reach installed state before returning. +func (k *Kubectl) WaitForPackagesInstalled(ctx context.Context, cluster *types.Cluster, name string, timeout string, namespace string) error { + return k.WaitJSONPathLoop(ctx, cluster.KubeconfigFile, timeout, "status.state", "installed", fmt.Sprintf("%s/%s", eksaPackagesType, name), namespace) +} + +// WaitForPodCompleted waits for a pod to be terminated with a Completed state before returning. +func (k *Kubectl) WaitForPodCompleted(ctx context.Context, cluster *types.Cluster, name string, timeout string, namespace string) error { + return k.WaitJSONPathLoop(ctx, cluster.KubeconfigFile, timeout, "status.containerStatuses[0].state.terminated.reason", "Completed", "pod/"+name, namespace) +} + func (k *Kubectl) Wait(ctx context.Context, kubeconfig string, timeout string, forCondition string, property string, namespace string, opts ...KubectlOpt) error { // On each retry kubectl wait timeout values will have to be adjusted to only wait for the remaining timeout duration. // Here we establish an absolute timeout time for this based on the caller-specified timeout. timeoutDuration, err := time.ParseDuration(timeout) if err != nil { - return fmt.Errorf("unparsable timeout specified: %v", err) + return fmt.Errorf("unparsable timeout specified: %w", err) } if timeoutDuration < 0 { - return fmt.Errorf("negative timeout specified: %v", err) + return fmt.Errorf("negative timeout specified: %w", err) } timeoutTime := time.Now().Add(timeoutDuration) @@ -378,7 +402,56 @@ func (k *Kubectl) Wait(ctx context.Context, kubeconfig string, timeout string, f }, ) if err != nil { - return fmt.Errorf("executing wait: %v", err) + return fmt.Errorf("executing wait: %w", err) + } + return nil +} + +// WaitJSONPathLoop will wait for a given JSONPath to reach a required state similar to wait command for objects without conditions. +// This will be deprecated in favor of WaitJSONPath after version 1.23. +func (k *Kubectl) WaitJSONPathLoop(ctx context.Context, kubeconfig string, timeout string, jsonpath, forCondition string, property string, namespace string, opts ...KubectlOpt) error { + // On each retry kubectl wait timeout values will have to be adjusted to only wait for the remaining timeout duration. + // Here we establish an absolute timeout time for this based on the caller-specified timeout. + timeoutDuration, err := time.ParseDuration(timeout) + if err != nil { + return fmt.Errorf("unparsable timeout specified: %w", err) + } + if timeoutDuration < 0 { + return fmt.Errorf("negative timeout specified: %w", err) + } + + retrier := retrier.New(timeoutDuration, retrier.WithRetryPolicy(kubectlWaitRetryPolicy)) + err = retrier.Retry( + func() error { + return k.waitJSONPathLoop(ctx, kubeconfig, timeout, jsonpath, forCondition, property, namespace, opts...) + }, + ) + if err != nil { + return fmt.Errorf("executing wait: %w", err) + } + return nil +} + +// WaitJSONPath will wait for a given JSONPath of a required state. Only compatible on K8s 1.23+. +func (k *Kubectl) WaitJSONPath(ctx context.Context, kubeconfig string, timeout string, jsonpath, forCondition string, property string, namespace string, opts ...KubectlOpt) error { + // On each retry kubectl wait timeout values will have to be adjusted to only wait for the remaining timeout duration. + // Here we establish an absolute timeout time for this based on the caller-specified timeout. + timeoutDuration, err := time.ParseDuration(timeout) + if err != nil { + return fmt.Errorf("unparsable timeout specified: %w", err) + } + if timeoutDuration < 0 { + return fmt.Errorf("negative timeout specified: %w", err) + } + + retrier := retrier.New(timeoutDuration, retrier.WithRetryPolicy(kubectlWaitRetryPolicy)) + err = retrier.Retry( + func() error { + return k.waitJSONPath(ctx, kubeconfig, timeout, jsonpath, forCondition, property, namespace, opts...) + }, + ) + if err != nil { + return fmt.Errorf("executing wait: %w", err) } return nil } @@ -416,11 +489,66 @@ func (k *Kubectl) wait(ctx context.Context, kubeconfig string, timeoutTime time. applyOpts(¶ms, opts...) _, err := k.Execute(ctx, params...) if err != nil { - return fmt.Errorf("executing wait: %v", err) + return fmt.Errorf("executing wait: %w", err) + } + return nil +} + +func (k *Kubectl) waitJSONPath(ctx context.Context, kubeconfig, timeout string, jsonpath string, forCondition string, property string, namespace string, opts ...KubectlOpt) error { + if jsonpath == "" || forCondition == "" { + return fmt.Errorf("empty conditions params passed to waitJSONPath()") + } + params := []string{ + "wait", "--timeout", timeout, fmt.Sprintf("--for=jsonpath='{.%s}'=%s", jsonpath, forCondition), property, "--kubeconfig", kubeconfig, "-n", namespace, + } + _, err := k.Execute(ctx, params...) + if err != nil { + return fmt.Errorf("executing wait: %w", err) } return nil } +// waitJsonPathLoop will be deprecated in favor of waitJsonPath after version 1.23. +func (k *Kubectl) waitJSONPathLoop(ctx context.Context, kubeconfig string, timeout string, jsonpath string, forCondition string, property string, namespace string, opts ...KubectlOpt) error { + if jsonpath == "" || forCondition == "" { + return fmt.Errorf("empty conditions params passed to waitJSONPathLoop()") + } + timeoutDur, err := time.ParseDuration(timeout) + if err != nil { + return fmt.Errorf("parsing duration %q: %w", timeout, err) + } + + timeoutCtx, cancel := context.WithTimeout(ctx, timeoutDur) + defer cancel() + timedOut := timeoutCtx.Done() + + const pollInterval = time.Second + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + + for { + select { + case <-timedOut: + return fmt.Errorf("waiting for %s %s on %s: timed out", jsonpath, forCondition, property) + case <-ticker.C: + params := []string{ + "get", property, + "-o", fmt.Sprintf("jsonpath='{.%s}'", jsonpath), + "--kubeconfig", kubeconfig, + "-n", namespace, + } + stdout, err := k.Execute(ctx, params...) + if err != nil { + return fmt.Errorf("waiting for %s %s on %s: %w", jsonpath, forCondition, property, err) + } + fmt.Printf("%v=%v\n", stdout.String(), fmt.Sprintf("'%s'", forCondition)) + if stdout.String() == fmt.Sprintf("'%s'", forCondition) { + return nil + } + } + } +} + func (k *Kubectl) DeleteEksaDatacenterConfig(ctx context.Context, eksaDatacenterResourceType string, eksaDatacenterConfigName string, kubeconfigFile string, namespace string) error { params := []string{"delete", eksaDatacenterResourceType, eksaDatacenterConfigName, "--kubeconfig", kubeconfigFile, "--namespace", namespace, "--ignore-not-found=true"} _, err := k.Execute(ctx, params...) @@ -466,6 +594,33 @@ func (k *Kubectl) DeleteFluxConfig(ctx context.Context, managementCluster *types return nil } +// GetPackageBundleController will retrieve the packagebundlecontroller from eksa-packages namespace and return the object. +func (k *Kubectl) GetPackageBundleController(ctx context.Context, kubeconfigFile, clusterName string) (packagesv1.PackageBundleController, error) { + params := []string{"get", "pbc", clusterName, "-o", "json", "--kubeconfig", kubeconfigFile, "--namespace", "eksa-packages", "--ignore-not-found=true"} + stdOut, _ := k.Execute(ctx, params...) + response := &packagesv1.PackageBundleController{} + err := json.Unmarshal(stdOut.Bytes(), response) + if err != nil { + return packagesv1.PackageBundleController{}, fmt.Errorf("unmarshalling kubectl response to GO struct %s: %v", clusterName, err) + } + return *response, nil +} + +// GetPackageBundleList will retrieve the packagebundle list from eksa-packages namespace and return the list. +func (k *Kubectl) GetPackageBundleList(ctx context.Context, kubeconfigFile string) ([]packagesv1.PackageBundle, error) { + params := []string{"get", "packagebundle", "-o", "json", "--kubeconfig", kubeconfigFile, "--namespace", "eksa-packages", "--ignore-not-found=true"} + stdOut, err := k.Execute(ctx, params...) + if err != nil { + return nil, fmt.Errorf("getting package bundle resource %v", err) + } + response := &packagesv1.PackageBundleList{} + err = json.Unmarshal(stdOut.Bytes(), response) + if err != nil { + return nil, fmt.Errorf("unmarshalling kubectl response to GO struct %v", err) + } + return response.Items, nil +} + func (k *Kubectl) DeletePackageResources(ctx context.Context, managementCluster *types.Cluster, clusterName string) error { params := []string{"delete", "pbc", clusterName, "--kubeconfig", managementCluster.KubeconfigFile, "--namespace", "eksa-packages", "--ignore-not-found=true"} _, err := k.Execute(ctx, params...) @@ -732,6 +887,47 @@ func (k *Kubectl) ValidatePods(ctx context.Context, kubeconfig string) error { return nil } +// RunBusyBoxPod will run Kubectl run with a busybox curl image and the command you pass in. +func (k *Kubectl) RunBusyBoxPod(ctx context.Context, namespace, name, kubeconfig string, command []string) (string, error) { + params := []string{"run", name, "--image=yauritux/busybox-curl", "-o", "json", "--kubeconfig", kubeconfig, "--namespace", namespace, "--restart=Never"} + params = append(params, command...) + _, err := k.Execute(ctx, params...) + if err != nil { + return "", err + } + return name, err +} + +// GetPodLogs returns the logs of the specified container (namespace/pod/container). +func (k *Kubectl) GetPodLogs(ctx context.Context, namespace, podName, containerName, kubeconfig string) (string, error) { + return k.getPodLogs(ctx, namespace, podName, containerName, kubeconfig, nil, nil) +} + +// GetPodLogsSince returns the logs of the specified container (namespace/pod/container) since a timestamp. +func (k *Kubectl) GetPodLogsSince(ctx context.Context, namespace, podName, containerName, kubeconfig string, since time.Time) (string, error) { + sinceTime := metav1.NewTime(since) + return k.getPodLogs(ctx, namespace, podName, containerName, kubeconfig, &sinceTime, nil) +} + +func (k *Kubectl) getPodLogs(ctx context.Context, namespace, podName, containerName, kubeconfig string, sinceTime *metav1.Time, tailLines *int) (string, error) { + params := []string{"logs", podName, containerName, "--kubeconfig", kubeconfig, "--namespace", namespace} + if sinceTime != nil { + params = append(params, "--since-time", sinceTime.Format(time.RFC3339)) + } + if tailLines != nil { + params = append(params, "--tail", strconv.Itoa(*tailLines)) + } + stdOut, err := k.Execute(ctx, params...) + if err != nil { + return "", err + } + logs := stdOut.String() + if strings.Contains(logs, "Internal Error") { + return "", fmt.Errorf("Fetched log contains \"Internal Error\": %q", logs) + } + return logs, err +} + func (k *Kubectl) SaveLog(ctx context.Context, cluster *types.Cluster, deployment *types.Deployment, fileName string, writer filewriter.FileWriter) error { params := []string{"--kubeconfig", cluster.KubeconfigFile} logParams := []string{ diff --git a/pkg/executables/kubectl_test.go b/pkg/executables/kubectl_test.go index 57080898d637..6ae30187effb 100644 --- a/pkg/executables/kubectl_test.go +++ b/pkg/executables/kubectl_test.go @@ -2836,3 +2836,208 @@ func TestKubectlDeleteEksaNutanixMachineConfigError(t *testing.T) { ).Return(bytes.Buffer{}, fmt.Errorf("error")) tt.Expect(tt.k.DeleteEksaNutanixMachineConfig(tt.ctx, "eksa-unit-test", tt.kubeconfig, tt.namespace)).NotTo(Succeed()) } + +func TestWaitForPod(t *testing.T) { + tt := newKubectlTest(t) + timeout := "2m" + expectedTimeout := "120.00s" + condition := "status.containerStatuses[0].state.terminated.reason" + target := "testpod" + tt.e.EXPECT().Execute(gomock.Any(), "wait", "--timeout", + []string{expectedTimeout, fmt.Sprintf("%s=%s", "--for=condition", condition), fmt.Sprintf("%s/%s", "pod", target), "--kubeconfig", tt.kubeconfig, "-n", "eksa-system"}, + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(bytes.Buffer{}, nil) + + tt.Expect(tt.k.WaitForPod(tt.ctx, tt.cluster, timeout, condition, target, "eksa-system")).To(Succeed()) +} + +func TestWaitForJob(t *testing.T) { + tt := newKubectlTest(t) + timeout := "2m" + expectedTimeout := "120.00s" + condition := "status.containerStatuses[0].state.terminated.reason" + target := "testpod" + tt.e.EXPECT().Execute(gomock.Any(), "wait", "--timeout", + []string{expectedTimeout, fmt.Sprintf("%s=%s", "--for=condition", condition), fmt.Sprintf("%s/%s", "job", target), "--kubeconfig", tt.kubeconfig, "-n", "eksa-system"}, + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), + ).Return(bytes.Buffer{}, nil) + + tt.Expect(tt.k.WaitForJobCompleted(tt.ctx, tt.cluster.KubeconfigFile, timeout, condition, target, "eksa-system")).To(Succeed()) +} + +func TestWaitForPodCompleted(t *testing.T) { + var b bytes.Buffer + b.WriteString("'Completed'") + tt := newKubectlTest(t) + expectedParam := []string{"get", "pod/testpod", "-o", fmt.Sprintf("%s=%s", "jsonpath", "'{.status.containerStatuses[0].state.terminated.reason}'"), "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + + tt.Expect(tt.k.WaitForPodCompleted(tt.ctx, tt.cluster, "testpod", "2m", "eksa-system")).To(Succeed()) +} + +func TestWaitForPackagesInstalled(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"get", "packages.packages.eks.amazonaws.com/testpackage", "-o", fmt.Sprintf("%s=%s", "jsonpath", "'{.status.state}'"), "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + err := tt.k.WaitForPackagesInstalled(tt.ctx, tt.cluster, "testpackage", "2m", "eksa-system") + tt.Expect(err).ToNot(HaveOccurred()) +} + +func TestWaitJSONPathLoop(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"get", "packages.packages.eks.amazonaws.com/testpackage", "-o", fmt.Sprintf("%s=%s", "jsonpath", "'{.status.state}'"), "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + err := tt.k.WaitJSONPathLoop(tt.ctx, tt.cluster.KubeconfigFile, "2m", "status.state", "installed", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system") + tt.Expect(err).ToNot(HaveOccurred()) +} + +func TestWaitJSONPathLoopTimeParseError(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"get", "packages.packages.eks.amazonaws.com/testpackage", "-o", fmt.Sprintf("%s=%s", "jsonpath", "'{.status.state}'"), "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + tt.Expect(tt.k.WaitJSONPathLoop(tt.ctx, tt.cluster.KubeconfigFile, "", "status.state", "installed", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system")).To(MatchError(ContainSubstring("unparsable timeout specified"))) +} + +func TestWaitJSONPathLoopNegativeTimeError(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"get", "packages.packages.eks.amazonaws.com/testpackage", "-o", fmt.Sprintf("%s=%s", "jsonpath", "'{.status.state}'"), "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + tt.Expect(tt.k.WaitJSONPathLoop(tt.ctx, tt.cluster.KubeconfigFile, "-1m", "status.state", "installed", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system")).To(MatchError(ContainSubstring("negative timeout specified"))) +} + +func TestWaitJSONPathLoopRetrierError(t *testing.T) { + var b bytes.Buffer + b.WriteString("") + tt := newKubectlTest(t) + expectedParam := []string{"get", "packages.packages.eks.amazonaws.com/testpackage", "-o", fmt.Sprintf("%s=%s", "jsonpath", "'{.}'"), "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + tt.Expect(tt.k.WaitJSONPathLoop(tt.ctx, tt.cluster.KubeconfigFile, "2m", "", "", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system")).To(MatchError(ContainSubstring("executing wait"))) +} + +func TestWaitJSONPath(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"wait", "--timeout", "2m", fmt.Sprintf("--for=jsonpath='{.%s}'=%s", "status.state", "installed"), "packages.packages.eks.amazonaws.com/testpackage", "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + err := tt.k.WaitJSONPath(tt.ctx, tt.cluster.KubeconfigFile, "2m", "status.state", "installed", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system") + tt.Expect(err).To(BeNil()) +} + +func TestWaitJSONPathTimeParseError(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"wait", "--timeout", "2m", fmt.Sprintf("--for=jsonpath='{.%s}'=%s", "status.state", "installed"), "packages.packages.eks.amazonaws.com/testpackage", "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + tt.Expect(tt.k.WaitJSONPath(tt.ctx, tt.cluster.KubeconfigFile, "", "status.state", "installed", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system")).To(MatchError(ContainSubstring("unparsable timeout specified"))) +} + +func TestWaitJSONPathNegativeTimeError(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"wait", "--timeout", "2m", fmt.Sprintf("--for=jsonpath='{.%s}'=%s", "status.state", "installed"), "packages.packages.eks.amazonaws.com/testpackage", "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + tt.Expect(tt.k.WaitJSONPath(tt.ctx, tt.cluster.KubeconfigFile, "-1m", "status.state", "installed", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system")).To(MatchError(ContainSubstring("negative timeout specified"))) +} + +func TestWaitJSONPathRetrierError(t *testing.T) { + var b bytes.Buffer + b.WriteString("'installed'") + tt := newKubectlTest(t) + expectedParam := []string{"wait", "--timeout", "2m", fmt.Sprintf("--for=jsonpath='{.%s}'=%s", "", ""), "packages.packages.eks.amazonaws.com/testpackage", "--kubeconfig", "c.kubeconfig", "-n", "eksa-system"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + tt.Expect(tt.k.WaitJSONPath(tt.ctx, tt.cluster.KubeconfigFile, "2m", "", "", "packages.packages.eks.amazonaws.com/testpackage", "eksa-system")).To(MatchError(ContainSubstring("executing wait"))) +} + +func TestGetPackageBundleController(t *testing.T) { + tt := newKubectlTest(t) + testPbc := &packagesv1.PackageBundleController{} + respJSON, err := json.Marshal(testPbc) + if err != nil { + t.Errorf("marshaling test service: %s", err) + } + ret := bytes.NewBuffer(respJSON) + expectedParam := []string{"get", "pbc", "testcluster", "-o", "json", "--kubeconfig", "c.kubeconfig", "--namespace", "eksa-packages", "--ignore-not-found=true"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(*ret, nil).AnyTimes() + if _, err := tt.k.GetPackageBundleController(tt.ctx, tt.cluster.KubeconfigFile, "testcluster"); err != nil { + t.Errorf("Kubectl.GetPackageBundleController() error = %v, want nil", err) + } +} + +func TestGetPackageBundleList(t *testing.T) { + tt := newKubectlTest(t) + testPbc := &packagesv1.PackageBundleList{} + respJSON, err := json.Marshal(testPbc) + if err != nil { + t.Errorf("marshaling test service: %s", err) + } + ret := bytes.NewBuffer(respJSON) + expectedParam := []string{"get", "packagebundle", "-o", "json", "--kubeconfig", "c.kubeconfig", "--namespace", "eksa-packages", "--ignore-not-found=true"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(*ret, nil).AnyTimes() + if _, err := tt.k.GetPackageBundleList(tt.ctx, tt.cluster.KubeconfigFile); err != nil { + t.Errorf("Kubectl.GetPackageBundleList() error = %v, want nil", err) + } +} + +func TestRunBusyBoxPod(t *testing.T) { + tt := newKubectlTest(t) + var b bytes.Buffer + expectedParam := []string{"run", "testpod-123", "--image=yauritux/busybox-curl", "-o", "json", "--kubeconfig", "c.kubeconfig", "--namespace", "eksa-packages", "--restart=Never", "pwd"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + if _, err := tt.k.RunBusyBoxPod(tt.ctx, "eksa-packages", "testpod-123", tt.cluster.KubeconfigFile, []string{"pwd"}); err != nil { + t.Errorf("Kubectl.RunBusyBoxPod() error = %v, want nil", err) + } +} + +func TestGetPodLogs(t *testing.T) { + tt := newKubectlTest(t) + var b bytes.Buffer + expectedParam := []string{"logs", "testpod", "testcontainer", "--kubeconfig", "c.kubeconfig", "--namespace", "eksa-packages"} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + if _, err := tt.k.GetPodLogs(tt.ctx, "eksa-packages", "testpod", "testcontainer", tt.cluster.KubeconfigFile); err != nil { + t.Errorf("Kubectl.GetPodLogs() error = %v, want nil", err) + } +} + +func TestGetPodLogsSince(t *testing.T) { + tt := newKubectlTest(t) + var b bytes.Buffer + now := time.Now() + expectedParam := []string{"logs", "testpod", "testcontainer", "--kubeconfig", "c.kubeconfig", "--namespace", "eksa-packages", "--since-time", now.Format(time.RFC3339)} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + if _, err := tt.k.GetPodLogsSince(tt.ctx, "eksa-packages", "testpod", "testcontainer", tt.cluster.KubeconfigFile, now); err != nil { + t.Errorf("Kubectl.GetPodLogsSince() error = %v, want nil", err) + } +} + +func TestGetPodLogsSinceInternalError(t *testing.T) { + tt := newKubectlTest(t) + now := time.Now() + var b bytes.Buffer + b.WriteString("Internal Error") + expectedParam := []string{"logs", "testpod", "testcontainer", "--kubeconfig", "c.kubeconfig", "--namespace", "eksa-packages", "--since-time", now.Format(time.RFC3339)} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, nil).AnyTimes() + _, err := tt.k.GetPodLogsSince(tt.ctx, "eksa-packages", "testpod", "testcontainer", tt.cluster.KubeconfigFile, now) + tt.Expect(err).To(MatchError(ContainSubstring("Internal Error"))) +} + +func TestGetPodLogsSinceExecuteError(t *testing.T) { + tt := newKubectlTest(t) + now := time.Now() + var b bytes.Buffer + b.WriteString("execute error") + expectedParam := []string{"logs", "testpod", "testcontainer", "--kubeconfig", "c.kubeconfig", "--namespace", "eksa-packages", "--since-time", now.Format(time.RFC3339)} + tt.e.EXPECT().Execute(gomock.Any(), gomock.Eq(expectedParam)).Return(b, fmt.Errorf("execute error")).AnyTimes() + _, err := tt.k.GetPodLogsSince(tt.ctx, "eksa-packages", "testpod", "testcontainer", tt.cluster.KubeconfigFile, now) + tt.Expect(err).To(MatchError(ContainSubstring("execute error"))) +} diff --git a/test/e2e/README.md b/test/e2e/README.md index 97ad8e4fbb26..d36a85432f31 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -51,6 +51,9 @@ VSPHERE_PASSWORD T_VSPHERE_CIDR GOVC_URL T_VSPHERE_CLUSTER_IP_POOL # comma-separated list of CP ip addresses +EKSA_AWS_ACCESS_KEY_ID # For Packages tests +EKSA_AWS_SECRET_ACCESS_KEY # For Packages tests +EKSA_AWS_REGION:# For Packages tests ``` ### Tests upgrading from old release @@ -174,4 +177,4 @@ The value is controlled by the `INTEGRATION_TEST_MAX_EC2_COUNT` env variable in env: variables: INTEGRATION_TEST_MAX_EC2_COUNT: -``` \ No newline at end of file +``` diff --git a/test/e2e/curated_packages_test.go b/test/e2e/curated_packages_test.go index b04d9cf10d41..9ae3d3f7c61c 100644 --- a/test/e2e/curated_packages_test.go +++ b/test/e2e/curated_packages_test.go @@ -22,7 +22,7 @@ const ( EksaPackageControllerHelmChartName = "eks-anywhere-packages" EksaPackagesSourceRegistry = "public.ecr.aws/l0g8r8j6" EksaPackageControllerHelmURI = "oci://" + EksaPackagesSourceRegistry + "/eks-anywhere-packages" - EksaPackageControllerHelmVersion = "0.2.7-eks-a-v0.0.0-dev-release-0.11-build.219" + EksaPackageControllerHelmVersion = "0.2.13-eks-a-v0.0.0-dev-build.4532" EksaPackageBundleURI = "oci://" + EksaPackagesSourceRegistry + "/eks-anywhere-packages-bundles" ) @@ -170,9 +170,12 @@ func packageBundleURI(version v1alpha1.KubernetesVersion) string { func runCuratedPackageInstallSimpleFlow(test *framework.ClusterE2ETest) { test.WithCluster(func(e *framework.ClusterE2ETest) { + test.SetPackageBundleActive() packageName := "hello-eks-anywhere" packagePrefix := "test" - e.InstallCuratedPackage(packageName, packagePrefix) + packageFile := e.BuildPackageConfigFile(packageName, packagePrefix, EksaPackagesNamespace) + e.KubectlClient.WaitForJobCompleted(context.TODO(), kubeconfig.FromClusterName(test.ClusterName), "1m", "complete", "eksa-auth-refresher", EksaPackagesNamespace) + e.InstallCuratedPackageFile(packageFile, kubeconfig.FromClusterName(test.ClusterName)) e.VerifyHelloPackageInstalled(packagePrefix + "-" + packageName) }) } @@ -187,6 +190,7 @@ func runCuratedPackageInstallSimpleFlow(test *framework.ClusterE2ETest) { // decisions about test packages or methodologies, right now. func TestCPackagesDockerUbuntuKubernetes120SimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewDocker(t), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube120)), @@ -198,6 +202,7 @@ func TestCPackagesDockerUbuntuKubernetes120SimpleFlow(t *testing.T) { } func TestCPackagesDockerUbuntuKubernetes121SimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewDocker(t), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube121)), @@ -209,6 +214,7 @@ func TestCPackagesDockerUbuntuKubernetes121SimpleFlow(t *testing.T) { } func TestCPackagesDockerUbuntuKubernetes122SimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewDocker(t), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube122)), @@ -220,6 +226,7 @@ func TestCPackagesDockerUbuntuKubernetes122SimpleFlow(t *testing.T) { } func TestCPackagesVSphereKubernetes120SimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewVSphere(t, framework.WithUbuntu120()), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube120)), @@ -231,6 +238,7 @@ func TestCPackagesVSphereKubernetes120SimpleFlow(t *testing.T) { } func TestCPackagesVSphereKubernetes121SimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewVSphere(t, framework.WithUbuntu121()), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube121)), @@ -242,6 +250,7 @@ func TestCPackagesVSphereKubernetes121SimpleFlow(t *testing.T) { } func TestCPackagesVSphereKubernetes121BottleRocketSimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewVSphere(t, framework.WithBottleRocket121()), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube121)), @@ -253,6 +262,7 @@ func TestCPackagesVSphereKubernetes121BottleRocketSimpleFlow(t *testing.T) { } func TestCPackagesVSphereKubernetes122SimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewVSphere(t, framework.WithUbuntu122()), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube122)), @@ -264,6 +274,7 @@ func TestCPackagesVSphereKubernetes122SimpleFlow(t *testing.T) { } func TestCPackagesVSphereKubernetes122BottleRocketSimpleFlow(t *testing.T) { + framework.CheckCuratedPackagesCredentials(t) test := framework.NewClusterE2ETest(t, framework.NewVSphere(t, framework.WithBottleRocket122()), framework.WithClusterFiller(api.WithKubernetesVersion(v1alpha1.Kube122)), diff --git a/test/framework/cluster.go b/test/framework/cluster.go index 7277a42bc360..12ea57886e88 100644 --- a/test/framework/cluster.go +++ b/test/framework/cluster.go @@ -6,10 +6,8 @@ import ( "context" "crypto/sha1" _ "embed" - "encoding/json" "fmt" "io" - "net/http" "os" "os/exec" "path/filepath" @@ -19,11 +17,13 @@ import ( "testing" "time" - "github.com/pkg/errors" rapi "github.com/tinkerbell/rufio/api/v1alpha1" rctrl "github.com/tinkerbell/rufio/controllers" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilrand "k8s.io/apimachinery/pkg/util/rand" "sigs.k8s.io/yaml" + packagesv1 "github.com/aws/eks-anywhere-packages/api/v1alpha1" "github.com/aws/eks-anywhere/internal/pkg/api" "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/constants" @@ -53,7 +53,6 @@ const ( EksaPackagesInstallation = "eks-anywhere-packages" ) -//go:embed testdata/oidc-roles.yaml var oidcRoles []byte type ClusterE2ETest struct { @@ -899,7 +898,27 @@ func (e *ClusterE2ETest) InstallCuratedPackagesController() { } } -func (e *ClusterE2ETest) InstallCuratedPackage(packageName, packagePrefix string, opts ...string) { +// SetPackageBundleActive will set the current packagebundle to the active state. +func (e *ClusterE2ETest) SetPackageBundleActive() { + kubeconfig := e.kubeconfigFilePath() + pbc, err := e.KubectlClient.GetPackageBundleController(context.Background(), kubeconfig, e.ClusterName) + if err != nil { + e.T.Fatalf("Error getting PackageBundleController: %v", err) + } + pb, err := e.KubectlClient.GetPackageBundleList(context.Background(), e.kubeconfigFilePath()) + if err != nil { + e.T.Fatalf("Error getting PackageBundle: %v", err) + } + if pbc.Spec.ActiveBundle != pb[0].ObjectMeta.Name { + e.RunEKSA([]string{ + "upgrade", "packages", + "--bundle-version", pb[0].ObjectMeta.Name, "-v=9", + }) + } +} + +// InstallCuratedPackage will install a curated package in the desired namespace. +func (e *ClusterE2ETest) InstallCuratedPackage(packageName, packagePrefix, kubeconfig, namespace string, opts ...string) { os.Setenv("CURATED_PACKAGES_SUPPORT", "true") // The package install command doesn't (yet?) have a --kubeconfig flag. os.Setenv("KUBECONFIG", e.kubeconfigFilePath()) @@ -912,6 +931,62 @@ func (e *ClusterE2ETest) InstallCuratedPackage(packageName, packagePrefix string }) } +// InstallCuratedPackageFile will install a curated package from a yaml file, this is useful since target namespace isn't supported on the CLI. +func (e *ClusterE2ETest) InstallCuratedPackageFile(packageFile, kubeconfig string, opts ...string) { + os.Setenv("CURATED_PACKAGES_SUPPORT", "true") + os.Setenv("KUBECONFIG", e.kubeconfigFilePath()) + e.T.Log("Installing EKS-A Packages file", packageFile) + e.RunEKSA([]string{ + "apply", "package", "-f", packageFile, "-v=9", strings.Join(opts, " "), + }) +} + +func (e *ClusterE2ETest) generatePackageConfig(ns, targetns, prefix, packageName string) []byte { + yamlB := make([][]byte, 0, 4) + generatedName := fmt.Sprintf("%s-%s", prefix, packageName) + if targetns == "" { + targetns = ns + } + ns = fmt.Sprintf("%s-%s", ns, e.ClusterName) + builtpackage := &packagesv1.Package{ + TypeMeta: metav1.TypeMeta{ + Kind: packagesv1.PackageKind, + APIVersion: "packages.eks.amazonaws.com/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: generatedName, + Namespace: ns, + }, + Spec: packagesv1.PackageSpec{ + PackageName: packageName, + TargetNamespace: targetns, + }, + } + builtpackageB, err := yaml.Marshal(builtpackage) + if err != nil { + e.T.Fatalf("marshalling package config: %v", err) + } + yamlB = append(yamlB, builtpackageB) + return templater.AppendYamlResources(yamlB...) +} + +// BuildPackageConfigFile will create the file in the test directory for the curated package. +func (e *ClusterE2ETest) BuildPackageConfigFile(packageName, prefix, ns string) string { + b := e.generatePackageConfig(ns, ns, prefix, packageName) + + writer, err := filewriter.NewWriter(e.ClusterConfigFolder) + if err != nil { + e.T.Fatalf("Error creating writer: %v", err) + } + packageFile := fmt.Sprintf("%s.yaml", packageName) + + writtenFile, err := writer.Write(packageFile, b, filewriter.PersistentFile) + if err != nil { + e.T.Fatalf("Error writing cluster config to file %s: %v", e.ClusterConfigLocation, err) + } + return writtenFile +} + func (e *ClusterE2ETest) CreateResource(ctx context.Context, resource string) { err := e.KubectlClient.ApplyKubeSpecFromBytes(ctx, e.cluster(), []byte(resource)) if err != nil { @@ -1004,119 +1079,47 @@ func (e *ClusterE2ETest) VerifyHarborPackageInstalled(prefix string) { } } +// VerifyHelloPackageInstalled is checking if the hello eks anywhere package gets installed correctly. func (e *ClusterE2ETest) VerifyHelloPackageInstalled(name string) { ctx := context.Background() - ns := constants.EksaPackagesName - err := e.KubectlClient.WaitForService(ctx, - e.cluster().KubeconfigFile, "5m", name, ns) + + e.T.Log("Waiting for Package", name, "To be installed") + err := e.KubectlClient.WaitForPackagesInstalled(ctx, + e.cluster(), name, "1m", fmt.Sprintf("%s-%s", ns, e.ClusterName)) if err != nil { - e.T.Fatalf("waiting for service timed out: %s", err) + e.T.Fatalf("waiting for hello-eks-anywhere package timed out: %s", err) } - // Ensure that the pod is up before trying to port-forward. In some test - // environments, the pod might not be running when the port-forward is - // attempted, and that will cause the port-forward to fail. + e.T.Log("Waiting for Package", name, "Deployment to be healthy") err = e.KubectlClient.WaitForDeployment(ctx, - e.cluster(), "5m", "Available", "hello-eks-anywhere", ns) + e.cluster(), "1m", "Available", "hello-eks-anywhere", ns) if err != nil { - e.T.Fatalf("waiting for hello-eks-anywhere pod timed out: %s", err) + e.T.Fatalf("waiting for hello-eks-anywhere deployment timed out: %s", err) } - timedCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) - defer cancel() - timedOut := timedCtx.Done() - // It's preferable to configure kubectl to use a random port, which - // it would write to stdout, indicating when the port-forward is - // active. However, the current Executable framework doesn't allow - // for reading stdout before the process exits. Polling provides a - // workable solution. - const port = 9980 // ...and hope it's available... - stopPF, pfErrCh := e.forwardPortToService(timedCtx, name, ns, port) - defer stopPF() - - ticker := time.NewTicker(time.Second) - defer ticker.Stop() - var resp *http.Response -outer: - for { - select { - case <-timedOut: - e.T.Fatalf("timed out: %s", timedCtx.Err()) - case err := <-pfErrCh: - e.T.Fatalf("port forwarding error: %s", err) - case <-ticker.C: - url := fmt.Sprintf("http://localhost:%d/index.json", port) - resp, err = http.Get(url) - if err != nil { - e.T.Logf("service error, will retry: %s", err) - continue - } - if resp.StatusCode < http.StatusOK || - resp.StatusCode >= http.StatusMultipleChoices { - resp.Body.Close() - e.T.Fatalf("expected a 2XX response, got: %d (%s)", - resp.StatusCode, http.StatusText(resp.StatusCode)) - } - defer resp.Body.Close() - break outer - } + svcAddress := name + "." + ns + ".svc.cluster.local" + randomname := fmt.Sprintf("%s-%s", "busybox-test", utilrand.String(7)) + clientPod, err := e.KubectlClient.RunBusyBoxPod(context.TODO(), ns, randomname, e.kubeconfigFilePath(), []string{"curl", svcAddress}) + if err != nil { + e.T.Fatalf("error launching busybox pod: %s", err) } + e.T.Log("Launching Busybox pod", clientPod, "to test Package", name) - buf := &bytes.Buffer{} - // A TeeReader will let us log the entire body in case of an error. - tee := io.TeeReader(resp.Body, buf) - respData := map[string]interface{}{} - if err = json.NewDecoder(tee).Decode(&respData); err != nil { - _, debugErr := io.ReadAll(tee) - if debugErr != nil { - // Just log this, since the test is already a failure. - e.T.Logf("trying to read the entire response body: %s", debugErr) - } - e.T.Fatalf("unmarshaling JSON response: %s\n%s", err, buf.String()) + err = e.KubectlClient.WaitForPodCompleted(ctx, + e.cluster(), clientPod, "3m", ns) + if err != nil { + e.T.Fatalf("waiting for busybox pod timed out: %s", err) } - title, ok := respData["title"].(string) - if !ok { - e.T.Fatalf("expected title to be a string, got %T", respData["title"]) + e.T.Log("Checking Busybox pod logs", clientPod) + logs, err := e.KubectlClient.GetPodLogs(context.TODO(), ns, clientPod, clientPod, e.kubeconfigFilePath()) + if err != nil { + e.T.Fatalf("failure getting pod logs %s", err) } - expected := "Amazon EKS Anywhere" - if !strings.EqualFold(title, expected) { - e.T.Fatalf("expected title to be %q, got %q", expected, title) + fmt.Printf("Logs from curl Hello EKS Anywhere\n %s\n", logs) + ok := strings.Contains(logs, "Amazon EKS Anywhere") + if !ok { + e.T.Fatalf("expected Amazon EKS Anywhere, got %T", logs) } } - -func (e *ClusterE2ETest) forwardPortToService(ctx context.Context, - name, namespace string, port int, -) (func(), <-chan error) { - // The current Executable framework doesn't allow reading stdout before - // the command completes, so there's no way to know when the port-forward - // is available, short of just trying it. - pfContext, pfCancel := context.WithCancel(ctx) - errCh := make(chan error, 1) - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer close(errCh) - defer wg.Done() - _, err := e.KubectlClient.Execute(pfContext, "port-forward", - "--kubeconfig="+e.kubeconfigFilePath(), "--namespace="+namespace, - "service/"+name, fmt.Sprintf("%d:80", port)) - if err != nil { - pfCtxErr := pfContext.Err() - // A canceled context indicates a controlled shutdown. - if errors.Is(pfCtxErr, context.Canceled) { - return - } - if pfCtxErr != nil { - e.T.Logf("port-forward context error: %s", err) - } - errCh <- err - } - }() - - return func() { - pfCancel() - wg.Wait() - }, errCh -} diff --git a/test/framework/curatedpackages.go b/test/framework/curatedpackages.go index cec4c5128f2f..7388ea10adcf 100644 --- a/test/framework/curatedpackages.go +++ b/test/framework/curatedpackages.go @@ -1,6 +1,7 @@ package framework import ( + "os" "testing" ) @@ -25,3 +26,18 @@ func WithPackageConfig(t *testing.T, bundleURI, chartName, chartURI, } } } + +// CheckCuratedPackagesCredentials will exit out if the Curated Packages environment variables are not set. +func CheckCuratedPackagesCredentials(t *testing.T) { + requiredEnvVars := []string{ + "EKSA_AWS_SECRET_ACCESS_KEY", + "EKSA_AWS_ACCESS_KEY_ID", + "EKSA_AWS_REGION", + } + for _, env := range requiredEnvVars { + _, ok := os.LookupEnv(env) + if !ok { + t.Fatalf("Error Unset Packages environment variable: %v is required", env) + } + } +}