diff --git a/api/v1alpha1/oadp_types.go b/api/v1alpha1/oadp_types.go index 08d461207ff..1fe46f34bf4 100644 --- a/api/v1alpha1/oadp_types.go +++ b/api/v1alpha1/oadp_types.go @@ -35,7 +35,7 @@ const ReconcileCompleteMessage = "Reconcile complete" const OadpOperatorLabel = "openshift.io/oadp" const RegistryDeploymentLabel = "openshift.io/oadp-registry" -// +kubebuilder:validation:Enum=aws;legacy-aws;gcp;azure;csi;vsm;openshift;kubevirt;hypershift +// +kubebuilder:validation:Enum=aws;legacy-aws;gcp;azure;csi;vsm;openshift;kubevirt type DefaultPlugin string const DefaultPluginAWS DefaultPlugin = "aws" @@ -46,7 +46,6 @@ const DefaultPluginCSI DefaultPlugin = "csi" const DefaultPluginVSM DefaultPlugin = "vsm" const DefaultPluginOpenShift DefaultPlugin = "openshift" const DefaultPluginKubeVirt DefaultPlugin = "kubevirt" -const DefaultPluginHypershift DefaultPlugin = "hypershift" type CustomPlugin struct { Name string `json:"name"` @@ -64,7 +63,6 @@ const AzurePluginImageKey UnsupportedImageKey = "azurePluginImageFqin" const GCPPluginImageKey UnsupportedImageKey = "gcpPluginImageFqin" const ResticRestoreImageKey UnsupportedImageKey = "resticRestoreImageFqin" const KubeVirtPluginImageKey UnsupportedImageKey = "kubevirtPluginImageFqin" -const HypershiftPluginImageKey UnsupportedImageKey = "hypershiftPluginImageFqin" const OperatorTypeKey UnsupportedImageKey = "operator-type" const OperatorTypeMTC = "mtc" @@ -360,7 +358,6 @@ type DataProtectionApplicationSpec struct { // - gcpPluginImageFqin // - resticRestoreImageFqin // - kubevirtPluginImageFqin - // - hypershiftPluginImageFqin // - operator-type // +optional UnsupportedOverrides map[UnsupportedImageKey]string `json:"unsupportedOverrides,omitempty"` diff --git a/bundle/manifests/oadp-operator.clusterserviceversion.yaml b/bundle/manifests/oadp-operator.clusterserviceversion.yaml index b0547c82c61..8503c353983 100644 --- a/bundle/manifests/oadp-operator.clusterserviceversion.yaml +++ b/bundle/manifests/oadp-operator.clusterserviceversion.yaml @@ -867,8 +867,6 @@ spec: value: quay.io/konveyor/velero-plugin-for-gcp:oadp-1.4 - name: RELATED_IMAGE_KUBEVIRT_VELERO_PLUGIN value: quay.io/konveyor/kubevirt-velero-plugin:v0.7.0 - - name: RELATED_IMAGE_HYPERSHIFT_VELERO_PLUGIN - value: quay.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-oadp-1-4 - name: RELATED_IMAGE_MUSTGATHER value: quay.io/konveyor/oadp-must-gather:oadp-1.4 image: quay.io/konveyor/oadp-operator:oadp-1.4 @@ -1020,8 +1018,6 @@ spec: name: velero-plugin-for-gcp - image: quay.io/konveyor/kubevirt-velero-plugin:v0.7.0 name: kubevirt-velero-plugin - - image: quay.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-oadp-1-4 - name: hypershift-velero-plugin - image: quay.io/konveyor/oadp-must-gather:oadp-1.4 name: mustgather replaces: oadp-operator.v1.4.9 diff --git a/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml b/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml index a48f8857d34..215d9a8c376 100644 --- a/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml +++ b/bundle/manifests/oadp.openshift.io_dataprotectionapplications.yaml @@ -820,7 +820,6 @@ spec: - vsm - openshift - kubevirt - - hypershift type: string type: array defaultSnapshotMoveData: @@ -1316,7 +1315,6 @@ spec: - gcpPluginImageFqin - resticRestoreImageFqin - kubevirtPluginImageFqin - - hypershiftPluginImageFqin - operator-type type: object required: diff --git a/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml b/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml index 7c93bda24f3..1a7ebd7fbfb 100644 --- a/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml +++ b/config/crd/bases/oadp.openshift.io_dataprotectionapplications.yaml @@ -820,7 +820,6 @@ spec: - vsm - openshift - kubevirt - - hypershift type: string type: array defaultSnapshotMoveData: @@ -1316,7 +1315,6 @@ spec: - gcpPluginImageFqin - resticRestoreImageFqin - kubevirtPluginImageFqin - - hypershiftPluginImageFqin - operator-type type: object required: diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index e1eeecb6730..0e38734abe6 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -58,8 +58,6 @@ spec: value: quay.io/konveyor/velero-plugin-for-gcp:oadp-1.4 - name: RELATED_IMAGE_KUBEVIRT_VELERO_PLUGIN value: quay.io/konveyor/kubevirt-velero-plugin:v0.7.0 - - name: RELATED_IMAGE_HYPERSHIFT_VELERO_PLUGIN - value: quay.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-oadp-1-4 - name: RELATED_IMAGE_MUSTGATHER value: quay.io/konveyor/oadp-must-gather:oadp-1.4 args: diff --git a/go.mod b/go.mod index ed12697aead..7160abcfc93 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,6 @@ require ( require ( github.com/deckarep/golang-set/v2 v2.3.0 github.com/google/go-cmp v0.7.0 - github.com/openshift/hypershift/api v0.0.0-20240522104800-604a957be25e github.com/vmware-tanzu/velero v1.14.0 k8s.io/klog/v2 v2.120.1 ) @@ -184,5 +183,3 @@ require ( replace github.com/vmware-tanzu/velero => github.com/openshift/velero v0.10.2-0.20240822153644-9ac863aaa452 replace github.com/kopia/kopia => github.com/project-velero/kopia v0.0.0-20240417031915-e07d5b7de567 - -replace github.com/openshift/hypershift => github.com/openshift/hypershift v0.1.52-0.20250828102706-84d7581f683c diff --git a/go.sum b/go.sum index 02b067c282b..cb9a8ce47c2 100644 --- a/go.sum +++ b/go.sum @@ -708,8 +708,6 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/openshift/api v0.0.0-20240214165302-89248c87b7fc h1:/QQKBisQey7+qtKJS4fReHmXx/GyGRS8Tb+IU2WOMh0= github.com/openshift/api v0.0.0-20240214165302-89248c87b7fc/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4= -github.com/openshift/hypershift/api v0.0.0-20240522104800-604a957be25e h1:8MuhfnkUWjKdJ35pjPLXkPELRTj89LgJfKwcdKLBII4= -github.com/openshift/hypershift/api v0.0.0-20240522104800-604a957be25e/go.mod h1:NUkcQ8wwJw0/U7VVhTKSKptjHmIvptSMjL1c0FnTqqs= github.com/openshift/velero v0.10.2-0.20240822153644-9ac863aaa452 h1:rIGzeje6KnnudkYkiua/LigiNloNZfE6Kr0hCKqhtZI= github.com/openshift/velero v0.10.2-0.20240822153644-9ac863aaa452/go.mod h1:T+tSiinatCuVO7K3zGVxbCcMNYwfIdnlfc8SmIVDI4U= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= diff --git a/pkg/common/common.go b/pkg/common/common.go index 73c47dedf9d..310c0e0dda6 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -61,15 +61,14 @@ var DefaultRestoreResourcePriorities = restore.Priorities{ // Images const ( - VeleroImage = "quay.io/konveyor/velero:latest" - OpenshiftPluginImage = "quay.io/konveyor/openshift-velero-plugin:latest" - AWSPluginImage = "quay.io/konveyor/velero-plugin-for-aws:latest" - LegacyAWSPluginImage = "quay.io/konveyor/velero-plugin-for-legacy-aws:latest" - AzurePluginImage = "quay.io/konveyor/velero-plugin-for-microsoft-azure:latest" - GCPPluginImage = "quay.io/konveyor/velero-plugin-for-gcp:latest" - RegistryImage = "quay.io/konveyor/registry:latest" - KubeVirtPluginImage = "quay.io/konveyor/kubevirt-velero-plugin:v0.7.0" - HypershiftPluginImage = "quay.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-oadp-1-4" + VeleroImage = "quay.io/konveyor/velero:latest" + OpenshiftPluginImage = "quay.io/konveyor/openshift-velero-plugin:latest" + AWSPluginImage = "quay.io/konveyor/velero-plugin-for-aws:latest" + LegacyAWSPluginImage = "quay.io/konveyor/velero-plugin-for-legacy-aws:latest" + AzurePluginImage = "quay.io/konveyor/velero-plugin-for-microsoft-azure:latest" + GCPPluginImage = "quay.io/konveyor/velero-plugin-for-gcp:latest" + RegistryImage = "quay.io/konveyor/registry:latest" + KubeVirtPluginImage = "quay.io/konveyor/kubevirt-velero-plugin:v0.7.0" ) // Plugin names @@ -80,7 +79,6 @@ const ( VeleroPluginForGCP = "velero-plugin-for-gcp" VeleroPluginForOpenshift = "openshift-velero-plugin" KubeVirtPlugin = "kubevirt-velero-plugin" - HypershiftPlugin = "hypershift-oadp-plugin" ) // Environment Vars keys diff --git a/pkg/credentials/credentials.go b/pkg/credentials/credentials.go index 9d34c055330..9062f9a3341 100644 --- a/pkg/credentials/credentials.go +++ b/pkg/credentials/credentials.go @@ -78,10 +78,6 @@ var ( IsCloudProvider: false, PluginName: common.KubeVirtPlugin, }, - oadpv1alpha1.DefaultPluginHypershift: { - IsCloudProvider: false, - PluginName: common.HypershiftPlugin, - }, } ) @@ -163,16 +159,6 @@ func getKubeVirtPluginImage(dpa *oadpv1alpha1.DataProtectionApplication) string return os.Getenv("RELATED_IMAGE_KUBEVIRT_VELERO_PLUGIN") } -func getHypershiftPluginImage(dpa *oadpv1alpha1.DataProtectionApplication) string { - if dpa.Spec.UnsupportedOverrides[oadpv1alpha1.HypershiftPluginImageKey] != "" { - return dpa.Spec.UnsupportedOverrides[oadpv1alpha1.HypershiftPluginImageKey] - } - if os.Getenv("RELATED_IMAGE_HYPERSHIFT_VELERO_PLUGIN") == "" { - return common.HypershiftPluginImage - } - return os.Getenv("RELATED_IMAGE_HYPERSHIFT_VELERO_PLUGIN") -} - func GetPluginImage(defaultPlugin oadpv1alpha1.DefaultPlugin, dpa *oadpv1alpha1.DataProtectionApplication) string { switch defaultPlugin { @@ -193,9 +179,6 @@ func GetPluginImage(defaultPlugin oadpv1alpha1.DefaultPlugin, dpa *oadpv1alpha1. case oadpv1alpha1.DefaultPluginKubeVirt: return getKubeVirtPluginImage(dpa) - - case oadpv1alpha1.DefaultPluginHypershift: - return getHypershiftPluginImage(dpa) } return "" } diff --git a/tests/e2e/hcp_backup_restore_suite_test.go b/tests/e2e/hcp_backup_restore_suite_test.go deleted file mode 100644 index 75805341018..00000000000 --- a/tests/e2e/hcp_backup_restore_suite_test.go +++ /dev/null @@ -1,236 +0,0 @@ -package e2e_test - -import ( - "context" - "fmt" - "log" - "time" - - "github.com/onsi/ginkgo/v2" - "github.com/onsi/gomega" - - "github.com/openshift/oadp-operator/tests/e2e/lib" - libhcp "github.com/openshift/oadp-operator/tests/e2e/lib/hcp" -) - -type HCPBackupRestoreCase struct { - BackupRestoreCase - Template string - Provider string -} - -func runHCPBackupAndRestore(brCase HCPBackupRestoreCase, updateLastBRcase func(brCase HCPBackupRestoreCase), h *libhcp.HCHandler) { - updateLastBRcase(brCase) - - log.Printf("Preparing backup and restore") - backupName, restoreName := prepareBackupAndRestore(brCase.BackupRestoreCase, func() {}) - - err := h.AddHCPPluginToDPA(dpaCR.Namespace, dpaCR.Name, false) - gomega.Expect(err).ToNot(gomega.HaveOccurred(), "failed to add HCP plugin to DPA: %v", err) - // TODO: move the wait for HC just after the DPA modification to allow reconciliation to go ahead without waiting for the HC to be created - - //Wait for HCP plugin to be added - gomega.Eventually(libhcp.IsHCPPluginAdded(h.Client, dpaCR.Namespace, dpaCR.Name), 3*time.Minute, 1*time.Second).Should(gomega.BeTrue()) - - // Create the HostedCluster for the test - h.HCPNamespace = libhcp.GetHCPNamespace(brCase.BackupRestoreCase.Name, libhcp.ClustersNamespace) - h.HostedCluster, err = h.DeployHCManifest(brCase.Template, brCase.Provider, brCase.BackupRestoreCase.Name) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - - if brCase.PreBackupVerify != nil { - err := brCase.PreBackupVerify(runTimeClientForSuiteRun, brCase.Namespace) - gomega.Expect(err).ToNot(gomega.HaveOccurred(), "failed to run HCP pre-backup verification: %v", err) - } - - // Backup HCP & HC - log.Printf("Backing up HC") - includedResources := libhcp.HCPIncludedResources - excludedResources := libhcp.HCPExcludedResources - includedNamespaces := append(libhcp.HCPIncludedNamespaces, libhcp.GetHCPNamespace(h.HostedCluster.Name, libhcp.ClustersNamespace)) - - nsRequiresResticDCWorkaround := runHCPBackup(brCase.BackupRestoreCase, backupName, h, includedNamespaces, includedResources, excludedResources) - - // Delete everything in HCP namespace - log.Printf("Deleting HCP & HC") - err = h.RemoveHCP(libhcp.Wait10Min) - gomega.Expect(err).ToNot(gomega.HaveOccurred(), "failed to remove HCP: %v", err) - - // Restore HC - log.Printf("Restoring HC") - runHCPRestore(brCase.BackupRestoreCase, backupName, restoreName, nsRequiresResticDCWorkaround) - - // Wait for HCP to be restored - log.Printf("Validating HC") - err = libhcp.ValidateHCP(libhcp.ValidateHCPTimeout, libhcp.Wait10Min, []string{}, h.HCPNamespace)(h.Client, libhcp.ClustersNamespace) - gomega.Expect(err).ToNot(gomega.HaveOccurred(), "failed to run HCP post-restore verification: %v", err) -} - -var _ = ginkgo.Describe("HCP Backup and Restore tests", ginkgo.Ordered, func() { - var ( - lastInstallTime time.Time - lastBRCase HCPBackupRestoreCase - h *libhcp.HCHandler - err error - ctx = context.Background() - ) - - updateLastBRcase := func(brCase HCPBackupRestoreCase) { - lastBRCase = brCase - } - - // Before All - var _ = ginkgo.BeforeAll(func() { - reqOperators := []libhcp.RequiredOperator{ - { - Name: libhcp.MCEName, - Namespace: libhcp.MCENamespace, - OperatorGroup: libhcp.MCEOperatorGroup, - }, - } - - // Install MCE and Hypershift operators - h, err = libhcp.InstallRequiredOperators(ctx, runTimeClientForSuiteRun, reqOperators) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - gomega.Expect(h).ToNot(gomega.BeNil()) - gomega.Eventually(lib.IsDeploymentReady(h.Client, libhcp.MCENamespace, libhcp.MCEOperatorName), libhcp.Wait10Min, time.Second*5).Should(gomega.BeTrue()) - - // Deploy the MCE manifest - err = h.DeployMCEManifest() - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - - // Deploy the MCE and wait for it to be ready - gomega.Eventually(lib.IsDeploymentReady(h.Client, libhcp.MCENamespace, libhcp.MCEOperatorName), libhcp.Wait10Min, time.Second*5).Should(gomega.BeTrue()) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - - // Validate the Hypershift operator - gomega.Eventually(lib.IsDeploymentReady(h.Client, libhcp.HONamespace, libhcp.HypershiftOperatorName), libhcp.Wait10Min, time.Second*5).Should(gomega.BeTrue()) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - }) - - // After All - var _ = ginkgo.AfterAll(func() { - err := h.RemoveHCP(libhcp.Wait10Min) - gomega.Expect(err).ToNot(gomega.HaveOccurred(), "failed to remove HCP: %v", err) - }) - - // After Each - var _ = ginkgo.AfterEach(func(ctx ginkgo.SpecContext) { - h.RemoveHCP(libhcp.Wait10Min) - tearDownBackupAndRestore(lastBRCase.BackupRestoreCase, lastInstallTime, ctx.SpecReport()) - }) - - ginkgo.DescribeTable("Basic HCP backup and restore test", - func(brCase HCPBackupRestoreCase, expectedErr error) { - if ginkgo.CurrentSpecReport().NumAttempts > 1 && !knownFlake { - ginkgo.Fail("No known FLAKE found in a previous run, marking test as failed.") - } - runHCPBackupAndRestore(brCase, updateLastBRcase, h) - }, - - // Test Cases - ginkgo.Entry("None HostedCluster backup and restore", ginkgo.Label("hcp"), HCPBackupRestoreCase{ - Template: libhcp.HCPNoneManifest, - Provider: "None", - BackupRestoreCase: BackupRestoreCase{ - Namespace: libhcp.GetHCPNamespace(fmt.Sprintf("%s-none", libhcp.HostedClusterPrefix), libhcp.ClustersNamespace), - Name: fmt.Sprintf("%s-none", libhcp.HostedClusterPrefix), - BackupRestoreType: lib.CSIDataMover, - PreBackupVerify: libhcp.ValidateHCP(libhcp.ValidateHCPTimeout, libhcp.Wait10Min, []string{}, libhcp.GetHCPNamespace(fmt.Sprintf("%s-none", libhcp.HostedClusterPrefix), libhcp.ClustersNamespace)), - PostRestoreVerify: libhcp.ValidateHCP(libhcp.ValidateHCPTimeout, libhcp.Wait10Min, []string{}, libhcp.GetHCPNamespace(fmt.Sprintf("%s-none", libhcp.HostedClusterPrefix), libhcp.ClustersNamespace)), - BackupTimeout: libhcp.HCPBackupTimeout, - }, - }, nil), - - ginkgo.Entry("Agent HostedCluster backup and restore", ginkgo.Label("hcp"), HCPBackupRestoreCase{ - Template: libhcp.HCPAgentManifest, - Provider: "Agent", - BackupRestoreCase: BackupRestoreCase{ - Namespace: libhcp.GetHCPNamespace(fmt.Sprintf("%s-agent", libhcp.HostedClusterPrefix), libhcp.ClustersNamespace), - Name: fmt.Sprintf("%s-agent", libhcp.HostedClusterPrefix), - BackupRestoreType: lib.CSIDataMover, - PreBackupVerify: libhcp.ValidateHCP(libhcp.ValidateHCPTimeout, libhcp.Wait10Min, []string{}, libhcp.GetHCPNamespace(fmt.Sprintf("%s-agent", libhcp.HostedClusterPrefix), libhcp.ClustersNamespace)), - PostRestoreVerify: libhcp.ValidateHCP(libhcp.ValidateHCPTimeout, libhcp.Wait10Min, []string{}, libhcp.GetHCPNamespace(fmt.Sprintf("%s-agent", libhcp.HostedClusterPrefix), libhcp.ClustersNamespace)), - BackupTimeout: libhcp.HCPBackupTimeout, - }, - }, nil), - ) -}) - -// TODO: Modify the runBackup function to inject the filtered error logs to avoid repeating code with this -func runHCPBackup(brCase BackupRestoreCase, backupName string, h *libhcp.HCHandler, namespaces []string, includedResources, excludedResources []string) bool { - nsRequiresResticDCWorkaround, err := lib.NamespaceRequiresResticDCWorkaround(h.Client, brCase.Namespace) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - - // create backup - log.Printf("Creating backup %s for case %s", backupName, brCase.Name) - err = lib.CreateCustomBackupForNamespaces(h.Client, namespace, backupName, namespaces, includedResources, excludedResources, brCase.BackupRestoreType == lib.RESTIC || brCase.BackupRestoreType == lib.KOPIA, brCase.BackupRestoreType == lib.CSIDataMover) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - - // wait for backup to not be running - gomega.Eventually(lib.IsBackupDone(h.Client, namespace, backupName), brCase.BackupTimeout, time.Second*10).Should(gomega.BeTrue()) - // TODO only log on fail? - describeBackup := lib.DescribeBackup(h.Client, namespace, backupName) - ginkgo.GinkgoWriter.Println(describeBackup) - - backupLogs := lib.BackupLogs(kubernetesClientForSuiteRun, h.Client, namespace, backupName) - backupErrorLogs := lib.BackupErrorLogs(kubernetesClientForSuiteRun, h.Client, namespace, backupName) - accumulatedTestLogs = append(accumulatedTestLogs, describeBackup, backupLogs) - - // Check error logs for non-relevant errors - filteredBackupErrorLogs := libhcp.FilterErrorLogs(backupErrorLogs) - - if !brCase.SkipVerifyLogs { - gomega.Expect(filteredBackupErrorLogs).Should(gomega.Equal([]string{})) - } - - // check if backup succeeded - succeeded, err := lib.IsBackupCompletedSuccessfully(kubernetesClientForSuiteRun, h.Client, namespace, backupName) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - gomega.Expect(succeeded).To(gomega.Equal(true)) - log.Printf("Backup for case %s succeeded", brCase.Name) - - if brCase.BackupRestoreType == lib.CSI { - // wait for volume snapshot to be Ready - gomega.Eventually(lib.AreVolumeSnapshotsReady(h.Client, backupName), time.Minute*4, time.Second*10).Should(gomega.BeTrue()) - } - - return nsRequiresResticDCWorkaround -} - -// TODO: Modify the runRestore function to inject the filtered error logs to avoid repeating code with this -func runHCPRestore(brCase BackupRestoreCase, backupName string, restoreName string, nsRequiresResticDCWorkaround bool) { - log.Printf("Creating restore %s for case %s", restoreName, brCase.Name) - err := lib.CreateRestoreFromBackup(dpaCR.Client, namespace, backupName, restoreName) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - gomega.Eventually(lib.IsRestoreDone(dpaCR.Client, namespace, restoreName), time.Minute*60, time.Second*10).Should(gomega.BeTrue()) - // TODO only log on fail? - describeRestore := lib.DescribeRestore(dpaCR.Client, namespace, restoreName) - ginkgo.GinkgoWriter.Println(describeRestore) - - restoreLogs := lib.RestoreLogs(kubernetesClientForSuiteRun, dpaCR.Client, namespace, restoreName) - restoreErrorLogs := lib.RestoreErrorLogs(kubernetesClientForSuiteRun, dpaCR.Client, namespace, restoreName) - accumulatedTestLogs = append(accumulatedTestLogs, describeRestore, restoreLogs) - - // Check error logs for non-relevant errors - filteredRestoreErrorLogs := libhcp.FilterErrorLogs(restoreErrorLogs) - - if !brCase.SkipVerifyLogs { - gomega.Expect(filteredRestoreErrorLogs).Should(gomega.Equal([]string{})) - } - - // Check if restore succeeded - succeeded, err := lib.IsRestoreCompletedSuccessfully(kubernetesClientForSuiteRun, dpaCR.Client, namespace, restoreName) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - gomega.Expect(succeeded).To(gomega.Equal(true)) - - if nsRequiresResticDCWorkaround { - // We run the dc-post-restore.sh script for both restic and - // kopia backups and for any DCs with attached volumes, - // regardless of whether it was restic or kopia backup. - // The script is designed to work with labels set by the - // openshift-velero-plugin and can be run without pre-conditions. - log.Printf("Running dc-post-restore.sh script.") - err = lib.RunDcPostRestoreScript(restoreName) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - } -} diff --git a/tests/e2e/lib/hcp/dpa.go b/tests/e2e/lib/hcp/dpa.go deleted file mode 100644 index 668747e1991..00000000000 --- a/tests/e2e/lib/hcp/dpa.go +++ /dev/null @@ -1,99 +0,0 @@ -package hcp - -import ( - "context" - "fmt" - "log" - - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - - oadpv1alpha1 "github.com/openshift/oadp-operator/api/v1alpha1" -) - -// AddHCPPluginToDPA adds the HCP plugin to a DPA -func (h *HCHandler) AddHCPPluginToDPA(namespace, name string, overrides bool) error { - addHCPlugin := true - - log.Printf("Adding HCP default plugin to DPA") - dpa := &oadpv1alpha1.DataProtectionApplication{} - err := h.Client.Get(h.Ctx, types.NamespacedName{Namespace: namespace, Name: name}, dpa) - if err != nil { - return err - } - - // Check if the hypershift plugin is already in the default plugins - for _, plugin := range dpa.Spec.Configuration.Velero.DefaultPlugins { - if plugin == oadpv1alpha1.DefaultPluginHypershift { - log.Printf("HCP plugin already in DPA") - if overrides { - log.Printf("Override set to true, removing HCP plugin from DPA") - addHCPlugin = false - break - } - return nil - } - } - - if addHCPlugin { - dpa.Spec.Configuration.Velero.DefaultPlugins = append(dpa.Spec.Configuration.Velero.DefaultPlugins, oadpv1alpha1.DefaultPluginHypershift) - } - - if overrides { - dpa.Spec.UnsupportedOverrides = map[oadpv1alpha1.UnsupportedImageKey]string{ - oadpv1alpha1.HypershiftPluginImageKey: "quay.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-oadp-1-5:oadp-1.5", - } - } - - err = h.Client.Update(h.Ctx, dpa) - if err != nil { - return fmt.Errorf("failed to update DPA: %v", err) - } - log.Printf("HCP plugin added to DPA") - return nil -} - -// RemoveHCPPluginFromDPA removes the HCP plugin from a DPA -func (h *HCHandler) RemoveHCPPluginFromDPA(namespace, name string) error { - log.Printf("Removing HCP plugin from DPA") - dpa := &oadpv1alpha1.DataProtectionApplication{} - err := h.Client.Get(h.Ctx, types.NamespacedName{Namespace: namespace, Name: name}, dpa) - if err != nil { - return err - } - delete(dpa.Spec.UnsupportedOverrides, oadpv1alpha1.HypershiftPluginImageKey) - // remove hypershift plugin from default plugins - for i, plugin := range dpa.Spec.Configuration.Velero.DefaultPlugins { - if plugin == oadpv1alpha1.DefaultPluginHypershift { - dpa.Spec.Configuration.Velero.DefaultPlugins = append(dpa.Spec.Configuration.Velero.DefaultPlugins[:i], dpa.Spec.Configuration.Velero.DefaultPlugins[i+1:]...) - break - } - } - err = h.Client.Update(h.Ctx, dpa) - if err != nil { - return fmt.Errorf("failed to update DPA: %v", err) - } - log.Printf("HCP plugin removed from DPA") - return nil -} - -// IsHCPPluginAdded checks if the HCP plugin is added to a DPA -func IsHCPPluginAdded(c client.Client, namespace, name string) bool { - dpa := &oadpv1alpha1.DataProtectionApplication{} - err := c.Get(context.Background(), types.NamespacedName{Namespace: namespace, Name: name}, dpa) - if err != nil { - return false - } - - if dpa.Spec.Configuration == nil || dpa.Spec.Configuration.Velero == nil { - return false - } - - for _, plugin := range dpa.Spec.Configuration.Velero.DefaultPlugins { - if plugin == oadpv1alpha1.DefaultPluginHypershift { - return true - } - } - - return false -} diff --git a/tests/e2e/lib/hcp/dpa_test.go b/tests/e2e/lib/hcp/dpa_test.go deleted file mode 100644 index a7c3fcd3071..00000000000 --- a/tests/e2e/lib/hcp/dpa_test.go +++ /dev/null @@ -1,295 +0,0 @@ -package hcp - -import ( - "context" - "testing" - - "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - oadpv1alpha1 "github.com/openshift/oadp-operator/api/v1alpha1" -) - -func TestAddHCPPluginToDPA(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - dpa *oadpv1alpha1.DataProtectionApplication - overrides bool - }{ - { - name: "Add plugin without overrides", - dpa: &oadpv1alpha1.DataProtectionApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dpa", - Namespace: "test-ns", - }, - Spec: oadpv1alpha1.DataProtectionApplicationSpec{ - Configuration: &oadpv1alpha1.ApplicationConfig{ - Velero: &oadpv1alpha1.VeleroConfig{ - DefaultPlugins: []oadpv1alpha1.DefaultPlugin{}, - }, - }, - }, - }, - overrides: false, - }, - { - name: "Add plugin with overrides", - dpa: &oadpv1alpha1.DataProtectionApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dpa", - Namespace: "test-ns", - }, - Spec: oadpv1alpha1.DataProtectionApplicationSpec{ - Configuration: &oadpv1alpha1.ApplicationConfig{ - Velero: &oadpv1alpha1.VeleroConfig{ - DefaultPlugins: []oadpv1alpha1.DefaultPlugin{}, - }, - }, - }, - }, - overrides: true, - }, - { - name: "Plugin already exists", - dpa: &oadpv1alpha1.DataProtectionApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dpa", - Namespace: "test-ns", - }, - Spec: oadpv1alpha1.DataProtectionApplicationSpec{ - Configuration: &oadpv1alpha1.ApplicationConfig{ - Velero: &oadpv1alpha1.VeleroConfig{ - DefaultPlugins: []oadpv1alpha1.DefaultPlugin{ - oadpv1alpha1.DefaultPluginHypershift, - }, - }, - }, - }, - }, - overrides: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme with DataProtectionApplication registered - scheme := runtime.NewScheme() - err := oadpv1alpha1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Create a new client with the scheme - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create the handler - h := &HCHandler{ - Ctx: context.Background(), - Client: client, - } - - // Create DPA - err = client.Create(context.Background(), tt.dpa) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Call AddHCPPluginToDPA - err = h.AddHCPPluginToDPA(tt.dpa.Namespace, tt.dpa.Name, tt.overrides) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Verify DPA was updated - updatedDPA := &oadpv1alpha1.DataProtectionApplication{} - err = client.Get(context.Background(), types.NamespacedName{Name: tt.dpa.Name, Namespace: tt.dpa.Namespace}, updatedDPA) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Check if plugin was added - pluginFound := false - for _, plugin := range updatedDPA.Spec.Configuration.Velero.DefaultPlugins { - if plugin == oadpv1alpha1.DefaultPluginHypershift { - pluginFound = true - break - } - } - g.Expect(pluginFound).To(gomega.BeTrue()) - - // Check if overrides were added - if tt.overrides { - g.Expect(updatedDPA.Spec.UnsupportedOverrides).To(gomega.HaveKey(oadpv1alpha1.HypershiftPluginImageKey)) - } - }) - } -} - -func TestRemoveHCPPluginFromDPA(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - dpa *oadpv1alpha1.DataProtectionApplication - }{ - { - name: "Remove plugin", - dpa: &oadpv1alpha1.DataProtectionApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dpa", - Namespace: "test-ns", - }, - Spec: oadpv1alpha1.DataProtectionApplicationSpec{ - Configuration: &oadpv1alpha1.ApplicationConfig{ - Velero: &oadpv1alpha1.VeleroConfig{ - DefaultPlugins: []oadpv1alpha1.DefaultPlugin{ - oadpv1alpha1.DefaultPluginHypershift, - }, - }, - }, - UnsupportedOverrides: map[oadpv1alpha1.UnsupportedImageKey]string{ - oadpv1alpha1.HypershiftPluginImageKey: "quay.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-oadp-1-5:oadp-1.5", - }, - }, - }, - }, - { - name: "Plugin not present", - dpa: &oadpv1alpha1.DataProtectionApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dpa", - Namespace: "test-ns", - }, - Spec: oadpv1alpha1.DataProtectionApplicationSpec{ - Configuration: &oadpv1alpha1.ApplicationConfig{ - Velero: &oadpv1alpha1.VeleroConfig{ - DefaultPlugins: []oadpv1alpha1.DefaultPlugin{}, - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme with DataProtectionApplication registered - scheme := runtime.NewScheme() - err := oadpv1alpha1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Create a new client with the scheme - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create the handler - h := &HCHandler{ - Ctx: context.Background(), - Client: client, - } - - // Create DPA - err = client.Create(context.Background(), tt.dpa) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Call RemoveHCPPluginFromDPA - err = h.RemoveHCPPluginFromDPA(tt.dpa.Namespace, tt.dpa.Name) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Verify DPA was updated - updatedDPA := &oadpv1alpha1.DataProtectionApplication{} - err = client.Get(context.Background(), types.NamespacedName{Name: tt.dpa.Name, Namespace: tt.dpa.Namespace}, updatedDPA) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Check if plugin was removed - pluginFound := false - for _, plugin := range updatedDPA.Spec.Configuration.Velero.DefaultPlugins { - if plugin == oadpv1alpha1.DefaultPluginHypershift { - pluginFound = true - break - } - } - g.Expect(pluginFound).To(gomega.BeFalse()) - - // Check if overrides were removed - g.Expect(updatedDPA.Spec.UnsupportedOverrides).NotTo(gomega.HaveKey(oadpv1alpha1.HypershiftPluginImageKey)) - }) - } -} - -func TestIsHCPPluginAdded(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - dpa *oadpv1alpha1.DataProtectionApplication - expectedResult bool - }{ - { - name: "HCP plugin exists", - dpa: &oadpv1alpha1.DataProtectionApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dpa", - Namespace: "test-ns", - }, - Spec: oadpv1alpha1.DataProtectionApplicationSpec{ - Configuration: &oadpv1alpha1.ApplicationConfig{ - Velero: &oadpv1alpha1.VeleroConfig{ - DefaultPlugins: []oadpv1alpha1.DefaultPlugin{ - oadpv1alpha1.DefaultPluginHypershift, - }, - }, - }, - }, - }, - expectedResult: true, - }, - { - name: "HCP plugin does not exist", - dpa: &oadpv1alpha1.DataProtectionApplication{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-dpa", - Namespace: "test-ns", - }, - Spec: oadpv1alpha1.DataProtectionApplicationSpec{ - Configuration: &oadpv1alpha1.ApplicationConfig{ - Velero: &oadpv1alpha1.VeleroConfig{ - DefaultPlugins: []oadpv1alpha1.DefaultPlugin{ - oadpv1alpha1.DefaultPluginAWS, - }, - }, - }, - }, - }, - expectedResult: false, - }, - { - name: "DPA is nil", - expectedResult: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme with DataProtectionApplication registered - scheme := runtime.NewScheme() - err := oadpv1alpha1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Create a new client with the scheme - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create DPA if it exists in the test case - if tt.dpa != nil { - err := client.Create(context.Background(), tt.dpa) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - // Call IsHCPPluginAdded - var result bool - if tt.dpa != nil { - result = IsHCPPluginAdded(client, tt.dpa.Namespace, tt.dpa.Name) - } else { - result = IsHCPPluginAdded(client, "non-existent", "non-existent") - } - g.Expect(result).To(gomega.Equal(tt.expectedResult)) - }) - } -} diff --git a/tests/e2e/lib/hcp/hcp.go b/tests/e2e/lib/hcp/hcp.go deleted file mode 100644 index 199fb332d47..00000000000 --- a/tests/e2e/lib/hcp/hcp.go +++ /dev/null @@ -1,579 +0,0 @@ -package hcp - -import ( - "context" - "encoding/base64" - "fmt" - "log" - "time" - - hypershiftv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/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/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/openshift/oadp-operator/tests/e2e/lib" -) - -func (h *HCHandler) RemoveHCP(timeout time.Duration) error { - // Delete the hostedCluster - if err := h.DeleteHostedCluster(); err != nil { - return err - } - - // Delete HCP Namespace - if err := h.DeleteHCPNamespace(false); err != nil { - return err - } - - // Delete HCP - if err := h.DeleteHostedControlPlane(); err != nil { - return err - } - - // Wait for HCP deletion with timeout - var hcpName string - if h.HostedCluster != nil { - hcpName = h.HostedCluster.Name - } else { - // If HostedCluster is nil, try to get the HCP name from the namespace - hcpName = "test-hc" // Default name if we can't determine it - } - - hcp := hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: hcpName, - Namespace: h.HCPNamespace, - }, - } - if err := h.WaitForHCPDeletion(&hcp); err != nil { - return fmt.Errorf("failed to delete HCP: %v", err) - } - log.Printf("\tHCP deleted") - - // Delete HC Secrets - if err := h.DeleteHCSecrets(); err != nil { - return err - } - - // Wait for the HC to be deleted - log.Printf("\tWaiting for the HC to be deleted") - err := wait.PollUntilContextTimeout(h.Ctx, time.Second*5, timeout, true, func(ctx context.Context) (bool, error) { - log.Printf("\tAttempting to verify HC deletion...") - result := IsHCDeleted(h) - log.Printf("\tHC deletion check result: %v", result) - return result, nil - }) - - if err != nil { - return fmt.Errorf("failed to wait for HC deletion: %v", err) - } - - return nil -} - -// DeleteHostedCluster deletes a HostedCluster and waits for its deletion -func (h *HCHandler) DeleteHostedCluster() error { - if h.HostedCluster == nil { - log.Printf("No HostedCluster to delete") - return nil - } - - log.Printf("Deleting HostedCluster %s in namespace %s", h.HostedCluster.Name, h.HostedCluster.Namespace) - if err := h.deleteResource(h.HostedCluster); err != nil && !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete HostedCluster: %v", err) - } - - // Wait for HC deletion - if err := h.WaitForHCDeletion(); err != nil { - return fmt.Errorf("failed waiting for HostedCluster deletion: %v", err) - } - - return nil -} - -// DeleteHCPNamespace deletes the HCP namespace and waits for its deletion if needed -func (h *HCHandler) DeleteHCPNamespace(shouldWait bool) error { - if h.HCPNamespace == "" { - log.Printf("No HCP namespace to delete") - return nil - } - - log.Printf("Deleting HCP namespace %s", h.HCPNamespace) - ns := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: h.HCPNamespace, - }, - } - - if err := h.deleteResource(ns); err != nil { - if apierrors.IsNotFound(err) { - log.Printf("Namespace %s already deleted", h.HCPNamespace) - return nil - } - return fmt.Errorf("failed to delete HCP namespace %s: %v", h.HCPNamespace, err) - } - - if !shouldWait { - return nil - } - - log.Printf("Waiting for namespace %s to be deleted", h.HCPNamespace) - err := wait.PollUntilContextTimeout(h.Ctx, WaitForNextCheckTimeout, Wait10Min, true, func(ctx context.Context) (bool, error) { - err := h.Client.Get(ctx, types.NamespacedName{Name: h.HCPNamespace}, ns) - if err == nil { - log.Printf("Namespace %s still exists, waiting...", h.HCPNamespace) - return false, nil - } - - if apierrors.IsNotFound(err) { - log.Printf("Namespace %s successfully deleted", h.HCPNamespace) - return true, nil - } - - // Handle retryable errors - if apierrors.IsTooManyRequests(err) || apierrors.IsServerTimeout(err) || apierrors.IsTimeout(err) { - log.Printf("Retryable error while checking namespace %s deletion: %v", h.HCPNamespace, err) - return false, nil - } - - return false, fmt.Errorf("unexpected error while checking namespace %s deletion: %v", h.HCPNamespace, err) - }) - - if err != nil { - return fmt.Errorf("timeout waiting for namespace %s to be deleted: %v", h.HCPNamespace, err) - } - - return nil -} - -// DeleteHostedControlPlane deletes a HostedControlPlane and waits for its deletion -func (h *HCHandler) DeleteHostedControlPlane() error { - if h.HCPNamespace == "" { - log.Printf("No HCP namespace specified") - return nil - } - - // Get the HCP name from HostedCluster if available, otherwise use default - var hcpName string - if h.HostedCluster != nil { - hcpName = h.HostedCluster.Name - } else { - hcpName = "test-hc" // Default name if HostedCluster is nil - } - - hcp := &hypershiftv1.HostedControlPlane{} - err := h.Client.Get(h.Ctx, types.NamespacedName{ - Namespace: h.HCPNamespace, - Name: hcpName, - }, hcp) - - if err != nil { - if apierrors.IsNotFound(err) { - log.Printf("No HostedControlPlane found in namespace %s", h.HCPNamespace) - return nil - } - return fmt.Errorf("failed to get HostedControlPlane: %v", err) - } - - log.Printf("Deleting HostedControlPlane %s in namespace %s", hcp.Name, hcp.Namespace) - if err := h.deleteResource(hcp); err != nil && !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete HostedControlPlane: %v", err) - } - - // Wait for HCP deletion - if err := h.WaitForHCPDeletion(hcp); err != nil { - return fmt.Errorf("failed waiting for HostedControlPlane deletion: %v", err) - } - - return nil -} - -// DeleteHCSecrets deletes secrets in the HCP namespace -func (h *HCHandler) DeleteHCSecrets() error { - if h.HCPNamespace == "" { - log.Printf("No HCP namespace specified") - return nil - } - - log.Printf("Deleting secrets in namespace %s", h.HCPNamespace) - secretList := &corev1.SecretList{} - if err := h.Client.List(h.Ctx, secretList, &client.ListOptions{ - Namespace: h.HCPNamespace, - }); err != nil { - return fmt.Errorf("failed to list secrets: %v", err) - } - - for _, secret := range secretList.Items { - if err := h.deleteResource(&secret); err != nil && !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete secret %s: %v", secret.Name, err) - } - } - - return nil -} - -// WaitForHCDeletion waits for the HostedCluster to be deleted -func (h *HCHandler) WaitForHCDeletion() error { - return wait.PollUntilContextTimeout(h.Ctx, WaitForNextCheckTimeout, Wait10Min, true, func(ctx context.Context) (bool, error) { - return IsHCDeleted(h), nil - }) -} - -// WaitForHCPDeletion waits for the HostedControlPlane to be deleted -func (h *HCHandler) WaitForHCPDeletion(hcp *hypershiftv1.HostedControlPlane) error { - return wait.PollUntilContextTimeout(h.Ctx, WaitForNextCheckTimeout, Wait10Min, true, func(ctx context.Context) (bool, error) { - return IsHCPDeleted(h, hcp), nil - }) -} - -// NukeHostedCluster removes all resources associated with a HostedCluster -func (h *HCHandler) NukeHostedCluster() error { - // List of resource types to check - log.Printf("\tNuking HostedCluster") - resourceTypes := []struct { - kind string - gvk schema.GroupVersionKind - }{ - {"HostedControlPlane", hypershiftv1.GroupVersion.WithKind("HostedControlPlane")}, - {"Cluster", clusterGVK}, - {"AWSCluster", awsClusterGVK}, - {"AgentCluster", capiAgentGVK}, - } - - for _, rt := range resourceTypes { - obj := &unstructured.UnstructuredList{} - obj.SetGroupVersionKind(rt.gvk) - - if err := h.Client.List(h.Ctx, obj, &client.ListOptions{Namespace: h.HCPNamespace}); err != nil { - log.Printf("Error listing %s: %v", rt.kind, err) - continue - } - - for _, item := range obj.Items { - if len(item.GetFinalizers()) > 0 { - log.Printf("\tNUKE: Removing finalizers from %s %s", rt.kind, item.GetName()) - item.SetFinalizers([]string{}) - if err := h.Client.Update(h.Ctx, &item); err != nil { - return fmt.Errorf("\tNUKE: Error removing finalizers from %s %s: %v", rt.kind, item.GetName(), err) - } - } - } - } - - return nil -} - -// DeployHCManifest deploys a HostedCluster manifest -func (h *HCHandler) DeployHCManifest(tmpl, provider string, hcName string) (*hypershiftv1.HostedCluster, error) { - log.Printf("Deploying HostedCluster manifest - %s", provider) - // Create the clusters ns - clustersNS := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: ClustersNamespace, - }, - } - - log.Printf("Creating clusters namespace") - err := h.Client.Create(h.Ctx, clustersNS) - if err != nil { - if !apierrors.IsAlreadyExists(err) { - return nil, fmt.Errorf("failed to create clusters namespace: %v", err) - } - } - - log.Printf("Getting pull secret") - pullSecret, err := getPullSecret(h.Ctx, h.Client) - if err != nil { - return nil, fmt.Errorf("failed to get pull secret: %v", err) - } - - log.Printf("Applying pull secret manifest") - err = ApplyYAMLTemplate(h.Ctx, h.Client, PullSecretManifest, true, map[string]interface{}{ - "HostedClusterName": hcName, - "ClustersNamespace": ClustersNamespace, - "PullSecret": base64.StdEncoding.EncodeToString([]byte(pullSecret)), - }) - if err != nil { - return nil, fmt.Errorf("failed to apply pull secret manifest: %v", err) - } - - log.Printf("Applying encryption key manifest") - err = ApplyYAMLTemplate(h.Ctx, h.Client, EtcdEncryptionKeyManifest, true, map[string]interface{}{ - "HostedClusterName": hcName, - "ClustersNamespace": ClustersNamespace, - "EtcdEncryptionKey": SampleETCDEncryptionKey, - }) - if err != nil { - return nil, fmt.Errorf("failed to apply encryption key manifest: %v", err) - } - - if provider == "Agent" { - log.Printf("Applying capi-provider-role manifest") - err = ApplyYAMLTemplate(h.Ctx, h.Client, CapiProviderRoleManifest, true, map[string]interface{}{ - "ClustersNamespace": ClustersNamespace, - }) - if err != nil { - return nil, fmt.Errorf("failed to apply capi-provider-role manifest from %s: %v", CapiProviderRoleManifest, err) - } - } - - log.Printf("Applying HostedCluster manifest") - err = ApplyYAMLTemplate(h.Ctx, h.Client, tmpl, false, map[string]interface{}{ - "HostedClusterName": hcName, - "ClustersNamespace": ClustersNamespace, - "HCOCPTestImage": h.HCOCPTestImage, - "InfraIDSeed": "test", - }) - if err != nil { - return nil, fmt.Errorf("failed to apply HostedCluster manifest: %v", err) - } - - // Wait for HC to be present - var hc hypershiftv1.HostedCluster - err = wait.PollUntilContextTimeout(h.Ctx, WaitForNextCheckTimeout, Wait10Min, true, func(ctx context.Context) (bool, error) { - err := h.Client.Get(ctx, types.NamespacedName{ - Name: hcName, - Namespace: ClustersNamespace, - }, &hc) - if err != nil { - if !apierrors.IsNotFound(err) && !apierrors.IsTooManyRequests(err) && !apierrors.IsServerTimeout(err) && !apierrors.IsTimeout(err) { - return false, fmt.Errorf("failed to get HostedCluster %s: %v", hcName, err) - } - log.Printf("Error getting HostedCluster %s, retrying...: %v", hcName, err) - return false, nil - } - return true, nil - }) - if err != nil { - return nil, fmt.Errorf("failed waiting for HostedCluster to be present: %v", err) - } - - return &hc, nil -} - -// ValidateETCD validates that the ETCD StatefulSet is ready -func ValidateETCD(ctx context.Context, ocClient client.Client, hcpNamespace string, timeout time.Duration) error { - log.Printf("Validating ETCD StatefulSet with timeout: %v", timeout) - - // Create a separate context for ETCD validation with a longer timeout - etcdCtx, etcdCancel := context.WithTimeout(ctx, timeout) - defer etcdCancel() - - err := wait.PollUntilContextTimeout(etcdCtx, time.Second*10, timeout, true, func(ctx context.Context) (bool, error) { - etcdSts := &appsv1.StatefulSet{} - err := ocClient.Get(ctx, types.NamespacedName{Name: "etcd", Namespace: hcpNamespace}, etcdSts) - if err != nil { - if !apierrors.IsNotFound(err) && !apierrors.IsTooManyRequests(err) && !apierrors.IsServerTimeout(err) && !apierrors.IsTimeout(err) { - log.Printf("ETCD StatefulSet not found yet, waiting...") - return false, fmt.Errorf("failed to get etcd statefulset: %v", err) - } - log.Printf("Error getting etcd statefulset, retrying...: %v", err) - return false, nil - } - if etcdSts.Status.Replicas != etcdSts.Status.ReadyReplicas { - log.Printf("ETCD STS is not ready (Available: %d, Replicas: %d)", etcdSts.Status.ReadyReplicas, etcdSts.Status.Replicas) - return false, nil - } - log.Printf("ETCD STS is ready") - return true, nil - }) - if err != nil { - return fmt.Errorf("failed to wait for ETCD StatefulSet: %v", err) - } - return nil -} - -// ValidateDeployments validates that all required deployments are ready -func ValidateDeployments(ctx context.Context, ocClient client.Client, hcpNamespace string, deployments []string, contingencyTimeout time.Duration) error { - for _, depName := range deployments { - log.Printf("Checking deployment: %s", depName) - ready := false - err := wait.PollUntilContextTimeout(ctx, time.Second*10, contingencyTimeout, true, func(ctx context.Context) (bool, error) { - deployment := &appsv1.Deployment{} - err := ocClient.Get(ctx, types.NamespacedName{Name: depName, Namespace: hcpNamespace}, deployment) - if err != nil { - if !apierrors.IsNotFound(err) && !apierrors.IsTooManyRequests(err) && !apierrors.IsServerTimeout(err) && !apierrors.IsTimeout(err) { - return false, fmt.Errorf("failed to get deployment %s: %v", depName, err) - } - log.Printf("Error getting deployment %s: %v", depName, err) - return false, nil - } - if deployment.Status.AvailableReplicas != deployment.Status.Replicas { - log.Printf("Deployment %s is not ready (Available: %d, Replicas: %d)", depName, deployment.Status.AvailableReplicas, deployment.Status.Replicas) - return false, nil - } - ready = true - return true, nil - }) - - if err != nil || !ready { - log.Printf("Deployment %s validation failed", depName) - err := handleDeploymentValidationFailure(ctx, ocClient, hcpNamespace, deployments, contingencyTimeout) - if err != nil { - return fmt.Errorf("deployment %s failed after contingency applied: %v", depName, err) - } - } - } - log.Printf("All deployments validated successfully") - return nil -} - -// ValidateHCP returns a VerificationFunction that checks if the HostedCluster pods are running -func ValidateHCP(timeout time.Duration, contingencyTimeout time.Duration, deployments []string, hcpNamespace string) func(client.Client, string) error { - log.Printf("Starting HCP validation with timeout: %v, contingency timeout: %v", timeout, contingencyTimeout) - - if len(deployments) == 0 { - deployments = RequiredWorkingOperators - } - - if timeout == 0 { - timeout = ValidateHCPTimeout - } - - if contingencyTimeout == 0 { - contingencyTimeout = Wait10Min - } - - return func(ocClient client.Client, _ string) error { - log.Printf("Checking deployments in namespace: %s", hcpNamespace) - - // Create a new context for validation that won't be canceled by the parent context - valCtx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - // Validate ETCD StatefulSet - if err := ValidateETCD(valCtx, ocClient, hcpNamespace, timeout); err != nil { - return err - } - - // Validate deployments - if err := ValidateDeployments(valCtx, ocClient, hcpNamespace, deployments, contingencyTimeout); err != nil { - return err - } - - return nil - } -} - -// handleDeploymentValidationFailure handles the case when a deployment validation fails -// The function should list all the pods in the HCP namespace and restart them if they are not running. -// This is because after the restore of an HCP, the pods got stuck and -func handleDeploymentValidationFailure(ctx context.Context, ocClient client.Client, namespace string, deployments []string, timeout time.Duration) error { - log.Printf("Handling validation failure for deployments in namespace %s", namespace) - // List all pods in the HCP namespace - pods := &corev1.PodList{} - err := ocClient.List(ctx, pods, &client.ListOptions{Namespace: namespace}) - if err != nil { - log.Printf("Error listing pods in namespace %s: %v", namespace, err) - return err - } - - // Delete all non-running pods - for _, pod := range pods.Items { - if pod.Status.Phase != corev1.PodRunning { - log.Printf("Deleting non-running pod %s", pod.Name) - err := ocClient.Delete(ctx, &pod) - if err != nil { - log.Printf("Error deleting pod %s: %v", pod.Name, err) - return err - } - } - } - - // Check if all deployments are ready with timeout - for _, deployment := range deployments { - err := wait.PollUntilContextTimeout(ctx, time.Second*10, timeout, true, func(ctx context.Context) (bool, error) { - dep := &appsv1.Deployment{} - err := ocClient.Get(ctx, types.NamespacedName{Name: deployment, Namespace: namespace}, dep) - if err != nil { - log.Printf("Error getting deployment %s, retrying...: %v", deployment, err) - return false, nil - } - done, err := lib.IsDeploymentReady(ocClient, dep.Namespace, dep.Name)() - if !done || err != nil { - return false, nil - } - - return true, nil - }) - - if err != nil { - return fmt.Errorf("deployment %s is not ready after timeout: %v", deployment, err) - } - } - - return nil -} - -// IsHCPDeleted checks if a HostedControlPlane has been deleted -func IsHCPDeleted(h *HCHandler, hcp *hypershiftv1.HostedControlPlane) bool { - if hcp == nil { - log.Printf("\tNo HCP provided, assuming deleted") - return true - } - log.Printf("\tChecking if HCP %s is deleted...", hcp.Name) - newHCP := &hypershiftv1.HostedControlPlane{} - err := h.Client.Get(h.Ctx, types.NamespacedName{Namespace: hcp.Namespace, Name: hcp.Name}, newHCP, &client.GetOptions{ - Raw: &metav1.GetOptions{}, - }) - if err != nil { - if apierrors.IsNotFound(err) { - log.Printf("\tHCP %s is confirmed deleted", hcp.Name) - return true - } - log.Printf("\tHCP %s deletion check failed with error: %v", hcp.Name, err) - return false - } - log.Printf("\tHCP %s still exists", hcp.Name) - return false -} - -// IsHCDeleted checks if a HostedCluster has been deleted -func IsHCDeleted(h *HCHandler) bool { - if h.HostedCluster == nil { - log.Printf("\tNo HostedCluster provided, assuming deleted") - return true - } - log.Printf("\tChecking if HC %s is deleted...", h.HostedCluster.Name) - newHC := &hypershiftv1.HostedCluster{} - err := h.Client.Get(h.Ctx, types.NamespacedName{Namespace: h.HostedCluster.Namespace, Name: h.HostedCluster.Name}, newHC, &client.GetOptions{ - Raw: &metav1.GetOptions{}, - }) - if err != nil { - if apierrors.IsNotFound(err) { - log.Printf("\tHC %s is confirmed deleted", h.HostedCluster.Name) - return true - } - log.Printf("\tHC %s deletion check failed with error: %v", h.HostedCluster.Name, err) - return false - } - log.Printf("\tHC %s still exists", h.HostedCluster.Name) - return false -} - -// GetHCPNamespace returns the namespace for a HostedControlPlane -func GetHCPNamespace(name, namespace string) string { - return fmt.Sprintf("%s-%s", namespace, name) -} - -// RestartHCPPods restarts the pods for a HostedControlPlane namespace which stays in Init state -func RestartHCPPods(HCPNamespace string, c client.Client) error { - pl := &corev1.PodList{} - err := c.List(context.Background(), pl, &client.ListOptions{Namespace: HCPNamespace}) - if err != nil { - return fmt.Errorf("failed to list pods: %v", err) - } - for _, pod := range pl.Items { - if pod.Status.Phase != corev1.PodRunning { - return fmt.Errorf("pod %s is not running", pod.Name) - } - } - return nil -} diff --git a/tests/e2e/lib/hcp/hcp_test.go b/tests/e2e/lib/hcp/hcp_test.go deleted file mode 100644 index f48db9f2fb5..00000000000 --- a/tests/e2e/lib/hcp/hcp_test.go +++ /dev/null @@ -1,863 +0,0 @@ -package hcp - -import ( - "context" - "testing" - "time" - - "github.com/onsi/gomega" - hypershiftv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - "github.com/openshift/oadp-operator/tests/e2e/lib" -) - -func TestRemoveHCP(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - hc *hypershiftv1.HostedCluster - hcp *hypershiftv1.HostedControlPlane - namespace *corev1.Namespace - secrets []*corev1.Secret - expectedResult bool - }{ - { - name: "All resources exist", - hc: &hypershiftv1.HostedCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters", - }, - }, - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters-test-hc", - }, - }, - namespace: &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "clusters-test-hc", - }, - }, - secrets: []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc-pull-secret", - Namespace: "clusters-test-hc", - }, - }, - }, - expectedResult: true, - }, - { - name: "Only HC exists", - hc: &hypershiftv1.HostedCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters", - }, - }, - expectedResult: true, - }, - { - name: "Only HCP exists", - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters-test-hc", - }, - }, - expectedResult: true, - }, - { - name: "Only namespace exists", - namespace: &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "clusters-test-hc", - }, - }, - expectedResult: true, - }, - { - name: "No resources exist", - expectedResult: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new client with the correct scheme - client := fake.NewClientBuilder().WithScheme(lib.Scheme).Build() - - // Create the handler - h := &HCHandler{ - Ctx: context.Background(), - Client: client, - HostedCluster: tt.hc, - } - - // Set HCPNamespace based on either namespace or HCP - if tt.namespace != nil { - h.HCPNamespace = tt.namespace.Name - } else if tt.hcp != nil { - h.HCPNamespace = tt.hcp.Namespace - } - - // Create resources if they exist in the test case - if tt.hc != nil { - err := client.Create(context.Background(), tt.hc) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - if tt.hcp != nil { - err := client.Create(context.Background(), tt.hcp) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - if tt.namespace != nil { - err := client.Create(context.Background(), tt.namespace) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - for _, secret := range tt.secrets { - err := client.Create(context.Background(), secret) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - // Call RemoveHCP - err := h.RemoveHCP(Wait10Min) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Verify resources are deleted - if tt.hc != nil { - hc := &hypershiftv1.HostedCluster{} - err = client.Get(context.Background(), types.NamespacedName{Name: tt.hc.Name, Namespace: tt.hc.Namespace}, hc) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - } - - if tt.hcp != nil { - hcp := &hypershiftv1.HostedControlPlane{} - err = client.Get(context.Background(), types.NamespacedName{Name: tt.hcp.Name, Namespace: tt.hcp.Namespace}, hcp) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - } - - if tt.namespace != nil { - ns := &corev1.Namespace{} - err = client.Get(context.Background(), types.NamespacedName{Name: tt.namespace.Name}, ns) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - } - - for _, secret := range tt.secrets { - s := &corev1.Secret{} - err = client.Get(context.Background(), types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, s) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - } - }) - } -} - -func TestIsHCPDeleted(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - hcp *hypershiftv1.HostedControlPlane - expectedResult bool - }{ - { - name: "HCP exists", - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters-test-hc", - }, - }, - expectedResult: false, - }, - { - name: "HCP is nil", - expectedResult: true, - }, - { - name: "HCP does not exist", - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "non-existent", - Namespace: "non-existent", - }, - }, - expectedResult: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new client with the correct scheme - client := fake.NewClientBuilder().WithScheme(lib.Scheme).Build() - - // Create the handler - h := &HCHandler{ - Ctx: context.Background(), - Client: client, - } - - // Create HCP if it exists in the test case - if tt.hcp != nil && tt.name != "HCP does not exist" { - err := client.Create(context.Background(), tt.hcp) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - // Call IsHCPDeleted - result := IsHCPDeleted(h, tt.hcp) - g.Expect(result).To(gomega.Equal(tt.expectedResult)) - }) - } -} - -func TestIsHCDeleted(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - hc *hypershiftv1.HostedCluster - expectedResult bool - }{ - { - name: "HC exists", - hc: &hypershiftv1.HostedCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters", - }, - }, - expectedResult: false, - }, - { - name: "HC is nil", - expectedResult: true, - }, - { - name: "HC does not exist", - hc: &hypershiftv1.HostedCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: "non-existent", - Namespace: "non-existent", - }, - }, - expectedResult: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new client with the correct scheme - client := fake.NewClientBuilder().WithScheme(lib.Scheme).Build() - - // Create the handler - h := &HCHandler{ - Ctx: context.Background(), - Client: client, - } - - // Create HC if it exists in the test case - if tt.hc != nil && tt.name != "HC does not exist" { - h.HostedCluster = tt.hc - err := client.Create(context.Background(), tt.hc) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - // Call IsHCDeleted - result := IsHCDeleted(h) - g.Expect(result).To(gomega.Equal(tt.expectedResult)) - }) - } -} - -func TestValidateHCP(t *testing.T) { - g := gomega.NewGomegaWithT(t) - hostedClusterName := "test-hc" - hcpNamespace := GetHCPNamespace(hostedClusterName, ClustersNamespace) - - // Define test cases - tests := []struct { - name string - deployments []*appsv1.Deployment - statefulsets []*appsv1.StatefulSet - expectedError bool - }{ - { - name: "All required deployments ready", - deployments: []*appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "kube-apiserver", - Namespace: hcpNamespace, - }, - Status: appsv1.DeploymentStatus{ - AvailableReplicas: 1, - Replicas: 1, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "kube-controller-manager", - Namespace: hcpNamespace, - }, - Status: appsv1.DeploymentStatus{ - AvailableReplicas: 1, - Replicas: 1, - }, - }, - }, - statefulsets: []*appsv1.StatefulSet{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "etcd", - Namespace: hcpNamespace, - }, - Status: appsv1.StatefulSetStatus{ - ReadyReplicas: 1, - Replicas: 1, - }, - }, - }, - expectedError: false, - }, - { - name: "Required deployment not ready", - deployments: []*appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "kube-apiserver", - Namespace: hcpNamespace, - }, - Status: appsv1.DeploymentStatus{ - AvailableReplicas: 0, - Replicas: 1, - }, - }, - }, - statefulsets: []*appsv1.StatefulSet{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "etcd", - Namespace: hcpNamespace, - }, - Status: appsv1.StatefulSetStatus{ - ReadyReplicas: 1, - Replicas: 1, - }, - }, - }, - expectedError: true, - }, - { - name: "ETCD not ready", - deployments: []*appsv1.Deployment{}, - statefulsets: []*appsv1.StatefulSet{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "etcd", - Namespace: hcpNamespace, - }, - Status: appsv1.StatefulSetStatus{ - ReadyReplicas: 0, - Replicas: 1, - }, - }, - }, - expectedError: true, - }, - { - name: "Deployment not found, accessing the handleDeploymentValidationFailure function", - deployments: []*appsv1.Deployment{}, - statefulsets: []*appsv1.StatefulSet{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "etcd", - Namespace: hcpNamespace, - }, - Status: appsv1.StatefulSetStatus{ - ReadyReplicas: 1, - Replicas: 1, - }, - }, - }, - expectedError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := runtime.NewScheme() - err := appsv1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - err = corev1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Combine deployments and statefulsets into a single slice of objects - objects := make([]client.Object, 0) - for _, deployment := range tt.deployments { - objects = append(objects, deployment) - } - for _, statefulset := range tt.statefulsets { - objects = append(objects, statefulset) - } - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build() - - // Run the validation function with both timeouts set to 5 seconds for testing - validateFunc := ValidateHCP(5*time.Second, 5*time.Second, []string{"kube-apiserver", "kube-controller-manager"}, hcpNamespace) - err = validateFunc(client, "") - - // Check results - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - } else { - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - }) - } -} - -func TestWaitForHCPDeletion(t *testing.T) { - // Register Gomega fail handler - gomega.RegisterTestingT(t) - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - hcp *hypershiftv1.HostedControlPlane - createObj bool - deleteObj bool - timeout time.Duration - deleteDelay time.Duration - expectedError bool - errorContains string - }{ - { - name: "HCP already deleted", - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters-test-hc", - }, - }, - createObj: false, - deleteObj: false, - timeout: Wait10Min, - deleteDelay: 0, - expectedError: false, - }, - { - name: "HCP deleted during wait", - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters-test-hc", - }, - }, - createObj: true, - deleteObj: true, - timeout: Wait10Min, - deleteDelay: WaitForNextCheckTimeout, - expectedError: false, - }, - { - name: "HCP not deleted within timeout", - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters-test-hc", - }, - }, - createObj: true, - deleteObj: false, - timeout: time.Second * 2, - deleteDelay: 0, - expectedError: true, - }, - { - name: "HCP with finalizers not deleted", - hcp: &hypershiftv1.HostedControlPlane{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-hc", - Namespace: "clusters-test-hc", - Finalizers: []string{"test-finalizer"}, - }, - }, - createObj: true, - deleteObj: true, - timeout: time.Second * 2, - deleteDelay: WaitForNextCheckTimeout, - expectedError: true, - }, - { - name: "HCP is nil", - hcp: nil, - createObj: false, - deleteObj: false, - timeout: Wait10Min, - deleteDelay: 0, - expectedError: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme with HostedControlPlane registered - scheme := runtime.NewScheme() - err := hypershiftv1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Create a new client with the scheme - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create a context with timeout - ctx, cancel := context.WithTimeout(context.Background(), tt.timeout) - defer cancel() - - // Create the handler - h := &HCHandler{ - Ctx: ctx, - Client: client, - } - - // Create HCP if needed - if tt.createObj { - err := client.Create(context.Background(), tt.hcp) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - // Start deletion in background if needed - if tt.deleteObj { - go func() { - time.Sleep(tt.deleteDelay) - err := client.Delete(context.Background(), tt.hcp) - g.Expect(err).NotTo(gomega.HaveOccurred()) - }() - } - - // Call WaitForHCPDeletion - err = h.WaitForHCPDeletion(tt.hcp) - - // Check results - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - } else { - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - }) - } -} - -func TestHandleDeploymentValidationFailure(t *testing.T) { - g := gomega.NewGomegaWithT(t) - namespace := "test-namespace" - deployments := []string{"test-deployment"} - timeout := 5 * time.Second - - tests := []struct { - name string - pods []*corev1.Pod - deployments []*appsv1.Deployment - expectedError bool - }{ - { - name: "Non-running pods are deleted and deployments become ready", - pods: []*corev1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "stuck-pod", - Namespace: namespace, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodPending, - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "running-pod", - Namespace: namespace, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - }, - }, - }, - deployments: []*appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Namespace: namespace, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - AvailableReplicas: 1, - }, - }, - }, - expectedError: false, - }, - { - name: "Deployment not ready after timeout", - pods: []*corev1.Pod{}, - deployments: []*appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Namespace: namespace, - }, - Status: appsv1.DeploymentStatus{ - Replicas: 1, - AvailableReplicas: 0, - }, - }, - }, - expectedError: true, - }, - { - name: "No pods or deployments found", - pods: []*corev1.Pod{}, - deployments: []*appsv1.Deployment{}, - expectedError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := runtime.NewScheme() - err := corev1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - err = appsv1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Create objects for the fake client - objects := []client.Object{} - for _, pod := range tt.pods { - objects = append(objects, pod) - } - for _, deployment := range tt.deployments { - objects = append(objects, deployment) - } - - // Create fake client with objects - fakeClient := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(objects...). - Build() - - // Create context with timeout - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - // Call the function - err = handleDeploymentValidationFailure(ctx, fakeClient, namespace, deployments, timeout) - - // Check results - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - } else { - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - // Verify pods were deleted if necessary - for _, pod := range tt.pods { - if pod.Status.Phase != corev1.PodRunning { - err := fakeClient.Get(ctx, types.NamespacedName{ - Name: pod.Name, - Namespace: pod.Namespace, - }, &corev1.Pod{}) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - } - } - }) - } -} - -func TestValidateETCD(t *testing.T) { - g := gomega.NewGomegaWithT(t) - hostedClusterName := "test-hc" - hcpNamespace := GetHCPNamespace(hostedClusterName, ClustersNamespace) - timeout := 5 * time.Second - - // Define test cases - tests := []struct { - name string - statefulsets []*appsv1.StatefulSet - expectedError bool - }{ - { - name: "ETCD ready", - statefulsets: []*appsv1.StatefulSet{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "etcd", - Namespace: hcpNamespace, - }, - Status: appsv1.StatefulSetStatus{ - ReadyReplicas: 1, - Replicas: 1, - }, - }, - }, - expectedError: false, - }, - { - name: "ETCD not ready", - statefulsets: []*appsv1.StatefulSet{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "etcd", - Namespace: hcpNamespace, - }, - Status: appsv1.StatefulSetStatus{ - ReadyReplicas: 0, - Replicas: 1, - }, - }, - }, - expectedError: true, - }, - { - name: "ETCD not found", - statefulsets: []*appsv1.StatefulSet{}, - expectedError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := runtime.NewScheme() - err := appsv1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Create objects for the fake client - objects := []client.Object{} - for _, statefulset := range tt.statefulsets { - objects = append(objects, statefulset) - } - - // Create fake client with objects - fakeClient := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(objects...). - Build() - - // Create context with timeout - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - // Call the function - err = ValidateETCD(ctx, fakeClient, hcpNamespace, timeout) - - // Check results - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - } else { - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - }) - } -} - -func TestValidateDeployments(t *testing.T) { - g := gomega.NewGomegaWithT(t) - namespace := "test-namespace" - deployments := []string{"test-deployment"} - timeout := 5 * time.Second - - tests := []struct { - name string - deployments []*appsv1.Deployment - expectedError bool - }{ - { - name: "All deployments ready", - deployments: []*appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Namespace: namespace, - }, - Status: appsv1.DeploymentStatus{ - AvailableReplicas: 1, - Replicas: 1, - }, - }, - }, - expectedError: false, - }, - { - name: "Deployment not ready", - deployments: []*appsv1.Deployment{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-deployment", - Namespace: namespace, - }, - Status: appsv1.DeploymentStatus{ - AvailableReplicas: 0, - Replicas: 1, - }, - }, - }, - expectedError: true, - }, - { - name: "Deployment not found", - deployments: []*appsv1.Deployment{}, - expectedError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := runtime.NewScheme() - err := appsv1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - err = corev1.AddToScheme(scheme) - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Create objects for the fake client - objects := []client.Object{} - for _, deployment := range tt.deployments { - objects = append(objects, deployment) - } - - // Create fake client with objects - fakeClient := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(objects...). - Build() - - // Create context with timeout - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - // Call the function - err = ValidateDeployments(ctx, fakeClient, namespace, deployments, timeout) - - // Check results - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - } else { - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - }) - } -} diff --git a/tests/e2e/lib/hcp/mce.go b/tests/e2e/lib/hcp/mce.go deleted file mode 100644 index 8df1b39a902..00000000000 --- a/tests/e2e/lib/hcp/mce.go +++ /dev/null @@ -1,152 +0,0 @@ -package hcp - -import ( - "context" - "fmt" - "log" - - operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - corev1 "k8s.io/api/core/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/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const ( - // MCE related constants - MCEOperatorNamespace = "multicluster-engine" - MCEOperatorGroupName = "multicluster-engine" - MCESubscriptionName = "multicluster-engine" -) - -// DeleteMCEOperand deletes the MCE operand -func (h *HCHandler) DeleteMCEOperand() error { - log.Printf("Deleting MCE operand %s", MCEOperandName) - mce := &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": "MultiClusterEngine", - "apiVersion": mceGVR.GroupVersion().String(), - "metadata": map[string]interface{}{ - "name": MCEOperandName, - "namespace": MCENamespace, - }, - }, - } - return h.deleteResource(mce) -} - -// DeleteMCEOperatorGroup deletes the MCE operator group -func (h *HCHandler) DeleteMCEOperatorGroup() error { - log.Printf("Deleting MCE operator group %s", MCEOperatorGroup) - og := &operatorsv1.OperatorGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: MCEOperatorGroup, - Namespace: MCENamespace, - }, - } - return h.deleteResource(og) -} - -// DeleteMCESubscription deletes the MCE subscription -func (h *HCHandler) DeleteMCESubscription() error { - log.Printf("Deleting MCE subscription %s", MCEOperatorName) - sub := &operatorsv1alpha1.Subscription{ - ObjectMeta: metav1.ObjectMeta{ - Name: MCEOperatorName, - Namespace: MCENamespace, - }, - } - return h.deleteResource(sub) -} - -// RemoveMCE removes the MCE operand, operator group, and subscription -func (h *HCHandler) RemoveMCE() error { - log.Printf("Removing MCE resources") - - // Delete MCE operand - if err := h.DeleteMCEOperand(); err != nil && !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete MCE operand: %v", err) - } - - // Delete MCE operator group - if err := h.DeleteMCEOperatorGroup(); err != nil && !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete MCE operator group: %v", err) - } - - // Delete MCE subscription - if err := h.DeleteMCESubscription(); err != nil && !apierrors.IsNotFound(err) { - return fmt.Errorf("failed to delete MCE subscription: %v", err) - } - - // Wait for MCE operand to be deleted - mce := &unstructured.Unstructured{} - mce.SetGroupVersionKind(mceGVR.GroupVersion().WithKind("MultiClusterEngine")) - mce.SetName(MCEOperandName) - mce.SetNamespace(MCENamespace) - - err := wait.PollUntilContextTimeout(h.Ctx, WaitForNextCheckTimeout, Wait10Min, true, func(ctx context.Context) (bool, error) { - if err := h.Client.Get(ctx, types.NamespacedName{Name: MCEOperandName, Namespace: MCENamespace}, mce); err != nil { - if !apierrors.IsNotFound(err) && !apierrors.IsTooManyRequests(err) && !apierrors.IsServerTimeout(err) && !apierrors.IsTimeout(err) { - return false, fmt.Errorf("failed to get MCE operand: %v", err) - } - log.Printf("Error getting MCE operand, retrying...: %v", err) - return false, nil - } - return true, nil - }) - if err != nil { - return fmt.Errorf("failed waiting for MCE operand deletion: %v", err) - } - - return nil -} - -func (op *HCHandler) DeployMCEManifest() error { - log.Printf("Checking MCE manifest") - - // Create an unstructured object to check if the MCE operand exists - mce := &unstructured.Unstructured{} - mce.SetGroupVersionKind(mceGVR.GroupVersion().WithKind("MultiClusterEngine")) - mce.SetName(MCEOperandName) - mce.SetNamespace(MCENamespace) - - if err := op.Client.Get(op.Ctx, types.NamespacedName{Name: MCEOperandName, Namespace: MCENamespace}, mce); err != nil { - if apierrors.IsNotFound(err) { - log.Printf("Creating MCE manifest") - err = ApplyYAMLTemplate(op.Ctx, op.Client, MCEOperandManifest, true, map[string]interface{}{ - "MCEOperandName": MCEOperandName, - "MCEOperandNamespace": MCENamespace, - }) - if err != nil { - return fmt.Errorf("failed to apply mce-operand from %s: %v", MCEOperandManifest, err) - } - } - } - - return nil -} - -func (h *HCHandler) IsMCEDeployed() bool { - log.Printf("Checking if MCE deployment is finished...") - mcePods := &corev1.PodList{} - err := h.Client.List(h.Ctx, mcePods, client.InNamespace(MCENamespace)) - if err != nil { - return false - } - - if len(mcePods.Items) == 0 { - return false - } - - for _, pod := range mcePods.Items { - if pod.Status.Phase != corev1.PodRunning { - return false - } - } - - return true -} diff --git a/tests/e2e/lib/hcp/mce_test.go b/tests/e2e/lib/hcp/mce_test.go deleted file mode 100644 index 358674a23e4..00000000000 --- a/tests/e2e/lib/hcp/mce_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package hcp - -import ( - "context" - "testing" - - "github.com/onsi/gomega" - operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - corev1 "k8s.io/api/core/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/types" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - - oadpv1alpha1 "github.com/openshift/oadp-operator/api/v1alpha1" -) - -// createTestScheme creates a new scheme with all required types registered -func createTestScheme() *runtime.Scheme { - scheme := runtime.NewScheme() - corev1.AddToScheme(scheme) - oadpv1alpha1.AddToScheme(scheme) - operatorsv1.AddToScheme(scheme) - operatorsv1alpha1.AddToScheme(scheme) - return scheme -} - -func TestRemoveMCE(t *testing.T) { - // Register Gomega fail handler - gomega.RegisterTestingT(t) - g := gomega.NewGomegaWithT(t) - - tests := []struct { - Name string - MCE *unstructured.Unstructured - OperatorGroup *operatorsv1.OperatorGroup - Subscription *operatorsv1alpha1.Subscription - ExpectedResult bool - }{ - { - Name: "All resources exist", - MCE: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": "MultiClusterEngine", - "apiVersion": mceGVR.GroupVersion().String(), - "metadata": map[string]interface{}{ - "name": MCEOperandName, - "namespace": MCENamespace, - }, - }, - }, - OperatorGroup: &operatorsv1.OperatorGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: MCEOperatorGroup, - Namespace: MCENamespace, - }, - }, - Subscription: &operatorsv1alpha1.Subscription{ - ObjectMeta: metav1.ObjectMeta{ - Name: MCEOperatorName, - Namespace: MCENamespace, - }, - }, - ExpectedResult: true, - }, - { - Name: "Only MCE exists", - MCE: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": "MultiClusterEngine", - "apiVersion": mceGVR.GroupVersion().String(), - "metadata": map[string]interface{}{ - "name": MCEOperandName, - "namespace": MCENamespace, - }, - }, - }, - OperatorGroup: nil, - Subscription: nil, - ExpectedResult: true, - }, - { - Name: "Only OperatorGroup exists", - MCE: nil, - OperatorGroup: &operatorsv1.OperatorGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: MCEOperatorGroup, - Namespace: MCENamespace, - }, - }, - Subscription: nil, - ExpectedResult: true, - }, - { - Name: "Only Subscription exists", - MCE: nil, - OperatorGroup: nil, - Subscription: &operatorsv1alpha1.Subscription{ - ObjectMeta: metav1.ObjectMeta{ - Name: MCEOperatorName, - Namespace: MCENamespace, - }, - }, - ExpectedResult: true, - }, - { - Name: "No resources exist", - MCE: nil, - OperatorGroup: nil, - Subscription: nil, - ExpectedResult: true, - }, - } - - for _, tt := range tests { - t.Run(tt.Name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := createTestScheme() - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create the handler - h := &HCHandler{ - Ctx: context.Background(), - Client: client, - } - - // Create resources if they exist in the test case - if tt.MCE != nil { - // Set the GVK for the MCE using the constant - tt.MCE.SetGroupVersionKind(mceGVR.GroupVersion().WithKind("MultiClusterEngine")) - err := client.Create(context.Background(), tt.MCE) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - if tt.OperatorGroup != nil { - err := client.Create(context.Background(), tt.OperatorGroup) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - if tt.Subscription != nil { - err := client.Create(context.Background(), tt.Subscription) - g.Expect(err).NotTo(gomega.HaveOccurred()) - } - - // Call RemoveMCE - err := h.RemoveMCE() - g.Expect(err).NotTo(gomega.HaveOccurred()) - - // Verify resources are deleted - mce := &unstructured.Unstructured{} - mce.SetGroupVersionKind(mceGVR.GroupVersion().WithKind("MultiClusterEngine")) - err = client.Get(context.Background(), types.NamespacedName{Name: MCEOperandName, Namespace: MCENamespace}, mce) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - - og := &operatorsv1.OperatorGroup{} - err = client.Get(context.Background(), types.NamespacedName{Name: MCEOperatorGroup, Namespace: MCENamespace}, og) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - - sub := &operatorsv1alpha1.Subscription{} - err = client.Get(context.Background(), types.NamespacedName{Name: MCEOperatorName, Namespace: MCENamespace}, sub) - g.Expect(apierrors.IsNotFound(err)).To(gomega.BeTrue()) - }) - } -} diff --git a/tests/e2e/lib/hcp/types.go b/tests/e2e/lib/hcp/types.go deleted file mode 100644 index 2d7caab4f11..00000000000 --- a/tests/e2e/lib/hcp/types.go +++ /dev/null @@ -1,159 +0,0 @@ -package hcp - -import ( - "context" - "path/filepath" - "time" - - hypershiftv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// Constants -const ( - MCEName = "multicluster-engine" - MCENamespace = "multicluster-engine" - MCEOperatorName = "multicluster-engine-operator" - MCEOperatorGroup = "multicluster-engine-operatorgroup" - HONamespace = "hypershift" - HypershiftOperatorName = "operator" - OCPMarketplaceNamespace = "openshift-marketplace" - RHOperatorsNamespace = "redhat-operators" - MCEOperandName = "mce-operand" - - ClustersNamespace = "clusters" - HostedClusterPrefix = "test-hc" - SampleETCDEncryptionKey = "7o9RQL/BlcNrBWfNBVrJg55oKrDDaDu2kfoULl9MNIE=" - HCOCPTestImage = "quay.io/openshift-release-dev/ocp-release:4.18.6-multi" -) - -// Template paths -var ( - MCEOperandManifest = filepath.Join(getProjectRoot(), "tests/e2e/sample-applications/hostedcontrolplanes/mce/mce-operand.yaml") - HCPNoneManifest = filepath.Join(getProjectRoot(), "tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-none.yaml") - HCPAgentManifest = filepath.Join(getProjectRoot(), "tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent.yaml") - PullSecretManifest = filepath.Join(getProjectRoot(), "tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-pull-secret.yaml") - EtcdEncryptionKeyManifest = filepath.Join(getProjectRoot(), "tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-etcd-enc-key.yaml") - CapiProviderRoleManifest = filepath.Join(getProjectRoot(), "tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent-capi-role.yaml") -) - -// Global variables -var ( - packageManifestGVR = schema.GroupVersionResource{ - Group: "packages.operators.coreos.com", - Version: "v1", - Resource: "packagemanifests", - } - - mceGVR = schema.GroupVersionResource{ - Group: "multicluster.openshift.io", - Version: "v1", - Resource: "multiclusterengines", - } - - capiAgentGVK = schema.GroupVersionKind{ - Group: "capi-provider.agent-install.openshift.io", - Version: "v1beta1", - Kind: "AgentCluster", - } - - awsClusterGVK = schema.GroupVersionKind{ - Group: "infrastructure.cluster.x-k8s.io", - Version: "v1beta2", - Kind: "AWSCluster", - } - - clusterGVK = schema.GroupVersionKind{ - Group: "cluster.x-k8s.io", - Version: "v1beta1", - Kind: "Cluster", - } - - RequiredWorkingOperators = []string{ - "cluster-api", - "control-plane-operator", - "kube-apiserver", - "kube-controller-manager", - "kube-scheduler", - "ignition-server", - "cluster-image-registry-operator", - "cluster-network-operator", - "cluster-node-tuning-operator", - "cluster-policy-controller", - "cluster-storage-operator", - "cluster-version-operator", - "control-plane-pki-operator", - "dns-operator", - "hosted-cluster-config-operator", - "ignition-server-proxy", - "konnectivity-agent", - "machine-approver", - "oauth-openshift", - "openshift-apiserver", - "openshift-controller-manager", - "openshift-oauth-apiserver", - "openshift-route-controller-manager", - } - - HCPIncludedNamespaces = []string{ - ClustersNamespace, - } - - HCPIncludedResources = []string{ - "sa", - "role", - "rolebinding", - "pod", - "pvc", - "pv", - "configmap", - "priorityclasses", - "pdb", - "hostedcluster", - "nodepool", - "secrets", - "services", - "deployments", - "statefulsets", - "hostedcontrolplane", - "cluster", - "awscluster", - "awsmachinetemplate", - "awsmachine", - "machinedeployment", - "machineset", - "machine", - "route", - "clusterdeployment", - } - - HCPExcludedResources = []string{} - - HCPErrorIgnorePatterns = []string{ - "-error-template", - } - - // Timeout constants - Wait10Min = 10 * time.Minute - WaitForNextCheckTimeout = 10 * time.Second - ValidateHCPTimeout = 25 * time.Minute - HCPBackupTimeout = 30 * time.Minute -) - -// HCHandler handles operations related to HostedClusters -type HCHandler struct { - Ctx context.Context - Client client.Client - HCOCPTestImage string - HCPNamespace string - HostedCluster *hypershiftv1.HostedCluster -} - -type RequiredOperator struct { - Name string - Namespace string - Channel string - Csv string - OperatorGroup string -} diff --git a/tests/e2e/lib/hcp/utils.go b/tests/e2e/lib/hcp/utils.go deleted file mode 100644 index 342e13186f1..00000000000 --- a/tests/e2e/lib/hcp/utils.go +++ /dev/null @@ -1,281 +0,0 @@ -package hcp - -import ( - "bytes" - "context" - "fmt" - "log" - "os" - "path/filepath" - "runtime" - "strings" - "text/template" - "time" - - operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - corev1 "k8s.io/api/core/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/serializer/yaml" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/utils/ptr" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// InstallRequiredOperators installs the required operators and returns a new HCHandler -func InstallRequiredOperators(ctx context.Context, c client.Client, reqOperators []RequiredOperator) (*HCHandler, error) { - for _, op := range reqOperators { - log.Printf("Installing operator %s", op.Name) - err := op.InstallOperator(ctx, c) - if err != nil { - return nil, fmt.Errorf("failed to install operator %s: %v", op.Name, err) - } - } - - return &HCHandler{ - Ctx: ctx, - Client: c, - HCOCPTestImage: HCOCPTestImage, - }, nil -} - -// InstallOperator installs a specific operator -func (op *RequiredOperator) InstallOperator(ctx context.Context, c client.Client) error { - log.Printf("Getting PackageManifest for operator %s", op.Name) - - // Create an unstructured object for the PackageManifest - pkg := &unstructured.Unstructured{} - pkg.SetGroupVersionKind(packageManifestGVR.GroupVersion().WithKind("PackageManifest")) - pkg.SetName(op.Name) - pkg.SetNamespace(op.Namespace) - - err := c.Get(ctx, types.NamespacedName{Name: op.Name, Namespace: op.Namespace}, pkg) - if err != nil { - return fmt.Errorf("failed to get PackageManifest for operator %s: %v", op.Name, err) - } - - log.Printf("Checking namespace for operator %s", op.Name) - ns := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: op.Namespace, - }, - } - - tempNS := &corev1.Namespace{} - if err := c.Get(ctx, types.NamespacedName{Name: op.Namespace}, tempNS); err != nil { - if apierrors.IsNotFound(err) { - log.Printf("Creating namespace for operator %s", op.Name) - // Create the namespace if it doesn't exist - err = c.Create(ctx, ns) - if err != nil { - return fmt.Errorf("failed to create namespace %s: %v", op.Namespace, err) - } - } else { - return fmt.Errorf("failed to get namespace %s: %v", op.Namespace, err) - } - } - - opGroup := &operatorsv1.OperatorGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: op.OperatorGroup, - Namespace: op.Namespace, - }, - Spec: operatorsv1.OperatorGroupSpec{ - TargetNamespaces: []string{op.Namespace}, - }, - } - - log.Printf("Checking operator group for operator %s", op.Name) - tempOpGroup := &operatorsv1.OperatorGroup{} - if err := c.Get(ctx, types.NamespacedName{Name: op.OperatorGroup, Namespace: op.Namespace}, tempOpGroup); err != nil { - if apierrors.IsNotFound(err) { - log.Printf("Creating operator group for operator %s", op.Name) - // Create the operator group - err = c.Create(ctx, opGroup) - if err != nil { - return fmt.Errorf("failed to create operator group %s: %v", op.OperatorGroup, err) - } - } else { - return fmt.Errorf("failed to get operator group %s: %v", op.OperatorGroup, err) - } - } - - // Create the subscription - subscription := &operatorsv1alpha1.Subscription{ - ObjectMeta: metav1.ObjectMeta{ - Name: op.Name, - Namespace: op.Namespace, - }, - Spec: &operatorsv1alpha1.SubscriptionSpec{ - CatalogSource: RHOperatorsNamespace, - CatalogSourceNamespace: OCPMarketplaceNamespace, - Package: op.Name, - InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic, - }, - } - - // If a channel is specified, use it - if op.Channel != "" { - subscription.Spec.Channel = op.Channel - } else { - // Get the default channel from the PackageManifest - defaultChannel, ok, err := unstructured.NestedString(pkg.UnstructuredContent(), "status", "defaultChannel") - if err != nil { - return fmt.Errorf("failed to get default channel from PackageManifest: %v", err) - } - if !ok || defaultChannel == "" { - return fmt.Errorf("no default channel found in PackageManifest for operator %s", op.Name) - } - subscription.Spec.Channel = defaultChannel - } - - // If a CSV is specified, use it - if op.Csv != "" { - subscription.Spec.StartingCSV = op.Csv - } - - log.Printf("Checking subscription for operator %s", op.Name) - tempSub := &operatorsv1alpha1.Subscription{} - if err := c.Get(ctx, types.NamespacedName{Name: op.Name, Namespace: op.Namespace}, tempSub); err != nil { - if apierrors.IsNotFound(err) { - log.Printf("Creating subscription for operator %s", op.Name) - err = c.Create(ctx, subscription) - if err != nil { - return fmt.Errorf("failed to create subscription for operator %s: %v", op.Name, err) - } - } else { - return fmt.Errorf("failed to get subscription for operator %s: %v", op.Name, err) - } - } - - return nil -} - -// WaitForUnstructuredObject waits for an unstructured object to be deleted -func WaitForUnstructuredObject(ctx context.Context, c client.Client, obj *unstructured.Unstructured, timeout time.Duration) error { - return wait.PollUntilContextTimeout(ctx, WaitForNextCheckTimeout, timeout, true, func(ctx context.Context) (bool, error) { - log.Printf("\tWaiting for object %s in namespace %s to be deleted...", obj.GetName(), obj.GetNamespace()) - newObj := &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": obj.GetKind(), - "apiVersion": obj.GetAPIVersion(), - "metadata": map[string]interface{}{ - "name": obj.GetName(), - "namespace": obj.GetNamespace(), - }, - }, - } - err := c.Get(ctx, types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, newObj) - log.Printf("\tObject %s exists in namespace %s: %v", obj.GetName(), obj.GetNamespace(), err) - return apierrors.IsNotFound(err), nil - }) -} - -// ApplyYAMLTemplate reads a YAML template file, renders it with the given data, and applies it using the client -func ApplyYAMLTemplate(ctx context.Context, c client.Client, manifestPath string, override bool, data interface{}) error { - // Read the manifest - log.Printf("\tReading YAML template %s", filepath.Base(manifestPath)) - manifest, err := os.ReadFile(manifestPath) - if err != nil { - return fmt.Errorf("failed to read manifest from %s: %v", manifestPath, err) - } - - // Parse the manifest - log.Printf("\tParsing manifest %s", filepath.Base(manifestPath)) - tmpl, err := template.New("manifest").Parse(string(manifest)) - if err != nil { - return fmt.Errorf("failed to parse manifest from %s: %v", manifestPath, err) - } - - // Execute the manifest - log.Printf("\tExecuting manifest %s", filepath.Base(manifestPath)) - var buf bytes.Buffer - if err := tmpl.Execute(&buf, data); err != nil { - return fmt.Errorf("failed to execute manifest from %s: %v", manifestPath, err) - } - - // Create a decoder for YAML - decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) - - // Decode the YAML into an unstructured object - log.Printf("\tDecoding YAML %s", filepath.Base(manifestPath)) - obj := &unstructured.Unstructured{} - _, _, err = decoder.Decode(buf.Bytes(), nil, obj) - if err != nil { - return fmt.Errorf("failed to decode YAML from %s: %v", manifestPath, err) - } - - // Apply the object using the client - log.Printf("\tApplying object %s", filepath.Base(manifestPath)) - err = c.Create(ctx, obj) - if err != nil { - if override && apierrors.IsAlreadyExists(err) { - log.Printf("\tObject already exists, overriding...") - err = c.Update(ctx, obj) - if err != nil { - return fmt.Errorf("failed to update object from %s: %v", manifestPath, err) - } - } else { - return fmt.Errorf("failed to create object from %s: %v", manifestPath, err) - } - } - - log.Printf("\tObject applied successfully") - - return nil -} - -// getPullSecret gets the pull secret from the openshift-config namespace -func getPullSecret(ctx context.Context, c client.Client) (string, error) { - secret := &corev1.Secret{} - err := c.Get(ctx, types.NamespacedName{Name: "pull-secret", Namespace: "openshift-config"}, secret) - if err != nil { - return "", fmt.Errorf("failed to get pull secret: %v", err) - } - if secret.Data == nil || len(secret.Data) == 0 { - return "", fmt.Errorf("pull secret data is empty") - } - dockerConfig, ok := secret.Data[".dockerconfigjson"] - if !ok { - return "", fmt.Errorf("pull secret does not contain .dockerconfigjson key") - } - return string(dockerConfig), nil -} - -// FilterErrorLogs filters out error logs based on predefined patterns -func FilterErrorLogs(logs []string) []string { - filteredLogs := []string{} - for _, logEntry := range logs { - shouldInclude := true - for _, pattern := range HCPErrorIgnorePatterns { - if strings.Contains(logEntry, pattern) { - shouldInclude = false - break - } - } - if shouldInclude { - filteredLogs = append(filteredLogs, logEntry) - } - } - return filteredLogs -} - -// deleteResource deletes a Kubernetes resource with grace period 0 -func (h *HCHandler) deleteResource(obj client.Object) error { - deleteOptions := &client.DeleteOptions{ - GracePeriodSeconds: ptr.To(int64(0)), - } - return h.Client.Delete(h.Ctx, obj, deleteOptions) -} - -// getProjectRoot returns the absolute path to the project root -func getProjectRoot() string { - _, filename, _, ok := runtime.Caller(0) - if !ok { - return "" - } - return filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filename))))) -} diff --git a/tests/e2e/lib/hcp/utils_test.go b/tests/e2e/lib/hcp/utils_test.go deleted file mode 100644 index 0df821329cb..00000000000 --- a/tests/e2e/lib/hcp/utils_test.go +++ /dev/null @@ -1,578 +0,0 @@ -package hcp - -import ( - "context" - "fmt" - "os" - "path/filepath" - "testing" - "time" - - "github.com/onsi/gomega" - operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestFilterErrorLogs(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - logs []string - expectedResult []string - }{ - { - name: "No error logs to filter", - logs: []string{ - "Normal log 1", - "Normal log 2", - "Normal log 3", - }, - expectedResult: []string{ - "Normal log 1", - "Normal log 2", - "Normal log 3", - }, - }, - { - name: "Filter error logs", - logs: []string{ - "Normal log 1", - "Error log with -error-template", - "Normal log 2", - "Another error with -error-template", - "Normal log 3", - }, - expectedResult: []string{ - "Normal log 1", - "Normal log 2", - "Normal log 3", - }, - }, - { - name: "Empty logs", - logs: []string{}, - expectedResult: []string{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := FilterErrorLogs(tt.logs) - g.Expect(result).To(gomega.Equal(tt.expectedResult)) - }) - } -} - -func TestApplyYAMLTemplate(t *testing.T) { - // Register Gomega fail handler - gomega.RegisterTestingT(t) - g := gomega.NewGomegaWithT(t) - manifestPath := "../../sample-applications/hostedcontrolplanes/hypershift/hostedcluster-etcd-enc-key.yaml" - hostedClusterName := "test-hc" - - tests := []struct { - name string - manifestPath string - data map[string]interface{} - override bool - expectedError bool - errorContains string - verifyResource func(*testing.T, client.Client) - }{ - { - name: "Valid etcd encryption key manifest", - manifestPath: manifestPath, - data: map[string]interface{}{ - "HostedClusterName": hostedClusterName, - "ClustersNamespace": ClustersNamespace, - "EtcdEncryptionKey": SampleETCDEncryptionKey, - }, - override: false, - expectedError: false, - verifyResource: func(t *testing.T, client client.Client) { - secret := &corev1.Secret{} - err := client.Get(context.Background(), types.NamespacedName{ - Name: fmt.Sprintf("%s-etcd-encryption-key", hostedClusterName), - Namespace: ClustersNamespace, - }, secret) - g.Expect(err).ToNot(gomega.HaveOccurred()) - g.Expect(secret.Data).To(gomega.HaveKey("key")) - }, - }, - { - name: "Missing template variable", - manifestPath: manifestPath, - data: map[string]interface{}{ - "HostedClusterName": hostedClusterName, - // Missing ClustersNamespace and EtcdEncryptionKey - }, - override: false, - expectedError: true, - }, - { - name: "Override existing resource", - manifestPath: manifestPath, - data: map[string]interface{}{ - "HostedClusterName": hostedClusterName, - "ClustersNamespace": ClustersNamespace, - "EtcdEncryptionKey": SampleETCDEncryptionKey, - }, - override: true, - expectedError: false, - verifyResource: func(t *testing.T, client client.Client) { - secret := &corev1.Secret{} - err := client.Get(context.Background(), types.NamespacedName{ - Name: fmt.Sprintf("%s-etcd-encryption-key", hostedClusterName), - Namespace: ClustersNamespace, - }, secret) - g.Expect(err).ToNot(gomega.HaveOccurred()) - g.Expect(secret.Data).To(gomega.HaveKey("key")) - }, - }, - { - name: "Non-existent manifest file", - manifestPath: "non-existent-file.yaml", - data: map[string]interface{}{ - "HostedClusterName": hostedClusterName, - "ClustersNamespace": ClustersNamespace, - "EtcdEncryptionKey": SampleETCDEncryptionKey, - }, - override: false, - expectedError: true, - errorContains: "failed to read manifest", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := createTestScheme() - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Test ApplyYAMLTemplate - err := ApplyYAMLTemplate(context.Background(), client, tt.manifestPath, tt.override, tt.data) - - // Check results - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - } else { - g.Expect(err).ToNot(gomega.HaveOccurred()) - if tt.verifyResource != nil { - tt.verifyResource(t, client) - } - } - }) - } -} - -func TestGetPullSecret(t *testing.T) { - // Register Gomega fail handler - gomega.RegisterTestingT(t) - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - pullSecret *corev1.Secret - expectedSecret string - expectError bool - }{ - { - name: "Valid pull secret", - pullSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pull-secret", - Namespace: "openshift-config", - }, - Data: map[string][]byte{ - ".dockerconfigjson": []byte("test-secret-data"), - }, - }, - expectedSecret: "test-secret-data", - expectError: false, - }, - { - name: "Pull secret without dockerconfigjson", - pullSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pull-secret", - Namespace: "openshift-config", - }, - Data: map[string][]byte{ - "other-key": []byte("other-data"), - }, - }, - expectedSecret: "", - expectError: true, - }, - { - name: "No pull secret exists", - pullSecret: nil, - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := createTestScheme() - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create pull secret if provided - if tt.pullSecret != nil { - err := client.Create(context.Background(), tt.pullSecret) - g.Expect(err).ToNot(gomega.HaveOccurred()) - } - - // Test getPullSecret - secret, err := getPullSecret(context.Background(), client) - - // Check results - if tt.expectError { - g.Expect(err).To(gomega.HaveOccurred()) - } else { - g.Expect(err).ToNot(gomega.HaveOccurred()) - g.Expect(secret).To(gomega.Equal(tt.expectedSecret)) - } - }) - } -} - -func TestInstallRequiredOperators(t *testing.T) { - // Register Gomega fail handler - gomega.RegisterTestingT(t) - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - packageManifest *unstructured.Unstructured - reqOperators []RequiredOperator - expectedError bool - errorContains string - verifyHandler func(*testing.T, *HCHandler, client.Client) - }{ - { - name: "Successfully install MCE operator", - packageManifest: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "status": map[string]interface{}{ - "defaultChannel": "stable-2.0", - }, - }, - }, - reqOperators: []RequiredOperator{ - { - Name: MCEName, - Namespace: MCENamespace, - OperatorGroup: MCEOperatorGroup, - }, - }, - expectedError: false, - verifyHandler: func(t *testing.T, h *HCHandler, c client.Client) { - g.Expect(h).ToNot(gomega.BeNil()) - g.Expect(h.HCOCPTestImage).To(gomega.Equal(HCOCPTestImage)) - - // Verify namespace was created - ns := &corev1.Namespace{} - err := c.Get(context.Background(), types.NamespacedName{Name: MCENamespace}, ns) - g.Expect(err).ToNot(gomega.HaveOccurred()) - - // Verify operator group was created - og := &operatorsv1.OperatorGroup{} - err = c.Get(context.Background(), types.NamespacedName{Name: MCEOperatorGroup, Namespace: MCENamespace}, og) - g.Expect(err).ToNot(gomega.HaveOccurred()) - g.Expect(og.Spec.TargetNamespaces).To(gomega.Equal([]string{MCENamespace})) - - // Verify subscription was created - sub := &operatorsv1alpha1.Subscription{} - err = c.Get(context.Background(), types.NamespacedName{Name: MCEName, Namespace: MCENamespace}, sub) - g.Expect(err).ToNot(gomega.HaveOccurred()) - g.Expect(sub.Spec.Channel).To(gomega.Equal("stable-2.0")) - g.Expect(sub.Spec.CatalogSource).To(gomega.Equal(RHOperatorsNamespace)) - g.Expect(sub.Spec.CatalogSourceNamespace).To(gomega.Equal(OCPMarketplaceNamespace)) - g.Expect(sub.Spec.Package).To(gomega.Equal(MCEName)) - g.Expect(sub.Spec.InstallPlanApproval).To(gomega.Equal(operatorsv1alpha1.ApprovalAutomatic)) - }, - }, - { - name: "Successfully install operator with specific channel", - packageManifest: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "status": map[string]interface{}{ - "defaultChannel": "stable-2.0", - }, - }, - }, - reqOperators: []RequiredOperator{ - { - Name: MCEName, - Namespace: MCENamespace, - OperatorGroup: MCEOperatorGroup, - Channel: "stable-2.1", - }, - }, - expectedError: false, - verifyHandler: func(t *testing.T, h *HCHandler, c client.Client) { - g.Expect(h).ToNot(gomega.BeNil()) - - // Verify subscription was created with correct channel - sub := &operatorsv1alpha1.Subscription{} - err := c.Get(context.Background(), types.NamespacedName{Name: MCEName, Namespace: MCENamespace}, sub) - g.Expect(err).ToNot(gomega.HaveOccurred()) - g.Expect(sub.Spec.Channel).To(gomega.Equal("stable-2.1")) - }, - }, - { - name: "Successfully install operator with specific CSV", - packageManifest: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "status": map[string]interface{}{ - "defaultChannel": "stable-2.0", - }, - }, - }, - reqOperators: []RequiredOperator{ - { - Name: MCEName, - Namespace: MCENamespace, - OperatorGroup: MCEOperatorGroup, - Csv: "multicluster-engine.v2.1.0", - }, - }, - expectedError: false, - verifyHandler: func(t *testing.T, h *HCHandler, c client.Client) { - g.Expect(h).ToNot(gomega.BeNil()) - - // Verify subscription was created with correct CSV - sub := &operatorsv1alpha1.Subscription{} - err := c.Get(context.Background(), types.NamespacedName{Name: MCEName, Namespace: MCENamespace}, sub) - g.Expect(err).ToNot(gomega.HaveOccurred()) - g.Expect(sub.Spec.StartingCSV).To(gomega.Equal("multicluster-engine.v2.1.0")) - }, - }, - { - name: "Fail to install operator with missing package manifest", - packageManifest: nil, - reqOperators: []RequiredOperator{ - { - Name: MCEName, - Namespace: MCENamespace, - OperatorGroup: MCEOperatorGroup, - }, - }, - expectedError: true, - errorContains: "failed to get PackageManifest", - }, - { - name: "Fail to install operator with missing default channel", - packageManifest: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "status": map[string]interface{}{}, - }, - }, - reqOperators: []RequiredOperator{ - { - Name: MCEName, - Namespace: MCENamespace, - OperatorGroup: MCEOperatorGroup, - }, - }, - expectedError: true, - errorContains: "no default channel found", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := createTestScheme() - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create package manifest if provided - if tt.packageManifest != nil { - tt.packageManifest.SetGroupVersionKind(packageManifestGVR.GroupVersion().WithKind("PackageManifest")) - tt.packageManifest.SetName(MCEName) - tt.packageManifest.SetNamespace(MCENamespace) - err := client.Create(context.Background(), tt.packageManifest) - g.Expect(err).ToNot(gomega.HaveOccurred()) - } - - // Test InstallRequiredOperators - h, err := InstallRequiredOperators(context.Background(), client, tt.reqOperators) - - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - if tt.errorContains != "" { - g.Expect(err.Error()).To(gomega.ContainSubstring(tt.errorContains)) - } - } else { - g.Expect(err).ToNot(gomega.HaveOccurred()) - if tt.verifyHandler != nil { - tt.verifyHandler(t, h, client) - } - } - }) - } -} - -func TestWaitForUnstructuredObject(t *testing.T) { - // Register Gomega fail handler - gomega.RegisterTestingT(t) - g := gomega.NewGomegaWithT(t) - - tests := []struct { - name string - obj *unstructured.Unstructured - createObj bool - deleteObj bool - timeout time.Duration - expectedError bool - errorContains string - }{ - { - name: "Object already deleted", - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": "Test", - "apiVersion": "test/v1", - "metadata": map[string]interface{}{ - "name": "test", - "namespace": "test", - }, - }, - }, - createObj: false, - deleteObj: false, - timeout: time.Minute, - expectedError: false, - }, - { - name: "Object deleted during wait", - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": "Test", - "apiVersion": "test/v1", - "metadata": map[string]interface{}{ - "name": "test", - "namespace": "test", - }, - }, - }, - createObj: true, - deleteObj: true, - timeout: time.Minute, - expectedError: false, - }, - { - name: "Object not deleted within timeout", - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": "Test", - "apiVersion": "test/v1", - "metadata": map[string]interface{}{ - "name": "test", - "namespace": "test", - }, - }, - }, - createObj: true, - deleteObj: false, - timeout: time.Second * 2, - expectedError: true, - errorContains: "context deadline exceeded", - }, - { - name: "Object with finalizers", - obj: &unstructured.Unstructured{ - Object: map[string]interface{}{ - "kind": "Test", - "apiVersion": "test/v1", - "metadata": map[string]interface{}{ - "name": "test", - "namespace": "test", - "finalizers": []interface{}{"test-finalizer"}, - }, - }, - }, - createObj: true, - deleteObj: true, - timeout: time.Second * 2, - expectedError: true, - errorContains: "context deadline exceeded", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a new scheme and client for each test case - scheme := createTestScheme() - client := fake.NewClientBuilder().WithScheme(scheme).Build() - - // Create the object if needed - if tt.createObj { - err := client.Create(context.Background(), tt.obj) - g.Expect(err).ToNot(gomega.HaveOccurred()) - } - - // Start deletion in background if needed - if tt.deleteObj { - go func() { - time.Sleep(100 * time.Millisecond) // Give time for the test to start - err := client.Delete(context.Background(), tt.obj) - g.Expect(err).ToNot(gomega.HaveOccurred()) - }() - } - - // Test WaitForUnstructuredObject - err := WaitForUnstructuredObject(context.Background(), client, tt.obj, tt.timeout) - - // Check results - if tt.expectedError { - g.Expect(err).To(gomega.HaveOccurred()) - if tt.errorContains != "" { - g.Expect(err.Error()).To(gomega.ContainSubstring(tt.errorContains)) - } - } else { - g.Expect(err).ToNot(gomega.HaveOccurred()) - } - }) - } -} - -func TestGetProjectRoot(t *testing.T) { - g := gomega.NewGomegaWithT(t) - - // Get the project root using the function - root := getProjectRoot() - - // Verify that the path exists - _, err := os.Stat(root) - g.Expect(err).ToNot(gomega.HaveOccurred(), "project root path should exist") - - // Verify that the path contains expected directories - expectedDirs := []string{ - "tests", - "go.mod", - "go.sum", - } - - for _, dir := range expectedDirs { - path := filepath.Join(root, dir) - _, err := os.Stat(path) - g.Expect(err).ToNot(gomega.HaveOccurred(), "expected directory/file %s should exist in project root", dir) - } - - // Verify that the path is absolute - g.Expect(filepath.IsAbs(root)).To(gomega.BeTrue(), "project root path should be absolute") - - // Verify that the path points to the correct directory by checking for a known file - knownFile := filepath.Join(root, "tests", "e2e", "lib", "hcp", "utils.go") - _, err = os.Stat(knownFile) - g.Expect(err).ToNot(gomega.HaveOccurred(), "should be able to find utils.go in the expected location") -} diff --git a/tests/e2e/lib/scheme.go b/tests/e2e/lib/scheme.go index ede8e8de6fd..929459342ef 100644 --- a/tests/e2e/lib/scheme.go +++ b/tests/e2e/lib/scheme.go @@ -8,7 +8,6 @@ import ( openshiftroutev1 "github.com/openshift/api/route/v1" openshiftsecurityv1 "github.com/openshift/api/security/v1" openshifttemplatev1 "github.com/openshift/api/template/v1" - hypershiftv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -35,7 +34,6 @@ func init() { _ = volumesnapshotv1.AddToScheme(Scheme) _ = operatorsv1alpha1.AddToScheme(Scheme) _ = operatorsv1.AddToScheme(Scheme) - _ = hypershiftv1.AddToScheme(Scheme) _ = appsv1.AddToScheme(Scheme) _ = openshiftconfigv1.AddToScheme(Scheme) } diff --git a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent-capi-role.yaml b/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent-capi-role.yaml deleted file mode 100644 index 068717ecb4c..00000000000 --- a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent-capi-role.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: capi-provider-role - namespace: {{ .ClustersNamespace }} -rules: -- apiGroups: - - agent-install.openshift.io - resources: - - agents - verbs: - - '*' \ No newline at end of file diff --git a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent.yaml b/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent.yaml deleted file mode 100644 index 36faf960f3b..00000000000 --- a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-agent.yaml +++ /dev/null @@ -1,63 +0,0 @@ -apiVersion: hypershift.openshift.io/v1beta1 -kind: HostedCluster -metadata: - name: {{ .HostedClusterName }} - namespace: {{ .ClustersNamespace }} - annotations: - hypershift.openshift.io/cleanup-cloud-resources: "true" - hypershift.openshift.io/skip-release-image-validation: "true" -spec: - configuration: - operatorhub: - disableAllDefaultSources: true - controllerAvailabilityPolicy: SingleReplica - dns: - baseDomain: example.com - etcd: - managed: - storage: - persistentVolume: - size: 8Gi - type: PersistentVolume - managementType: Managed - fips: false - infraID: {{ .HostedClusterName }}-{{ .InfraIDSeed }} - networking: - clusterNetwork: - - cidr: 10.132.0.0/14 - networkType: OVNKubernetes - serviceNetwork: - - cidr: 172.31.0.0/16 - olmCatalogPlacement: management - platform: - type: Agent - agent: - agentNamespace: {{ .ClustersNamespace }} - pullSecret: - name: {{ .HostedClusterName }}-pull-secret - release: - image: {{ .HCOCPTestImage }} - secretEncryption: - aescbc: - activeKey: - name: {{ .HostedClusterName }}-etcd-encryption-key - type: aescbc - services: - - service: APIServer - servicePublishingStrategy: - type: NodePort - nodePort: - address: 10.0.133.132 - - service: Ignition - servicePublishingStrategy: - type: Route - - service: Konnectivity - servicePublishingStrategy: - type: Route - - service: OAuthServer - servicePublishingStrategy: - type: Route - - service: OIDC - servicePublishingStrategy: - type: Route - sshKey: {} diff --git a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-etcd-enc-key.yaml b/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-etcd-enc-key.yaml deleted file mode 100644 index 372e00e0272..00000000000 --- a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-etcd-enc-key.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -data: - key: {{ .EtcdEncryptionKey }} -kind: Secret -metadata: - labels: - hypershift.openshift.io/safe-to-delete-with-cluster: "true" - name: {{ .HostedClusterName }}-etcd-encryption-key - namespace: {{ .ClustersNamespace }} -type: Opaque diff --git a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-none.yaml b/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-none.yaml deleted file mode 100644 index 8fc612b62f2..00000000000 --- a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-none.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: hypershift.openshift.io/v1beta1 -kind: HostedCluster -metadata: - name: {{ .HostedClusterName }} - namespace: {{ .ClustersNamespace }} - annotations: - hypershift.openshift.io/cleanup-cloud-resources: "true" - hypershift.openshift.io/skip-release-image-validation: "true" -spec: - configuration: - operatorhub: - disableAllDefaultSources: true - controllerAvailabilityPolicy: SingleReplica - dns: - baseDomain: example.com - etcd: - managed: - storage: - persistentVolume: - size: 8Gi - type: PersistentVolume - managementType: Managed - fips: false - infraID: {{ .HostedClusterName }}-{{ .InfraIDSeed }} - networking: - clusterNetwork: - - cidr: 10.132.0.0/14 - networkType: OVNKubernetes - serviceNetwork: - - cidr: 172.31.0.0/16 - olmCatalogPlacement: management - platform: - type: None - pullSecret: - name: {{ .HostedClusterName }}-pull-secret - release: - image: {{ .HCOCPTestImage }} - secretEncryption: - aescbc: - activeKey: - name: {{ .HostedClusterName }}-etcd-encryption-key - type: aescbc - services: - - service: APIServer - servicePublishingStrategy: - type: NodePort - nodePort: - address: 10.0.133.132 - - service: Ignition - servicePublishingStrategy: - type: Route - - service: Konnectivity - servicePublishingStrategy: - type: Route - - service: OAuthServer - servicePublishingStrategy: - type: Route - - service: OIDC - servicePublishingStrategy: - type: Route - sshKey: {} diff --git a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-pull-secret.yaml b/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-pull-secret.yaml deleted file mode 100644 index 288008582ed..00000000000 --- a/tests/e2e/sample-applications/hostedcontrolplanes/hypershift/hostedcluster-pull-secret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -data: - .dockerconfigjson: {{ .PullSecret }} -kind: Secret -metadata: - labels: - hypershift.openshift.io/safe-to-delete-with-cluster: "true" - name: {{ .HostedClusterName }}-pull-secret - namespace: {{ .ClustersNamespace }} diff --git a/tests/e2e/sample-applications/hostedcontrolplanes/mce/mce-operand.yaml b/tests/e2e/sample-applications/hostedcontrolplanes/mce/mce-operand.yaml deleted file mode 100644 index 784a8aea8dc..00000000000 --- a/tests/e2e/sample-applications/hostedcontrolplanes/mce/mce-operand.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: multicluster.openshift.io/v1 -kind: MultiClusterEngine -metadata: - name: {{ .MCEOperandName }} - namespace: {{ .MCEOperandNamespace }} -spec: - availabilityConfig: Basic - overrides: - components: - - configOverrides: {} - enabled: true - name: assisted-service - - configOverrides: {} - enabled: true - name: cluster-lifecycle - - configOverrides: {} - enabled: true - name: cluster-manager - - configOverrides: {} - enabled: true - name: discovery - - configOverrides: {} - enabled: true - name: hive - - configOverrides: {} - enabled: true - name: server-foundation - - configOverrides: {} - enabled: true - name: local-cluster - - configOverrides: {} - enabled: true - name: hypershift-local-hosting - - configOverrides: {} - enabled: true - name: console-mce - - configOverrides: {} - enabled: true - name: cluster-proxy-addon - - configOverrides: {} - enabled: true - name: hypershift - - configOverrides: {} - enabled: true - name: managedserviceaccount - - configOverrides: {} - enabled: false - name: image-based-install-operator \ No newline at end of file diff --git a/tests/e2e/scripts/aws_settings.sh b/tests/e2e/scripts/aws_settings.sh index 2064ef07e57..af7549a425d 100644 --- a/tests/e2e/scripts/aws_settings.sh +++ b/tests/e2e/scripts/aws_settings.sh @@ -11,13 +11,12 @@ cat > $TMP_DIR/oadpcreds < $TMP_DIR/oadpcreds < $TMP_DIR/oadpcreds < $TMP_DIR/oadpcreds < $TMP_DIR/oadpcreds <