diff --git a/pkg/tests/non-dependant/bug_multiple_smcp_test.go b/pkg/tests/non-dependant/bug_multiple_smcp_test.go index 9c2c4a97..ea98e19e 100644 --- a/pkg/tests/non-dependant/bug_multiple_smcp_test.go +++ b/pkg/tests/non-dependant/bug_multiple_smcp_test.go @@ -2,6 +2,7 @@ package non_dependant import ( _ "embed" + "fmt" "testing" "github.com/maistra/maistra-test-tool/pkg/tests/ossm" @@ -10,7 +11,9 @@ import ( "github.com/maistra/maistra-test-tool/pkg/util/oc" "github.com/maistra/maistra-test-tool/pkg/util/pod" "github.com/maistra/maistra-test-tool/pkg/util/retry" + "github.com/maistra/maistra-test-tool/pkg/util/shell" . "github.com/maistra/maistra-test-tool/pkg/util/test" + "github.com/maistra/maistra-test-tool/pkg/util/version" ) func TestSMCPMultiple(t *testing.T) { @@ -18,49 +21,70 @@ func TestSMCPMultiple(t *testing.T) { t.Log("This test verifies whether the operator only reconciles one SMCP when two exist in a namespace") t.Log("See https://issues.redhat.com/browse/OSSM-2419") - smcp1 := ossm.DefaultSMCP().WithName("smcp1") - smcp2 := ossm.DefaultSMCP().WithName("smcp2") - t.Cleanup(func() { t.LogStepf("Delete namespace %s", meshNamespace) oc.RecreateNamespace(t, meshNamespace) - t.LogStep("Delete operator to recreate the ValidationWebhookConfiguration") - oc.DeletePod(t, pod.MatchingSelector("name=istio-operator", env.GetOperatorNamespace())) - - t.LogStep("Wait for operator pod to be ready") - oc.WaitPodReady(t, pod.MatchingSelector("name=istio-operator", env.GetOperatorNamespace())) - - t.LogStep("Check whether ValidatingWebhookConfiguration exists") - retry.UntilSuccess(t, func(t TestHelper) { - oc.Get(t, "", "validatingwebhookconfiguration", "openshift-operators.servicemesh-resources.maistra.io") - t.LogSuccess("ValidatingWebhookConfiguration was recreated by the operator") - }) + if env.GetOperatorVersion().LessThan(version.OPERATOR_2_6_0) { + t.LogStep("Delete operator to recreate the ValidationWebhookConfiguration") + oc.DeletePod(t, pod.MatchingSelector("name=istio-operator", env.GetOperatorNamespace())) + t.LogStep("Wait for operator pod to be ready") + oc.WaitPodReady(t, pod.MatchingSelector("name=istio-operator", env.GetOperatorNamespace())) + t.LogStep("Check whether ValidatingWebhookConfiguration exists") + retry.UntilSuccess(t, func(t TestHelper) { + oc.Get(t, "", "validatingwebhookconfiguration", "openshift-operators.servicemesh-resources.maistra.io") + t.LogSuccess("ValidatingWebhookConfiguration was recreated by the operator") + }) + } }) t.LogStepf("Delete and recreate namespace %s", meshNamespace) oc.RecreateNamespace(t, meshNamespace) - t.LogStep("Delete the operator's ValidationWebhookConfiguration") - oc.DeleteResource(t, "", "validatingwebhookconfiguration", "openshift-operators.servicemesh-resources.maistra.io") - t.LogStep("Create the first SMCP") + smcp1 := ossm.DefaultSMCP().WithName("smcp1") ossm.InstallSMCPCustom(t, meshNamespace, smcp1) t.LogStep("Check whether the first SMCP gets reconciled and becomes ready") oc.WaitSMCPReady(t, meshNamespace, smcp1.Name) t.LogSuccess("First SMCP is ready") - t.LogStep("Create the second SMCP") - ossm.InstallSMCPCustom(t, meshNamespace, smcp2) + if env.GetOperatorVersion().GreaterThanOrEqual(version.OPERATOR_2_6_0) { + t.LogStep("Check that the validationwebhook prevent of creating the second SMCP") + shell.Execute(t, + fmt.Sprintf("echo \"%s\" | oc create -f - -n %s || true", simpleSmcp(), meshNamespace), + assert.OutputContains("admission webhook \"smcp.validation.maistra.io\" denied the request: only one service mesh may be installed per project/namespace", + "Validationwebhook prevents of creating the second SMCP", + "Expect that creation of second smcp fails on validationwebhook")) + } else { + t.LogStep("Delete the operator's ValidationWebhookConfiguration to be able to install the second SMCP") + oc.DeleteResource(t, "", "validatingwebhookconfiguration", "openshift-operators.servicemesh-resources.maistra.io") + t.LogStep("Create the second SMCP") + smcp2 := ossm.DefaultSMCP().WithName("smcp2") + ossm.InstallSMCPCustom(t, meshNamespace, smcp2) + ossm.InstallSMCPCustom(t, meshNamespace, smcp2) - t.LogStep("Check whether the second SMCP shows ErrMultipleSMCPs") - retry.UntilSuccess(t, func(t TestHelper) { - oc.Get(t, meshNamespace, - "smcp", smcp2.Name, - assert.OutputContains("ErrMultipleSMCPs", - "The second SMCP status is ErrMultipleSMCPs", - "The second SMCP status is not ErrMultipleSMCPs")) - }) + ossm.InstallSMCPCustom(t, meshNamespace, smcp2) + + t.LogStep("Check whether the second SMCP shows ErrMultipleSMCPs") + retry.UntilSuccess(t, func(t TestHelper) { + oc.Get(t, meshNamespace, + "smcp", smcp2.Name, + assert.OutputContains("ErrMultipleSMCPs", + "The second SMCP status is ErrMultipleSMCPs", + "The second SMCP status is not ErrMultipleSMCPs")) + }) + } }) } + +func simpleSmcp() string { + return fmt.Sprintf(` +kind: ServiceMeshControlPlane +apiVersion: maistra.io/v2 +metadata: + name: basic +spec: + version: %s +`, env.GetSMCPVersion()) +} diff --git a/pkg/tests/non-dependant/olm_webhooks_test.go b/pkg/tests/non-dependant/olm_webhooks_test.go new file mode 100644 index 00000000..95b2e0a3 --- /dev/null +++ b/pkg/tests/non-dependant/olm_webhooks_test.go @@ -0,0 +1,116 @@ +package non_dependant + +import ( + _ "embed" + "testing" + "time" + + "github.com/maistra/maistra-test-tool/pkg/tests/ossm" + "github.com/maistra/maistra-test-tool/pkg/util/env" + "github.com/maistra/maistra-test-tool/pkg/util/oc" + "github.com/maistra/maistra-test-tool/pkg/util/retry" + . "github.com/maistra/maistra-test-tool/pkg/util/test" + "github.com/maistra/maistra-test-tool/pkg/util/version" +) + +func TestOlmWebhookCreation(t *testing.T) { + NewTest(t).Groups(Full, ARM).Run(func(t TestHelper) { + t.Log("This test verifies that OLM creates all validating/mutating webhooks") + t.Log("See https://issues.redhat.com/browse/OSSM-6762") + if env.GetOperatorVersion().LessThan(version.OPERATOR_2_6_0) { + t.Skip("Skipping until 2.6 operator") + } + + t.Cleanup(func() { + t.LogStepf("Delete namespace %s", meshNamespace) + oc.RecreateNamespace(t, meshNamespace) + }) + + t.LogStepf("Delete and recreate namespace %s", meshNamespace) + oc.RecreateNamespace(t, meshNamespace) + + t.NewSubTest("Check global webhooks").Run(func(t TestHelper) { + t.Log("Check that global validatingwebhookconfiguration's were created by OLM") + checkGlobalWebhooks(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smcp.validation.maistra.io") + checkGlobalWebhooks(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smmr.validation.maistra.io") + checkGlobalWebhooks(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smm.validation.maistra.io") + + t.Log("Check that global validatingwebhookconfiguration's were recreated by OLM after deletion") + deleteGlobalWebhook(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smcp.validation.maistra.io") + deleteGlobalWebhook(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smmr.validation.maistra.io") + deleteGlobalWebhook(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smm.validation.maistra.io") + retry.UntilSuccessWithOptions(t, retry.Options().MaxAttempts(30).DelayBetweenAttempts(5*time.Second), func(t TestHelper) { + checkGlobalWebhooks(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smcp.validation.maistra.io") + checkGlobalWebhooks(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smmr.validation.maistra.io") + checkGlobalWebhooks(t, "validatingwebhookconfiguration", "olm.webhook-description-generate-name=smm.validation.maistra.io") + }) + + t.Log("Check that global mutatingwebhookconfiguration's were created by OLM") + checkGlobalWebhooks(t, "mutatingwebhookconfiguration", "olm.webhook-description-generate-name=smcp.mutation.maistra.io") + checkGlobalWebhooks(t, "mutatingwebhookconfiguration", "olm.webhook-description-generate-name=smmr.mutation.maistra.io") + + t.Log("Check that global mutatingwebhookconfiguration's were recreated by OLM after deletion") + deleteGlobalWebhook(t, "mutatingwebhookconfiguration", "olm.webhook-description-generate-name=smcp.mutation.maistra.io") + deleteGlobalWebhook(t, "mutatingwebhookconfiguration", "olm.webhook-description-generate-name=smmr.mutation.maistra.io") + retry.UntilSuccessWithOptions(t, retry.Options().MaxAttempts(30).DelayBetweenAttempts(5*time.Second), func(t TestHelper) { + checkGlobalWebhooks(t, "mutatingwebhookconfiguration", "olm.webhook-description-generate-name=smcp.mutation.maistra.io") + checkGlobalWebhooks(t, "mutatingwebhookconfiguration", "olm.webhook-description-generate-name=smmr.mutation.maistra.io") + }) + }) + + t.NewSubTest("Check smcp related webhooks").Run(func(t TestHelper) { + t.Log("Check that smcp related webhooks doesn't exist") + checkSmcpWebhooksDoesNotExist(t, "validatingwebhookconfiguration", "maistra.io/owner-name="+env.GetDefaultSMCPName()) + checkSmcpWebhooksDoesNotExist(t, "mutatingwebhookconfiguration", "maistra.io/owner-name="+env.GetDefaultSMCPName()) + + t.LogStep("Create the SMCP") + ossm.DeployControlPlane(t) + + t.Log("Check that smcp related webhooks were created by OLM") + checkSmcpWebhooksExist(t, "validatingwebhookconfiguration", "maistra.io/owner-name="+env.GetDefaultSMCPName()) + checkSmcpWebhooksExist(t, "mutatingwebhookconfiguration", "maistra.io/owner-name="+env.GetDefaultSMCPName()) + + t.LogStep("Delete the SMCP") + oc.RecreateNamespace(t, meshNamespace) + + t.Log("Check that smcp related webhooks were created by OLM") + checkSmcpWebhooksDoesNotExist(t, "validatingwebhookconfiguration", "maistra.io/owner-name="+env.GetDefaultSMCPName()) + checkSmcpWebhooksDoesNotExist(t, "mutatingwebhookconfiguration", "maistra.io/owner-name="+env.GetDefaultSMCPName()) + }) + }) +} + +func checkGlobalWebhooks(t TestHelper, kind string, label string) { + if oc.ResourceByLabelExists(t, "", kind, label) { + t.LogSuccessf("Got the expected %s with label %s", kind, label) + } else { + t.Fatalf("Expect to find %s with label %s created automatically by OLM", kind, label) + } +} + +func checkSmcpWebhooksExist(t TestHelper, kind string, label string) { + retry.UntilSuccess(t, func(t TestHelper) { + t.Logf("Check that smcp %s was created by OLM", kind) + if oc.ResourceByLabelExists(t, "", kind, label) { + t.LogSuccessf("Got the expected %s with label %s", kind, label) + } else { + t.Fatalf("Expect to find %s with label %s for smcp", kind, label) + } + }) +} + +func checkSmcpWebhooksDoesNotExist(t TestHelper, kind string, label string) { + retry.UntilSuccess(t, func(t TestHelper) { + t.Logf("Check that smcp %s was deleted by OLM", kind) + if oc.ResourceByLabelExists(t, "", kind, label) { + t.Fatalf("Expect to not find %s with label %s for smcp but it was found", kind, label) + } else { + t.LogSuccessf("Expect to not find %s with label %s for smcp", kind, label) + } + }) +} + +func deleteGlobalWebhook(t TestHelper, kind string, label string) { + name := oc.GetResouceNameByLabel(t, "", kind, label) + oc.DeleteResource(t, "", kind, name) +} diff --git a/pkg/tests/ossm/smcp_must_gather_test.go b/pkg/tests/ossm/smcp_must_gather_test.go index 9cfd6417..377972df 100644 --- a/pkg/tests/ossm/smcp_must_gather_test.go +++ b/pkg/tests/ossm/smcp_must_gather_test.go @@ -28,6 +28,7 @@ import ( "github.com/maistra/maistra-test-tool/pkg/util/ns" "github.com/maistra/maistra-test-tool/pkg/util/oc" "github.com/maistra/maistra-test-tool/pkg/util/shell" + "github.com/maistra/maistra-test-tool/pkg/util/version" . "github.com/maistra/maistra-test-tool/pkg/util/test" ) @@ -109,11 +110,29 @@ func TestMustGather(t *testing.T) { } t.LogStep("Verify cluster-scoped-resources files exist in cluster-scoped-resources folder") - assertFilesExist(t, - dir, - "**/cluster-scoped-resources/rbac.authorization.k8s.io/clusterrolebindings/istiod-internal-basic-istio-system.yaml", - "**/cluster-scoped-resources/admissionregistration.k8s.io/mutatingwebhookconfigurations/openshift-operators.servicemesh-resources.maistra.io.yaml", - "**/cluster-scoped-resources/rbac.authorization.k8s.io/clusterroles/istiod-clusterrole-basic-istio-system.yaml") + if env.GetOperatorVersion().LessThan(version.OPERATOR_2_6_0) { + assertFilesExist(t, + dir, + "**/cluster-scoped-resources/rbac.authorization.k8s.io/clusterrolebindings/istiod-internal-basic-istio-system.yaml", + "**/cluster-scoped-resources/admissionregistration.k8s.io/mutatingwebhookconfigurations/openshift-operators.servicemesh-resources.maistra.io.yaml", + "**/cluster-scoped-resources/admissionregistration.k8s.io/mutatingwebhookconfigurations/istiod-basic-istio-system.yaml", + "**/cluster-scoped-resources/admissionregistration.k8s.io/validatingwebhookconfigurations/openshift-operators.servicemesh-resources.maistra.io.yaml", + "**/cluster-scoped-resources/admissionregistration.k8s.io/validatingwebhookconfigurations/istio-validator-basic-istio-system.yaml", + "**/cluster-scoped-resources/rbac.authorization.k8s.io/clusterroles/istiod-clusterrole-basic-istio-system.yaml") + } else { + assertFilesExist(t, + dir, + "**/cluster-scoped-resources/rbac.authorization.k8s.io/clusterrolebindings/istiod-internal-basic-istio-system.yaml", + //TODO uncomment when we resolve whether the olm created resources must be in must-gather imaga + // "**/cluster-scoped-resources/admissionregistration.k8s.io/mutatingwebhookconfigurations/smcp.validation.maistra.io-*.yaml", + // "**/cluster-scoped-resources/admissionregistration.k8s.io/mutatingwebhookconfigurations/smmr.validation.maistra.io-*.yaml", + "**/cluster-scoped-resources/admissionregistration.k8s.io/mutatingwebhookconfigurations/istiod-basic-istio-system.yaml", + // "**/cluster-scoped-resources/admissionregistration.k8s.io/validatingwebhookconfigurations/smcp.validation.maistra.io-*.yaml", + // "**/cluster-scoped-resources/admissionregistration.k8s.io/validatingwebhookconfigurations/smmr.validation.maistra.io-*.yaml", + // "**/cluster-scoped-resources/admissionregistration.k8s.io/validatingwebhookconfigurations/smm.validation.maistra.io-*.yaml", + "**/cluster-scoped-resources/admissionregistration.k8s.io/validatingwebhookconfigurations/istio-validator-basic-istio-system.yaml", + "**/cluster-scoped-resources/rbac.authorization.k8s.io/clusterroles/istiod-clusterrole-basic-istio-system.yaml") + } }) t.NewSubTest("resource for namespaces exist").Run(func(t TestHelper) { diff --git a/pkg/util/oc/oc.go b/pkg/util/oc/oc.go index 4eb4b47c..80075c30 100644 --- a/pkg/util/oc/oc.go +++ b/pkg/util/oc/oc.go @@ -85,6 +85,16 @@ func DeleteResource(t test.TestHelper, ns string, kind string, name ...string) { DefaultOC.DeleteResource(t, ns, kind, name...) } +func GetResouceNameByLabel(t test.TestHelper, ns string, kind string, label string) string { + t.T().Helper() + return DefaultOC.GetResouceNameByLabel(t, ns, kind, label) +} + +func ResourceByLabelExists(t test.TestHelper, ns string, kind string, label string) bool { + t.T().Helper() + return DefaultOC.ResourceByLabelExists(t, ns, kind, label) +} + func DeleteNamespace(t test.TestHelper, namespaces ...string) { t.T().Helper() DefaultOC.DeleteNamespace(t, namespaces...) diff --git a/pkg/util/oc/oc_struct.go b/pkg/util/oc/oc_struct.go index fb532f92..dcb8e5ab 100644 --- a/pkg/util/oc/oc_struct.go +++ b/pkg/util/oc/oc_struct.go @@ -485,6 +485,31 @@ func (o OC) ResourceExists(t test.TestHelper, ns, kind, name string) bool { return exists } +func (o OC) GetResouceNameByLabel(t test.TestHelper, ns, kind, label string) string { + t.T().Helper() + var value string + o.withKubeconfig(t, func() { + t.T().Helper() + value = shell.Execute(t, fmt.Sprintf("oc %s get %s -l %s -o custom-columns=NAME:.metadata.name --no-headers || true", nsFlag(ns), kind, label)) + value = strings.TrimSpace(value) + if value == "" { + t.Fatalf("Could not find resource %s with label %s in namespace %s", kind, label, ns) + } + }) + return value +} + +func (o OC) ResourceByLabelExists(t test.TestHelper, ns, kind, label string) bool { + t.T().Helper() + var exists bool + o.withKubeconfig(t, func() { + t.T().Helper() + output := shell.Execute(t, fmt.Sprintf("oc %s get %s -l %s || true", nsFlag(ns), kind, label)) + exists = !(strings.Contains(output, "Error from server (NotFound)") || strings.Contains(output, "No resources found")) + }) + return exists +} + func setEnv(t test.TestHelper, key string, value string) { if err := os.Setenv(key, value); err != nil { t.Fatalf("could not set %s: %v", key, err)