Skip to content

test(e2e): credential-free mock E2E suite + real-AWS CI cap (1 PR / 2 post-merge / weekly matrix)#844

Merged
ArangoGutierrez merged 7 commits into
NVIDIA:mainfrom
ArangoGutierrez:feat/aws-mock-e2e
Jul 9, 2026
Merged

test(e2e): credential-free mock E2E suite + real-AWS CI cap (1 PR / 2 post-merge / weekly matrix)#844
ArangoGutierrez merged 7 commits into
NVIDIA:mainfrom
ArangoGutierrez:feat/aws-mock-e2e

Conversation

@ArangoGutierrez

Copy link
Copy Markdown
Collaborator

Problem

Every CI run provisions real AWS infrastructure: 3 real E2E jobs per PR push and a
14-job real matrix (plus arm64 + integration-test) per merge — ~60 min wall time and
real EC2/EIP/GPU-instance cost per pipeline, with credentials required even for fork
PRs. Meanwhile, failure modes that real E2E can't afford to exercise (API errors
mid-create, rollback, idempotent teardown) had no coverage at all.

Approach

AWS ships no official mocks for aws-sdk-go-v2; its documented testing guidance is
consumer-defined interfaces + DI + your own fakes — exactly the shape holodeck
already has (internal/aws.{EC2Client,SSMClient,ELBv2Client} + WithEC2Client/...
options). This PR:

  1. internal/aws/awsfake — one stateful, mutex-guarded in-memory fake
    implementing all three interfaces (typed against the official SDK structs;
    compile-time assertions make SDK upgrades fail loudly). Realistic lifecycle
    semantics: ID generation, instance state transitions compatible with the SDK
    waiters, exact AWS *.NotFound error codes that the delete/retry paths string-match,
    error injection (FailNext) and a call/input recorder.
  2. Mock E2E suite (tests/e2e_mock_test.go, label mock) — 11 specs driving the
    real Provider.Create/Delete/DryRun code through the fake, credential-free, ~2.4s:
    full lifecycle for 4 topologies (single-node, minimal, dedicated-CP, HA+NLB),
    rollback-on-injected-failure at 5 create phases (asserts zero orphaned resources),
    cluster partial-failure cleanup, idempotent delete.
  3. Real tests label-gated — every existing E2E entry keeps its labels and gains
    real-aws; E2E_SSH_KEY is now asserted per-real-spec (fail-fast) instead of
    suite-wide, so mock runs need nothing.
  4. CI split — PR push: mock suite (no secrets) + 1 real single-node GPU smoke.
    Merge to main/release: mock suite + 1 real minimal cluster + the existing
    issue-on-failure job. New e2e-weekly.yaml: the displaced 15-label real matrix
    (incl. nvidia-driver) + arm64 + integration-test, weekly cron + manual
    workflow_dispatch with a label-filter input.
  5. Provider unit tests migrated off the 494-line hand-written MockEC2Client
    (deleted) onto the same fake — coverage envelope preserved 1:1, one previously
    vacuous assertion made real, plus new pinning tests for SG-revoke and NLB
    teardown-order behavior. Harness modernization: ginkgo CLI pinned to the go.mod
    version, JSON reports, no sleeps.
  6. Contributor docs rewritten for the new tiers and how-to-add-a-test flow.

Testing done

  • go test ./pkg/provider/aws/... -count=1: 157 Go tests + 84 Ginkgo specs, 0 failures
  • go test ./internal/aws/awsfake/ -count=1: 34 tests (fake behavior contract)
  • Mock suite credential-free (env -u AWS_* -u E2E_SSH_KEY): 11/11 in ~2.4s
  • --label-filter='real-aws' --dry-run: 24 specs selected; smoke and post-merge
    filters each select exactly 1 spec
  • Keyless non-dry-run of the smoke filter fails fast at the env guard in ~3s with
    zero AWS API activity (guard proven)
  • Test quality verified by mutation: 12+ deliberate breakages of fake/provider
    behavior each turned the corresponding assertions red (rollback, NotFound codes,
    ENI drain, owner-id filter, teardown order, fault-injection seam)
  • yamllint (relaxed) + actionlint on all touched workflows: no new findings

Cost/latency impact

Pipeline Real AWS jobs before after
PR push 3 1
Merge (main/release) 14 + arm64 + integration 2 (1 smoke + 1 cluster)
Weekly (new) 15-label matrix + arm64 + integration (scheduled/manual)

Breaking changes

None to the CLI or provider API. CI behavior: rpm/dra/kernel/git-source real coverage
moves from per-merge to weekly + on-demand dispatch.

Known follow-ups (intentionally out of scope)

  • arm64 weekly job is a pre-existing zero-spec no-op (no spec carries that label) — kept verbatim; needs a real arm64 spec or removal.
  • post-merge label is now informational only (documented as such).
  • Weekly and post-merge create-issue jobs share the e2e-failure dedup label — weekly failures comment onto open post-merge issues.
  • Empty-filter workflow_dispatch runs nothing silently; dispatch failures don't file issues.
  • A second hand-written MockEC2Client remains in pkg/testutil/mocks (used by pkg/cleanup) — this PR scopes to pkg/provider/aws.
  • Dead code in the cluster path (createPrivateRouteTable/createNATGateway, zero callers) — candidate for removal.
  • Main pushes run the mock suite twice (smoke + full) — cheap (~2.4s) but dedupable.

Reviewer FAQ

Why not LocalStack/moto? Community LocalStack EC2 is emulation-only — no SSH-able
VMs, so holodeck's provisioner phase can't run against it either; it adds a Docker
dependency and ~30s startup per job for no coverage the fake doesn't give. The fake is
typed against the official SDK structs and follows AWS's documented Go SDK v2 testing
pattern.

How do we catch fake-vs-real drift? SDK shape changes: compile-time interface
assertions. Semantic drift: the 2 real jobs per merge + weekly matrix; policy — a
mock-green/real-red divergence is treated as a fake bug, not a flake.

Who owns weekly failures? The weekly create-issue job files/updates a GitHub
issue; suggest assigning a triage owner so the matrix doesn't rot (risk register).


Merge strategy note for maintainers: the branch is 6 logical commits, each
building and testing green standalone — squash and rebase are both safe.

…terfaces

AWS ships no official mocks for aws-sdk-go-v2; its documented testing
guidance is consumer-defined interfaces + DI + own fakes. This fake
implements holodeck's internal/aws interfaces (39 EC2 + 12 ELBv2 + 1 SSM
methods) over a mutex-guarded in-memory store with realistic lifecycle
semantics: SDK-waiter-compatible instance states, exact *.NotFound error
codes the provider's retry branches string-match, create/describe/delete
consistency, error injection (FailNext) and a call/input recorder.
Compile-time interface assertions make SDK shape changes fail the build.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…ten mocks

Replaces the 494-line per-method-closure MockEC2Client (and the local
MockELBv2Client in nlb_delete_test.go) with the shared stateful fake.
Coverage envelope preserved 1:1 (155 subtests + 84 ginkgo specs before
and after); one previously vacuous owner-id assertion made real; adds
mutation-verified pinning tests for SG cross-reference revocation and
NLB listener->target-group->LB teardown order. Shared helpers move to
helpers_test.go.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…n seam

11 specs (label 'mock') drive the real Provider.Create/Delete/DryRun
code against awsfake in ~2.4s with no AWS credentials or SSH key:
lifecycle for 4 topologies (single-node, minimal, dedicated-CP, HA+NLB),
rollback-on-injected-failure at 5 create phases asserting zero orphaned
resources, cluster partial-failure cleanup via Delete, idempotent
delete. newProvider gains an aws.Option passthrough; the suite-wide
E2E_SSH_KEY requirement moves to a per-spec requireRealAWSEnv guard;
the ginkgo CLI is pinned to the go.mod version instead of @latest.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
All 24 real-AWS entries gain 'real-aws' as their first label (existing
labels preserved verbatim) and both DescribeTable bodies fail fast via
requireRealAWSEnv when E2E_SSH_KEY is absent — keyless runs abort in
seconds with zero AWS API activity instead of failing mid-provision.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…e cluster, add weekly matrix

PR pushes: mock suite (secret-free) + exactly 1 real job
('real-aws && default && !rpm', was 3 real jobs). Merges to
main/release: mock suite + 1 real minimal-cluster job + the existing
issue-on-failure automation. The displaced 15-label real matrix
(including nvidia-driver, previously smoke's third label) plus arm64
and integration-test move to e2e-weekly.yaml: weekly cron + manual
workflow_dispatch with an env-indirected label_filter input.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…tiers

Documents the three CI tiers, the credential-free mock suite and its
local run command, label taxonomy (real-aws + topic labels; post-merge
is currently informational only), and how to add a test so it actually
runs in a scheduled tier.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 29030211153

Coverage increased (+1.9%) to 50.689%

Details

  • Coverage increased (+1.9%) from the base build.
  • Patch coverage: 394 uncovered changes across 4 files (826 of 1220 lines covered, 67.7%).
  • 10 coverage regressions across 2 files.

Uncovered Changes

File Changed Covered %
internal/aws/awsfake/ec2.go 731 448 61.29%
internal/aws/awsfake/elbv2.go 246 144 58.54%
internal/aws/awsfake/store.go 225 218 96.89%
internal/aws/awsfake/ssm.go 18 16 88.89%

Coverage Regressions

10 previously-covered lines in 2 files lost coverage.

File Lines Losing Coverage Coverage
pkg/provider/aws/delete.go 8 56.5%
pkg/provider/aws/create.go 2 87.69%

Coverage Stats

Coverage Status
Relevant Lines: 12778
Covered Lines: 6477
Line Coverage: 50.69%
Coverage Strength: 0.56 hits per line

💛 - Coveralls

…ace nested lists

mdl (MD055/MD057) does not parse tables indented under list items, and
the repo style pins MD007 unordered-list indent to 2 spaces. Verified
against the CI linter exactly: mdl 0.13.0 with docs/mdl-style.rb, rc=0.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
@ArangoGutierrez ArangoGutierrez marked this pull request as ready for review July 9, 2026 16:23
@ArangoGutierrez ArangoGutierrez merged commit eb415e5 into NVIDIA:main Jul 9, 2026
29 of 31 checks passed
This was referenced Jul 9, 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