Skip to content

experiment: vendor setup-envtest (mirror of #516)#518

Closed
frobware wants to merge 4 commits into
bpfman:mainfrom
frobware:experiment/setup-envtest-vendoring
Closed

experiment: vendor setup-envtest (mirror of #516)#518
frobware wants to merge 4 commits into
bpfman:mainfrom
frobware:experiment/setup-envtest-vendoring

Conversation

@frobware
Copy link
Copy Markdown
Contributor

@frobware frobware commented May 5, 2026

Draft. Not for merge. Stepwise demonstration that PR #516's masked-pass on CI is the result of three independent layers of masking, any one of which would have caught the broken vendor tree on its own.

CI logs on github.com expire (default ~90 days), so each stage's evidence is mirrored inline below.

Summary

Stage Commit Change Run Conclusion
A c99d9daa cherry-pick of #516 verbatim (broken vendor) 25372315984 success (false negative)
B 04c7abc7 drop $(shell ...) masking in Makefile 25372937936 failure (Test)
C d8088215 assert vendor cleanliness in Verify job 25373577585 failure (Test + Verify)
D abb1d9a3 git add vendor/ for the three missing dirs 25374160435 success (genuine)

Stage A -- cherry-pick of #516 verbatim (broken vendor)

Smoking gun -- Test job log

Job: https://github.com/bpfman/bpfman-operator/actions/runs/25372315984/job/74398941940 (conclusion: success)

2026-05-05T10:59:40Z go fmt ./...
2026-05-05T10:59:42Z cannot find module providing package
  sigs.k8s.io/controller-runtime/tools/setup-envtest:
  import lookup disabled by -mod=vendor
2026-05-05T10:59:42Z (Go version in go.mod is at least 1.14 and vendor directory exists.)
2026-05-05T10:59:42Z KUBEBUILDER_ASSETS="" go test ./... -coverprofile cover.out

The recipe KUBEBUILDER_ASSETS="$(shell go run sigs.k8s.io/...setup-envtest ...)" invokes setup-envtest, which fails because three top-level vendor directories were never git added by the original PR (afero, x/text/runes, controller-runtime/tools). GNU Make's $(shell ...) captures stdout and silently discards the exit code, so the variable expands to the empty string. Tests then run with no envtest assets and pass anyway, because no test in the suite consumes KUBEBUILDER_ASSETS.

Verify job -- no-op vendor check

Job: https://github.com/bpfman/bpfman-operator/actions/runs/25372315984/job/74398941911 (conclusion: success)

2026-05-05T11:00:22Z ##[group]Run go mod vendor
2026-05-05T11:00:22Z go mod vendor

The "Check clean vendors" step runs go mod vendor and discards the result; no git diff or git status follow-up. With the broken vendor tree, go mod vendor materialises the missing directories at runtime, exits 0, and the step passes -- drift undetected.

Three layers of masking

Layer Mechanism Fixed in stage
1 $(shell ...) swallows non-zero exits in Make B
2 "Check clean vendors" step never asserts a clean tree C
3 No test in the suite currently consumes KUBEBUILDER_ASSETS not in this exercise

Stage B -- drop $(shell ...) masking in Makefile

Smoking gun -- Test job log

Job: https://github.com/bpfman/bpfman-operator/actions/runs/25372937936/job/74400895857 (conclusion: failure)

2026-05-05T11:13:27Z go fmt ./...
2026-05-05T11:13:30Z cannot find module providing package
  sigs.k8s.io/controller-runtime/tools/setup-envtest:
  import lookup disabled by -mod=vendor
2026-05-05T11:13:30Z make: *** [Makefile:321: test] Error 1

Same cannot find module error as Stage A, but now the recipe propagates the failure: no silent fallback to KUBEBUILDER_ASSETS="", no spurious test pass. The Test job exits 2, the run conclusion is failure, and the PR is correctly flagged.

This proves layer 1 was masking a real, immediate runtime breakage. Layer 2 (the Verify job) is still passing -- it would not have caught this without Stage C.


Stage C -- assert vendor cleanliness in Verify job

Smoking gun -- Verify job log

Job: https://github.com/bpfman/bpfman-operator/actions/runs/25373577585/job/74403075256 (conclusion: failure)

2026-05-05T11:28:59Z vendor/, go.mod or go.sum is out of sync with 'go mod vendor':
2026-05-05T11:28:59Z ?? vendor/github.com/spf13/afero/
2026-05-05T11:28:59Z ?? vendor/golang.org/x/text/runes/
2026-05-05T11:28:59Z ?? vendor/sigs.k8s.io/controller-runtime/tools/

The new check runs go mod vendor and then asserts with git status --porcelain vendor go.mod go.sum that the working tree is unchanged. Because the broken tree was missing those three directories, go mod vendor materialised them and the step exited 1, naming the offending paths in the workflow log. This is the cause of the breakage, not a downstream symptom -- and it is reported at the verify stage, before Test even runs.

Test job log (still failing on the same Stage B reason)

Job: https://github.com/bpfman/bpfman-operator/actions/runs/25373577585/job/74403075196 (conclusion: failure)

2026-05-05T11:28:19Z go fmt ./...
2026-05-05T11:28:20Z cannot find module providing package
  sigs.k8s.io/controller-runtime/tools/setup-envtest:
  import lookup disabled by -mod=vendor
2026-05-05T11:28:20Z make: *** [Makefile:321: test] Error 1

Test continues to fail until Stage D commits the missing vendor source; Stage C only fixes detection, not the underlying breakage.


Stage D -- git add vendor/ for the three missing dirs

Test job log -- envtest assets resolved

Job: https://github.com/bpfman/bpfman-operator/actions/runs/25374160435/job/74405070815 (conclusion: success)

2026-05-05T11:41:39Z go fmt ./...
2026-05-05T11:41:48Z KUBEBUILDER_ASSETS="/home/runner/work/bpfman-operator/bpfman-operator/bin/k8s/1.25.0-linux-amd64" go test ./... -coverprofile cover.out
2026-05-05T11:43:14Z ok  	github.com/bpfman/bpfman-operator/controllers/bpfman-agent	0.375s	coverage: 61.3% of statements

KUBEBUILDER_ASSETS now points at the real envtest binaries directory (Stage B's recipe asserts non-empty before running go test, and the assertion holds). Compare with Stage A and Stage C, where the same line read KUBEBUILDER_ASSETS="".

Verify job -- vendor assertion passes

Job: https://github.com/bpfman/bpfman-operator/actions/runs/25374160435/job/74405070735 (conclusion: success)

The "Check clean vendors" step runs go mod vendor against the now-complete tree. git status --porcelain reports no changes; no out of sync message is emitted; the step exits 0.


Conclusions

  • Layer 1 ($(shell) masking) was the most dangerous. Without Stage B, even the explicit verify-step check from Stage C only flags vendor drift; the runtime breakage stays masked at every other layer.
  • Layer 2 (no-op verify step) was easy to overlook because the step was named "Check clean vendors". The name implied an assertion; the body was an effect-free invocation.
  • Layer 3 (no test consumes KUBEBUILDER_ASSETS) is latent. The first test that actually needs envtest assets would have failed loudly on a Stage-A-shaped tree even with both upstream layers masking. None has been added yet.
  • The minimum diff that turns this into a genuinely safe pipeline is Stage B + Stage C. Stage D is just the missing source the original PR forgot to commit.

alebedev87 and others added 4 commits May 5, 2026 11:56
Add `tools/tools.go` with a blank import of `setup-envtest` so the
tool is pulled into the vendor directory. Uses the `release-0.22`
branch pseudo-version which requires Go 1.24 (compatible with the
project's Go 1.25).

Replace `go install` invocation of `setup-envtest` in the Makefile
with `go run`, removing the `envtest` target and `ENVTEST` variable.

Co-Authored-By: Claude
Signed-off-by: Andrey Lebedev <alebedev@redhat.com>
The previous recipe used `KUBEBUILDER_ASSETS="$(shell go run
sigs.k8s.io/...setup-envtest ...)"` to compute the assets path.  GNU
Make's `$(shell ...)` captures stdout only and silently discards the
exit code, so when `go run` failed (for example with `cannot find
module providing package ... import lookup disabled by -mod=vendor`)
the variable expanded to the empty string and tests proceeded with
`KUBEBUILDER_ASSETS=""`.  Tests that do not currently consume the
assets path then passed regardless, masking the real failure.

Move the substitution into the recipe shell.  After `go run`, assert
that `KUBEBUILDER_ASSETS` is non-empty and exit with a clear message
otherwise.  Echo the resolved invocation before running it so the
build log records the exact `go test` command, mirroring the
readability of the previous one-liner.

Signed-off-by: Andrew McDermott <amcdermo@redhat.com>
The "Check clean vendors" step previously ran `go mod vendor` and
discarded the result, so a pull request could leave `vendor/`,
`go.mod` or `go.sum` out of sync with the module graph and CI would
still pass.  In particular, when a contributor runs `go mod vendor`
locally but forgets to `git add` newly created vendor directories,
the resulting tree builds against the module proxy yet fails when
later consumed with `-mod=vendor`.

Run `go mod tidy` and then `go mod vendor`, and assert with
`git status --porcelain` that no tracked changes and no untracked
entries remain under `vendor/`, `go.mod` or `go.sum`.  This catches
two classes of drift: missing or stale `vendor/` source (forgot to
commit a newly vendored directory), and `go.mod` or `go.sum`
divergence from the actual import graph (forgot to tidy after
manually editing requires).  `git diff --exit-code` alone is
insufficient because it ignores untracked files, which is precisely
the failure mode we need to catch.  On failure, print the offending
paths and the diff to make the cause obvious in the workflow log.

Co-authored-by: Andrey Lebedev <alebedev87@gmail.com>
Signed-off-by: Andrew McDermott <amcdermo@redhat.com>
The earlier commit `Add setup-envtest to tools/tools.go for vendoring`
updated `go.mod`, `go.sum` and `vendor/modules.txt` but did not
`git add` the three top-level directories that `go mod vendor`
materialises:

  vendor/github.com/spf13/afero/
  vendor/golang.org/x/text/runes/
  vendor/sigs.k8s.io/controller-runtime/tools/

`vendor/modules.txt` referenced packages whose source was absent on
disk, so any build with `-mod=vendor` (the default when a `vendor`
directory exists) reported `cannot find module providing package`.
That failure was previously masked twice: first by `$(shell ...)` in
the Makefile silently swallowing the non-zero exit, and second by the
"Check clean vendors" CI step never asserting a clean tree after
`go mod vendor`.  Both layers have been removed in earlier commits on
this branch; this commit removes the underlying breakage by committing
the missing source.

After this commit, `go run sigs.k8s.io/controller-runtime/tools/setup-envtest`
resolves under `-mod=vendor`, the Test job runs to completion with a
populated `KUBEBUILDER_ASSETS`, and the Verify job's vendor assertion
passes because `go mod vendor` is a no-op against the committed tree.

Signed-off-by: Andrew McDermott <amcdermo@redhat.com>
@frobware frobware force-pushed the experiment/setup-envtest-vendoring branch from abb1d9a to 3b982e6 Compare May 5, 2026 12:28
@frobware
Copy link
Copy Markdown
Contributor Author

frobware commented May 5, 2026

Changes being pulled back into #516

@frobware frobware closed this May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants