From e86d5205304db05441da7931a15393f9c5ccf6e1 Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Thu, 21 May 2026 16:49:19 +0200 Subject: [PATCH 1/2] Adding missing permissions to add ownerref on apikeyapproval Signed-off-by: Eguzki Astiz Lezaun --- config/rbac/role.yaml | 16 +- .../controller/apikeyapproval_controller.go | 2 +- test/e2e/apikey_approval_gc_test.go | 374 ++++++++++++++++++ 3 files changed, 378 insertions(+), 14 deletions(-) create mode 100644 test/e2e/apikey_approval_gc_test.go diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 5055197..87cfec1 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -18,8 +18,11 @@ rules: - devportal.kuadrant.io resources: - apikeyapprovals + - apikeyrequests + - apiproducts verbs: - create + - delete - get - list - patch @@ -36,19 +39,6 @@ rules: - get - patch - update -- apiGroups: - - devportal.kuadrant.io - resources: - - apikeyrequests - - apiproducts - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - devportal.kuadrant.io resources: diff --git a/internal/controller/apikeyapproval_controller.go b/internal/controller/apikeyapproval_controller.go index 4e7a700..4fa33c9 100644 --- a/internal/controller/apikeyapproval_controller.go +++ b/internal/controller/apikeyapproval_controller.go @@ -38,7 +38,7 @@ type APIKeyApprovalReconciler struct { Scheme *runtime.Scheme } -// +kubebuilder:rbac:groups=devportal.kuadrant.io,resources=apikeyapprovals,verbs=get;list;watch;update;patch +// +kubebuilder:rbac:groups=devportal.kuadrant.io,resources=apikeyapprovals,verbs=get;list;watch;update;patch;delete // +kubebuilder:rbac:groups=devportal.kuadrant.io,resources=apikeyrequests,verbs=get;list;watch // Reconcile handles reconciling all APIKeyApprovals in a single call. Any resource event should enqueue the diff --git a/test/e2e/apikey_approval_gc_test.go b/test/e2e/apikey_approval_gc_test.go new file mode 100644 index 0000000..798b6f5 --- /dev/null +++ b/test/e2e/apikey_approval_gc_test.go @@ -0,0 +1,374 @@ +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "os/exec" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kuadrant/developer-portal-controller/test/utils" +) + +var _ = Describe("APIKeyApproval Garbage Collection", Ordered, func() { + const ( + ownerNamespace = "gc-owner-test" + consumerNamespace = "gc-consumer-test" + kuadrantNamespace = "gc-kuadrant-ns" + controllerNamespace = "developer-portal-controller-system" + ) + + AfterEach(func() { + specReport := CurrentSpecReport() + if specReport.Failed() { + By("Fetching controller manager pod name") + cmd := exec.Command("kubectl", "get", + "pods", "-l", "control-plane=controller-manager", + "-o", "go-template={{ range .items }}"+ + "{{ if not .metadata.deletionTimestamp }}"+ + "{{ .metadata.name }}"+ + "{{ \"\\n\" }}{{ end }}{{ end }}", + "-n", controllerNamespace, + ) + podOutput, err := utils.Run(cmd) + if err != nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get controller pod name: %s\n", err) + return + } + podNames := utils.GetNonEmptyLines(podOutput) + if len(podNames) == 0 { + _, _ = fmt.Fprintf(GinkgoWriter, "No controller pod found\n") + return + } + controllerPodName := podNames[0] + + By("Fetching controller manager pod logs") + cmd = exec.Command("kubectl", "logs", controllerPodName, "-n", controllerNamespace) + controllerLogs, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n%s\n", controllerLogs) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s\n", err) + } + + By("Fetching Kubernetes events in owner namespace") + cmd = exec.Command("kubectl", "get", "events", "-n", ownerNamespace, "--sort-by=.lastTimestamp") + eventsOutput, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Events in %s:\n%s\n", ownerNamespace, eventsOutput) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get events in %s: %s\n", ownerNamespace, err) + } + + By("Fetching Kubernetes events in consumer namespace") + cmd = exec.Command("kubectl", "get", "events", "-n", consumerNamespace, "--sort-by=.lastTimestamp") + eventsOutput, err = utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Events in %s:\n%s\n", consumerNamespace, eventsOutput) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get events in %s: %s\n", consumerNamespace, err) + } + } + }) + + BeforeAll(func() { + By("creating the owner namespace") + cmd := exec.Command("kubectl", "create", "ns", ownerNamespace) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create owner namespace") + + By("creating the consumer namespace") + cmd = exec.Command("kubectl", "create", "ns", consumerNamespace) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create consumer namespace") + + By("creating the kuadrant namespace") + cmd = exec.Command("kubectl", "create", "ns", kuadrantNamespace) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create kuadrant namespace") + + By("creating the kuadrant instance") + kuadrantYAML := fmt.Sprintf(` +apiVersion: kuadrant.io/v1beta1 +kind: Kuadrant +metadata: + name: kuadrant + namespace: %s +spec: {} +`, kuadrantNamespace) + + cmd = exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = utils.StringReader(kuadrantYAML) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create Kuadrant") + }) + + AfterAll(func() { + By("cleaning up kuadrant namespace") + cmd := exec.Command("kubectl", "delete", "ns", kuadrantNamespace, "--wait=false") + _, _ = utils.Run(cmd) + + By("cleaning up owner namespace") + cmd = exec.Command("kubectl", "delete", "ns", ownerNamespace, "--wait=false") + _, _ = utils.Run(cmd) + + By("cleaning up consumer namespace") + cmd = exec.Command("kubectl", "delete", "ns", consumerNamespace, "--wait=false") + _, _ = utils.Run(cmd) + }) + + SetDefaultEventuallyTimeout(2 * time.Minute) + SetDefaultEventuallyPollingInterval(2 * time.Second) + + Context("APIKeyApproval owner reference and garbage collection", func() { + It("should garbage collect APIKeyApproval when APIKey is deleted", func() { + By("creating an HTTPRoute as a reference target") + httpRouteYAML := fmt.Sprintf(` +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: test-route-gc + namespace: %s +spec: + parentRefs: + - name: test-gateway + rules: + - matches: + - path: + type: PathPrefix + value: /api + backendRefs: + - name: test-service + port: 8080 +`, ownerNamespace) + + cmd := exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = utils.StringReader(httpRouteYAML) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create HTTPRoute") + + By("creating an AuthPolicy with API key authentication") + authPolicyYAML := fmt.Sprintf(` +apiVersion: kuadrant.io/v1 +kind: AuthPolicy +metadata: + name: test-auth-policy-gc + namespace: %s +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: test-route-gc + rules: + authentication: + "api-key": + apiKey: + selector: + matchLabels: + kuadrant.io/apikeys: "true" + credentials: + authorizationHeader: + prefix: "API-KEY" +`, ownerNamespace) + + cmd = exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = utils.StringReader(authPolicyYAML) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create AuthPolicy") + + By("updating AuthPolicy status to Accepted and Enforced") + authPolicyStatusPatch := `{ + "status": { + "conditions": [ + { + "type": "Accepted", + "status": "True", + "reason": "Accepted", + "message": "AuthPolicy has been accepted", + "lastTransitionTime": "2024-01-01T00:00:00Z" + }, + { + "type": "Enforced", + "status": "True", + "reason": "Enforced", + "message": "AuthPolicy has been successfully enforced", + "lastTransitionTime": "2024-01-01T00:00:00Z" + } + ] + } + }` + + cmd = exec.Command("kubectl", "patch", "authpolicy", "test-auth-policy-gc", + "-n", ownerNamespace, + "--type=merge", + "--subresource=status", + "-p", authPolicyStatusPatch) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to update AuthPolicy status") + + By("creating an APIProduct with automatic approval mode") + apiProductGCName := "gc-test-api" + apiProductYAML := fmt.Sprintf(` +apiVersion: devportal.kuadrant.io/v1alpha1 +kind: APIProduct +metadata: + name: %s + namespace: %s +spec: + displayName: "Garbage Collection Test API" + description: "API Product for testing garbage collection" + approvalMode: automatic + publishStatus: Published + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: test-route-gc +`, apiProductGCName, ownerNamespace) + + cmd = exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = utils.StringReader(apiProductYAML) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create APIProduct") + + By("creating a secret with API key in the consumer namespace") + apiKeyGCName := "gc-test-apikey" + secretYAML := fmt.Sprintf(` +apiVersion: v1 +kind: Secret +metadata: + name: %s-secret + namespace: %s +type: Opaque +stringData: + api_key: gc-test-key-value-12345 +`, apiKeyGCName, consumerNamespace) + + cmd = exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = utils.StringReader(secretYAML) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create secret") + + By("creating an APIKey in the consumer namespace") + apiKeyYAML := fmt.Sprintf(` +apiVersion: devportal.kuadrant.io/v1alpha1 +kind: APIKey +metadata: + name: %s + namespace: %s +spec: + apiProductRef: + name: %s + namespace: %s + secretRef: + name: %s-secret + planTier: premium + useCase: "Testing garbage collection" + requestedBy: + userId: gc-test-user-123 + email: gctest@example.com +`, apiKeyGCName, consumerNamespace, apiProductGCName, ownerNamespace, apiKeyGCName) + + cmd = exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = utils.StringReader(apiKeyYAML) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create APIKey") + + By("verifying APIKeyRequest was created in the owner namespace") + var apiKeyRequestName string + verifyAPIKeyRequestCreated := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "apikeyrequest", + "-n", ownerNamespace, + "-o", "jsonpath={.items[?(@.spec.apiKeyRef.name=='"+apiKeyGCName+"')].metadata.name}") + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).NotTo(BeEmpty(), "APIKeyRequest should be created") + apiKeyRequestName = output + } + Eventually(verifyAPIKeyRequestCreated).Should(Succeed()) + + By("verifying APIKeyApproval was automatically created") + apiKeyApprovalName := fmt.Sprintf("%s-auto", apiKeyRequestName) + verifyApprovalCreated := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "apikeyapproval", apiKeyApprovalName, + "-n", ownerNamespace, "-o", "jsonpath={.metadata.name}") + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal(apiKeyApprovalName), "APIKeyApproval should be created") + } + Eventually(verifyApprovalCreated).Should(Succeed()) + + By("verifying APIKeyApproval has owner reference to APIKeyRequest") + verifyOwnerReference := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "apikeyapproval", apiKeyApprovalName, + "-n", ownerNamespace, + "-o", "jsonpath={.metadata.ownerReferences[0].name}") + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal(apiKeyRequestName), "APIKeyApproval should have owner reference to APIKeyRequest") + } + Eventually(verifyOwnerReference).Should(Succeed()) + + By("verifying owner reference kind is APIKeyRequest") + verifyOwnerKind := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "apikeyapproval", apiKeyApprovalName, + "-n", ownerNamespace, + "-o", "jsonpath={.metadata.ownerReferences[0].kind}") + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("APIKeyRequest"), "Owner reference kind should be APIKeyRequest") + } + Eventually(verifyOwnerKind).Should(Succeed()) + + By("verifying owner reference controller is set to true") + verifyOwnerController := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "apikeyapproval", apiKeyApprovalName, + "-n", ownerNamespace, + "-o", "jsonpath={.metadata.ownerReferences[0].controller}") + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("true"), "Owner reference controller should be true") + } + Eventually(verifyOwnerController).Should(Succeed()) + + By("deleting the APIKey") + cmd = exec.Command("kubectl", "delete", "apikey", apiKeyGCName, "-n", consumerNamespace) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to delete APIKey") + + By("verifying APIKeyRequest is deleted") + verifyAPIKeyRequestDeleted := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "apikeyrequest", apiKeyRequestName, + "-n", ownerNamespace) + _, err := utils.Run(cmd) + g.Expect(err).To(HaveOccurred(), "APIKeyRequest should be deleted") + } + Eventually(verifyAPIKeyRequestDeleted, 30*time.Second).Should(Succeed()) + + By("verifying APIKeyApproval is garbage collected") + verifyApprovalDeleted := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "apikeyapproval", apiKeyApprovalName, + "-n", ownerNamespace) + _, err := utils.Run(cmd) + g.Expect(err).To(HaveOccurred(), "APIKeyApproval should be garbage collected") + } + Eventually(verifyApprovalDeleted, 30*time.Second).Should(Succeed()) + }) + }) +}) From d6a539ab96d7ff58dfa469b2ffde31c4abd9efe0 Mon Sep 17 00:00:00 2001 From: Eguzki Astiz Lezaun Date: Thu, 21 May 2026 17:00:42 +0200 Subject: [PATCH 2/2] =?UTF-8?q?address=20lint=20issues=C2=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Eguzki Astiz Lezaun --- test/e2e/apikey_approval_gc_test.go | 35 +++--------------- test/e2e/automatic_approval_test.go | 35 +++--------------- test/e2e/e2e_test.go | 8 +++-- test/e2e/test_helpers.go | 56 +++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 test/e2e/test_helpers.go diff --git a/test/e2e/apikey_approval_gc_test.go b/test/e2e/apikey_approval_gc_test.go index 798b6f5..2f9ba56 100644 --- a/test/e2e/apikey_approval_gc_test.go +++ b/test/e2e/apikey_approval_gc_test.go @@ -89,35 +89,11 @@ var _ = Describe("APIKeyApproval Garbage Collection", Ordered, func() { }) BeforeAll(func() { - By("creating the owner namespace") - cmd := exec.Command("kubectl", "create", "ns", ownerNamespace) - _, err := utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create owner namespace") - - By("creating the consumer namespace") - cmd = exec.Command("kubectl", "create", "ns", consumerNamespace) - _, err = utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create consumer namespace") - - By("creating the kuadrant namespace") - cmd = exec.Command("kubectl", "create", "ns", kuadrantNamespace) - _, err = utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create kuadrant namespace") - - By("creating the kuadrant instance") - kuadrantYAML := fmt.Sprintf(` -apiVersion: kuadrant.io/v1beta1 -kind: Kuadrant -metadata: - name: kuadrant - namespace: %s -spec: {} -`, kuadrantNamespace) + SetDefaultEventuallyTimeout(2 * time.Minute) + SetDefaultEventuallyPollingInterval(2 * time.Second) - cmd = exec.Command("kubectl", "apply", "-f", "-") - cmd.Stdin = utils.StringReader(kuadrantYAML) - _, err = utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create Kuadrant") + By("setting up namespaces and Kuadrant instance") + SetupNamespacesAndKuadrant(ownerNamespace, consumerNamespace, kuadrantNamespace) }) AfterAll(func() { @@ -134,9 +110,6 @@ spec: {} _, _ = utils.Run(cmd) }) - SetDefaultEventuallyTimeout(2 * time.Minute) - SetDefaultEventuallyPollingInterval(2 * time.Second) - Context("APIKeyApproval owner reference and garbage collection", func() { It("should garbage collect APIKeyApproval when APIKey is deleted", func() { By("creating an HTTPRoute as a reference target") diff --git a/test/e2e/automatic_approval_test.go b/test/e2e/automatic_approval_test.go index 85570b5..80fa876 100644 --- a/test/e2e/automatic_approval_test.go +++ b/test/e2e/automatic_approval_test.go @@ -100,35 +100,11 @@ var _ = Describe("Automatic Approval", Ordered, func() { }) BeforeAll(func() { - By("creating the owner namespace") - cmd := exec.Command("kubectl", "create", "ns", ownerNamespace) - _, err := utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create owner namespace") - - By("creating the consumer namespace") - cmd = exec.Command("kubectl", "create", "ns", consumerNamespace) - _, err = utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create consumer namespace") - - By("creating the kuadrant namespace") - cmd = exec.Command("kubectl", "create", "ns", kuadrantNamespace) - _, err = utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create kuadrant namespace") - - By("creating the kuadrant instance") - kuadrantYAML := fmt.Sprintf(` -apiVersion: kuadrant.io/v1beta1 -kind: Kuadrant -metadata: - name: kuadrant - namespace: %s -spec: {} -`, kuadrantNamespace) + SetDefaultEventuallyTimeout(2 * time.Minute) + SetDefaultEventuallyPollingInterval(2 * time.Second) - cmd = exec.Command("kubectl", "apply", "-f", "-") - cmd.Stdin = utils.StringReader(kuadrantYAML) - _, err = utils.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to create Kuadrant") + By("setting up namespaces and Kuadrant instance") + SetupNamespacesAndKuadrant(ownerNamespace, consumerNamespace, kuadrantNamespace) }) AfterAll(func() { @@ -145,9 +121,6 @@ spec: {} _, _ = utils.Run(cmd) }) - SetDefaultEventuallyTimeout(2 * time.Minute) - SetDefaultEventuallyPollingInterval(2 * time.Second) - Context("APIKey with automatic approval mode", func() { It("should create APIKeyRequest, automatic approval, and approve the APIKey", func() { By("creating an HTTPRoute as a reference target") diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 9f92774..38f50c8 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -45,6 +45,11 @@ const metricsRoleBindingName = "developer-portal-controller-metrics-binding" var _ = Describe("Manager", Ordered, func() { var controllerPodName string + BeforeAll(func() { + SetDefaultEventuallyTimeout(2 * time.Minute) + SetDefaultEventuallyPollingInterval(time.Second) + }) + AfterAll(func() { By("cleaning up the curl pod for metrics") cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace) @@ -98,9 +103,6 @@ var _ = Describe("Manager", Ordered, func() { } }) - SetDefaultEventuallyTimeout(2 * time.Minute) - SetDefaultEventuallyPollingInterval(time.Second) - Context("Manager", func() { It("should run successfully", func() { By("validating that the controller-manager pod is running as expected") diff --git a/test/e2e/test_helpers.go b/test/e2e/test_helpers.go new file mode 100644 index 0000000..aef0fc0 --- /dev/null +++ b/test/e2e/test_helpers.go @@ -0,0 +1,56 @@ +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "os/exec" + + "github.com/onsi/gomega" + + "github.com/kuadrant/developer-portal-controller/test/utils" +) + +// SetupNamespacesAndKuadrant creates the owner, consumer, and kuadrant namespaces, +// and creates a Kuadrant instance. This is a common setup step for e2e tests. +func SetupNamespacesAndKuadrant(ownerNamespace, consumerNamespace, kuadrantNamespace string) { + cmd := exec.Command("kubectl", "create", "ns", ownerNamespace) + _, err := utils.Run(cmd) + gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to create owner namespace") + + cmd = exec.Command("kubectl", "create", "ns", consumerNamespace) + _, err = utils.Run(cmd) + gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to create consumer namespace") + + cmd = exec.Command("kubectl", "create", "ns", kuadrantNamespace) + _, err = utils.Run(cmd) + gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to create kuadrant namespace") + + kuadrantYAML := fmt.Sprintf(` +apiVersion: kuadrant.io/v1beta1 +kind: Kuadrant +metadata: + name: kuadrant + namespace: %s +spec: {} +`, kuadrantNamespace) + + cmd = exec.Command("kubectl", "apply", "-f", "-") + cmd.Stdin = utils.StringReader(kuadrantYAML) + _, err = utils.Run(cmd) + gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to create Kuadrant") +}