Repository navigation
test: make unit and envtest suites hermetic and deterministic (#67) - #79
senolcolak wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The new monitor keyring secret builder stores base64 text in Secret.Data instead of decoded bytes, which makes the fixture diverge from real Kubernetes behavior and can mask controller-copy semantics.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR introduces a hermetic, deterministic testing foundation by separating pure unit tests (webhooks + typed fixture builders) from controller envtest integration tests, and replacing file-based YAML fixtures with typed Go builders.
Changes:
- Split envtest suites behind
//go:build envtestand simplify webhook tests into pure unit specs with randomized order. - Replace captured Rook monitor YAML fixtures with typed builders plus a contract test to guard against drift.
- Add hermetic
make test-unit,test-unit-repeat, andtest-envtesttargets; updatemake testto run the layered suite without mutating source or cloning dependencies.
File summaries
| File | Description |
|---|---|
| test/builders/monitor.go | Adds typed builders for monitor Deployment/Secrets/ConfigMap to replace YAML fixtures. |
| test/builders/monitor_test.go | Adds contract tests to lock down the builder fields the controller relies on. |
| pkg/webhook/v1alpha1/suite_test.go | Converts webhook suite to pure unit Ginkgo entrypoint (no envtest/bootstrap). |
| pkg/controller/suite_test.go | Gates controller suite behind envtest build tag and switches fixtures to builders. |
| pkg/controller/remotecluster_controller_test.go | Adds envtest build tag to exclude from unit runs. |
| pkg/controller/remotearbiter_controller_test.go | Adds envtest build tag to exclude from unit runs. |
| Makefile | Adds hermetic unit/envtest targets and makes test run them without pretty/deps. |
| docs/superpowers/specs/2026-09-03-testing-foundation-design.md | Documents the testing-foundation architecture and rollout plan. |
| contrib/k8s/test/override-configmap.yaml | Removes YAML fixture (replaced by typed builder). |
| contrib/k8s/test/mon-deployment.yaml | Removes YAML fixture (replaced by typed builder). |
| contrib/k8s/test/keyring-secret.yaml | Removes YAML fixture (replaced by typed builder). |
| contrib/k8s/test/env-var-secret.yaml | Removes YAML fixture (replaced by typed builder). |
Review details
- Files reviewed: 12/12 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
The envtest path still depends on missing/unvendored Rook CRDs (contrib/k8s/3rdparty) and the new keyring builder currently mishandles YAML base64 Secret data unless decoding is added, breaking the “fresh checkout” hermetic goal.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
docs/superpowers/specs/2026-09-03-testing-foundation-design.md:38
- This design doc states there is no build/test CI in the repo, but then claims #66 has already shipped
.github/workflows/ci.yaml(and references shipped jobs). In this PR branch,.github/workflows/ci.yamlis not present, so these statements are inconsistent and will mislead readers about current status vs planned work.
test/builders/monitor.go:221
- MonitorKeyringSecret currently stores the base64 string bytes into Secret.Data; once
defaultKeyringDatais decoded, this should use the decoded byte slice so the controller copies/mounts the real keyring content.
return &corev1.Secret{
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"},
ObjectMeta: metav1.ObjectMeta{Name: o.name, Namespace: o.namespace},
Data: map[string][]byte{"keyring": []byte(defaultKeyringData)},
}
test/builders/monitor.go:34
defaultKeyringDatais the base64 string from the YAML Secretdata:field, butcorev1.Secret.Datamust contain decoded bytes. Decode the base64 string once at build time so envtests exercise the controller with a realistic keyring payload.
// keyring value copied verbatim from the captured keyring-secret.yaml.
defaultKeyringData = "Clttb24uXQoJa2V5ID0gQVFCVm1RTnAxSm94SkJBQW5GUHVZUGZhcUZORkJIWUhCb0N0VGc9PQoJY2FwcyBtb24gPSAiYWxsb3cgKiIKCgpbY2xpZW50LmFkbWluXQoJa2V5ID0gQVFCVm1RTnAxbEF2SlJBQVp0V3ZZdDZnYXpPMWdTR1M5SEtYSFE9PQoJY2FwcyBtZHMgPSAiYWxsb3cgKiIKCWNhcHMgbW9uID0gImFsbG93ICoiCgljYXBzIG9zZCA9ICJhbGxvdyAqIgoJY2FwcyBtZ3IgPSAiYWxsb3cgKiIK"
)
type options struct {
- Files reviewed: 12/12 changed files
- Comments generated: 1
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
The new monitor keyring Secret builder stores base64-encoded fixture data as literal bytes, diverging from real Kubernetes-decoded Secret.Data and risking incorrect envtest behavior/fidelity.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
docs/superpowers/specs/2026-09-03-testing-foundation-design.md:31
- This status note claims #66/#68 already shipped and references
.github/workflows/ci.yaml, but that workflow file is not present in this PR branch. As written, the doc reads as if CI work is already in-tree, which is misleading given this PR’s scope (#67 only).
This issue also appears on line 36 of the same file.
test/builders/monitor.go:233
- MonitorKeyringSecret sets Secret.Data["keyring"] to the literal bytes of defaultKeyringData, but defaultKeyringData is copied from the deleted YAML fixture’s
data:field (base64-encoded on the wire). When the YAML was deserialized previously, Kubernetes decoded the base64 into the real keyring bytes; the new builder currently double-encodes the keyring and will cause the controller to propagate the wrong secret contents in envtest (it copiess.monitorKeyringSecret.Dataverbatim). Decode the base64 string (or store the decoded keyring text directly) before assigning to Data.
func MonitorKeyringSecret(opts ...Option) *corev1.Secret {
o := resolve(defaultKeyringSecret, "", opts)
return &corev1.Secret{
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"},
ObjectMeta: metav1.ObjectMeta{Name: o.name, Namespace: o.namespace},
Data: map[string][]byte{"keyring": []byte(defaultKeyringData)},
}
docs/superpowers/specs/2026-09-03-testing-foundation-design.md:36
- The #66 deliverable row states
.github/workflows/ci.yamland its jobs are already shipped, but that workflow isn’t present in this PR. Mark this as planned/future work (or remove detailed job claims) to avoid documenting non-existent artifacts.
| #66 | `.github/workflows/ci.yaml` — SHA-pinned, least-privilege, concurrency-cancel, timeouts, artifact-on-failure. Shipped jobs: `generate` (gen/helm/imports diff), `verify` (fmt/vet/lint/unit-race-shuffle/build), `envtest`, `helm` (lint+template), `vuln` (govulncheck); REUSE runs from the separate `reuse.yaml`. Documented required-check names. Split fast (PR) vs scheduled (nightly). |
- Files reviewed: 12/12 changed files
- Comments generated: 0 new
- Review effort level: Lite
Separate pure unit tests from envtest integration tests and remove the non-hermetic, source-mutating test workflow. - Gate controller envtest suites behind the `envtest` build tag; the unit layer (webhook defaulter/validator specs, fixture builders) now runs with plain `go test` and no Kubernetes API server or KUBEBUILDER_ASSETS. - Convert the webhook suite to pure unit tests: the specs call Default/Validate directly, so the envtest + webhook-server bootstrap that nothing exercised is removed in favor of a minimal RunSpecs entrypoint with RandomizeAllSpecs enabled. - Replace the captured mon-deployment.yaml (+ secret/configmap) fixtures with typed Go builders in test/builders, reproducing only the fields the controller consumes, with functional options and a contract test. - Add make targets that do not mutate source or clone Rook over the network: test-unit (race, shuffle), test-unit-repeat (20x for order dependence), test-envtest (explicit assets, envtest tag), test-all; redefine `test`. RandomizeAllSpecs is left off the controller suite pending #78 (a pre-existing finalizer-cleanup order dependence surfaced by this work). Package-level -shuffle=on is kept. Refs #67 Signed-off-by: senol.colak <senol.colak@sap.com>
… the strip path The mon fixture omitted --mon-host and --mon-initial-members, so the controller's modifyContainers strip branches for those args were never exercised by the envtest suite. The source mons this builder replaces carry both. Add them to the fixture. docs: reconcile the testing-foundation design doc with what shipped — mark #76 as planned (PR 4, not delivered), and correct the #66 job list to the jobs that actually exist in ci.yaml (generate/verify/envtest/helm/vuln + REUSE from reuse.yaml) instead of the aspirational names (crd-validate, race, etc.). Signed-off-by: senol.colak <senol.colak@sap.com>
…ontract test Address PR #79 review: the chown initContainer had no Args, so the controller's modifyContainers rewrite of a /var/lib/ceph/mon/ceph- path arg was never exercised for that container. Restore the real Rook chown args (chown -R ceph:ceph over the ceph dirs, including the mon data dir) and add a contract-test assertion that the initContainer carries a rewritable mon path. Also correct the Makefile 'test' comment: the suite does not clone Rook, but the envtest CRDs come from contrib/k8s/3rdparty/ (populated by 'make deps', a one-time network clone) and 'make env' downloads kube binaries — so it is reproducible, not fully network-free. Signed-off-by: senol.colak <senol.colak@sap.com>
448ac8f to
e085375
Compare
marcelradke
left a comment
There was a problem hiding this comment.
LGTM
I could run the unit tests
Seems a good preparation for further unit tests:
Replacing the yaml files to go-structures avoids local files and enables possible variations more easily.
* chore: add marcelradke and jsre as code owner Signed-off-by: senol.colak <senol.colak@sap.com> * chore: add jrse (Jan Radon) as code owner Signed-off-by: senol.colak <senol.colak@sap.com> --------- Signed-off-by: senol.colak <senol.colak@sap.com>
* fix(lima): replace flannel with cilium and fix containerd config - Replace Flannel v0.24.0 with Cilium v1.20.1 as the CNI plugin - Install Helm and Cilium CLI in the VM provisioning - Fix containerd crash caused by duplicate TOML CRI plugin table: use a drop-in config in conf.d/ instead of appending to config.toml - Update kubeadm config API from v1beta3 to v1beta4 for K8s 1.34+ - Set cilium-operator replicas to 1 for single-node cluster - Increase CoreDNS probe timeout to 600s for Cilium bootstrap time - Kubernetes 1.34.1 -> 1.37.0 - Rook 1.18.6 -> 1.20.7 - cert-manager v1.19.2 -> v1.21.1 - Ceph image quay.io/ceph/ceph:v19 -> v19.2.4 (test pin patch version) Signed-off-by: Jan Radon <jan.fabian.radon@sap.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* chore(contrib): upgrade VM base image to Ubuntu 26.04 LTS Signed-off-by: Jan Radon <jan.fabian.radon@sap.com> * fix(lima): add netplanOptional workaround for Ubuntu 26.04 boot hang Signed-off-by: Jan Radon <jan.fabian.radon@sap.com> --------- Signed-off-by: Jan Radon <jan.fabian.radon@sap.com>
Bumps golang from 1.26.2 to 1.27.1. --- updated-dependencies: - dependency-name: golang dependency-version: 1.27.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
@dependabot rebase |
Summary
First PR of the testing-foundation series. Makes the operator's test suites hermetic (no network, no source mutation) and deterministic, and separates the pure-unit layer from the API-server integration layer.
Closes #67.
What changed
//go:build envtest. The unit layer (webhook defaulter/validator specs, fixture builders) runs with plaingo test— no Kubernetes API server, noKUBEBUILDER_ASSETS.Default/Validatedirectly, so the envtest + webhook-server bootstrap (which nothing exercised through admission) is replaced by a minimalRunSpecsentrypoint withRandomizeAllSpecsenabled.test/builders/monitor.goreproduces only the fields the controller consumes, via functional options, with a contract test (monitor_test.go) that asserts the selector labels, mon container--fsid/--id=/--public-addr=/--setuser-match-path=args, theROOK_CEPH_MON_HOSTsecretKeyRef, and the three named volumes — so the builders cannot silently drift from what the controller rewrites. Deletes the 4 fixtures undercontrib/k8s/test/.test-unit,test-unit-repeat(loops 20×,-race, fresh shuffle/Ginkgo seed each run to expose order dependence),test-envtest,test-all,test. None mutate source or clone Rook over the network.docs/superpowers/specs/.Verification
go build ./...andgo vet ./...clean in both plain and-tags envtestmodes.-race -shuffle=on.Known follow-ups (not blockers)
namespaceCleanUpdeletes finalizer-bearing Deployments without waiting).RandomizeAllSpecsis left disabled on the controller suite only (package ordering and per-run seeds still shuffle) with aNOTE(#67)explaining the mechanism; the fix lands in Expand controller, webhook, API, and lifecycle test coverage #68.