diff --git a/ginkgo-test/eso/eso.go b/ginkgo-test/eso/eso.go new file mode 100644 index 00000000..41b2d0a0 --- /dev/null +++ b/ginkgo-test/eso/eso.go @@ -0,0 +1,1814 @@ +package oap + +import ( + "context" + "encoding/base64" + "fmt" + "path/filepath" + "regexp" + "strings" + "time" + + g "github.com/onsi/ginkgo/v2" + o "github.com/onsi/gomega" + exutil "github.com/openshift/openshift-tests-private/test/extended/util" + gcpcrm "google.golang.org/api/cloudresourcemanager/v1" + gcpiam "google.golang.org/api/iam/v1" + "k8s.io/apimachinery/pkg/util/wait" + e2e "k8s.io/kubernetes/test/e2e/framework" +) + +var _ = g.Describe("[sig-oap] OAP eso", func() { + defer g.GinkgoRecover() + + var ( + oc = exutil.NewCLI("eso", exutil.KubeConfigPath()) + buildPruningBaseDir = exutil.FixturePath("testdata", "oap/eso") + cfg = olmInstallConfig{ + mode: "OLMv0", + operatorNamespace: ESONamespace, + buildPruningBaseDir: buildPruningBaseDir, + subscriptionName: ESOSubscriptionName, + channel: ESOChannelName, + packageName: "external-secrets-operator", + extensionName: ESOExtensionName, + serviceAccountName: "sa-eso", + } + ) + g.BeforeEach(func() { + + if !isDeploymentReady(oc, ESONamespace, ESODeploymentName) { + e2e.Logf("Creating External Secrets Operator...") + installExternalSecretsOperator(oc, cfg) + } + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-High-80066-Get the secret value from AWS Secrets Manager", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80066" + externalsecretName = "externalsecret-80066" + secretRegion = "us-east-2" + ) + ns := oc.Namespace() + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + defer func() { + e2e.Logf("Cleanup the secret store") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secretstore", secretstoreName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", "NAME=" + secretstoreName, "REGION=" + secretRegion, "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + defer func() { + e2e.Logf("Cleanup the external secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "externalsecret", externalsecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", "NAME=" + externalsecretName, "REFREASHINTERVAL=" + "1m", "SECRETSTORENAME=" + secretstoreName, "SECRETNAME=" + "secret-from-awssm"} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", "secret-from-awssm", "-o=jsonpath={.data.secret-value-from-awssm}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + value, err := base64.StdEncoding.DecodeString(data) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(value).To(o.ContainSubstring(`"username":"jitli"`)) + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-High-80069-Check the secret value is updated from AWS Secrets Manager", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80069" + externalsecretName = "externalsecret-80069" + secretRegion = "us-east-2" + secretName = "jitliSecret" + secretKey = "password-80069" + generatedSecretName = "secret-from-awssm" + ) + var ( + newPasswd = getRandomString(8) + ns = oc.Namespace() + ) + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "PROPERTY=" + secretKey} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(data).NotTo(o.BeEmpty()) + + exutil.By("Update secret value") + err = UpdateSecretValueByKeyAWS(accessKeyID, secureKey, secretRegion, secretName, secretKey, newPasswd) + if err != nil { + e2e.Failf("Failed to update secret: %v", err) + } + e2e.Logf("Secret key %v updated successfully!", secretKey) + + exutil.By("Check the secret value be synced") + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, newPasswd) + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-High-80759-Sync secret from AWS Parameter Store and verify updates", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80759" + externalsecretName = "externalsecret-80759" + secretRegion = "us-east-2" + parameterName = "esoParameter" + secretKey = "value-80759" + generatedSecretName = "secret-from-parameter-store" + ) + var ( + newValue = "/eso/test80759 = " + getRandomString(8) + ns = oc.Namespace() + ) + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "SERVICE=" + "ParameterStore", + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awsps.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "KEY=" + parameterName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(data).NotTo(o.BeEmpty()) + + exutil.By("Update parameter value") + err = UpdateParameterAWS(accessKeyID, secureKey, secretRegion, parameterName, newValue) + if err != nil { + e2e.Failf("Failed to update parameter: %v", err) + } + e2e.Logf("Parameter %v updated successfully!", parameterName) + + exutil.By("Check the parameter value be synced") + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, newValue) + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-NonPreRelease-PreChkUpgrade-ROSA-Medium-80703-needs prepare test data before OCP upgrade", func() { + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80703" + externalsecretName = "externalsecret-80703" + secretRegion = "us-east-2" + sharedNamespace = "ocp-65134-shared-ns" + secretName = "jitliSecret" + secretKey = "password-80703" + generatedSecretName = "secret-from-awssm" + ) + + exutil.By("create a shared testing namespace") + err := oc.AsAdmin().WithoutNamespace().Run("create").Args("namespace", sharedNamespace).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret that contains AWS accessKey") + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err = oc.AsAdmin().Run("create").Args("-n", sharedNamespace, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", "NAME=" + secretstoreName, "REGION=" + secretRegion, "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, sharedNamespace, params...) + err = waitForResourceReadiness(oc, sharedNamespace, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, sharedNamespace, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "PROPERTY=" + secretKey} + exutil.ApplyNsResourceFromTemplate(oc, sharedNamespace, params...) + err = waitForResourceReadiness(oc, sharedNamespace, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, sharedNamespace, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", sharedNamespace, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + value, err := base64.StdEncoding.DecodeString(data) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(value).NotTo(o.BeEmpty()) + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-NonPreRelease-PstChkUpgrade-ROSA-Medium-80703-functions should work normally after OCP upgrade", func() { + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80703" + externalsecretName = "externalsecret-80703" + secretRegion = "us-east-2" + sharedNamespace = "ocp-65134-shared-ns" + secretName = "jitliSecret" + secretKey = "password-80703" + generatedSecretName = "secret-from-awssm" + ) + + // check if the shared testing namespace exists first + err := oc.AsAdmin().WithoutNamespace().Run("get").Args("namespace", sharedNamespace).Execute() + if err != nil { + g.Skip("Skip the PstChkUpgrade test as namespace '" + sharedNamespace + "' does not exist, PreChkUpgrade test did not finish successfully") + } + defer oc.AsAdmin().WithoutNamespace().Run("delete").Args("namespace", sharedNamespace, "--ignore-not-found").Execute() + + exutil.By("log the CSV post OCP upgrade") + err = oc.AsAdmin().WithoutNamespace().Run("get").Args("csv", "-n", ESONamespace).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("check the operator and operands pods status, all of them should be Ready") + exutil.AssertAllPodsToBeReadyWithPollerParams(oc, ESONamespace, 10*time.Second, 120*time.Second) + //exutil.AssertAllPodsToBeReadyWithPollerParams(oc, operandNamespace, 10*time.Second, 120*time.Second) + + exutil.By("check the existing secretstore and externalsecret status, all of them should be Ready") + err = waitForResourceReadiness(oc, sharedNamespace, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, sharedNamespace, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + err = waitForResourceReadiness(oc, sharedNamespace, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, sharedNamespace, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", sharedNamespace, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + value, err := base64.StdEncoding.DecodeString(data) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(value).NotTo(o.BeEmpty()) + + exutil.By("Update secret value") + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + newPasswd := getRandomString(8) + err = UpdateSecretValueByKeyAWS(accessKeyID, secureKey, secretRegion, secretName, secretKey, newPasswd) + if err != nil { + e2e.Failf("Failed to update secret: %v", err) + } + e2e.Logf("Secret key %v updated successfully!", secretKey) + + exutil.By("Check the secret value be synced") + waitForSecretUpdate(oc, sharedNamespace, generatedSecretName, secretKey, newPasswd) + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-High-80711-Install HashiCorp Vault and sync secrets via ESO", func() { + + const ( + vaultSecretName = "vault-token" + secretstoreName = "secretstore-80711" + externalsecretName = "externalsecret-80711" + secretPath = "secret" + secretName = "Secret80711" + secretKey = "password-80711" + generatedSecretName = "secret-from-vault" + ) + var ( + passwd = getRandomString(8) + newPasswd = getRandomString(8) + ns = oc.Namespace() + vaultReleaseName = "vault-" + getRandomString(4) + route = "" + ) + + exutil.By("Create Vault server") + helmConfigFile := filepath.Join(buildPruningBaseDir, "helm-vault-config.yaml") + vaultPodName, vaultRootToken := setupVaultServer(oc, ns, vaultReleaseName, helmConfigFile, false) + + exutil.By("Create a secret in Vault server") + initVaultSecret(oc, ns, vaultPodName, vaultRootToken, secretPath) + putVaultSecret(oc, ns, vaultPodName, secretPath, secretName, secretKey, passwd) + + err := oc.AsAdmin().Run("expose").Args("-n", ns, "service", vaultReleaseName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + errWait := wait.PollUntilContextTimeout(context.TODO(), 2*time.Second, 10*time.Second, false, func(ctx context.Context) (bool, error) { + route, err = oc.AsAdmin().WithoutNamespace().Run("get").Args("route", vaultReleaseName, "-n", ns, "-o=jsonpath={.spec.host}").Output() + if err != nil { + e2e.Logf("output is %v, error is %v, and try next", route, err) + return false, nil + } + if route == "" { + e2e.Logf("route is empty") + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "get vault route failed") + + exutil.By("Create secret that contains vault server token") + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", vaultSecretName, "--from-literal=token="+vaultRootToken).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-vault.yaml") + serverURl := "http://" + route + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "SERVER=" + serverURl, + "PATH=" + secretPath} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-vault.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "KEY=" + secretName, + "PROPERTY=" + secretKey} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + value, err := base64.StdEncoding.DecodeString(data) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(value).To(o.ContainSubstring(passwd)) + + exutil.By("Update secret value") + putVaultSecret(oc, ns, vaultPodName, secretPath, secretName, secretKey, newPasswd) + + exutil.By("Check the secret value be synced") + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, newPasswd) + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-Medium-81818-Back up local Vault secret to AWS Parameter Store", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + vaultSecretName = "vault-token" + secretstoreVaultName = "secretstore-vault-81818" + externalsecretName = "externalsecret-81818" + secretPath = "secret" + secretName = "Secret81818" + secretKey = "password-81818" + generatedSecretName = "secret-from-vault" + awsSecretName = "aws-creds" + secretstoreAWSName = "secretstore-aws-81818" + pushSecretName = "pushsecret-81818" + secretRegion = "us-east-2" + parameterName = "Parameter-81818" + ) + var ( + passwd = getRandomString(8) + newPasswd = getRandomString(8) + ns = oc.Namespace() + vaultReleaseName = "vault-" + getRandomString(4) + route = "" + ) + + exutil.By("Create Vault server") + helmConfigFile := filepath.Join(buildPruningBaseDir, "helm-vault-config.yaml") + vaultPodName, vaultRootToken := setupVaultServer(oc, ns, vaultReleaseName, helmConfigFile, false) + + exutil.By("Create a secret in Vault server") + initVaultSecret(oc, ns, vaultPodName, vaultRootToken, secretPath) + putVaultSecret(oc, ns, vaultPodName, secretPath, secretName, secretKey, passwd) + + err := oc.AsAdmin().Run("expose").Args("-n", ns, "service", vaultReleaseName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + errWait := wait.PollUntilContextTimeout(context.TODO(), 2*time.Second, 10*time.Second, false, func(ctx context.Context) (bool, error) { + route, err = oc.AsAdmin().WithoutNamespace().Run("get").Args("route", vaultReleaseName, "-n", ns, "-o=jsonpath={.spec.host}").Output() + if err != nil { + e2e.Logf("output is %v, error is %v, and try next", route, err) + return false, nil + } + if route == "" { + e2e.Logf("route is empty") + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "get vault route failed") + + exutil.By("Create secret that contains vault server token") + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", vaultSecretName, "--from-literal=token="+vaultRootToken).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-vault.yaml") + serverURl := "http://" + route + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreVaultName, + "SERVER=" + serverURl, + "PATH=" + secretPath} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreVaultName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreVaultName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-vault.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreVaultName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "KEY=" + secretName, + "PROPERTY=" + secretKey} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + value, err := base64.StdEncoding.DecodeString(data) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(value).To(o.ContainSubstring(passwd)) + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate = filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params = []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreAWSName, + "SERVICE=" + "ParameterStore", + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreAWSName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreAWSName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create a PushSecret use this Generator as source") + pushSecretTemplate := filepath.Join(buildPruningBaseDir, "pushsecret-aws-secretkey.yaml") + params = []string{"-f", pushSecretTemplate, "-p", + "NAME=" + pushSecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETSTORENAME=" + secretstoreAWSName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "KEY=" + parameterName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForPushSecretStatus(oc, ns, pushSecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "pushsecret", pushSecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for pushsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + secret, err := GetSecretAWSPS(accessKeyID, secureKey, secretRegion, parameterName) + if err != nil && !strings.Contains(secret, secretKey) { + e2e.Failf("Failed to get secret %v. or value not correct %v,%v", err, value, secretKey) + } + + exutil.By("Update secret value") + putVaultSecret(oc, ns, vaultPodName, secretPath, secretName, secretKey, newPasswd) + + exutil.By("Check the secret value be synced") + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, newPasswd) + + exutil.By("Check the parameter value be updated") + errWait = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 60*time.Second, false, func(ctx context.Context) (bool, error) { + secret, err = GetSecretAWSPS(accessKeyID, secureKey, secretRegion, parameterName) + if err != nil { + e2e.Logf("Error fetching secret: %v", err) + return false, nil + } + if !strings.Contains(secret, newPasswd) { + e2e.Logf("Value not correct %v,%v", secret, newPasswd) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error parameter store not updated") + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-High-80443-Validate creationPolicy lifecycle across all modes", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80443" + externalsecretName = "externalsecret-80443" + secretRegion = "us-east-2" + secretName = "Secret-80443" + secretKey = "password-80443" + generatedSecretName = "secret-from-awssm" + ) + ns := oc.Namespace() + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create secret with an irrelevant key") + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", generatedSecretName, "--from-literal=secret-store="+secretstoreName, "--from-literal=username="+secretRegion).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create external secret with creationPolicy is Owner") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "CREATIONPOLICY=" + "Owner", + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret username is updated") + waitForSecretUpdate(oc, ns, generatedSecretName, "username", "jitli") + + exutil.By("Delete ExternalSecret, check secret has been deleted") + err = oc.AsAdmin().Run("delete").Args("-n", ns, "externalsecret", externalsecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + output, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "secret", generatedSecretName).Output() + o.Expect(err).To(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("not found")) + e2e.Logf("Secret deleted successfully! %v", output) + + exutil.By("Create external secret with creationPolicy is Merge") + externalSecretTemplate = filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "CREATIONPOLICY=" + "Merge", + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists is SecretMissing, secret will not be created") + errWait := wait.PollUntilContextTimeout(context.TODO(), 3*time.Second, 15*time.Second, false, func(ctx context.Context) (bool, error) { + status, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "es", externalsecretName, `-o=jsonpath={.status.conditions[?(@.type=="Ready")].reason}`).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + if status != "SecretMissing" { + e2e.Logf("status: %v, expecte: SecretMissing", status) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "status expecte: SecretMissing") + output, err = oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "secret", generatedSecretName).Output() + o.Expect(err).To(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("not found")) + + exutil.By("Create secret with an irrelevant key and a key contained in the external key") + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", generatedSecretName, "--from-literal=secretRegion="+secretRegion, "--from-literal=username="+secretRegion).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + errWait = wait.PollUntilContextTimeout(context.TODO(), 3*time.Second, 15*time.Second, false, func(ctx context.Context) (bool, error) { + status, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "es", externalsecretName, `-o=jsonpath={.status.conditions[?(@.type=="Ready")].reason}`).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + if status != "SecretSynced" { + e2e.Logf("status: %v, expecte: SecretSynced", status) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "status expecte: SecretSynced") + + exutil.By("Check the secret value") + waitForSecretUpdate(oc, ns, generatedSecretName, "username", "jitli") + waitForSecretUpdate(oc, ns, generatedSecretName, "secretRegion", secretRegion) + waitForSecretUpdate(oc, ns, generatedSecretName, "email", "jitli@redhat.com") + + exutil.By("Delete ExternalSecret, check secret not be deleted") + err = oc.AsAdmin().Run("delete").Args("-n", ns, "externalsecret", externalsecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + waitForSecretUpdate(oc, ns, generatedSecretName, "username", "jitli") + waitForSecretUpdate(oc, ns, generatedSecretName, "secretRegion", secretRegion) + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, "80443") + err = oc.AsAdmin().Run("delete").Args("-n", ns, "secret", generatedSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create external secret with creationPolicy is Orphan") + externalSecretTemplate = filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "CREATIONPOLICY=" + "Orphan", + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + waitForSecretUpdate(oc, ns, generatedSecretName, "email", "jitli@redhat.com") + + exutil.By("Delete ExternalSecret, check secret not be deleted") + err = oc.AsAdmin().Run("delete").Args("-n", ns, "externalsecret", externalsecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, "80443") + err = oc.AsAdmin().Run("delete").Args("-n", ns, "secret", generatedSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create external secret with creationPolicy is None") + externalSecretTemplate = filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "CREATIONPOLICY=" + "None", + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + output, err = oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "secret", generatedSecretName).Output() + o.Expect(err).To(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("not found")) + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-Medium-80549-Validate deletionPolicy lifecycle across all modes", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80549" + externalsecretName = "externalsecret-80549" + secretRegion = "us-east-2" + secretName = "Secret-80549" + secretKey = "password-80549" + generatedSecretName = "secret-from-awssm" + ) + ns := oc.Namespace() + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create remote secret in AWSSM") + defer func() { + e2e.Logf("Cleanup the created secret in AWSSM") + err = DeleteSecretAWS(accessKeyID, secureKey, secretRegion, secretName, true) + if err != nil { + e2e.Failf("Delete secret failed: %v", err) + } + }() + newValue := getRandomString(8) + err = CreateSecretAWS(accessKeyID, secureKey, secretRegion, secretName, `{"password":"`+newValue+`"}`) + if err != nil { + e2e.Failf("Create secret failed: %v", err) + } + + exutil.By("Create external secret with default deletionPolicy is Retain") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "CREATIONPOLICY=" + "Owner", + "DELPOLICY=" + "Retain", + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret username is updated") + waitForSecretUpdate(oc, ns, generatedSecretName, "password", newValue) + + exutil.By("Retain the secret if all provider secrets have been deleted") + err = DeleteSecretAWS(accessKeyID, secureKey, secretRegion, secretName, true) + if err != nil { + e2e.Failf("Delete secret failed: %v", err) + } + waitForExternalSecretStatus(oc, ns, externalsecretName, "SecretSyncedError", 15*time.Second) + + exutil.By("Check the secret value") + waitForSecretUpdate(oc, ns, generatedSecretName, "password", newValue) + + err = oc.AsAdmin().Run("delete").Args("-n", ns, "externalsecret", externalsecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + err = CreateSecretAWS(accessKeyID, secureKey, secretRegion, secretName, `{"password":"`+newValue+`"}`) + if err != nil { + e2e.Failf("Create secret failed: %v", err) + } + + exutil.By("Create external secret with deletionPolicy is Delete") + externalSecretTemplate = filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "DELPOLICY=" + "Delete", + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + waitForSecretUpdate(oc, ns, generatedSecretName, "password", newValue) + + exutil.By("Delete the secret if all provider secrets have been deleted") + err = DeleteSecretAWS(accessKeyID, secureKey, secretRegion, secretName, true) + if err != nil { + e2e.Failf("Delete secret failed: %v", err) + } + waitForExternalSecretStatus(oc, ns, externalsecretName, "SecretDeleted", 15*time.Second) + + output, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "secret", generatedSecretName).Output() + o.Expect(err).To(o.HaveOccurred()) + o.Expect(output).To(o.ContainSubstring("not found")) + + err = oc.AsAdmin().Run("delete").Args("-n", ns, "externalsecret", externalsecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret with an irrelevant key") + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", generatedSecretName, "--from-literal=secret-store="+secretstoreName, "--from-literal=username="+secretRegion).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + err = CreateSecretAWS(accessKeyID, secureKey, secretRegion, secretName, `{"password":"`+newValue+`"}`) + if err != nil { + e2e.Failf("Create secret failed: %v", err) + } + + exutil.By("Create external secret with deletionPolicy is Merge") + externalSecretTemplate = filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "CREATIONPOLICY=" + "Merge", + "DELPOLICY=" + "Merge", + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + waitForSecretUpdate(oc, ns, generatedSecretName, "password", newValue) + waitForSecretUpdate(oc, ns, generatedSecretName, "username", secretRegion) + + exutil.By("Recover the secret if all provider secrets have been deleted") + err = DeleteSecretAWS(accessKeyID, secureKey, secretRegion, secretName, true) + if err != nil { + e2e.Failf("Delete secret failed: %v", err) + } + + waitForExternalSecretStatus(oc, ns, externalsecretName, "SecretSynced", 15*time.Second) + waitForSecretUpdate(oc, ns, generatedSecretName, "username", secretRegion) + errWait := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) { + data, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "secret", secretName, "-o=jsonpath={.data.password").Output() + if data != "" && err == nil { + return false, fmt.Errorf("error secret password not deleted: %v", data) + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, fmt.Sprintf("Error: secret %s not updated", secretName)) + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-Medium-80569-ESO will take ownership of orphan secrets", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-80569" + externalsecretName = "externalsecret-80569" + secretRegion = "us-east-2" + secretName = "jitliSecret" + secretKey = "password-80569" + generatedSecretName = "secret-from-awssm-80569" + ) + var ( + newValue = getRandomString(8) + ns = oc.Namespace() + ) + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create a orphan secret") + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", generatedSecretName, "--from-literal=password-80569="+newValue, "--from-literal=unrelatedkey="+newValue).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Check the orphan secret ownerReferences") + ownerReferences, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.ownerReferences}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(ownerReferences).To(o.BeEmpty()) + + exutil.By("Create external secret use the orphan secret same name") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "CREATIONPOLICY=" + "Owner", + "PROPERTY=" + secretKey} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check ESO will take ownership of orphan secret") + ownerReferences, err = oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, `-o=jsonpath={.metadata.ownerReferences[?(@.kind=="ExternalSecret")].controller}`).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(ownerReferences).To(o.ContainSubstring("true")) + + ownerReferences, err = oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, `-o=jsonpath={.metadata.ownerReferences[?(@.kind=="ExternalSecret")].blockOwnerDeletion}`).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(ownerReferences).To(o.ContainSubstring("true")) + + exutil.By("Check unrelated key is gone") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data.unrelatedkey}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(data).To(o.BeEmpty()) + + exutil.By("Check managed key exists and has correct") + data, err = oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(data).NotTo(o.BeEmpty()) + value, err := base64.StdEncoding.DecodeString(data) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(string(value)).NotTo(o.Equal(newValue)) + + exutil.By("Create a secret that already has ownership, create es, and fail to fight for ownership") + externalSecretTemplate = filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName + "-vie", + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "CREATIONPOLICY=" + "Owner", + "PROPERTY=" + secretKey} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + errWait := wait.PollUntilContextTimeout(context.TODO(), 3*time.Second, 15*time.Second, false, func(ctx context.Context) (bool, error) { + status, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "es", externalsecretName+"-vie", `-o=jsonpath={.status.conditions[?(@.type=="Ready")].reason}`).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + message, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "es", externalsecretName+"-vie", `-o=jsonpath={.status.conditions[?(@.type=="Ready")].message}`).Output() + o.Expect(err).NotTo(o.HaveOccurred()) + if status == "SecretSyncedError" && strings.Contains(message, "target is owned by another ExternalSecret") { + return true, nil + } + e2e.Logf("status: %v, expecte: SecretSyncedError , message: %v", status, message) + return false, nil + }) + exutil.AssertWaitPollNoErr(errWait, "status expecte: SecretSyncedError") + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-Low-81666-Check ESO decoding strategies", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-81666" + externalsecretName = "externalsecret-81666" + secretRegion = "us-east-2" + secretName = "jitliSecret" + secretKey = "password-81666" + generatedSecretName = "secret-from-awssm" + ) + var ( + ns = oc.Namespace() + ) + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-awssm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "5s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 120*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Get secret value") + secretValue, err := GetSecretValueByKeyAWS(accessKeyID, secureKey, secretRegion, secretName, secretKey) + if err != nil { + e2e.Failf("Failed to get secret: %v", err) + } + + exutil.By("Check the secret exists and verify the secret content") + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, secretValue) + + exutil.By("Enable the decoding strategies") + err = oc.AsAdmin().Run("patch").Args("externalsecret", externalsecretName, "-p", `{"spec":{"dataFrom":[{"extract":{"key":"jitliSecret","decodingStrategy":"Auto"}}]}}`, "--type=merge").Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Check decoding value") + errWait := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) { + data, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + if err != nil { + e2e.Logf("Error fetching secret: %v", err) + return false, nil + } + if secretValue != data { + e2e.Logf("Secret %s: expected value %v, got value %v", generatedSecretName, secretValue, data) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error secret not decoding") + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-Medium-81695-Generate random passwords using ESO", func() { + + const ( + clustergeneratorName = "clustergenerator-81695" + generatorName = "generator-81695" + externalsecretName = "externalsecret-81695" + secretKey = "password" + generatedSecretName = "secret-from-generator" + generatedClusterSecretName = "secret-from-clustergenerator" + ) + ns := oc.Namespace() + + defer func() { + e2e.Logf("Cleanup the cluster generator") + err := oc.AsAdmin().Run("delete").Args("clustergenerator", clustergeneratorName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + exutil.By("Create a kind password cluster generator") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "clustergenerator-password.yaml") + params := []string{"-f", secretStoreTemplate, "-p", "NAME=" + clustergeneratorName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-generator.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETNAME=" + generatedClusterSecretName, + "GENERATORKIND=" + "ClusterGenerator", + "GENERATOR=" + clustergeneratorName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err := waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Get secret value") + decodedValue, err := getSecretValueDecoded(oc, ns, generatedClusterSecretName, secretKey) + o.Expect(err).NotTo(o.HaveOccurred()) + decodedStr := string(decodedValue) + e2e.Logf("Decoded secret value: %s", decodedStr) + + o.Expect(len(decodedStr)).To(o.Equal(16), "expected generated password to have length 16") + o.Expect(regexp.MustCompile(`[0-9]`).FindAllString(decodedStr, -1)).To(o.HaveLen(5), "expected at least 5 digits") + o.Expect(regexp.MustCompile(`[-_\$@]`).FindAllString(decodedStr, -1)).To(o.HaveLen(5), "expected at least 5 symbols from -_$@") + o.Expect(regexp.MustCompile(`[A-Z]`).MatchString(decodedStr)).To(o.BeTrue(), "expected at least one uppercase letter") + o.Expect(regexp.MustCompile(`[a-z]`).MatchString(decodedStr)).To(o.BeTrue(), "expected at least one lowercase letter") + + exutil.By("Check the secret update and verify the secret content") + errWait := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) { + newValue, err := getSecretValueDecoded(oc, ns, generatedClusterSecretName, secretKey) + if err != nil { + e2e.Logf("Error get decoded secret: %v", err) + return false, nil + } + if string(decodedValue) == string(newValue) { + e2e.Logf("Secret %s: expected value updated %v, got value %v", generatedClusterSecretName, string(decodedValue), string(newValue)) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error secret not updated") + + err = oc.AsAdmin().Run("delete").Args("-n", ns, "externalsecret", externalsecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create a Namespaced generator") + secretStoreTemplate = filepath.Join(buildPruningBaseDir, "generator-password.yaml") + params = []string{"-f", secretStoreTemplate, "-p", "NAME=" + generatorName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + + exutil.By("Create external secret") + externalSecretTemplate = filepath.Join(buildPruningBaseDir, "externalsecret-generator.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETNAME=" + generatedSecretName, + "GENERATORKIND=" + "Password", + "GENERATOR=" + generatorName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Get secret value") + decodedValue, err = getSecretValueDecoded(oc, ns, generatedSecretName, secretKey) + o.Expect(err).NotTo(o.HaveOccurred()) + decodedStr = string(decodedValue) + e2e.Logf("Decoded secret value: %s", decodedStr) + + o.Expect(len(decodedStr)).To(o.Equal(16), "expected generated password to have length 16") + o.Expect(regexp.MustCompile(`[0-9]`).FindAllString(decodedStr, -1)).To(o.HaveLen(5), "expected at least 5 digits") + o.Expect(regexp.MustCompile(`[-_\$@]`).FindAllString(decodedStr, -1)).To(o.HaveLen(5), "expected at least 5 symbols from -_$@") + o.Expect(regexp.MustCompile(`[A-Z]`).MatchString(decodedStr)).To(o.BeTrue(), "expected at least one uppercase letter") + o.Expect(regexp.MustCompile(`[a-z]`).MatchString(decodedStr)).To(o.BeTrue(), "expected at least one lowercase letter") + + exutil.By("Check the secret update and verify the secret content") + errWait = wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) { + newValue, err := getSecretValueDecoded(oc, ns, generatedSecretName, secretKey) + if err != nil { + e2e.Logf("Error get decoded secret: %v", err) + return false, nil + } + if string(decodedValue) == string(newValue) { + e2e.Logf("Secret %s: expected value updated %v, got value %v", generatedSecretName, string(decodedValue), string(newValue)) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error secret not updated") + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ARO-OSD_CCS-Medium-80722-ESO can be uninstalled from CLI and then reinstalled [Disruptive]", func() { + exutil.By("uninstall the external-secrets-operator and cleanup its operand resources") + cleanupExternalSecretsOperator(oc, cfg) + + exutil.By("install the external-secrets-operator again") + installExternalSecretsOperator(oc, cfg) + + exutil.By("checking the resource types should be ready") + statusErr := wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 60*time.Second, false, func(ctx context.Context) (bool, error) { + err := oc.AsAdmin().Run("get").Args("secretstore").Execute() + if err == nil { + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollNoErr(statusErr, "timeout waiting for the CRDs ready") + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-High-81709-Push the entire Secret AWS Parameter Store", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-81709" + pushSecretName = "pushsecret-81709" + secretRegion = "us-east-2" + parameterName = "Parameter-81709" + secretKey = "value-81709" + secretName = "secret-push-parameter-store" + ) + var ( + newValue = getRandomString(8) + ns = oc.Namespace() + ) + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "SERVICE=" + "ParameterStore", + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", secretName, "--from-literal=secret-access-key="+secretKey).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create a pushsecret to push the secret") + pushSecretTemplate := filepath.Join(buildPruningBaseDir, "pushsecret-aws.yaml") + params = []string{"-f", pushSecretTemplate, "-p", + "NAME=" + pushSecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + secretName, + "KEY=" + parameterName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForPushSecretStatus(oc, ns, pushSecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "pushsecret", pushSecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for pushsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + value, err := GetSecretAWSPS(accessKeyID, secureKey, secretRegion, parameterName) + if err != nil && !strings.Contains(value, secretKey) { + e2e.Failf("Failed to get secret %v. or value not correct %v,%v", err, value, secretKey) + } + + exutil.By("Update the secret value") + err = oc.AsAdmin().Run("patch").Args("secret", secretName, "-p", `{"data":{"access-key":"`+newValue+`"}}`, "--type=merge").Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Check the parameter value be synced") + errWait := wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 60*time.Second, false, func(ctx context.Context) (bool, error) { + value, err = GetSecretAWSPS(accessKeyID, secureKey, secretRegion, parameterName) + if err != nil { + e2e.Logf("Error fetching secret: %v", err) + return false, nil + } + if !strings.Contains(value, newValue) { + e2e.Logf("Value not correct %v,%v", value, newValue) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error parameter store not updated") + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-ConnectedOnly-High-81708-Push key value to AWS Secrets Manager", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-81708" + pushSecretName = "pushsecret-81708" + secretRegion = "us-east-2" + smSecretName = "Secret-81708" + secretKey = "value-81708" + pushSecret = "secret-push-secret-manager" + ) + var ( + newValue = getRandomString(8) + ns = oc.Namespace() + ) + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err := oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "SERVICE=" + "SecretsManager", + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", pushSecret, "--from-literal=secret-access-key="+secretKey).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create a pushsecret to push the secret") + pushSecretTemplate := filepath.Join(buildPruningBaseDir, "pushsecret-aws-secretkey.yaml") + params = []string{"-f", pushSecretTemplate, "-p", + "NAME=" + pushSecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + pushSecret, + "SECRETKEY=" + "secret-access-key", + "KEY=" + smSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForPushSecretStatus(oc, ns, pushSecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "pushsecret", pushSecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for pushsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + errWait := wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 60*time.Second, false, func(ctx context.Context) (bool, error) { + secretValue, err := GetSecretAWS(accessKeyID, secureKey, secretRegion, smSecretName) + if err != nil && !strings.Contains(secretValue, secretKey) { + e2e.Logf("Failed to get secret %v. or value not correct %v,%v", err, secretValue, secretKey) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error AWSSM not updated") + + exutil.By("Update the secret value") + secretVault := `{"password":"` + newValue + `"}` + encodedValue := base64.StdEncoding.EncodeToString([]byte(secretVault)) + err = oc.AsAdmin().Run("patch").Args("secret", pushSecret, "-p", `{"data":{"secret-access-key":"`+encodedValue+`"}}`, "--type=merge").Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Check the AWSSM value be synced") + errWait = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 60*time.Second, false, func(ctx context.Context) (bool, error) { + value, err := GetSecretAWS(accessKeyID, secureKey, secretRegion, smSecretName) + if err != nil { + e2e.Logf("Error fetching secret: %v", err) + return false, nil + } + if !strings.Contains(value, newValue) { + e2e.Logf("Value not correct %v,%v", value, newValue) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error AWSSM not updated") + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ConnectedOnly-High-80719-Get the secret value from Google Secret Manager", func() { + + exutil.SkipIfPlatformTypeNot(oc, "GCP") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + exutil.SkipIfPlatformTypeNot(oc, "GCP") + const projectID = "openshift-qe" + if id, _ := exutil.GetGcpProjectID(oc); id != projectID { + e2e.Logf("current GCP project ID: %s", id) + g.Skip("Skip as the testing environment is only pre-setup under 'openshift-qe' project") + } + + const ( + secretstoreName = "secretstore-80719" + externalsecretName = "externalsecret-80719" + secretName = "Secret-80719" + secretKey = "password-80719" + generatedSecretName = "secret-from-gcpsm" + pushSecretName = "secret-push-gcpsm" + saSecretName = "google-eso-sa-key" + saPrefix = "test-private-80719-eso-" + ) + ns := oc.Namespace() + + exutil.By("create the GCP IAM and CloudResourceManager client") + // Note that in Prow CI, the credentials source is automatically pre-configured to by the step 'openshift-extended-test' + // See https://github.com/openshift/release/blob/69b2b9c4f28adcfcc5b9ff4820ecbd8d2582a3d7/ci-operator/step-registry/openshift-extended/test/openshift-extended-test-commands.sh#L43 + iamService, err := gcpiam.NewService(context.Background()) + o.Expect(err).NotTo(o.HaveOccurred()) + crmService, err := gcpcrm.NewService(context.Background()) + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("create a GCP service account") + serviceAccountName := saPrefix + getRandomString(4) + request := &gcpiam.CreateServiceAccountRequest{ + AccountId: serviceAccountName, + ServiceAccount: &gcpiam.ServiceAccount{ + DisplayName: "google-secret-manager service account for ESO", + }, + } + result, err := iamService.Projects.ServiceAccounts.Create("projects/"+projectID, request).Do() + o.Expect(err).NotTo(o.HaveOccurred()) + defer func() { + e2e.Logf("cleanup the created GCP service account") + _, err = iamService.Projects.ServiceAccounts.Delete(result.Name).Do() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + + exutil.By("add IAM policy binding with role 'secretmanager.admin' to GCP project") + projectRole := "roles/secretmanager.admin" + projectMember := fmt.Sprintf("serviceAccount:%s", result.Email) + defer func() { + e2e.Logf("cleanup the added IAM policy binding from GCP project") + updateIamPolicyBinding(crmService, projectID, projectRole, projectMember, false) + }() + updateIamPolicyBinding(crmService, projectID, projectRole, projectMember, true) + + exutil.By("create key for the GCP service account and store as a secret") + resource := fmt.Sprintf("projects/-/serviceAccounts/%s", result.Email) + key, err := iamService.Projects.ServiceAccounts.Keys.Create(resource, &gcpiam.CreateServiceAccountKeyRequest{}).Do() + o.Expect(err).NotTo(o.HaveOccurred()) + value, err := base64.StdEncoding.DecodeString(key.PrivateKeyData) + o.Expect(err).NotTo(o.HaveOccurred()) + oc.NotShowInfo() + err = oc.AsAdmin().Run("create").Args("-n", oc.Namespace(), "secret", "generic", saSecretName, "--from-literal=key.json="+string(value)).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + oc.SetShowInfo() + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-gcpsm.yaml") + params := []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "SECRETNAME=" + saSecretName, + "SECRETACCESSKEY=" + "key.json"} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-gcpsm.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedSecretName, + "SECRETKEY=" + secretKey, + "KEY=" + secretName, + "FROMKEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + data, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", ns, "secret", generatedSecretName, "-o=jsonpath={.data."+secretKey+"}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(data).NotTo(o.BeEmpty()) + + newValue := getRandomString(8) + exutil.By("Create a pushsecret to push the secret") + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", pushSecretName, "--from-literal=secret-access-key="+newValue).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + pushSecretTemplate := filepath.Join(buildPruningBaseDir, "pushsecret-aws.yaml") + params = []string{"-f", pushSecretTemplate, "-p", + "NAME=" + pushSecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + pushSecretName, + "KEY=" + secretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForPushSecretStatus(oc, ns, pushSecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "pushsecret", pushSecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for pushsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + waitForSecretUpdate(oc, ns, generatedSecretName, secretKey, newValue) + + }) + + // author: jitli@redhat.com + g.It("Author:jitli-ROSA-Medium-81813-Use Generator to dynamically generate and push password", func() { + + exutil.SkipIfPlatformTypeNot(oc, "AWS") + if exutil.IsSTSCluster(oc) { + g.Skip("Skip for STS cluster") + } + exutil.SkipOnProxyCluster(oc) + + const ( + awsSecretName = "aws-creds" + secretstoreName = "secretstore-81813" + pushSecretName = "pushsecret-81813" + secretRegion = "us-east-2" + parameterName = "Parameter-81813" + secretKey = "password" + clustergeneratorName = "clustergenerator-81813" + externalsecretName = "externalsecret-81813" + generatedClusterSecretName = "secret-from-clustergenerator" + ) + ns := oc.Namespace() + + defer func() { + e2e.Logf("Cleanup the cluster generator") + err := oc.AsAdmin().Run("delete").Args("clustergenerator", clustergeneratorName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + exutil.By("Create a kind password cluster generator") + generatorTemplate := filepath.Join(buildPruningBaseDir, "clustergenerator-password.yaml") + params := []string{"-f", generatorTemplate, "-p", "NAME=" + clustergeneratorName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + + exutil.By("Create external secret") + externalSecretTemplate := filepath.Join(buildPruningBaseDir, "externalsecret-generator.yaml") + params = []string{"-f", externalSecretTemplate, "-p", + "NAME=" + externalsecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETNAME=" + generatedClusterSecretName, + "GENERATORKIND=" + "ClusterGenerator", + "GENERATOR=" + clustergeneratorName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err := waitForResourceReadiness(oc, ns, "externalsecret", externalsecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "externalsecret", externalsecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for externalsecret to become Ready") + + exutil.By("Get secret value") + decodedValue, err := getSecretValueDecoded(oc, ns, generatedClusterSecretName, secretKey) + o.Expect(err).NotTo(o.HaveOccurred()) + decodedStr := string(decodedValue) + e2e.Logf("Decoded secret value: %s", decodedStr) + o.Expect(len(decodedStr)).To(o.Equal(16), "expected generated password to have length 16") + + exutil.By("Create secret that contains AWS accessKey") + defer func() { + e2e.Logf("Cleanup the created secret") + err := oc.AsAdmin().Run("delete").Args("-n", ns, "secret", awsSecretName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + }() + accessKeyID, secureKey := getCredentialFromCluster(oc, "aws") + oc.NotShowInfo() + err = oc.AsAdmin().Run("create").Args("-n", ns, "secret", "generic", awsSecretName, "--from-literal=access-key="+accessKeyID, "--from-literal=secret-access-key="+secureKey).Execute() + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + exutil.By("Create secret store") + secretStoreTemplate := filepath.Join(buildPruningBaseDir, "secretstore-awssm.yaml") + params = []string{"-f", secretStoreTemplate, "-p", + "NAME=" + secretstoreName, + "SERVICE=" + "ParameterStore", + "REGION=" + secretRegion, + "SECRETNAME=" + awsSecretName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForResourceReadiness(oc, ns, "secretstore", secretstoreName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "secretstore", secretstoreName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for secretstore to become Ready") + + exutil.By("Create a PushSecret use this Generator as source") + pushSecretTemplate := filepath.Join(buildPruningBaseDir, "pushsecret-aws-secretkey.yaml") + params = []string{"-f", pushSecretTemplate, "-p", + "NAME=" + pushSecretName, + "REFREASHINTERVAL=" + "10s", + "SECRETSTORENAME=" + secretstoreName, + "SECRETNAME=" + generatedClusterSecretName, + "SECRETKEY=" + secretKey, + "KEY=" + parameterName} + exutil.ApplyNsResourceFromTemplate(oc, ns, params...) + err = waitForPushSecretStatus(oc, ns, pushSecretName, 10*time.Second, 60*time.Second) + if err != nil { + dumpResource(oc, ns, "pushsecret", pushSecretName, "-o=yaml") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for pushsecret to become Ready") + + exutil.By("Check the secret exists and verify the secret content") + value, err := GetSecretAWSPS(accessKeyID, secureKey, secretRegion, parameterName) + if err != nil && !strings.Contains(value, secretKey) { + e2e.Failf("Failed to get secret %v. or value not correct %v,%v", err, value, secretKey) + } + + exutil.By("Check the parameter value be updated") + errWait := wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 60*time.Second, false, func(ctx context.Context) (bool, error) { + value, err = GetSecretAWSPS(accessKeyID, secureKey, secretRegion, parameterName) + if err != nil { + e2e.Logf("Error fetching secret: %v", err) + return false, nil + } + if !strings.Contains(value, decodedStr) { + e2e.Logf("Value not correct %v,%v", value, decodedStr) + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollNoErr(errWait, "Error parameter store not updated") + + }) + +}) diff --git a/ginkgo-test/eso/eso_utils.go b/ginkgo-test/eso/eso_utils.go new file mode 100644 index 00000000..98d33d49 --- /dev/null +++ b/ginkgo-test/eso/eso_utils.go @@ -0,0 +1,642 @@ +package oap + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "path/filepath" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/secretsmanager" + "github.com/aws/aws-sdk-go-v2/service/ssm" + g "github.com/onsi/ginkgo/v2" + o "github.com/onsi/gomega" + olmv1util "github.com/openshift/openshift-tests-private/test/extended/operators/olmv1util" + exutil "github.com/openshift/openshift-tests-private/test/extended/util" + "k8s.io/apimachinery/pkg/util/wait" + e2e "k8s.io/kubernetes/test/e2e/framework" +) + +// Immutable constant variables +const ( + ESOPackageName = "openshift-external-secrets-operator" + ESONamespace = "external-secrets-operator" + ESOperatorLabel = "name=external-secrets-operator" + ESODeploymentName = "external-secrets-operator-controller-manager" + ESOManagerLabel = "app.kubernetes.io/name=external-secrets" + ESOWebhookLabel = "app.kubernetes.io/name=external-secrets-webhook" + ESOCertControllerLabel = "app.kubernetes.io/name=external-secrets-cert-controller" + ESOperandsNamespace = "external-secrets" + ESOperandsLabel = "app.kubernetes.io/instance=external-secrets" + ESOCRDLabel = "operators.coreos.com/openshift-external-secrets-operator.external-secrets-operator" + ESOperandsDefaultPodNum = 3 +) + +// Other constant variables universally used +const ( + ESOSubscriptionName = "openshift-external-secrets-operator" + ESOChannelName = "tech-preview-v0.1" + ESOExtensionName = "clusterextension-eso" + ESOFBCName = "konflux-fbc-eso" + ESOFBCImage = "quay.io/redhat-user-workloads/external-secrets-oap-tenant/external-secrets-operator-fbc/external-secrets-operator-fbc:latest" + ESOPreRelease = true +) + +type olmInstallConfig struct { + mode string + operatorNamespace string + buildPruningBaseDir string + subscriptionName string // OLMv0 + catalogSourceName string // OLMv0 + catalogSourceNamespace string // OLMv0 + channel string + packageName string // OLMv1 + extensionName string // OLMv1 + serviceAccountName string // OLMv1 +} + +// Create External Secrets Operator +func installExternalSecretsOperator(oc *exutil.CLI, cfg olmInstallConfig) { + + exutil.SkipNoOLMCore(oc) + createOperatorNamespace(oc, cfg.buildPruningBaseDir) + + switch cfg.mode { + case "OLMv0": + catalogSourceName, catalogSourceNamespace := determineCatalogSource(oc) + + installViaOLMv0(oc, cfg.operatorNamespace, cfg.buildPruningBaseDir, cfg.subscriptionName, catalogSourceName, catalogSourceNamespace, cfg.channel) + case "OLMv1": + installViaOLMv1(oc, cfg.operatorNamespace, cfg.packageName, cfg.extensionName, cfg.channel, cfg.serviceAccountName) + default: + e2e.Failf("Please set correct olmMode:%v expected: 'OLMv0' or 'OLMv1'", cfg.mode) + } + + e2e.Logf("Create customresource for operand") + operandConfig := filepath.Join(cfg.buildPruningBaseDir, "operandConfig.yaml") + err := oc.AsAdmin().WithoutNamespace().Run("apply").Args("-f", operandConfig, "-n", ESONamespace).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + verifyOperandsForESO(oc, ESOperandsNamespace) +} + +func determineCatalogSource(oc *exutil.CLI) (string, string) { + e2e.Logf("=========Determining which catalogsource to use=========") + output, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", DefaultCatalogSourceNamespace, "catalogsource", "-o=jsonpath={.items[*].metadata.name}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + + var catalogSourceName, catalogSourceNamespace string + if ESOPreRelease && strings.Contains(output, QECatalogSourceName) && strings.Contains(output, RHCatalogSourceName) && !strings.Contains(output, AutoReleaseCatalogSourceName) { + e2e.Logf("=========Creating the catalogsource for ESO=========") + catalogSourceName = ESOFBCName + catalogSourceNamespace = ESONamespace + createFBC(oc, catalogSourceName, catalogSourceNamespace, ESOFBCImage) + } else { + catalogSourceName = RHCatalogSourceName + catalogSourceNamespace = DefaultCatalogSourceNamespace + } + + // check if packagemanifest exists under the selected catalogsource + output, err = oc.AsAdmin().WithoutNamespace().Run("get").Args("packagemanifest", "-n", catalogSourceNamespace, "-l", "catalog="+catalogSourceName, "--field-selector", "metadata.name="+ESOPackageName).Output() + if !strings.Contains(output, ESOPackageName) || err != nil { + g.Skip("skip since no available packagemanifest was found") + } + e2e.Logf("=> using catalogsource '%s' from namespace '%s'", catalogSourceName, catalogSourceNamespace) + return catalogSourceName, catalogSourceNamespace +} + +// create operator Namespace +func createOperatorNamespace(oc *exutil.CLI, buildPruningBaseDir string) { + e2e.Logf("=========Create the operator namespace=========") + namespaceFile := filepath.Join(buildPruningBaseDir, "namespace.yaml") + output, err := oc.AsAdmin().WithoutNamespace().Run("apply").Args("-f", namespaceFile).Output() + if strings.Contains(output, "being deleted") { + g.Skip("skip the install process as the namespace is being terminated due to other env issue e.g. we ever hit such failures caused by OCPBUGS-31443") + } + if err != nil && !strings.Contains(output, "AlreadyExists") { + e2e.Failf("Failed to apply namespace: %v", err) + } +} + +// install Via OLMv0 +func installViaOLMv0(oc *exutil.CLI, operatorNamespace, buildPruningBaseDir, subscriptionName, catalogSourceName, catalogSourceNamespace, channel string) { + e2e.Logf("=========Installing via OLMv0 (Subscription)=========") + // Create operator group + operatorGroupFile := filepath.Join(buildPruningBaseDir, "operatorgroup.yaml") + err := oc.AsAdmin().WithoutNamespace().Run("apply").Args("-f", operatorGroupFile).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + e2e.Logf("=========Create the subscription=========") + subscriptionTemplate := filepath.Join(buildPruningBaseDir, "subscription.yaml") + params := []string{"-f", subscriptionTemplate, "-p", "NAME=" + subscriptionName, "SOURCE=" + catalogSourceName, "SOURCE_NAMESPACE=" + catalogSourceNamespace, "CHANNEL=" + channel} + exutil.ApplyNsResourceFromTemplate(oc, operatorNamespace, params...) + // Wait for subscription state to become AtLatestKnown + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 180*time.Second, true, func(context.Context) (bool, error) { + output, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("sub", subscriptionName, "-n", operatorNamespace, "-o=jsonpath={.status.state}").Output() + if strings.Contains(output, "AtLatestKnown") { + return true, nil + } + return false, nil + }) + if err != nil { + dumpResource(oc, operatorNamespace, "sub", subscriptionName, "-o=jsonpath={.status}") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for subscription state to become AtLatestKnown") + + e2e.Logf("=========retrieve the installed CSV name=========") + csvName, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("sub", subscriptionName, "-n", operatorNamespace, "-o=jsonpath={.status.installedCSV}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(csvName).NotTo(o.BeEmpty()) + // Wait for csv phase to become Succeeded + err = wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 180*time.Second, true, func(context.Context) (bool, error) { + output, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("csv", csvName, "-n", operatorNamespace, "-o=jsonpath={.status.phase}").Output() + if strings.Contains(output, "Succeeded") { + e2e.Logf("csv '%s' installed successfully", csvName) + return true, nil + } + return false, nil + }) + if err != nil { + dumpResource(oc, operatorNamespace, "csv", csvName, "-o=jsonpath={.status}") + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for csv phase to become Succeeded") +} + +// install Via OLMv1 +func installViaOLMv1(oc *exutil.CLI, operatorNamespace, packageName, clusterextensionName, channel, saCrbName string) { + e2e.Logf("=========Installing via OLMv1 (ClusterExtension)=========") + e2e.Logf("Create SA for clusterextension") + var ( + baseDir = exutil.FixturePath("testdata", "olm", "v1") + clusterextensionTemplate = filepath.Join(baseDir, "clusterextensionWithoutVersion.yaml") + saClusterRoleBindingTemplate = filepath.Join(baseDir, "sa-admin.yaml") + saCrb = olmv1util.SaCLusterRolebindingDescription{ + Name: saCrbName, + Namespace: operatorNamespace, + Template: saClusterRoleBindingTemplate, + } + clusterextension = olmv1util.ClusterExtensionDescription{ + Name: clusterextensionName, + InstallNamespace: operatorNamespace, + PackageName: packageName, + Channel: channel, + SaName: saCrb.Name, + Template: clusterextensionTemplate, + } + ) + saCrb.Create(oc) + + e2e.Logf("Create ClusterExtension") + clusterextension.Create(oc) +} + +func verifyOperandsForESO(oc *exutil.CLI, ns string) { + e2e.Logf("=========Checking the operand pods readiness in %s=========", ns) + // Wait for pods phase to become Running + err := wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 120*time.Second, true, func(context.Context) (bool, error) { + output, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("pod", "-n", ns, "-l", ESOperandsLabel, "--field-selector=status.phase=Running", "-o=jsonpath={.items[*].metadata.name}").Output() + if len(strings.Fields(output)) == ESOperandsDefaultPodNum { + e2e.Logf("all operand pods are up and running!") + return true, nil + } + return false, nil + }) + if err != nil { + oc.AsAdmin().WithoutNamespace().Run("get").Args("pod", "-n", ns, "-l", ESOperandsLabel).Execute() + } + exutil.AssertWaitPollNoErr(err, "timeout waiting for all operand pods phase to become Running") + + waitForPodReadiness(oc, ns, ESOManagerLabel, 10*time.Second, 120*time.Second) + waitForPodReadiness(oc, ns, ESOWebhookLabel, 10*time.Second, 120*time.Second) + waitForPodReadiness(oc, ns, ESOCertControllerLabel, 10*time.Second, 120*time.Second) +} + +func waitForPodReadiness(oc *exutil.CLI, namespace, label string, interval, timeout time.Duration) { + err := wait.PollUntilContextTimeout(context.TODO(), interval, timeout, true, func(context.Context) (bool, error) { + output, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("pod", "-n", namespace, "-l", label, `-o=jsonpath={..status.conditions[?(@.type=="Ready")].status}`).Output() + if output == "True" { + e2e.Logf("Pod with label %s is Ready!", label) + return true, nil + } + return false, nil + }) + if err != nil { + oc.AsAdmin().WithoutNamespace().Run("get").Args("pod", "-n", namespace, "-l", label).Execute() + } + exutil.AssertWaitPollNoErr(err, fmt.Sprintf("timeout waiting for pod with label %s to become Ready", label)) +} + +func waitForPushSecretStatus(oc *exutil.CLI, namespace, name string, interval, timeout time.Duration) error { + statusErr := wait.PollUntilContextTimeout(context.TODO(), interval, timeout, true, func(context.Context) (bool, error) { + output, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("pushsecret", "-n", namespace, name, `-o=jsonpath={..status.conditions[?(@.type=="Ready")].status}`).Output() + if output == "True" { + e2e.Logf("pushsecret is Ready!") + return true, nil + } + e2e.Logf("pushsecret Ready status is %v", output) + return false, nil + }) + return statusErr +} + +// uninstall External Secrets Operator and cleanup its operand resources +func cleanupExternalSecretsOperator(oc *exutil.CLI, cfg olmInstallConfig) { + + switch cfg.mode { + case "OLMv0": + uninstallViaOLMv0(oc, cfg.operatorNamespace, cfg.subscriptionName) + case "OLMv1": + uninstallViaOLMv1(oc, cfg.operatorNamespace, cfg.extensionName) + default: + e2e.Failf("Please set correct olmMode:%v expected: 'OLMv0' or 'OLMv1'", cfg.mode) + } + + e2e.Logf("=========Delete the operatorconfig cluster object=========") + // remove the finalizers from that object first, otherwise the deletion would be stuck + err := oc.AsAdmin().WithoutNamespace().Run("patch").Args("operatorconfigs", "cluster", "--type=merge", `-p={"metadata":{"finalizers":null}}`).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("operatorconfigs", "cluster").Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + deleteNamespace(oc, ESONamespace) + //deleteNamespace(oc, ESOperandsNamespace) + + e2e.Logf("=========Delete the operator CRD=========") + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("crd", "-l", ESOCRDLabel).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + e2e.Logf("=========Checking any of the resource types should be gone=========") + statusErr := wait.PollUntilContextTimeout(context.TODO(), 10*time.Second, 60*time.Second, false, func(ctx context.Context) (bool, error) { + err = oc.AsAdmin().Run("get").Args("secretstore").Execute() + if err != nil { + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollNoErr(statusErr, "timeout waiting for the CRDs deletion to take effect") + + e2e.Logf("=========Delete the admission webhook configurations of the the cert-manager=========") + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("validatingwebhookconfigurations", "-l", ESOperandsLabel).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("mutatingwebhookconfigurations", "-l", ESOperandsLabel).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + e2e.Logf("=========Delete the clusterrolebindings and clusterroles=========") + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("clusterrolebindings", "-l", ESOCRDLabel).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("clusterrole", "-l", ESOCRDLabel).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("clusterrolebindings", "-l", ESOperandsLabel).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("clusterrole", "-l", ESOperandsLabel).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) +} + +// uninstall Via OLMv0 +func uninstallViaOLMv0(oc *exutil.CLI, operatorNamespace, subscriptionName string) { + e2e.Logf("=========Uninstalling via OLMv0 (Subscription)=========") + e2e.Logf("=========Delete the subscription and installed CSV=========") + csvName, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("sub", subscriptionName, "-n", operatorNamespace, "-o=jsonpath={.status.installedCSV}").Output() + o.Expect(err).NotTo(o.HaveOccurred()) + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("sub", subscriptionName, "-n", operatorNamespace).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("csv", csvName, "-n", operatorNamespace).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + waitForPodToBeDeleted(oc, operatorNamespace, ESOperatorLabel, 10*time.Second, 60*time.Second) +} + +// uninstall Via OLMv1 +func uninstallViaOLMv1(oc *exutil.CLI, operatorNamespace, clusterextensionName string) { + e2e.Logf("=========Uninstalling via OLMv1 (ClusterExtension)=========") + e2e.Logf("=========Delete the clusterextension=========") + err := oc.AsAdmin().WithoutNamespace().Run("delete").Args("clusterextension", clusterextensionName).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + + waitForPodToBeDeleted(oc, operatorNamespace, ESOperatorLabel, 10*time.Second, 60*time.Second) +} + +// delete Namespace +func deleteNamespace(oc *exutil.CLI, namespace string) { + e2e.Logf("=========delete namespace %v=========", namespace) + err := oc.AsAdmin().WithoutNamespace().Run("delete").Args("all", "--all", "-n", namespace, "--force", "--grace-period=0", "--wait=false").Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + err = oc.AsAdmin().WithoutNamespace().Run("delete").Args("ns", namespace, "--force", "--grace-period=0", "--wait=false", "-v=6").Execute() + o.Expect(err).NotTo(o.HaveOccurred()) +} + +func waitForPodToBeDeleted(oc *exutil.CLI, namespace, label string, interval, timeout time.Duration) { + err := wait.PollUntilContextTimeout(context.TODO(), interval, timeout, true, func(context.Context) (bool, error) { + output, _ := oc.AsAdmin().WithoutNamespace().Run("get").Args("pod", "-n", namespace, "-l", label).Output() + if strings.Contains(output, "No resources found") { + e2e.Logf("pod with label '%s' deleted", label) + return true, nil + } + return false, nil + }) + exutil.AssertWaitPollNoErr(err, "timeout waiting for pod to be deleted") +} + +// GetAWSSecret retrieves a secret from AWS Secrets Manager +func GetSecretAWS(accessKeyID, secureKey, region, secretName string) (string, error) { + + awsConfig, err := config.LoadDefaultConfig( + context.TODO(), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secureKey, "")), + config.WithRegion(region), + ) + if err != nil { + return "", fmt.Errorf("failed to load AWS config: %v", err) + } + + svc := secretsmanager.NewFromConfig(awsConfig) + result, err := svc.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{ + SecretId: aws.String(secretName), + }) + if err != nil { + return "", fmt.Errorf("failed to get secret: %v", err) + } + + // SecretString + if result.SecretString != nil { + return *result.SecretString, nil + } + + // SecretBinary + if result.SecretBinary != nil { + return string(result.SecretBinary), nil + } + + return "", fmt.Errorf("secret value is nil") +} + +// UpdateSecret updates the value of a secret in AWS Secrets Manager +func UpdateSecretAWS(accessKeyID, secureKey, region, secretName, newSecretValue string) error { + + awsConfig, err := config.LoadDefaultConfig( + context.TODO(), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secureKey, "")), + config.WithRegion(region), + ) + if err != nil { + return fmt.Errorf("failed to load AWS config: %v", err) + } + + svc := secretsmanager.NewFromConfig(awsConfig) + _, err = svc.UpdateSecret(context.TODO(), &secretsmanager.UpdateSecretInput{ + SecretId: aws.String(secretName), + SecretString: aws.String(newSecretValue), + }) + if err != nil { + return fmt.Errorf("failed to update secret: %v", err) + } + e2e.Logf("Secret updated successfully!") + return nil +} + +// GetSecretValueByKeyAWS retrieve a specific secret value from AWS Secrets Manager +func GetSecretValueByKeyAWS(accessKeyID, secureKey, region, secretName, key string) (string, error) { + + secretValue, err := GetSecretAWS(accessKeyID, secureKey, region, secretName) + if err != nil { + return "", err + } + e2e.Logf("Secret Value: %v", secretValue) + + var secretData map[string]string + if err := json.Unmarshal([]byte(secretValue), &secretData); err != nil { + return "", fmt.Errorf("failed to parse secret JSON: %v", err) + } + + // Extract the value of the specified Key + value, exists := secretData[key] + if !exists { + return "", fmt.Errorf("key %v not found in secret", key) + } + + return value, nil +} + +// UpdateSecretValueByKeyAWS update specific fields in AWS Secrets Manager +func UpdateSecretValueByKeyAWS(accessKeyID, secureKey, region, secretName, key, newValue string) error { + + secretValue, err := GetSecretAWS(accessKeyID, secureKey, region, secretName) + if err != nil { + return fmt.Errorf("failed to get secret: %v", err) + } + + var secretData map[string]string + if err := json.Unmarshal([]byte(secretValue), &secretData); err != nil { + return fmt.Errorf("failed to parse secret JSON: %v", err) + } + + secretData[key] = newValue + updatedSecretValue, err := json.Marshal(secretData) + if err != nil { + return fmt.Errorf("failed to encode updated secret JSON: %v", err) + } + + return UpdateSecretAWS(accessKeyID, secureKey, region, secretName, string(updatedSecretValue)) +} + +// CreateSecretAWS creates a new secret in AWS Secrets Manager +func CreateSecretAWS(accessKeyID, secretKey, region, secretName, secretValue string) error { + awsConfig, err := config.LoadDefaultConfig( + context.TODO(), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secretKey, "")), + config.WithRegion(region), + ) + if err != nil { + return fmt.Errorf("failed to load AWS config: %v", err) + } + + svc := secretsmanager.NewFromConfig(awsConfig) + + _, err = svc.CreateSecret(context.TODO(), &secretsmanager.CreateSecretInput{ + Name: aws.String(secretName), + SecretString: aws.String(secretValue), + }) + if err != nil { + return fmt.Errorf("failed to create secret: %v", err) + } + + e2e.Logf("Secret %s created successfully!", secretName) + return nil +} + +// DeleteSecretAWS deletes a secret from AWS Secrets Manager +func DeleteSecretAWS(accessKeyID, secretKey, region, secretName string, forceDelete bool) error { + awsConfig, err := config.LoadDefaultConfig( + context.TODO(), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secretKey, "")), + config.WithRegion(region), + ) + if err != nil { + return fmt.Errorf("failed to load AWS config: %v", err) + } + + svc := secretsmanager.NewFromConfig(awsConfig) + + input := &secretsmanager.DeleteSecretInput{ + SecretId: aws.String(secretName), + } + + // If forceDelete is true, skip recovery window and delete immediately + if forceDelete { + input.ForceDeleteWithoutRecovery = aws.Bool(true) + } else { + // Optional: specify recovery window in days (7 to 30) + input.RecoveryWindowInDays = aws.Int64(7) + } + + _, err = svc.DeleteSecret(context.TODO(), input) + if err != nil { + return fmt.Errorf("failed to delete secret: %v", err) + } + + if forceDelete { + e2e.Logf("Secret %s deleted permanently (no recovery).", secretName) + } else { + e2e.Logf("Secret %s scheduled for deletion (recoverable for 7 days).", secretName) + } + return nil +} + +// initVaultSecret init Vault,enable KV v2 +func initVaultSecret(oc *exutil.CLI, ns, vaultPodName, vaultToken, secretPath string) { + // login to Vault with the VAULT_ROOT_TOKEN + cmd := `vault login ` + vaultToken + oc.NotShowInfo() + _, err := exutil.RemoteShPod(oc, ns, vaultPodName, "sh", "-c", cmd) + oc.SetShowInfo() + o.Expect(err).NotTo(o.HaveOccurred()) + + // enable KV v2 + cmd = `vault secrets enable -path=` + secretPath + ` -version=2 kv` + _, err = exutil.RemoteShPod(oc, ns, vaultPodName, "sh", "-c", cmd) + o.Expect(err).NotTo(o.HaveOccurred()) +} + +// putVaultSecret put Secret and check +func putVaultSecret(oc *exutil.CLI, ns, vaultPodName, secretPath, SecretName, secretKey, secretValue string) { + // put Secret + cmd := fmt.Sprintf(`vault kv put %s/%s %s='%s'`, secretPath, SecretName, secretKey, secretValue) + _, err := exutil.RemoteShPod(oc, ns, vaultPodName, "sh", "-c", cmd) + o.Expect(err).NotTo(o.HaveOccurred()) + + // check Secret + cmd = fmt.Sprintf(`vault kv get -format=json %s/%s`, secretPath, SecretName) + _, err = exutil.RemoteShPod(oc, ns, vaultPodName, "sh", "-c", cmd) + o.Expect(err).NotTo(o.HaveOccurred()) +} + +// getSecretValue fetches the base64-encoded value of a specific key in a Secret +func getSecretValue(oc *exutil.CLI, ns, secretName, secretKey string) (string, error) { + data, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "secret", secretName, "-o=jsonpath={.data."+secretKey+"}").Output() + if err != nil { + return "", fmt.Errorf("error fetching secret %s: %w", secretName, err) + } + return data, nil +} + +// getSecretValueDecoded fetches and decodes the base64-encoded value of a Secret key +func getSecretValueDecoded(oc *exutil.CLI, ns, secretName, secretKey string) ([]byte, error) { + encoded, err := getSecretValue(oc, ns, secretName, secretKey) + if err != nil { + return nil, err + } + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return nil, fmt.Errorf("error decoding value of secret %s: %w", secretName, err) + } + return decoded, nil +} + +// waitForSecretUpdate wait for the specified key of the specified Secret to be updated to the expected value +func waitForSecretUpdate(oc *exutil.CLI, ns, secretName, secretKey, expectedValue string) { + errWait := wait.PollUntilContextTimeout(context.TODO(), 5*time.Second, 30*time.Second, false, func(ctx context.Context) (bool, error) { + currentValEncoded, err := getSecretValue(oc, ns, secretName, secretKey) + if err != nil { + e2e.Logf("Error fetching secret: %v", err) + return false, nil + } + currentVal, err := base64.StdEncoding.DecodeString(currentValEncoded) + if err != nil { + e2e.Logf("Error decoding secret: %v", err) + return false, nil + } + if !strings.Contains(string(currentVal), expectedValue) { + e2e.Logf("Secret %s not updated yet. Old value: %s, New value: %s", secretName, expectedValue, string(currentVal)) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(errWait, fmt.Sprintf("Error: secret %s not updated", secretName)) +} + +func waitForExternalSecretStatus(oc *exutil.CLI, ns, name, expectedReason string, timeout time.Duration) { + err := wait.PollUntilContextTimeout(context.TODO(), 3*time.Second, timeout, false, func(ctx context.Context) (bool, error) { + status, err := oc.WithoutNamespace().AsAdmin().Run("get").Args("-n", ns, "es", name, `-o=jsonpath={.status.conditions[?(@.type=="Ready")].reason}`).Output() + if status != expectedReason || err != nil { + e2e.Logf("status: %v, expected: %s", status, expectedReason) + return false, nil + } + return true, nil + }) + exutil.AssertWaitPollNoErr(err, fmt.Sprintf("status expected: %s", expectedReason)) +} + +// UpdateParameterAWS updates the value of a parameter in AWS SSM Parameter Store +func UpdateParameterAWS(accessKeyID, secureKey, region, parameterName, newValue string) error { + awsConfig, err := config.LoadDefaultConfig( + context.TODO(), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secureKey, "")), + config.WithRegion(region), + ) + if err != nil { + return fmt.Errorf("failed to load AWS config: %v", err) + } + + ssmClient := ssm.NewFromConfig(awsConfig) + _, err = ssmClient.PutParameter(context.TODO(), &ssm.PutParameterInput{ + Name: aws.String(parameterName), + Value: aws.String(newValue), + Overwrite: aws.Bool(true), + }) + if err != nil { + return fmt.Errorf("failed to update parameter: %v", err) + } + + return nil +} + +// GetSecretAWSPS retrieves the value of a parameter from AWS SSM Parameter Store +func GetSecretAWSPS(accessKeyID, secureKey, region, parameterName string) (string, error) { + awsConfig, err := config.LoadDefaultConfig( + context.TODO(), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secureKey, "")), + config.WithRegion(region), + ) + if err != nil { + return "", fmt.Errorf("failed to load AWS config: %v", err) + } + + ssmClient := ssm.NewFromConfig(awsConfig) + output, err := ssmClient.GetParameter(context.TODO(), &ssm.GetParameterInput{ + Name: aws.String(parameterName), + }) + if err != nil { + return "", fmt.Errorf("failed to get parameter: %v", err) + } + if output.Parameter == nil || output.Parameter.Value == nil { + return "", fmt.Errorf("parameter %s not found or value is nil", parameterName) + } + + return *output.Parameter.Value, nil +} diff --git a/ginkgo-test/testdata/eso/clustergenerator-password.yaml b/ginkgo-test/testdata/eso/clustergenerator-password.yaml new file mode 100644 index 00000000..02bfb367 --- /dev/null +++ b/ginkgo-test/testdata/eso/clustergenerator-password.yaml @@ -0,0 +1,21 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: generator-template +objects: + - apiVersion: generators.external-secrets.io/v1alpha1 + kind: ClusterGenerator + metadata: + name: "${NAME}" + spec: + kind: Password + generator: + passwordSpec: + length: 16 + digits: 5 + symbols: 5 + symbolCharacters: "-_$@" + noUpper: false + allowRepeat: true +parameters: + - name: NAME diff --git a/ginkgo-test/testdata/eso/externalsecret-awsps.yaml b/ginkgo-test/testdata/eso/externalsecret-awsps.yaml new file mode 100644 index 00000000..e1835c90 --- /dev/null +++ b/ginkgo-test/testdata/eso/externalsecret-awsps.yaml @@ -0,0 +1,37 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + secretStoreRef: + name: "${SECRETSTORENAME}" + kind: SecretStore + target: + name: "${SECRETNAME}" + creationPolicy: "${CREATIONPOLICY}" + data: + - secretKey: "${SECRETKEY}" + remoteRef: + key: "${KEY}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETSTORENAME + - name: SECRETNAME + value: "secret-from-awssm" + - name: CREATIONPOLICY + value: "Owner" + - name: SECRETKEY + value: "secret-value-from-awssm" + - name: KEY + value: "esoSecret" + + + diff --git a/ginkgo-test/testdata/eso/externalsecret-awssm.yaml b/ginkgo-test/testdata/eso/externalsecret-awssm.yaml new file mode 100644 index 00000000..93c95c43 --- /dev/null +++ b/ginkgo-test/testdata/eso/externalsecret-awssm.yaml @@ -0,0 +1,45 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + secretStoreRef: + name: "${SECRETSTORENAME}" + kind: SecretStore + target: + name: "${SECRETNAME}" + creationPolicy: "${CREATIONPOLICY}" + deletionPolicy: "${DELPOLICY}" + data: + - secretKey: "${SECRETKEY}" + remoteRef: + key: "${KEY}" + property: "${PROPERTY}" + dataFrom: + - extract: + key: "${KEY}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETSTORENAME + - name: SECRETNAME + value: "secret-from-awssm" + - name: CREATIONPOLICY + value: "Owner" + - name: DELPOLICY + value: "Retain" + - name: SECRETKEY + value: "secret-value-from-awssm" + - name: KEY + value: "jitliSecret" + - name: PROPERTY + + + diff --git a/ginkgo-test/testdata/eso/externalsecret-gcpsm-version.yaml b/ginkgo-test/testdata/eso/externalsecret-gcpsm-version.yaml new file mode 100644 index 00000000..0450e978 --- /dev/null +++ b/ginkgo-test/testdata/eso/externalsecret-gcpsm-version.yaml @@ -0,0 +1,44 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + secretStoreRef: + name: "${SECRETSTORENAME}" + kind: SecretStore + target: + name: "${SECRETNAME}" + creationPolicy: "${CREATIONPOLICY}" + deletionPolicy: "${DELETEPOLICY}" + data: + - secretKey: "${SECRETKEY}" + remoteRef: + key: "${KEY}" + version: "${VERSION}" + dataFrom: + - extract: + key: "${FROMKEY}" + version: "${FROMVERSION}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETSTORENAME + - name: SECRETNAME + value: "secret-from-awssm" + - name: CREATIONPOLICY + value: "Owner" + - name: DELETEPOLICY + value: "Retain" + - name: SECRETKEY + value: "secret-value-from-awssm" + - name: KEY + - name: VERSION + - name: FROMKEY + - name: FROMVERSION diff --git a/ginkgo-test/testdata/eso/externalsecret-gcpsm.yaml b/ginkgo-test/testdata/eso/externalsecret-gcpsm.yaml new file mode 100644 index 00000000..d1fcec36 --- /dev/null +++ b/ginkgo-test/testdata/eso/externalsecret-gcpsm.yaml @@ -0,0 +1,40 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + secretStoreRef: + name: "${SECRETSTORENAME}" + kind: SecretStore + target: + name: "${SECRETNAME}" + creationPolicy: "${CREATIONPOLICY}" + deletionPolicy: "${DELETEPOLICY}" + data: + - secretKey: "${SECRETKEY}" + remoteRef: + key: "${KEY}" + dataFrom: + - extract: + key: "${FROMKEY}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETSTORENAME + - name: SECRETNAME + value: "secret-from-awssm" + - name: CREATIONPOLICY + value: "Owner" + - name: DELETEPOLICY + value: "Retain" + - name: SECRETKEY + value: "secret-value-from-awssm" + - name: KEY + - name: FROMKEY diff --git a/ginkgo-test/testdata/eso/externalsecret-generator.yaml b/ginkgo-test/testdata/eso/externalsecret-generator.yaml new file mode 100644 index 00000000..4a6cf788 --- /dev/null +++ b/ginkgo-test/testdata/eso/externalsecret-generator.yaml @@ -0,0 +1,30 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + target: + name: "${SECRETNAME}" + creationPolicy: "${CREATIONPOLICY}" + dataFrom: + - sourceRef: + generatorRef: + apiVersion: generators.external-secrets.io/v1alpha1 + kind: "${GENERATORKIND}" + name: "${GENERATOR}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETNAME + value: "secret-from-generator" + - name: CREATIONPOLICY + value: "Owner" + - name: GENERATORKIND + - name: GENERATOR diff --git a/ginkgo-test/testdata/eso/externalsecret-vault.yaml b/ginkgo-test/testdata/eso/externalsecret-vault.yaml new file mode 100644 index 00000000..6aa96678 --- /dev/null +++ b/ginkgo-test/testdata/eso/externalsecret-vault.yaml @@ -0,0 +1,35 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + secretStoreRef: + name: "${SECRETSTORENAME}" + kind: SecretStore + target: + name: "${SECRETNAME}" + creationPolicy: "${CREATIONPOLICY}" + data: + - secretKey: "${SECRETKEY}" + remoteRef: + key: "${KEY}" + property: "${PROPERTY}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETSTORENAME + - name: SECRETNAME + value: "secret-from-vault" + - name: CREATIONPOLICY + value: "Owner" + - name: SECRETKEY + value: "secret-value-from-vault" + - name: KEY + - name: PROPERTY diff --git a/ginkgo-test/testdata/eso/generator-password.yaml b/ginkgo-test/testdata/eso/generator-password.yaml new file mode 100644 index 00000000..f92c624b --- /dev/null +++ b/ginkgo-test/testdata/eso/generator-password.yaml @@ -0,0 +1,18 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: generator-template +objects: + - apiVersion: generators.external-secrets.io/v1alpha1 + kind: Password + metadata: + name: "${NAME}" + spec: + length: 16 + digits: 5 + symbols: 5 + symbolCharacters: "-_$@" + noUpper: false + allowRepeat: true +parameters: + - name: NAME diff --git a/ginkgo-test/testdata/eso/helm-vault-config.yaml b/ginkgo-test/testdata/eso/helm-vault-config.yaml new file mode 100644 index 00000000..6b074f60 --- /dev/null +++ b/ginkgo-test/testdata/eso/helm-vault-config.yaml @@ -0,0 +1,30 @@ +# xref: https://github.com/hashicorp/vault-helm/blob/main/values.yaml All available parameters and default values for the Vault chart. +# Set 'server.dataStorage.size' to 1Gi as the default 10Gi is too expensive and unnecessary for testing propose in CI. +global: + enabled: true + tlsDisable: true + openshift: true +injector: + enabled: false +server: + ui: + enabled: true + image: + repository: "quay.io/openshifttest/vault" + tag: "1.19.0" + dataStorage: + enabled: true + size: 1Gi + extraEnvironmentVars: {} + standalone: + enabled: true + config: | + listener "tcp" { + tls_disable = 1 + address = "[::]:8200" + cluster_address = "[::]:8201" + } + storage "file" { + path = "/vault/data" + } + ui = true diff --git a/ginkgo-test/testdata/eso/namespace.yaml b/ginkgo-test/testdata/eso/namespace.yaml new file mode 100644 index 00000000..05a6acc5 --- /dev/null +++ b/ginkgo-test/testdata/eso/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: external-secrets-operator diff --git a/ginkgo-test/testdata/eso/operandConfig.yaml b/ginkgo-test/testdata/eso/operandConfig.yaml new file mode 100644 index 00000000..4e528230 --- /dev/null +++ b/ginkgo-test/testdata/eso/operandConfig.yaml @@ -0,0 +1,8 @@ +apiVersion: operator.openshift.io/v1alpha1 +kind: ExternalSecrets +metadata: + labels: + app.kubernetes.io/name: external-secrets-operator + app.kubernetes.io/managed-by: kustomize + name: cluster +spec: {} diff --git a/ginkgo-test/testdata/eso/operatorgroup.yaml b/ginkgo-test/testdata/eso/operatorgroup.yaml new file mode 100644 index 00000000..93c27aee --- /dev/null +++ b/ginkgo-test/testdata/eso/operatorgroup.yaml @@ -0,0 +1,5 @@ +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: external-secrets-og + namespace: external-secrets-operator diff --git a/ginkgo-test/testdata/eso/pushsecret-aws-secretkey.yaml b/ginkgo-test/testdata/eso/pushsecret-aws-secretkey.yaml new file mode 100644 index 00000000..1b776f56 --- /dev/null +++ b/ginkgo-test/testdata/eso/pushsecret-aws-secretkey.yaml @@ -0,0 +1,31 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1alpha1 + kind: PushSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + secretStoreRefs: + - name: "${SECRETSTORENAME}" + kind: SecretStore + selector: + secret: + name: "${SECRETNAME}" + data: + - match: + secretKey: "${SECRETKEY}" + remoteRef: + remoteKey: "${KEY}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETSTORENAME + - name: SECRETNAME + value: "secret-push-parameter-store" + - name: SECRETKEY + - name: KEY diff --git a/ginkgo-test/testdata/eso/pushsecret-aws.yaml b/ginkgo-test/testdata/eso/pushsecret-aws.yaml new file mode 100644 index 00000000..f16991c3 --- /dev/null +++ b/ginkgo-test/testdata/eso/pushsecret-aws.yaml @@ -0,0 +1,29 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: externalsecret-template +objects: + - apiVersion: external-secrets.io/v1alpha1 + kind: PushSecret + metadata: + name: "${NAME}" + spec: + refreshInterval: "${REFREASHINTERVAL}" + secretStoreRefs: + - name: "${SECRETSTORENAME}" + kind: SecretStore + selector: + secret: + name: "${SECRETNAME}" + data: + - match: + remoteRef: + remoteKey: "${KEY}" +parameters: + - name: NAME + - name: REFREASHINTERVAL + value: "1m" + - name: SECRETSTORENAME + - name: SECRETNAME + value: "secret-push-parameter-store" + - name: KEY diff --git a/ginkgo-test/testdata/eso/secretstore-awssm.yaml b/ginkgo-test/testdata/eso/secretstore-awssm.yaml new file mode 100644 index 00000000..4df2daf8 --- /dev/null +++ b/ginkgo-test/testdata/eso/secretstore-awssm.yaml @@ -0,0 +1,32 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: secretstore-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: SecretStore + metadata: + name: "${NAME}" + spec: + provider: + aws: + service: "${SERVICE}" + region: "${REGION}" + auth: + secretRef: + accessKeyIDSecretRef: + name: "${SECRETNAME}" + key: "${ACCESSKEY}" + secretAccessKeySecretRef: + name: "${SECRETNAME}" + key: "${SECRETACCESSKEY}" +parameters: + - name: NAME + - name: SERVICE + value: "SecretsManager" + - name: REGION + - name: SECRETNAME + - name: ACCESSKEY + value: "access-key" + - name: SECRETACCESSKEY + value: "secret-access-key" diff --git a/ginkgo-test/testdata/eso/secretstore-gcpsm.yaml b/ginkgo-test/testdata/eso/secretstore-gcpsm.yaml new file mode 100644 index 00000000..eaf130a5 --- /dev/null +++ b/ginkgo-test/testdata/eso/secretstore-gcpsm.yaml @@ -0,0 +1,26 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: secretstore-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: SecretStore + metadata: + name: "${NAME}" + spec: + provider: + gcpsm: + projectID: "${PROJECTID}" + auth: + secretRef: + secretAccessKeySecretRef: + name: "${SECRETNAME}" + key: "${SECRETACCESSKEY}" +parameters: + - name: NAME + - name: PROJECTID + value: "openshift-qe" + - name: SECRETNAME + value: "google-eso-sa-key" + - name: SECRETACCESSKEY + value: "key.json" diff --git a/ginkgo-test/testdata/eso/secretstore-vault.yaml b/ginkgo-test/testdata/eso/secretstore-vault.yaml new file mode 100644 index 00000000..0dcf55df --- /dev/null +++ b/ginkgo-test/testdata/eso/secretstore-vault.yaml @@ -0,0 +1,30 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: secretstore-template +objects: + - apiVersion: external-secrets.io/v1beta1 + kind: SecretStore + metadata: + name: "${NAME}" + spec: + provider: + vault: + server: "${SERVER}" + path: "${PATH}" + version: "${VERSION}" + auth: + tokenSecretRef: + name: "${SECRETNAME}" + key: "${ACCESSKEY}" + +parameters: + - name: NAME + - name: SERVER + - name: PATH + - name: VERSION + value: "v2" + - name: SECRETNAME + value: "vault-token" + - name: ACCESSKEY + value: "token" diff --git a/ginkgo-test/testdata/eso/subscription.yaml b/ginkgo-test/testdata/eso/subscription.yaml new file mode 100644 index 00000000..e03040ac --- /dev/null +++ b/ginkgo-test/testdata/eso/subscription.yaml @@ -0,0 +1,20 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: subscription-template +objects: + - apiVersion: operators.coreos.com/v1alpha1 + kind: Subscription + metadata: + name: "${NAME}" + spec: + channel: "${CHANNEL}" + installPlanApproval: Automatic + name: "${NAME}" + source: "${SOURCE}" + sourceNamespace: "${SOURCE_NAMESPACE}" +parameters: + - name: NAME + - name: SOURCE + - name: SOURCE_NAMESPACE + - name: CHANNEL