This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-operator.git
The following commit(s) were added to refs/heads/main by this push:
new f62a1cd Enable e2e tests on PRs (select tests only) (#883)
f62a1cd is described below
commit f62a1cd66782766e5912e6dbbe486dce034106f2
Author: Jan Høydahl <[email protected]>
AuthorDate: Fri Nov 6 02:09:36 2026 +0100
Enable e2e tests on PRs (select tests only) (#883)
* Run a reduced e2e profile in CI for pull requests
* Silence the cert-manager Helm warnings in the e2e cluster setup
* Tolerate a leftover bootstrap ClusterIssuer when reusing a KinD cluster
---
.github/workflows/e2e-tests.yaml | 107 +++++++++++++++++++++++-------
dev-docs/e2e-testing.md | 32 ++++++++-
tests/e2e/backups_test.go | 2 +-
tests/e2e/prometheus_exporter_test.go | 2 +-
tests/e2e/solrcloud_basic_test.go | 2 +-
tests/e2e/solrcloud_ingress_test.go | 2 +-
tests/e2e/solrcloud_scaling_test.go | 4 +-
tests/e2e/solrcloud_security_json_test.go | 2 +-
tests/e2e/solrcloud_storage_test.go | 2 +-
tests/e2e/solrcloud_tls_test.go | 6 +-
tests/e2e/suite_test.go | 8 +++
tests/e2e/utils_tls_test.go | 7 +-
tests/scripts/manage_e2e_tests.sh | 7 +-
13 files changed, 142 insertions(+), 41 deletions(-)
diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml
index 3d28e7a..dbff160 100644
--- a/.github/workflows/e2e-tests.yaml
+++ b/.github/workflows/e2e-tests.yaml
@@ -15,41 +15,69 @@
name: E2E / Integration Tests
+# The full e2e suite is too heavy for GitHub-hosted runners (4 vCPU / 16 GB
for public repos), so pull
+# requests run the reduced "pr" profile: the specs labelled Label("pr") in
tests/e2e, one cheap and
+# representative spec per feature area. See
dev-docs/e2e-testing.md#continuous-integration.
+#
+# The job only runs for pull requests that change something the e2e suite
actually exercises: the
+# operator code and its image, the CRDs and dependencies installed into the
cluster, the solr-operator
+# Helm chart used to deploy it, and the e2e harness itself. Docs, examples,
release tooling and the
+# "solr" Helm chart (not used by the suite) do not trigger it.
on:
pull_request:
branches:
- 'main'
- 'release-*'
- paths-ignore:
- - '.github/workflows/**'
- - '!.github/workflows/e2e-tests.yaml'
- - '.run/**'
- - 'config/**'
- - 'dev-docs/**'
- - 'docs/**'
- - 'example/**'
- - 'hack/**'
- - '.gitignore'
- - '.asf.yaml'
- - 'README.md'
- - 'CONTRIBUTING.md'
- - 'LICENSE'
- - 'NOTICE'
+ paths:
+ - 'api/**'
+ - 'controllers/**'
+ - 'main.go'
+ - 'go.mod'
+ - 'go.sum'
+ - 'build/**'
+ - 'config/crd/**'
+ - 'config/dependencies/**'
+ - 'helm/solr-operator/**'
+ - 'tests/e2e/**'
+ - 'tests/scripts/**'
+ - 'Makefile'
+ - '.github/workflows/e2e-tests.yaml'
+ # Manual runs, e.g. for the full suite or another Solr/Kubernetes version.
+ workflow_dispatch:
+ inputs:
+ test-labels:
+ description: 'Ginkgo label filter (TEST_LABELS). Leave empty to run
the full suite.'
+ type: string
+ default: 'pr'
+ solr-version:
+ description: 'Solr image tag to test with (SOLR_IMAGE=solr:<tag>)'
+ type: string
+ default: '9.11.0'
+ kube-version:
+ description: 'Kubernetes version for the KinD cluster (kindest/node
tag)'
+ type: string
+ default: 'v1.33.7'
+
+permissions:
+ contents: read
+
+# A new push to the same pull request supersedes the running job.
+concurrency:
+ group: e2e-tests-${{ github.event.pull_request.number || github.run_id }}
+ cancel-in-progress: true
jobs:
e2eTests:
name: Run E2E / Integration Tests
- # Github runners do not currently have the resources to run these tests
- if: false
-
runs-on: ubuntu-latest
+ timeout-minutes: 90
strategy:
matrix:
- go-version: [ '1.26' ]
- kube-version: [ 'v1.26.6' ]
- solr-version: [ '9' ] # [ '8', '9' ]
+ go-version: [ '1.27' ]
+ kube-version: [ "${{ inputs.kube-version || 'v1.33.7' }}" ]
+ solr-version: [ "${{ inputs.solr-version || '9.11.0' }}" ]
steps:
# Setup
@@ -61,7 +89,7 @@ jobs:
- run: make clean
- - name: Install dev dependencies
+ - name: Cache dev dependencies
id: cache-bin
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
@@ -72,5 +100,36 @@ jobs:
if: steps.cache-bin.outputs.cache-hit != 'true'
run: make install-dependencies
- # Build
- - run: make e2e-tests SOLR_IMAGE="solr:${{ matrix.solr-version }}"
KUBERNETES_VERSION="${{ matrix.kube-version }}" TEST_PARALLELISM=1
+ - name: Cache go modules
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: ~/go/pkg/mod
+ key: ${{ runner.os }}-go-modules-${{ hashFiles('go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-modules
+
+ # Test
+ - name: Run the e2e tests
+ run: |
+ # Pull requests always run the reduced profile. Manual runs use the
given filter, where an
+ # empty filter means the whole suite.
+ TEST_LABELS="pr"
+ if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+ TEST_LABELS="${{ inputs.test-labels }}"
+ fi
+ make e2e-tests \
+ SOLR_IMAGE="solr:${{ matrix.solr-version }}" \
+ KUBERNETES_VERSION="${{ matrix.kube-version }}" \
+ TEST_PARALLELISM=2 \
+ TEST_LABELS="${TEST_LABELS}"
+
+ # The suite writes pod logs, resource dumps and the operator log for
every spec, plus junit.xml and
+ # report.json, to tests/e2e/output. Keep them when something fails.
+ - name: Upload e2e test output
+ if: failure()
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
# v7.0.2
+ with:
+ name: e2e-test-output-solr-${{ matrix.solr-version }}-k8s-${{
matrix.kube-version }}
+ path: tests/e2e/output/
+ if-no-files-found: ignore
+ retention-days: 14
diff --git a/dev-docs/e2e-testing.md b/dev-docs/e2e-testing.md
index 8e6664a..c20731e 100644
--- a/dev-docs/e2e-testing.md
+++ b/dev-docs/e2e-testing.md
@@ -67,7 +67,8 @@ There are a number of ways to filter the tests that are run,
and each can be spe
See below for more information.
- **TEST_LABELS** - Equivalent to Ginkgo's `--label-filter`.
If set, only specs with labels that match the label-filter will be run.
- Note: A test has to have labels for this to be effective.
+ Note: A test has to have labels for this to be effective.
+ The only label in use today is `pr`, which selects the [reduced profile run
in CI](#continuous-integration).
The passed-in expression can include boolean operations (`!`, `&&`, `||`,
`,`),
groupings via `()`, and regular expressions `/regexp/`. e.g. `(cat || dog)
&& !fruit`
- **TEST_SKIP** - Equivalent to Ginkgo's `--skip`.
@@ -123,6 +124,35 @@ Debugging output for each spec (pod logs, events, resource
status and the operat
Ginkgo also writes `junit.xml` and `report.json` to that directory, containing
the failure messages of the whole run.
Since the directory is recreated at the start of every run, copy these files
elsewhere if you need to keep them.
+## Continuous Integration
+
+The full suite is too heavy for the GitHub-hosted runners available to the
project (4 vCPU and 16 GB of memory),
+so pull requests do not run all of it.
+Instead, the [e2e workflow](../.github/workflows/e2e-tests.yaml) runs the
**`pr` profile**: the specs that carry
+the Ginkgo label `pr`, selected with `TEST_LABELS=pr`.
+The profile holds one cheap, representative spec per feature area (basic
startup, security.json, TLS via secrets
+and via a mounted directory, scale up and scale down with replica migration,
backups, the Prometheus exporter,
+persistent storage expansion and ingress/addressability).
+Slow specs, such as the rolling upgrade, and the many variants of the same
feature only run in the full suite.
+
+The workflow only runs for pull requests that touch something the suite
exercises: the Go code and the operator
+image, the CRDs and dependencies installed into the test cluster, the
`solr-operator` Helm chart, and the e2e
+harness itself.
+Changes to documentation, examples, release tooling or the `solr` Helm chart
do not trigger it.
+
+The workflow can also be started manually from the GitHub Actions UI.
+Leave the label filter empty to run the full suite, or pick another Solr or
Kubernetes version.
+
+To run exactly what CI runs:
+
+```bash
+$ make e2e-tests TEST_LABELS=pr TEST_PARALLELISM=2
+```
+
+When adding a spec, give it the `pr` label only if it exercises an operator
feature the profile does not cover
+yet, and prefer specs that need at most three Solr pods.
+Labels go on the `Context` (or `It`) as a decorator, e.g. `FContext("Provided
Zookeeper", Label("pr"), func() {`.
+
# IntelliJ & GoLand Testing
The e2e-tests can be tested via IDEA, just as the unit tests can,
diff --git a/tests/e2e/backups_test.go b/tests/e2e/backups_test.go
index 0e41de3..eff0c1f 100644
--- a/tests/e2e/backups_test.go
+++ b/tests/e2e/backups_test.go
@@ -148,7 +148,7 @@ var _ = FDescribe("E2E - Backups", Ordered, func() {
})
})
- FContext("Local Directory - Single", func() {
+ FContext("Local Directory - Single", Label("pr"), func() {
BeforeEach(func() {
solrBackup.Spec.RepositoryName = localBackupRepository
solrBackup.Spec.Recurrence = nil
diff --git a/tests/e2e/prometheus_exporter_test.go
b/tests/e2e/prometheus_exporter_test.go
index 003d1ea..f68e5f8 100644
--- a/tests/e2e/prometheus_exporter_test.go
+++ b/tests/e2e/prometheus_exporter_test.go
@@ -86,7 +86,7 @@ var _ = FDescribe("E2E - Prometheus Exporter", Ordered,
func() {
checkMetrics(ctx, solrPrometheusExporter, solrCloud,
solrCollection)
})
- FContext("Default - Solr Reference", func() {
+ FContext("Default - Solr Reference", Label("pr"), func() {
BeforeEach(func() {
solrPrometheusExporter.Spec.SolrReference =
solrv1beta1.SolrReference{
Cloud: &solrv1beta1.SolrCloudReference{
diff --git a/tests/e2e/solrcloud_basic_test.go
b/tests/e2e/solrcloud_basic_test.go
index be3961a..5eb8ff7 100644
--- a/tests/e2e/solrcloud_basic_test.go
+++ b/tests/e2e/solrcloud_basic_test.go
@@ -49,7 +49,7 @@ var _ = FDescribe("E2E - SolrCloud - Basic", func() {
createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
})
- FContext("Provided Zookeeper", func() {
+ FContext("Provided Zookeeper", Label("pr"), func() {
BeforeEach(func() {
solrCloud.Spec.ZookeeperRef = &solrv1beta1.ZookeeperRef{
ProvidedZookeeper: &solrv1beta1.ZookeeperSpec{
diff --git a/tests/e2e/solrcloud_ingress_test.go
b/tests/e2e/solrcloud_ingress_test.go
index e9a45ab..92bceec 100644
--- a/tests/e2e/solrcloud_ingress_test.go
+++ b/tests/e2e/solrcloud_ingress_test.go
@@ -57,7 +57,7 @@ var _ = FDescribe("E2E - SolrCloud - Ingress", func() {
createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
})
- FContext("Can Remove Ingress and Services when changing
addressability", func() {
+ FContext("Can Remove Ingress and Services when changing
addressability", Label("pr"), func() {
FIt("Can adapt to changing needs", func(ctx context.Context) {
By("testing the Solr StatefulSet")
diff --git a/tests/e2e/solrcloud_scaling_test.go
b/tests/e2e/solrcloud_scaling_test.go
index 78ac80f..e193386 100644
--- a/tests/e2e/solrcloud_scaling_test.go
+++ b/tests/e2e/solrcloud_scaling_test.go
@@ -61,7 +61,7 @@ var _ = FDescribe("E2E - SolrCloud - Scale Down", func() {
createAndQueryCollection(ctx, solrCloud, solrCollection2, 1, 1,
2)
})
- FContext("with replica migration", func() {
+ FContext("with replica migration", Label("pr"), func() {
FIt("Scales Down", func(ctx context.Context) {
originalSolrCloud := solrCloud.DeepCopy()
solrCloud.Spec.Replicas = pointer.Int32(1)
@@ -348,7 +348,7 @@ var _ = FDescribe("E2E - SolrCloud - Scale Up", func() {
createAndQueryCollection(ctx, solrCloud, solrCollection2, 2, 1)
})
- FContext("with replica migration", func() {
+ FContext("with replica migration", Label("pr"), func() {
FIt("Scales Up", func(ctx context.Context) {
originalSolrCloud := solrCloud.DeepCopy()
diff --git a/tests/e2e/solrcloud_security_json_test.go
b/tests/e2e/solrcloud_security_json_test.go
index 8bc8695..f362a2a 100644
--- a/tests/e2e/solrcloud_security_json_test.go
+++ b/tests/e2e/solrcloud_security_json_test.go
@@ -54,7 +54,7 @@ var _ = FDescribe("E2E - SolrCloud - Security JSON", func() {
createAndQueryCollection(ctx, solrCloud, "basic", 2, 1)
})
- FContext("Provided Security JSON", func() {
+ FContext("Provided Security JSON", Label("pr"), func() {
BeforeEach(func(ctx context.Context) {
solrCloud.Spec.ZookeeperRef = &solrv1beta1.ZookeeperRef{
ProvidedZookeeper: &solrv1beta1.ZookeeperSpec{
diff --git a/tests/e2e/solrcloud_storage_test.go
b/tests/e2e/solrcloud_storage_test.go
index 9c96d05..b7ab045 100644
--- a/tests/e2e/solrcloud_storage_test.go
+++ b/tests/e2e/solrcloud_storage_test.go
@@ -64,7 +64,7 @@ var _ = FDescribe("E2E - SolrCloud - Storage", func() {
createAndQueryCollection(ctx, solrCloud, solrCollection2, 2, 1)
})
- FContext("Persistent Data - Expansion", func() {
+ FContext("Persistent Data - Expansion", Label("pr"), func() {
BeforeEach(func() {
solrCloud.Spec.StorageOptions =
solrv1beta1.SolrDataStorageOptions{
PersistentStorage:
&solrv1beta1.SolrPersistentDataStorageOptions{
diff --git a/tests/e2e/solrcloud_tls_test.go b/tests/e2e/solrcloud_tls_test.go
index d162225..439989a 100644
--- a/tests/e2e/solrcloud_tls_test.go
+++ b/tests/e2e/solrcloud_tls_test.go
@@ -57,7 +57,7 @@ var _ = FDescribe("E2E - SolrCloud - TLS - Secrets", func() {
createAndQueryCollection(ctx, solrCloud, solrCollection, 1, 2)
})
- FContext("No Client TLS", func() {
+ FContext("No Client TLS", Label("pr"), func() {
BeforeEach(func(ctx context.Context) {
solrCloud = generateBaseSolrCloudWithSecretTLS(ctx, 2,
false, true)
@@ -176,7 +176,7 @@ var _ = FDescribe("E2E - SolrCloud - TLS - Secrets", func()
{
})
})
- FContext("With Client TLS - Client Auth Need", func() {
+ FContext("With Client TLS - Client Auth Need", Label("pr"), func() {
BeforeEach(func(ctx context.Context) {
solrCloud = generateBaseSolrCloudWithSecretTLS(ctx, 2,
true, true)
@@ -235,7 +235,7 @@ var _ = FDescribe("E2E - SolrCloud - TLS - Mounted Dir",
func() {
createAndQueryCollection(ctx, solrCloud, solrCollection, 1, 2)
})
- FContext("ClientAuth - Want", func() {
+ FContext("ClientAuth - Want", Label("pr"), func() {
BeforeEach(func(ctx context.Context) {
solrCloud = generateBaseSolrCloudWithCSITLS(1, false,
false)
diff --git a/tests/e2e/suite_test.go b/tests/e2e/suite_test.go
index 6dd5e77..3875c53 100644
--- a/tests/e2e/suite_test.go
+++ b/tests/e2e/suite_test.go
@@ -69,6 +69,14 @@ const (
interval = time.Millisecond * 250
)
+// Ginkgo labels used to select a subset of the suite.
+//
+// "pr" marks the reduced profile that CI runs on pull requests (see
.github/workflows/e2e-tests.yaml),
+// selected with TEST_LABELS=pr. GitHub-hosted runners cannot run the full
suite in a reasonable time, so
+// this profile holds one cheap, representative spec per feature area. Add the
label to a new spec only if
+// it exercises an operator feature not already covered by the profile, and
prefer specs that need at most
+// three Solr pods. Everything else only runs in the full suite (make
e2e-tests without TEST_LABELS).
+
var (
k8sClient client.Client
rawK8sClient *kubernetes.Clientset
diff --git a/tests/e2e/utils_tls_test.go b/tests/e2e/utils_tls_test.go
index 24603b3..701acd4 100644
--- a/tests/e2e/utils_tls_test.go
+++ b/tests/e2e/utils_tls_test.go
@@ -29,6 +29,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/utils/pointer"
+ "sigs.k8s.io/controller-runtime/pkg/client"
)
const (
@@ -256,9 +257,11 @@ func installBootstrapIssuer(ctx context.Context) {
},
},
}
- Expect(k8sClient.Create(ctx, bootstrapIssuer)).To(Succeed(), "Failed to
install SelfSigned ClusterIssuer for bootstrapping CA")
+ // The issuer is cluster-scoped, so a reused KinD cluster
(REUSE_KIND_CLUSTER_IF_EXISTS) can still hold the
+ // one left behind by an interrupted run. It is identical, so keep it.
+ Expect(client.IgnoreAlreadyExists(k8sClient.Create(ctx,
bootstrapIssuer))).To(Succeed(), "Failed to install SelfSigned ClusterIssuer
for bootstrapping CA")
DeferCleanup(func(ctx context.Context) {
- Expect(k8sClient.Delete(ctx, bootstrapIssuer)).To(Succeed(),
"Failed to delete SelfSigned bootstrapping ClusterIssuer")
+ Expect(client.IgnoreNotFound(k8sClient.Delete(ctx,
bootstrapIssuer))).To(Succeed(), "Failed to delete SelfSigned bootstrapping
ClusterIssuer")
})
}
diff --git a/tests/scripts/manage_e2e_tests.sh
b/tests/scripts/manage_e2e_tests.sh
index 4b2d0c4..11d5193 100755
--- a/tests/scripts/manage_e2e_tests.sh
+++ b/tests/scripts/manage_e2e_tests.sh
@@ -98,8 +98,9 @@ export
REUSE_KIND_CLUSTER_IF_EXISTS="${REUSE_KIND_CLUSTER_IF_EXISTS:-true}" # Th
export LEAVE_KIND_CLUSTER_ON_SUCCESS="${LEAVE_KIND_CLUSTER_ON_SUCCESS:-false}"
# This is only used when using run_tests or run_with_cluster
export RAWFILE_LOCAL_PV_VERSION=0.13.1
-export CERT_MANAGER_VERSION=1.17.4
-export CERT_MANAGER_CSI_DRIVER_VERSION=0.5.0
+# The cert-manager charts version their releases with a "v" prefix; without it
Helm warns and falls back.
+export CERT_MANAGER_VERSION=v1.17.4
+export CERT_MANAGER_CSI_DRIVER_VERSION=v0.5.0
# Load a locally built image into the Kind cluster. Images that come from a
registry are
# never loaded; the Kind nodes pull those themselves.
@@ -215,7 +216,7 @@ function setup_cluster() {
printf "Installing Cert Manager\n"
helm repo add cert-manager https://charts.jetstack.io --force-update
- helm upgrade -i -n cert-manager --create-namespace cert-manager
cert-manager/cert-manager --version "${CERT_MANAGER_VERSION}" --set
installCRDs=true
+ helm upgrade -i -n cert-manager --create-namespace cert-manager
cert-manager/cert-manager --version "${CERT_MANAGER_VERSION}" --set
crds.enabled=true
helm upgrade -i -n cert-manager cert-manager-csi-driver
cert-manager/cert-manager-csi-driver --version
"${CERT_MANAGER_CSI_DRIVER_VERSION}"
echo ""
}