Skip to content

fix: Phase 3 — Long-term improvements (SSH TOFU, EC2Client split, context propagation) + E2E tests#625

Merged
ArangoGutierrez merged 1 commit into
NVIDIA:mainfrom
ArangoGutierrez:fix/phase3-longterm-improvements
Feb 9, 2026
Merged

fix: Phase 3 — Long-term improvements (SSH TOFU, EC2Client split, context propagation) + E2E tests#625
ArangoGutierrez merged 1 commit into
NVIDIA:mainfrom
ArangoGutierrez:fix/phase3-longterm-improvements

Conversation

@ArangoGutierrez

Copy link
Copy Markdown
Collaborator

Summary

Phase 3 of the 360-degree codebase evaluation. Addresses long-term architectural improvements.

Architectural Improvements

  • SSH TOFU host key verification — Add Trust-On-First-Use host key verification for SSH connections instead of blindly accepting all keys
  • EC2Client interface split — Break monolithic EC2Client interface into smaller, focused interfaces (Creator, Deleter, Describer)
  • Context propagation — Thread context.Context through all AWS SDK calls for proper cancellation and timeout support

Provisioner Resilience

  • install_packages_with_retry — Retry on all failures, refresh apt cache, fix broken dpkg state
  • NVIDIA driver — cuda-keyring 1.1-1, architecture detection, 4-stage DKMS fallback chain
  • Container toolkit — Fix duplicate Execute() bug, add retry wrappers
  • Docker/Containerd — Add retry wrappers, default containerd to latest
  • AWS delete — Increase termination waiter to 10min (with ctx propagation)

E2E Test Suite (NEW)

  • Ginkgo/Gomega E2E tests: containerd + kubeadm (K8s v1.35.0) and docker + NVIDIA runtime
  • Full AWS lifecycle testing on g4dn.xlarge

Test Plan

  • go vet ./... passes
  • Unit tests pass
  • E2E containerd+kubeadm+K8s v1.35.0: PASS
  • E2E docker+NVIDIA runtime: PASS
  • Commits signed with -s -S (DCO + SSH)

Made with Cursor

Copilot AI review requested due to automatic review settings February 7, 2026 16:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Phase 3 of the 360-degree evaluation: adds long-term architectural improvements (context propagation, EC2 client interface split) plus provisioner resiliency and a new Ginkgo/Gomega E2E suite for AWS lifecycle validation.

Changes:

  • Introduces Ginkgo/Gomega E2E tests and AWS environment fixtures for containerd+kubeadm and docker+nvidia runtime flows.
  • Propagates context.Context through AWS provider lifecycle (New/Create/Delete/DryRun) and introduces a narrow EC2Client interface for testability.
  • Improves provisioner templates with retries and more resilient package/driver installation logic.

Reviewed changes

Copilot reviewed 24 out of 639 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/e2e_test.go Adds Ginkgo suite bootstrap + env initialization for E2E tests
tests/aws_test.go Implements AWS E2E table tests (create → provision → validate → cleanup)
tests/common/common.go Adds helper UID generator for unique E2E resource names
tests/data/test_aws.yml Adds AWS E2E environment fixture for containerd + kubeadm
tests/data/test_aws_docker.yml Adds AWS E2E environment fixture for docker runtime
pkg/provisioner/templates/common.go Makes apt installs more resilient with broader retries/repair
pkg/provisioner/templates/nv-driver.go Adds arch detection + driver install fallback chain
pkg/provisioner/templates/docker.go Wraps apt update with retry
pkg/provisioner/templates/containerd.go Adds retry, installs “latest” by default, minor hardening
pkg/provisioner/templates/container-toolkit.go Fixes duplicate Execute() and adds retry wrappers
pkg/provisioner/provisioner.go Renames HostUrl→HostURL and updates kubeadm endpoint host wiring
internal/aws/ec2_client.go Adds composed EC2 interfaces to decompose a monolithic client
pkg/provider/aws/aws.go Switches EC2 client to interface + adds ctx to New
pkg/provider/aws/create.go Threads ctx through EC2 create calls and updates ImageID field usage
pkg/provider/aws/delete.go Threads ctx through EC2 delete calls and extends termination waiter
pkg/provider/aws/dryrun.go Uses ctx for dryrun calls + updates ImageID field usage
api/holodeck/v1alpha1/types.go Renames HostUrl/ImageId/OwnerId fields to HostURL/ImageID/OwnerID
api/holodeck/v1alpha1/zz_generated.deepcopy.go Regenerates deepcopy for renamed API fields
cmd/cli/create/create.go Adds ctx plumbing into AWS create flow + HostURL rename usage
cmd/cli/delete/delete.go Adds ctx plumbing into AWS delete flow
cmd/cli/dryrun/dryrun.go Adds ctx plumbing into AWS dryrun flow + HostURL rename usage
cmd/action/ci/entrypoint.go Adds ctx plumbing into AWS create flow for GitHub Action
cmd/action/ci/cleanup.go Adds ctx plumbing into AWS delete flow for GitHub Action
go.mod Bumps Go version and adds Ginkgo/Gomega dependencies for E2E

Comment thread tests/e2e_test.go
Comment on lines +36 to +41
func TestMain(t *testing.T) {
suiteName := "E2E Holodeck"

RegisterFailHandler(Fail)
RunSpecs(t, suiteName)
}

Copilot AI Feb 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestMain has special meaning in Go (func TestMain(m *testing.M)), so naming a regular Ginkgo entry test TestMain(t *testing.T) is confusing and can block future use of an actual TestMain. Rename this to something like TestE2E(t *testing.T) (or switch to the real TestMain(m *testing.M) signature if you intended process-level setup/teardown).

Copilot uses AI. Check for mistakes.
Comment thread tests/aws_test.go
Comment on lines +115 to +126
defer func() {
if p.Client != nil {
session, err := p.Client.NewSession()
if err == nil {
session.Close()
if err := p.Client.Close(); err != nil {
Expect(err).NotTo(HaveOccurred(), "Failed to close ssh client")
}
}
p.Client = nil
}
}()

Copilot AI Feb 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SSH client is only closed when NewSession() succeeds. If NewSession() fails, this leaks the underlying connection and can leave resources open across retries/tests. Close p.Client unconditionally when it’s non-nil; only gate session.Close() on session creation success (and note that you don’t need a session just to close the client).

Copilot uses AI. Check for mistakes.
Comment thread tests/common/common.go
Comment on lines +22 to +32
"log"
)

func GenerateUID() string {
b := make([]byte, 4)
_, err := rand.Read(b)
if err != nil {
log.Fatalf("failed to generate secure random UID: %v", err)
}
return hex.EncodeToString(b)
}

Copilot AI Feb 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper hard-exits the entire test process via log.Fatalf, which bypasses Ginkgo failure reporting and can skip deferred cleanup (potentially leaving AWS resources behind). Prefer returning (string, error) (or failing via Ginkgo in the caller) and consider increasing the entropy beyond 4 bytes to reduce collision risk for resource naming under parallel/CI runs.

Suggested change
"log"
)
func GenerateUID() string {
b := make([]byte, 4)
_, err := rand.Read(b)
if err != nil {
log.Fatalf("failed to generate secure random UID: %v", err)
}
return hex.EncodeToString(b)
}
"fmt"
)
func GenerateUID() (string, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return "", fmt.Errorf("failed to generate secure random UID: %w", err)
}
return hex.EncodeToString(b), nil
}

Copilot uses AI. Check for mistakes.
Comment thread tests/data/test_aws.yml Outdated
type: g4dn.xlarge
region: us-west-1
ingressIpRanges:
- "0.0.0.0/0"

Copilot AI Feb 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing SSH/K8s ingress from 0.0.0.0/0 is a high-risk default even for E2E, since it opens the instance to the public internet. Prefer restricting this to the CI runner’s egress IP (or a tightly scoped CIDR provided via CI/env templating) so tests don’t create broadly exposed infrastructure.

Suggested change
- "0.0.0.0/0"
- "192.0.2.1/32" # restricted example CIDR; override with CI runner egress IP in real use

Copilot uses AI. Check for mistakes.
Comment thread tests/data/test_aws_docker.yml Outdated
type: g4dn.xlarge
region: us-west-1
ingressIpRanges:
- "0.0.0.0/0"

Copilot AI Feb 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing SSH ingress from 0.0.0.0/0 is a high-risk default even for E2E. Prefer restricting this to the CI runner’s egress IP (or a tightly scoped CIDR provided via CI/env templating) so tests don’t create broadly exposed infrastructure.

Suggested change
- "0.0.0.0/0"
- "${HOLODECK_E2E_INGRESS_CIDR}"

Copilot uses AI. Check for mistakes.
Comment thread pkg/provisioner/templates/nv-driver.go Outdated
install_packages_with_retry cuda-drivers
# Install the latest cuda-keyring
wget -q "https://developer.download.nvidia.com/compute/cuda/repos/${distribution}/${NVIDIA_ARCH}/cuda-keyring_1.1-1_all.deb" \
|| wget -q "https://developer.download.nvidia.com/compute/cuda/repos/${distribution}/${NVIDIA_ARCH}/cuda-keyring_1.0-1_all.deb"

Copilot AI Feb 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both wget attempts fail, the dpkg -i cuda-keyring_*.deb glob may not match anything (or may match an unexpected file), causing a misleading failure path. Add an explicit check that a keyring .deb was downloaded before running dpkg, and emit a clear error if neither URL is available for the detected distro/arch.

Suggested change
|| wget -q "https://developer.download.nvidia.com/compute/cuda/repos/${distribution}/${NVIDIA_ARCH}/cuda-keyring_1.0-1_all.deb"
|| wget -q "https://developer.download.nvidia.com/compute/cuda/repos/${distribution}/${NVIDIA_ARCH}/cuda-keyring_1.0-1_all.deb"
# Ensure that a cuda-keyring .deb was actually downloaded before installing
if ! ls cuda-keyring_*.deb >/dev/null 2>&1; then
echo "ERROR: failed to download cuda-keyring for ${distribution}/${NVIDIA_ARCH}" >&2
exit 1
fi

Copilot uses AI. Check for mistakes.
Comment thread pkg/provisioner/provisioner.go Outdated
Comment on lines 55 to 59
func New(log *logger.FunLogger, keyPath, userName, hostURL string) (*Provisioner, error) {
client, err := connectOrDie(keyPath, userName, hostURL)
if err != nil {
return nil, fmt.Errorf("failed to connect to %s: %v", hostUrl, err)
return nil, fmt.Errorf("failed to connect to %s: %v", hostURL, err)
}

Copilot AI Feb 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wraps the underlying error using %v, which prevents callers from unwrapping/inspecting the root cause. Prefer wrapping with %w (and keep the contextual host string) so higher-level code can use errors.Is/As effectively.

Copilot uses AI. Check for mistakes.
@ArangoGutierrez
ArangoGutierrez force-pushed the fix/phase3-longterm-improvements branch from b500ab1 to 0938b0c Compare February 9, 2026 13:15
@coveralls

coveralls commented Feb 9, 2026

Copy link
Copy Markdown

Pull Request Test Coverage Report for Build 21831869854

Details

  • 28 of 37 (75.68%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.2%) to 46.051%

Changes Missing Coverage Covered Lines Changed/Added Lines %
pkg/provisioner/provisioner.go 28 37 75.68%
Totals Coverage Status
Change from base Build 21831469801: 0.2%
Covered Lines: 2309
Relevant Lines: 5014

💛 - Coveralls

@ArangoGutierrez
ArangoGutierrez enabled auto-merge (squash) February 9, 2026 13:29
@ArangoGutierrez
ArangoGutierrez force-pushed the fix/phase3-longterm-improvements branch 4 times, most recently from cc1ec92 to f9b3f41 Compare February 9, 2026 15:42
- Replace ssh.InsecureIgnoreHostKey() with Trust-On-First-Use (TOFU)
  pattern that records host keys on first connection and verifies on
  subsequent ones, eliminating MITM vulnerability
- Host keys cached in ~/.cache/holodeck/known_hosts with 0600 permissions

Re-implemented against current upstream/main.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ArangoGutierrez
ArangoGutierrez force-pushed the fix/phase3-longterm-improvements branch from f9b3f41 to 13229b1 Compare February 9, 2026 15:47
@ArangoGutierrez
ArangoGutierrez merged commit 3e25692 into NVIDIA:main Feb 9, 2026
19 checks passed
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 10, 2026
- Replace ssh.InsecureIgnoreHostKey() with Trust-On-First-Use (TOFU)
  pattern that records host keys on first connection and verifies on
  subsequent ones, eliminating MITM vulnerability
- Host keys cached in ~/.cache/holodeck/known_hosts with 0600 permissions

Re-implemented against current upstream/main.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 10, 2026
- Replace ssh.InsecureIgnoreHostKey() with Trust-On-First-Use (TOFU)
  pattern that records host keys on first connection and verifies on
  subsequent ones, eliminating MITM vulnerability
- Host keys cached in ~/.cache/holodeck/known_hosts with 0600 permissions

Re-implemented against current upstream/main.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 10, 2026
- Replace ssh.InsecureIgnoreHostKey() with Trust-On-First-Use (TOFU)
  pattern that records host keys on first connection and verifies on
  subsequent ones, eliminating MITM vulnerability
- Host keys cached in ~/.cache/holodeck/known_hosts with 0600 permissions

Re-implemented against current upstream/main.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 12, 2026
- Replace ssh.InsecureIgnoreHostKey() with Trust-On-First-Use (TOFU)
  pattern that records host keys on first connection and verifies on
  subsequent ones, eliminating MITM vulnerability
- Host keys cached in ~/.cache/holodeck/known_hosts with 0600 permissions

Re-implemented against current upstream/main.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 13, 2026
- Replace ssh.InsecureIgnoreHostKey() with Trust-On-First-Use (TOFU)
  pattern that records host keys on first connection and verifies on
  subsequent ones, eliminating MITM vulnerability
- Host keys cached in ~/.cache/holodeck/known_hosts with 0600 permissions

Re-implemented against current upstream/main.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
ArangoGutierrez added a commit to ArangoGutierrez/holodeck that referenced this pull request Feb 13, 2026
- Replace ssh.InsecureIgnoreHostKey() with Trust-On-First-Use (TOFU)
  pattern that records host keys on first connection and verifies on
  subsequent ones, eliminating MITM vulnerability
- Host keys cached in ~/.cache/holodeck/known_hosts with 0600 permissions

Re-implemented against current upstream/main.

Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
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.

3 participants