Repository navigation
Sync upstream sandbox, snapshot, and HTTPS egress updates #357
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright 2026 Google LLC | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| name: pr-workflow | |
| on: | |
| pull_request: | |
| merge_group: | |
| push: | |
| branches: [main] | |
| schedule: | |
| # Weekly run on the default branch keeps the micro-VM asset cache warm (GitHub | |
| # evicts caches idle for 7 days). The push-to-main run populates the cache that | |
| # PRs inherit; this refreshes it so PRs keep hitting it (no asset re-download). | |
| - cron: '37 4 * * 1' | |
| # Nothing here writes to the repository: the jobs check the tree out, build it, | |
| # and run it in a throwaway cluster. | |
| permissions: | |
| contents: read | |
| # Superseded pushes to a pull request are cancelled; every commit that lands on | |
| # main still gets its own full run. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| run-tests: | |
| runs-on: ubuntu-latest | |
| # 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. | |
| timeout-minutes: 45 | |
| env: | |
| # Where each runner writes its JUnit. Every invocation needs its own file; | |
| # a shared path means the last writer wins. | |
| ARTIFACTS: ${{ github.workspace }}/_artifacts | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Go | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 | |
| with: | |
| go-version-file: 'go.mod' | |
| # Unconditional: a released migration becoming permanent is exactly what | |
| # this guards, and that happens on the push to main, not on the PR. | |
| - name: Verify immutable PostgreSQL migrations | |
| run: hack/verify/postgresql-migrations.sh | |
| # we only test on amd64, make sure everything still builds for arm64 | |
| - name: build for arm64 | |
| run: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build ./... | |
| # gotestsum wraps go test to emit JUnit; the go test arguments are unchanged. | |
| - name: unit tests | |
| run: hack/run-tool.sh gotestsum --junitfile "${ARTIFACTS}/unit.xml" | |
| --jsonfile "${ARTIFACTS}/unit.json" --format standard-verbose | |
| -- -race -v ./... | |
| # tools/apitool has its own module so we need to run it explicitly. | |
| # run-tool.sh resolves the repo root itself, so it works from the subdir. | |
| - name: apitool tests | |
| working-directory: tools/apitool | |
| run: ../../hack/run-tool.sh gotestsum --junitfile "${ARTIFACTS}/apitool.xml" | |
| --jsonfile "${ARTIFACTS}/apitool.json" --format standard-verbose | |
| -- -race -v ./... | |
| # Kept apart from the root module so it can move to its own repository. | |
| - name: gcp-secret-manager credential provider tests | |
| working-directory: internal/plugins/gcp-secret-manager | |
| run: ../../../hack/run-tool.sh gotestsum --junitfile "${ARTIFACTS}/gcp-secret-manager.xml" | |
| --jsonfile "${ARTIFACTS}/gcp-secret-manager.json" --format standard-verbose | |
| -- -race -v ./... | |
| # The root verifiers stop at the module's go.mod, so it gets its own run: | |
| # gofmt, go vet and the tidy check, then golangci-lint with the root config. | |
| - name: gcp-secret-manager credential provider verify | |
| working-directory: internal/plugins/gcp-secret-manager | |
| run: | | |
| make verify | |
| ../../../hack/run-tool.sh golangci-lint run --config ../../../.golangci.yaml ./... | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index | |
| ~/.cargo/registry/cache | |
| ~/.cargo/git/db | |
| cmd/dataplane/envoy/dynamic-modules/*/target | |
| key: cargo-${{ runner.os }}-${{ hashFiles('cmd/dataplane/envoy/dynamic-modules/*/Cargo.lock') }} | |
| restore-keys: cargo-${{ runner.os }}- | |
| - name: dynamic module tests | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y -qq --no-install-recommends clang libclang-dev | |
| hack/test-dynamic-modules.sh | |
| # Root-gated tests (overlay mounts, whiteout mknod, trusted.* xattrs, ...) | |
| # skip for the unprivileged runner user above; rerun the packages that | |
| # contain them (any test importing internal/roottest) under sudo. | |
| - name: root-gated tests | |
| env: | |
| ROOT_JUNIT_FILE: ${{ github.workspace }}/_artifacts/root.xml | |
| run: hack/run-root-tests.sh -race -v | |
| # An exit code cannot distinguish "all passed" from "none ran". A registered | |
| # file that was never written fails too. | |
| - name: Require tests to have run | |
| if: always() | |
| run: go -C tools/junittool run . verify -manifest "${ARTIFACTS}/expected-junit.txt" | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: test-results-unit | |
| path: ${{ env.ARTIFACTS }} | |
| if-no-files-found: error | |
| - name: verify | |
| run: hack/verify-all.sh | |
| # 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. | |
| e2e-test: | |
| name: e2e-test | |
| runs-on: ubuntu-latest | |
| # 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 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - dataplane: agentgateway | |
| env: | |
| E2E_ATENET_DATAPLANE: ${{ matrix.dataplane }} | |
| # The installer requires a credential provider selection, and deploys the | |
| # bundled Kubernetes Secrets provider with the control plane. Job-wide | |
| # because every ate-setup invocation reads it, including the control | |
| # plane redeploy in run-microvm-demo-kind.sh. | |
| ATE_CREDENTIAL_PROVIDER: '{"name":"k8s.io"}' | |
| # E2E_JUNIT_FILE is set per step, not here: the lanes below share this | |
| # job, so one job-level path would have each overwrite the last. | |
| ARTIFACTS: ${{ github.workspace }}/_artifacts | |
| # Cache the envoy-dataplane Rust build. Only main writes it, so PRs | |
| # share one copy instead of each storing their own. | |
| DOCKER_BUILD_FLAGS: >- | |
| --cache-from type=gha,scope=envoy-dataplane | |
| ${{ github.ref == 'refs/heads/main' && matrix.dataplane == 'envoy' | |
| && '--cache-to type=gha,mode=max,scope=envoy-dataplane,ignore-error=true' || '' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 | |
| - name: Setup Go | |
| uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 | |
| with: | |
| go-version-file: 'go.mod' | |
| # Use the e2e cache below instead. setup-go's cache key is shared with | |
| # run-tests, whose unit-test builds don't match ko's flags | |
| # (CGO_ENABLED=0, -trimpath), so ko got no cache hits from it. | |
| cache: false | |
| - name: Restore Go build cache | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: | | |
| ~/.cache/go-build | |
| ~/go/pkg/mod | |
| key: e2e-go-${{ runner.os }}-${{ github.sha }} | |
| restore-keys: e2e-go-${{ runner.os }}- | |
| - name: Set up Docker Buildx | |
| # The docker-container driver is what can use the GitHub Actions cache. | |
| # Host networking and plain HTTP let it push to the kind registry. | |
| uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 | |
| with: | |
| driver-opts: network=host | |
| buildkitd-config-inline: | | |
| [registry."localhost:5001"] | |
| http = true | |
| - name: Expose GitHub runtime to buildx | |
| # buildx's gha cache backend reads these from the environment, but the | |
| # runner only passes them to actions, not to run steps. | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| for (const [key, value] of Object.entries(process.env)) { | |
| if (key.startsWith('ACTIONS_')) core.exportVariable(key, value); | |
| } | |
| - name: Cache micro-VM assets | |
| id: microvm-assets | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| # Assembling the assets (download kata-static + cloud-hypervisor + the | |
| # prebuilt virtiofsd — all downloads on amd64) is fully pinned by assemble.sh, | |
| # so key the cache on its hash. The push-to-main run populates the | |
| # default-branch cache PRs inherit; the weekly schedule refreshes it. | |
| # run-microvm-demo-kind.sh skips assembling when these are present. | |
| path: bin/microvm-assets/amd64 | |
| key: microvm-assets-amd64-${{ hashFiles('hack/microvm-assets/assemble.sh') }} | |
| - name: Enable KVM | |
| # Grant the runner access to /dev/kvm so create-kind-cluster.sh mounts it into | |
| # the node and labels it for the micro-VM sandbox class. | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \ | |
| | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Create cluster | |
| run: hack/create-kind-cluster.sh | |
| - name: Install Agent Substrate (${{ matrix.dataplane }}) | |
| # The dataplane selection applies to both the ingress router and egress | |
| # gateway. | |
| run: hack/install-ate-kind.sh --deploy-ate-system --atenet-dataplane=${{ matrix.dataplane }} --rollout-timeout=300s | |
| - name: Enable NFS | |
| # Load NFS kernel modules so in-cluster NFS server and CSI driver can run. | |
| run: | | |
| sudo modprobe nfs || true | |
| sudo modprobe nfsd || true | |
| - name: Install CSI NFS driver | |
| run: hack/install-ate-kind.sh --setup-csi=nfs | |
| - name: Deploy micro-VM counter demo | |
| # Stages the (cached) assets into the cluster's rustfs and deploys the | |
| # counter-microvm demo onto the control plane installed above. | |
| run: hack/run-microvm-demo-kind.sh --skip-control-plane | |
| - name: Deploy gVisor counter demo | |
| run: hack/install-ate-kind.sh --deploy-demo-counter | |
| - name: Deploy egress demos | |
| # TestActorEgress in the networking suite builds its Actor from the egress | |
| # ActorTemplate for the class under test, so both fixtures have to exist | |
| # before their respective lane runs. The demo's actors trust the CA the | |
| # gateway mints its leaves from. | |
| run: | | |
| hack/install-ate-kind.sh --deploy-demo-egress | |
| hack/install-ate-kind.sh --deploy-demo-egress-microvm | |
| - name: Trust local TLS origins in egress gateway | |
| run: hack/setup-e2e-egress-tls-kind.sh | |
| # scale the idle sandbox class's demo pools to zero so each lane has the node | |
| - name: Focus the node on gVisor | |
| run: hack/e2e-focus-sandbox-class.sh gvisor | |
| - name: Run E2E tests (gVisor) | |
| env: | |
| E2E_PARALLELISM: 8 | |
| E2E_JUNIT_FILE: ${{ github.workspace }}/_artifacts/e2e-gvisor.xml | |
| run: hack/run-e2e-kind.sh -v -args --no-color | |
| - name: Focus the node on micro-VM | |
| run: hack/e2e-focus-sandbox-class.sh microvm | |
| - name: Run E2E tests (micro-VM) | |
| # The same suites again, with every fixture repointed at its micro-VM | |
| # variant by the single E2E_SANDBOX_CLASS knob (see internal/e2e/sandbox.go). | |
| env: | |
| E2E_SANDBOX_CLASS: microvm | |
| E2E_PARALLELISM: 8 | |
| E2E_JUNIT_FILE: ${{ github.workspace }}/_artifacts/e2e-microvm.xml | |
| run: hack/run-e2e-kind.sh -v -args --no-color | |
| # Every lane must have reported tests; a `-run` filter that stops matching | |
| # otherwise leaves its lane green and empty. The set comes from the manifest | |
| # each lane registers itself in, so adding a lane needs no change here, and | |
| # run-e2e.sh refuses to run in CI without E2E_JUNIT_FILE. | |
| - name: Require every lane to have run tests | |
| if: always() | |
| run: go -C tools/junittool run . verify -manifest "${ARTIFACTS}/expected-junit.txt" | |
| - name: Save Go build cache | |
| # Main only, so PRs restore the latest main build rather than each | |
| # storing its own copy. | |
| if: ${{ !cancelled() && github.ref == 'refs/heads/main' }} | |
| uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: | | |
| ~/.cache/go-build | |
| ~/go/pkg/mod | |
| key: e2e-go-${{ runner.os }}-${{ github.sha }} | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: test-results-e2e-${{ matrix.dataplane }} | |
| path: ${{ env.ARTIFACTS }} | |
| if-no-files-found: error | |
| - name: Dump diagnostics on failure | |
| if: failure() | |
| run: | | |
| kubectl --context kind-kind get workerpool,pods -A -o wide || true | |
| dump() { | |
| echo "=== logs: $1/$2 ===" | |
| kubectl --context kind-kind logs -n "$1" "$2" --all-containers --tail=300 2>/dev/null || true | |
| } | |
| for p in $(kubectl --context kind-kind get pods -n ate-system -o name 2>/dev/null); do | |
| dump ate-system "$p" | |
| done | |
| # Every worker pod in any namespace: the demo pools plus the e2e suites' | |
| # randomly-named per-test namespaces, which the suites keep on failure. | |
| # The failing actor runs in one of these, so this is where its ateom logs | |
| # (and, for a micro-VM worker, the guest console tail) live. | |
| kubectl --context kind-kind get pods -A -l ate.dev/worker-pool \ | |
| -o 'custom-columns=:.metadata.namespace,:.metadata.name' --no-headers 2>/dev/null \ | |
| | while read -r ns name; do dump "$ns" "$name"; done |