Skip to content

test: integrate controller-manager image publishing + e2e GINKGO_NODES parallelism - #19

Merged
indradhanush merged 16 commits into
mainfrom
dg/feature/ci
Jul 17, 2026
Merged

test: integrate controller-manager image publishing + e2e GINKGO_NODES parallelism#19
indradhanush merged 16 commits into
mainfrom
dg/feature/ci

Conversation

@indradhanush

Copy link
Copy Markdown
Collaborator

Summary

Integration/testing branch combining two in-progress efforts so they can be validated together before landing on the real repo's main:

  • dg/predictable-agent-versioning: git-describe-based versioning for agent/byohctl artifacts, plus new build-push-agent-bundle.yml / build-push-controller-manager.yml workflows that publish images to quay.io/platform9/cluster-api-provider-bringyourownhost/*.
  • dg/fix-all-e2e: e2e CI switched from a 7-way GINKGO_FOCUS matrix to a single job using Ginkgo's own GINKGO_NODES parallelism (with the concurrency-safety fixes that requires), plus aligning the default controller-manager image reference (Makefile, kustomization, e2e provider config) with the new quay.io registry so make docker-build's default output is directly usable by e2e with no custom image reference juggling.

Both branches were merged in cleanly (one fast-forward, one auto-merge, no conflicts).

Test plan

  • Confirm build-push-controller-manager.yml runs on this PR and publishes successfully
  • Confirm e2e-suite runs as a single job with GINKGO_NODES=4 and check whether the shared kind control plane holds up under concurrent specs (this is the main open question — resource contention moved from sequential to concurrent, unproven either way)
  • If e2e passes, treat both source branches as validated for landing on origin's main

indradhanush and others added 15 commits July 16, 2026 14:17
The hardcoded ByohAgentDebPackageURL constant required a manual
hand-edit every time the agent bundle changed. Replace it with a
ByohAgentBundleURL() function that composes the OCI repo path with
byohctl's own baked-in version.GetVersion(), so byohctl and its
expected agent bundle are always tied to the same git-describe version
with zero manual edits.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Version now comes from `git describe --dirty --tags --match='v*'`
(overridable via `BYOH_DEB_VERSION` env var) instead of an externally-
supplied `BUILD_NUMBER`. This ensures the published bundle tag matches
what byohctl bakes in at build time with no manual coordination.

Also, update the publish target to match the new
quay.io/platform9/cluster-api-provider-bringyourownhost/agent path that
byohctl now expects. This ensures the CI pipeline pushes to the same OCI
registry location that the agent installer references.
Closes the loop: every push to main that passes CI now automatically
builds and publishes the agent deb bundle tagged with that commit's
git-describe version. This replaces the previous manual rebuild-and-
hand-edit-a-constant process.

Triggers via `workflow_run` off the existing CI workflow's success
(avoiding a duplicate test run) plus a manual `workflow_dispatch` for
dry runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a new `tag` target that prints GIT_VERSION, derived from
git-describe-based versioning. This becomes the one place every
agent-side artifact (byohctl binary, agent bundle) gets its version
from, instead of each consumer independently recomputing the same
git-describe expression.

The tag target is distinct from the existing TAG variable, which is
the separate controller-manager image tag (still static "dev" by
default). Unifying controller-manager versioning is follow-up work.

Matches kaapi's philosophy in ~/pf9/kaapi/Makefile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Previously, cmd/byohctl/Makefile, .ci/build-push-agent-deb.sh, and
.github/workflows/build-push-agent-bundle.yml each computed their own
copy of the git-describe expression independently:

  git describe --dirty --tags --match='v*' 2>/dev/null || echo "v0.0.0-..."

Now all three defer to the root Makefile's `make tag` target instead.
This ensures exactly one place owns the version computation and every
agent-side artifact gets the same version without duplication or drift.

byohctl's VERSION override escape hatch (`make build VERSION=...`)
still works since make tag is only consulted via ?=.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The git describe call backing make tag now passes --abbrev=8 to
standardize on 8-character short SHAs instead of git's default 7
characters. The fallback git rev-parse --short=8 is updated to match,
ensuring both code paths produce the same short SHA length.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Print tag step previously computed the tag into a shell variable
and exported it to $GITHUB_ENV for the build step to reuse. This is now
redundant since .ci/build-push-agent-deb.sh already calls make tag
itself as its own fallback, so the step now simply runs make tag and
prints the value directly without the extra plumbing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This extends the same git-describe-based versioning philosophy already
applied to the agent bundle to the controller-manager Docker image.
.ci/build-push-controller.sh now computes IMAGE_TAG from make tag
instead of a manually-set \$BYOHCM_VERSION.\$BUILD_NUMBER, and publishes
to quay.io/platform9/cluster-api-provider-bringyourownhost/controller-manager
(nested under the same path as the agent bundle, just a different image
name) instead of the separately-named quay.io/platform9/byoh-controller-manager.

The configure_docker_registry function's registry-match guard was updated
to match the new default registry path; it gates whether docker login runs
and would have silently skipped login otherwise.

The new workflow mirrors the agent bundle workflow's trigger design
(workflow_run off CI success on main, plus workflow_dispatch for manual
dry runs) but requires a real Set up Go step (unlike the agent bundle
workflow) since the script's go mod vendor call runs on the host.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ci.yml: Add draft filter to jobs and extend trigger types.
- Added `if: ${{ !github.event.pull_request.draft }}` to both
  workflow jobs to skip execution on draft PRs.
- Added `ready_for_review` to trigger types list (default types are
  opened/synchronize/reopened, which don't include the draft-to-ready
  transition). This re-runs CI when a draft PR is marked ready without
  requiring a new push.

build-push-agent-bundle.yml and build-push-controller-manager.yml:
Add pull_request trigger for PR builds and publishes.
- Previously these workflows only ran via workflow_run (on CI success on
  main) or workflow_dispatch. Now they also trigger on non-draft PR
  push (same draft-skip via `if: ${{ !github.event.pull_request.draft }}`
  and same ready_for_review addition).
- This enables building and pushing real tagged artifacts on every
  non-draft PR push, per explicit request. (Fork PRs will fail at quay
  login due to GitHub Actions not exposing repo secrets to fork-sourced
  pull_request events — a known, accepted limitation.)

Checkout ref fix for pull_request events:
- The checkout `ref` now falls back to `github.event.pull_request.head.sha`
  before `github.sha`. For pull_request events, `github.sha` points at a
  synthetic merge commit, not the actual PR branch's head. Using the
  synthetic commit would cause `git describe` (and thus `make tag`) to
  compute a different version than the same commit checked out any other
  way, leading to inconsistent versioning. The explicit PR head SHA
  ensures consistent version computation across all checkout contexts.
GNU Make automatically enables --print-directory whenever -C is used, printing
'make: Entering directory' and 'Leaving directory' messages. This repo's dev
Mac ships an ancient GNU Make 3.81 that does not do this, but the GitHub
Actions Ubuntu runner's newer Make (4.x) does.

In .ci/build-push-controller.sh, IMAGE_TAG=$(make -C "${project_root}" tag)
captured that chatter into IMAGE_TAG via command substitution, corrupting the
docker build -t argument and failing the controller-manager publish workflow.
The same latent bug existed in cmd/byohctl/Makefile's VERSION assignment, just
not yet triggered since byohctl had only been built locally with the old Make.

Fixed both by passing --no-print-directory to make -C. Also added a fail-loud
whitespace guard on IMAGE_TAG in the controller script so this class of bug
cannot silently corrupt a docker tag again.

Verified: reproduced the failure locally with GNU Make 4.4.1, confirmed
--no-print-directory fixes it, and re-verified go build, go test, and shell
linting all pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The e2e workflow previously ran all 7 Ginkgo specs sequentially in a single
job, taking ~59 minutes with resource contention (shared kind cluster/docker
daemon) causing 600s control-plane timeouts on 4/7 specs. Each spec is
independently scoped (creates/tears down its own namespace, hosts, cluster)
with no technical coupling.

This branch only exercised 2 of 7 specs; the catch-all step that ran
everything had been removed.

Restructure the workflow to give each spec a unique Ginkgo focus label and
run them as separate parallel jobs via GINKGO_FOCUS matrix (fail-fast: false).
Each spec now runs in isolation without resource contention, significantly
reducing total wall-clock time and preventing timeouts.

Go changes add unique focus labels to specs that had none or shared a
non-exclusive tag, plus a Platform9 copyright header addition (no logic changes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When a job uses a matrix strategy without an explicit name, GitHub Actions
auto-appends every matrix key to the job's display name, creating noise with
escaped regex characters. Setting name: e2e (${{ matrix.name }}) provides a
cleaner, consistent UI by showing just the GINKGO_FOCUS variant name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Prior commit (633390b) split the e2e workflow into 7 GitHub Actions jobs,
one per Ginkgo spec, which fixed coverage but redundantly repeated setup
(docker builds, kind cluster, CAPI provider install, host-agent compile)
across each job. This switch consolidates to a single job with Ginkgo's
built-in GINKGO_NODES=4 process parallelism, leveraging the existing
SynchronizedBeforeSuite/SynchronizedAfterSuite scaffolding already in
test/e2e/e2e_suite_test.go (designed exactly for this) to share resources
once. This eliminates per-job redundant setup entirely and drops the dead
"Install ginkgo" step from the workflow. Workflow also bumps
actions/setup-go to v7 with go-version 1.26.2 (matching go.mod directive)
and enables action cache.

Making specs run concurrently in one process required fixing verified
concurrency bugs: hardcoded Docker container names ("byohost1"/"byohost2")
and /tmp log file paths (including collisions among the templated versions)
duplicated across multiple spec files, a shared /tmp kubeconfig-staging
path used during container setup made race-prone by concurrent access (now
unique per call), and two /tmp debug-script paths made per-process-unique
via PID suffix on test failure.

Trade-off accepted: resource contention moves from sequential (original
59-minute single-process run with 4/7 specs timing out) to concurrent
(all specs at once on one runner, one shared docker daemon and kind API
server). This is a different, unproven failure mode that requires
validation with a real run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A separate sibling workflow (build-push-controller-manager.yml) now publishes
the controller-manager image to
quay.io/platform9/cluster-api-provider-bringyourownhost/controller-manager
instead of the old gcr.io/k8s-staging-cluster-api/cluster-api-byoh-controller.
Before this commit, the e2e test config (test/e2e/config/provider.yaml) still
hardcoded the old gcr.io reference with loadBehavior: mustLoad, which would
prevent the locally-built image (via `make docker-build` using the Makefile's
STAGING_REGISTRY/IMAGE_NAME defaults) from matching what e2e's bootstrap
cluster setup expects to load, and the rendered manager Deployment manifest
would reference the old name.

This commit updates Makefile's STAGING_REGISTRY and IMAGE_NAME defaults,
the kustomize image transformer's newName in config/manager/kustomization.yaml,
and both references in test/e2e/config/provider.yaml (the mustLoad image entry
and the tag-rewrite replacements rule) so they all consistently point at the
new quay.io path. Intent: the image built by `make docker-build` with no
overrides should be usable directly by e2e tests, with no separate image
reference juggling needed.

config/manager/manager.yaml's base image field was deliberately left unchanged
because the kustomize image transformer always overrides it at build/deploy time
regardless, verified not to affect `make deploy`/`make install`/`make
publish-infra-yaml`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@indradhanush
indradhanush merged commit 09f3961 into main Jul 17, 2026
8 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant