diff --git a/.github/workflows/helm-e2e.yaml b/.github/workflows/helm-e2e.yaml index 62edbf740e..74bf22c4fa 100644 --- a/.github/workflows/helm-e2e.yaml +++ b/.github/workflows/helm-e2e.yaml @@ -21,8 +21,10 @@ permissions: contents: read jobs: e2e-test: - runs-on: ubuntu-latest + runs-on: blacksmith-4vcpu-ubuntu-2404 env: + # go tool -n needs Go's disk cache to return a persistent executable path. + GOCACHEPROG: "" VERSION: helm-e2e E2E_ATENET_DATAPLANE: agentgateway E2E_CREDENTIAL_PROVIDER: "1" diff --git a/.github/workflows/pr-workflow.yaml b/.github/workflows/pr-workflow.yaml index 97d2912653..d0993a043c 100644 --- a/.github/workflows/pr-workflow.yaml +++ b/.github/workflows/pr-workflow.yaml @@ -34,11 +34,13 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: run-tests: - runs-on: ubuntu-latest + runs-on: blacksmith-4vcpu-ubuntu-2404 # A bound well clear of the observed runtime. Without one the platform - # default applies, and a wedged step burns a free-tier slot for six hours. + # default applies, and a wedged step occupies a runner for six hours. timeout-minutes: 45 env: + # go tool -n needs Go's disk cache to return a persistent executable path. + GOCACHEPROG: "" # Where each runner writes its JUnit. Every invocation needs its own file; # a shared path means the last writer wins. ARTIFACTS: ${{ github.workspace }}/_artifacts @@ -139,14 +141,13 @@ jobs: env: BASE_REF: ${{ github.base_ref || github.event.merge_group.base_ref }} run: hack/verify/postgresql-migrations.sh "origin/${BASE_REF#refs/heads/}" - # One kind cluster exercises BOTH runtimes. Free x86-64 ubuntu-latest runners - # expose /dev/kvm (with a udev rule), so create-kind-cluster.sh mounts it and the - # micro-VM (kata + cloud-hypervisor) sandbox class works alongside gVisor. + # One kind cluster exercises both gVisor and microVM runtimes. + # The runner must expose /dev/kvm for the microVM sandbox class. # Each preview mode runs on a fresh cluster with agentgateway. # The default install retains the required e2e-test check name. e2e-test: name: ${{ matrix.preview == 'off' && 'e2e-test' || 'e2e-test (preview)' }} - runs-on: ubuntu-latest + runs-on: blacksmith-4vcpu-ubuntu-2404 # Cluster bring-up, image builds and two sequential lanes; generous, but far # short of the six-hour platform default a hung lane would otherwise consume. timeout-minutes: 120 @@ -156,6 +157,8 @@ jobs: dataplane: ['agentgateway'] preview: ['off', 'on'] env: + # go tool -n needs Go's disk cache to return a persistent executable path. + GOCACHEPROG: "" E2E_ATENET_DATAPLANE: ${{ matrix.dataplane }} # Apply the e2e test component (short atelet snapshot delays) across the # initial install and the control plane redeploy in run-microvm-demo-kind.sh. diff --git a/internal/ateworker/actornet/sandbox.go b/internal/ateworker/actornet/sandbox.go index 67e020a31a..884dbc5404 100644 --- a/internal/ateworker/actornet/sandbox.go +++ b/internal/ateworker/actornet/sandbox.go @@ -128,10 +128,8 @@ func SetupSandboxNetwork(ctx context.Context, cfg SandboxNetworkConfig) (_ *Sand }, nil } -// prepareNamespace pins sandboxSysctls and brings up lo. It must run before -// the namespace has any link but lo: a link copies conf/default when it is -// created, and a later write to conf/default does not reach links whose -// settings the node had set explicitly. +// prepareNamespace pins sandboxSysctls and brings up lo before sandbox links +// are created. Those links copy conf/default at creation time. func prepareNamespace(context.Context) error { if err := netns.SetSysctls(sandboxSysctls...); err != nil { return err diff --git a/internal/ateworker/actornet/sandbox_linux_test.go b/internal/ateworker/actornet/sandbox_linux_test.go index 8f3381a7a4..e6a5e91395 100644 --- a/internal/ateworker/actornet/sandbox_linux_test.go +++ b/internal/ateworker/actornet/sandbox_linux_test.go @@ -240,14 +240,16 @@ func TestSetupSandboxNetworkWithoutVeth(t *testing.T) { t.Errorf("got a second namespace (%v vs %v); this shape needs one", n.GatewayNetNS, n.RuntimeNetNS) } - // No veth was built, so nothing but lo is here until the tap arrives. + // Kernel tunnel modules can add dormant interfaces to a new namespace. + // No veth was built, and only lo should be up until the tap arrives. if err := netns.Do(ctx, n.RuntimeNetNS, func(context.Context) error { links, err := netlink.LinkList() if err != nil { return err } for _, l := range links { - if l.Attrs().Name != "lo" { + _, isVeth := l.(*netlink.Veth) + if isVeth || (l.Attrs().Name != "lo" && l.Attrs().Flags&net.FlagUp != 0) { t.Errorf("unexpected interface %q in the actor namespace", l.Attrs().Name) } } @@ -552,8 +554,9 @@ func TestSandboxNamespacesDoNotInheritNodeSettings(t *testing.T) { } for _, l := range links { name := l.Attrs().Name - // lo predates the sysctls. - if l.Attrs().Flags&net.FlagLoopback != 0 { + // Loopback and kernel tunnel devices predate the sysctls. + // Check the veths added during and after setup. + if _, ok := l.(*netlink.Veth); !ok { continue } for key, value := range perLink {