From 431fb74cad1cf297f8046264ccb3d8f3d29ea9f7 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 20 Jan 2025 14:58:14 +0100 Subject: [PATCH] chore(E2E): Make sure the E2E tests can fully test that the application is reachable on K8s (using an Ingress on GH) This is a gap I wanted to close with the current nightly checks. On OCP, the tests ensure access via the Route created by the Operator, but on K8s (like here in GH against Minikube), this would just be skipped. In a few situations, I had to manually run the tests against OCP to check that everything worked as expected from a user standpoint. Without waiting for issues.redhat.com/browse/RHIDP-2176 to be implemented, this tries to check such access at least, mainly because that's also how we instruct our users on managed K8s clusters to access their Operator-backed instances. This way, the test can create an Ingress Resource and ensure the application is fully reachable, using the same steps an end-user would perform to make expose their Operator-backed app. --- .github/workflows/nightly.yaml | 9 ++++++ Makefile | 2 +- tests/e2e/README.md | 6 ++-- tests/e2e/e2e_test.go | 48 ++++++++++++++++++++++++++++++++ tests/helper/helper_backstage.go | 6 +++- 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 01d1ad35..6cf94555 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -53,10 +53,19 @@ jobs: - name: Start Minikube if: ${{ steps.operator-image-existence-checker.outputs.OPERATOR_IMAGE_EXISTS == 'true' }} uses: medyagh/setup-minikube@d8c0eb871f6f455542491d86a574477bd3894533 # v0.0.18 + with: + addons: ingress + + - name: Build Ingress Domain + if: ${{ steps.operator-image-existence-checker.outputs.OPERATOR_IMAGE_EXISTS == 'true' }} + run: | + echo "K8S_INGRESS_DOMAIN=$(minikube ip).sslip.io" >> $GITHUB_ENV - name: Run E2E tests if: ${{ steps.operator-image-existence-checker.outputs.OPERATOR_IMAGE_EXISTS == 'true' }} env: BACKSTAGE_OPERATOR_TESTS_PLATFORM: minikube + BACKSTAGE_OPERATOR_TESTS_K8S_CREATE_INGRESS: 'true' + BACKSTAGE_OPERATOR_TESTS_K8S_INGRESS_DOMAIN: ${{ env.K8S_INGRESS_DOMAIN }} IMG: ${{ env.OPERATOR_IMAGE }} run: make test-e2e diff --git a/Makefile b/Makefile index ec890e37..d090b95e 100644 --- a/Makefile +++ b/Makefile @@ -447,7 +447,7 @@ ENVTEST_VERSION ?= release-0.17 GOLANGCI_LINT_VERSION ?= v1.59.1 GOIMPORTS_VERSION ?= v0.16.1 GOSEC_VERSION ?= v2.20.0 -GINKGO_VERSION ?= v2.22.1 +GINKGO_VERSION ?= v2.22.2 ## Gosec options - default format is sarif so we can integrate with Github code scanning GOSEC_FMT ?= sarif # for other options, see https://github.com/securego/gosec#output-formats diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 1dc07816..e586264c 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -22,7 +22,7 @@ The behavior is configurable using the following environment variables: | Name | Type | Description | Default value | Example | |------------------------------------------------------------------------------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------|---------------------------------------------------------| | `BACKSTAGE_OPERATOR_TEST_MODE` | string | The test mode:
- if not set, it will call `make deploy`
- `olm`: it will call `make deploy-olm`
- `rhdh-latest` or `rhdh-next`: it will install the operator using the [`install-rhdh-catalog-source.sh`](../../.rhdh/scripts/install-rhdh-catalog-source.sh) script
- `rhdh-airgap`: it will install the operator using the [`prepare-restricted-environment.sh`](../../.rhdh/scripts/prepare-restricted-environment.sh) script. | | `rhdh-latest` | -| `IMG` (or any variables from the Makefile that are used by `make deploy` or `make deploy-olm`) | string | The image to use. Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is not set or set to `olm` | `VERSION` defined in [`Makefile`](../../Makefile) | `quay.io/rhdh/rhdh-rhel9-operator:0.0.1-latest` | +| `IMG` (or any variables from the Makefile that are used by `make deploy` or `make deploy-olm`) | string | The image to use. Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is not set or set to `olm` | `VERSION` defined in [`Makefile`](../../Makefile) | `quay.io/rhdh/rhdh-rhel9-operator:0.0.1-latest` | | `BACKSTAGE_OPERATOR_TESTS_BUILD_IMAGES` | bool | If set to `true`, it will build the operator image with `make image-build`.
Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is not set or set to `olm`. | | `false` | | `BACKSTAGE_OPERATOR_TESTS_PUSH_IMAGES` | bool | If set to `true`, it will push the operator image with `make image-push`.
Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is not set or set to `olm`. | | `false` | | `BACKSTAGE_OPERATOR_TESTS_PLATFORM` | string | The platform type, to directly load the operator image if supported instead of pushing it.
Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is not set or set to `olm`.br>Supported values: [`kind`](#building-and-testing-local-changes-on-kind), [`k3d`](#building-and-testing-local-changes-on-k3d), [`minikube`](#building-and-testing-local-changes-on-minikube) | | `kind` | @@ -30,7 +30,9 @@ The behavior is configurable using the following environment variables: | `BACKSTAGE_OPERATOR_TESTS_K3D_CLUSTER` | string | Name of the local k3d cluster to use. Relevant only if `BACKSTAGE_OPERATOR_TESTS_PLATFORM` is `k3d`. | `k3s-default` | `k3d-local-k8s-cluster` | | `BACKSTAGE_OPERATOR_TESTS_AIRGAP_INDEX_IMAGE` | string | Index image to use in the airgap scenario.
Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is `rhdh-airgap`. | `quay.io/rhdh/iib:latest-v4.14-x86_64` | `registry.redhat.io/redhat/redhat-operator-index:v4.14` | | `BACKSTAGE_OPERATOR_TESTS_AIRGAP_OPERATOR_VERSION` | string | Operator version to use in the airgap scenario.
Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is `rhdh-airgap`. | `v1.1.0` | `v1.1.0` | -| `BACKSTAGE_OPERATOR_TESTS_AIRGAP_MIRROR_REGISTRY` | string | Existing mirror registry to use in the airgap scenario.
Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is `rhdh-airgap`
. | | `my-registry.example.com` | +| `BACKSTAGE_OPERATOR_TESTS_AIRGAP_MIRROR_REGISTRY` | string | Existing mirror registry to use in the airgap scenario.
Relevant if `BACKSTAGE_OPERATOR_TEST_MODE` is `rhdh-airgap`. | | `my-registry.example.com` | +| `BACKSTAGE_OPERATOR_TESTS_K8S_CREATE_INGRESS` | bool | Whether to test access using an Ingress resource on K8s | | `true` | +| `BACKSTAGE_OPERATOR_TESTS_K8S_INGRESS_DOMAIN` | string | Ingress domain. Relevant only if `BACKSTAGE_OPERATOR_TESTS_K8S_CREATE_INGRESS` is `true`. | | `$(minikube ip).nip.io` | ### Examples diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index d99ea34f..db5b54a3 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -3,6 +3,7 @@ package e2e import ( "fmt" "io" + "os" "os/exec" "path/filepath" "strconv" @@ -301,6 +302,53 @@ subjects: ensureRouteIsReachable(ns, tt.crName, tt.additionalApiEndpointTests) }) } + } else { + // This is how we currently instruct users to deploy the application on vanilla K8s clusters, + // where an Ingress resource is not created OOTB by the Operator. + // TODO(rm3l): this is until https://issues.redhat.com/browse/RHIDP-2176 is supported. + // For now, we want to make sure the tests cover the same area as on OpenShift, i.e., + // making sure that the application is reachable end-to-end from a user standpoint. + if os.Getenv("BACKSTAGE_OPERATOR_TESTS_K8S_CREATE_INGRESS") == "true" { + ingressDomain := os.Getenv("BACKSTAGE_OPERATOR_TESTS_K8S_INGRESS_DOMAIN") + if ingressDomain == "" { + Fail("Ingress Domain should be configured via the BACKSTAGE_OPERATOR_TESTS_K8S_INGRESS_DOMAIN env var") + } + ingressHost := fmt.Sprintf("%s.%s", tt.crName, ingressDomain) + By("manually creating a K8s Ingress", func() { + cmd := exec.Command(helper.GetPlatformTool(), "-n", ns, "create", "-f", "-") + stdin, err := cmd.StdinPipe() + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + go func() { + defer stdin.Close() + _, _ = io.WriteString(stdin, fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: %[1]s +spec: + rules: + - host: %[2]s + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: backstage-%[1]s + port: + name: http-backend +`, tt.crName, ingressHost)) + }() + _, err = helper.Run(cmd) + Expect(err).ShouldNot(HaveOccurred()) + }) + + By("ensuring the application is fully reachable", func() { + Eventually(helper.VerifyBackstageAppAccess, 8*time.Minute, time.Second). + WithArguments(fmt.Sprintf("http://%s", ingressHost), tt.additionalApiEndpointTests). + Should(Succeed()) + }) + } } var isRouteEnabledNow bool diff --git a/tests/helper/helper_backstage.go b/tests/helper/helper_backstage.go index 04a62187..f6848701 100644 --- a/tests/helper/helper_backstage.go +++ b/tests/helper/helper_backstage.go @@ -176,8 +176,11 @@ func VerifyBackstageRoute(g Gomega, ns string, crName string, tests []ApiEndpoin fmt.Fprintln(GinkgoWriter, host) g.Expect(err).ShouldNot(HaveOccurred()) g.Expect(host).ShouldNot(BeEmpty()) - baseUrl := fmt.Sprintf("https://%s", host) + VerifyBackstageAppAccess(g, fmt.Sprintf("https://%s", host), tests) +} + +func VerifyBackstageAppAccess(g Gomega, baseUrl string, tests []ApiEndpointTest) { tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, // #nosec G402 -- test code only, not used in production @@ -213,6 +216,7 @@ func VerifyBackstageRoute(g Gomega, ns string, crName string, tests []ApiEndpoin g.Expect(bodyStr).Should(tt.BodyMatcher, "context: "+tt.Endpoint) } } + allTests := append(defaultApiEndpointTests, tests...) for _, tt := range allTests { performTest(tt)