Skip to content

ci: run integration tests as nix flake checks - #3342

Draft
kradalby wants to merge 10 commits into
juanfont:mainfrom
kradalby:integration-nix-checks
Draft

ci: run integration tests as nix flake checks#3342
kradalby wants to merge 10 commits into
juanfont:mainfrom
kradalby:integration-nix-checks

Conversation

@kradalby

@kradalby kradalby commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

hypothetical pr to see if we can speed up and run the CI tests inside a Nix enclosure. The goal is to have to rebuild less things by using Nix caches for things that dont change, and to make them run on Garnix which I hope to set up.

@kradalby
kradalby force-pushed the integration-nix-checks branch 2 times, most recently from a227589 to 78aeef9 Compare June 23, 2026 19:50
Comment thread integration/dsic/dsic.go Outdated
Comment thread nix/tests/integration.nix
Comment thread nix/images.nix Outdated
Comment thread nix/images.nix Outdated
Comment thread nix/images.nix Outdated
Comment thread nix/images.nix Outdated
Comment thread nix/images.nix Outdated
Comment thread nix/images.nix Outdated
Comment thread nix/images.nix
Comment thread .github/workflows/gh-action-integration-generator.go
@kradalby
kradalby force-pushed the integration-nix-checks branch 4 times, most recently from 36c5a61 to 4a3e731 Compare June 25, 2026 08:08
kradalby and others added 6 commits July 1, 2026 12:11
The hermetic nix checks cannot pull tailscale images at eval time, so the tested
version set and the per-arch image digests are pinned in
integration/tailscale-versions.json. capver.IntegrationVersions() is the single
source the suite, the drift guard and the pin generator share; the generator
(tools/tailscale-versions, run via go generate) refreshes it after a capver bump
and reuses already-present pins so a routine generate makes no network call.
Replaces the Dockerfile builds for the flake-check path. headscale is built
from the Go-filtered source; one tailscale-HEAD image serves client, daemon,
derper and the websocket-DERP test. Released tailscale versions and postgres
are pinned registry pulls, arch following the build system. Tools the suite
used to apk-add (curl/ssh/dig/python3) and the SSH login users are baked in at
build time so no container is mutated at startup, and busybox su is included so
taildrive can fork its per-user fileserver.
Read the HEADSCALE_INTEGRATION_*_IMAGE knobs through one envknob helper and
assert required tooling is present instead of installing it at runtime (the
nix VM is offline). dsic waits for both the derper cert and key before starting
derper, so the container can't die between the two uploads. The nested-docker
VM is slower than a host daemon, so every integration timeout (convergence,
docker exec, peer sync, pool wait) now flows through one
dockertestutil.ScaleTimeout honouring HEADSCALE_INTEGRATION_TIMEOUT_SCALE.
Each test becomes a hermetic check: a VM boots docker, loads the prebuilt images
and runs that one test (nix/tests/integration.nix), so the suite runs offline as
nix build .#checks.<name>. The generator emits the test list as nix: heavy
multi-subtest tests are split into per-subtest checks, and tests the nested
docker VM can't host are excluded for the legacy pipeline. flake.nix wires the
checks, the images, and the Go build/test/lint checks (with the version JSON on
the embed whitelist).
…cy pipeline

CI runs nix build .#checks.<system>.integration-* instead of the bespoke docker
matrix — the generated checks are the matrix. The tests the hermetic VM can't
run (real-internet DERP, docker cable-pull) run via cmd/hi in a slim legacy
workflow, which also keeps cmd/hi and the integration Dockerfiles exercised.
check-tests guards the pinned versions against capver drift.
Garnix builds every flake check and package directly, so the GitHub
Actions wrappers around them are redundant:

  - nix-checks       checks.{build,gotest,golangci-lint,formatting}
  - nix-module-test  checks.headscale (NixOS module test)
  - test-integration the integration-* NixOS-VM check matrix
  - build            packages.headscale (nix build)

Kept are the pipelines garnix cannot reproduce: servertest (excluded
from the flake), integration-legacy (needs real DERP, so it cannot run
hermetically), and the check-generated / check-tests generators that
keep the nix integration matrix and protobuf output in sync.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxukxKem3HU4i5CqNrvvdj
@kradalby
kradalby force-pushed the integration-nix-checks branch from 89b4898 to a4a3633 Compare July 1, 2026 12:15
Rebasing onto main pulled in TestK8sOperator and the OAuth client CLI tests; regenerate integration/tests.nix so the NixOS-VM check matrix runs them too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxukxKem3HU4i5CqNrvvdj
@kradalby
kradalby force-pushed the integration-nix-checks branch from a4a3633 to 566a978 Compare July 1, 2026 13:57
kradalby and others added 3 commits July 1, 2026 15:07
The self-hosted garnix builder runs many checks at once (nix max-jobs is 16 on our box), so the 8 GB integration VMs overcommit its 64 GB RAM (16 * 8 = 128 GB) and the whole matrix thrashes with none completing. Drop each VM to 3.5 GB (16 * 3.5 = 56 GB, leaving host headroom) so the checks actually run; raise it back toward 8 GB once the builder caps max-jobs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxukxKem3HU4i5CqNrvvdj
The 3.5 GB reduction was a diagnostic. The matrix wedged because the self-hosted builder runs unbounded max-jobs and does not cancel superseded runs (stacked ghost VM builds), not because 8 GB VMs are too big. Keep the tested-safe size; the fix is capping the builder's max-jobs so memorySize * max-jobs fits its RAM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxukxKem3HU4i5CqNrvvdj
Running each of the 161 integration tests as its own 8 GB NixOS-VM check overcommits a single self-hosted garnix builder: garnix fans them out past the builder's RAM and the OOM killer takes down the build (and the garnix coordinator VM itself). Group the matrix into 6 sqlite batches + 1 postgres batch, each running its tests sequentially (-test.parallel=1) in one 6 GB VM, so at most 7 VMs (~42 GB) ever run regardless of garnix's build concurrency. Trade-off: per-test checks collapse to per-batch (a failure shows in the batch log, not as its own check).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WxukxKem3HU4i5CqNrvvdj
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.

1 participant