diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index f176953048..d3e4230636 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -129,188 +129,6 @@ jobs: OPENSHELL_MCP_CONFORMANCE_CLIENT_IMAGE: ${{ format('openshell-mcp-conformance-client:{0}', inputs.image-tag) }} run: ${{ matrix.cmd }} - e2e-podman-rootless: - name: E2E (rust-podman-${{ matrix.suite }}, ${{ matrix.runner }}) - # Run directly on the Ubuntu host so the test observes the host's AppArmor - # and unprivileged-user-namespace policy. A privileged job container masks - # the restrictions that production rootless Podman installations enforce. - # Ubuntu 26.04 provides the supported Podman 5.x and pasta combination. - # Re-add older/slirp4netns environments when direct callbacks through a - # rootless-network namespace relay are supported. - runs-on: ${{ matrix.runner }} - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - # Keep package versions explicit so hosted-runner tool overrides - # cannot silently change the supported test environment. - - suite: rootless - runner: ubuntu-26.04 - podman_major: "5" - podman_package_version: "5.7.0+ds2-3build1" - conmon_package_version: "2.1.13+ds1-2" - cmd: "mise run --no-deps --skip-deps e2e:podman:rootless" - - suite: external-driver - runner: ubuntu-26.04 - podman_major: "5" - podman_package_version: "5.7.0+ds2-3build1" - conmon_package_version: "2.1.13+ds1-2" - cmd: "env -u OPENSHELL_GATEWAY_BIN mise run --no-deps --skip-deps e2e:podman:external-driver" - - suite: provider-refresh-keycloak - runner: ubuntu-26.04 - podman_major: "5" - podman_package_version: "5.7.0+ds2-3build1" - conmon_package_version: "2.1.13+ds1-2" - cmd: "mise run --no-deps --skip-deps e2e:provider-refresh-keycloak" - env: - IMAGE_TAG: ${{ inputs.image-tag }} - MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENSHELL_REGISTRY: ghcr.io/nvidia/openshell - OPENSHELL_REGISTRY_HOST: ghcr.io - OPENSHELL_REGISTRY_NAMESPACE: nvidia/openshell - OPENSHELL_REGISTRY_USERNAME: ${{ github.actor }} - OPENSHELL_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - OPENSHELL_SUPERVISOR_IMAGE: ${{ format('ghcr.io/nvidia/openshell/supervisor:{0}', inputs.image-tag) }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ inputs['checkout-ref'] || github.sha }} - persist-credentials: false - - - name: Use prebuilt OpenShell CLI - if: inputs.cli-artifact-prefix != '' - uses: ./.github/actions/setup-e2e-cli - with: - artifact-prefix: ${{ inputs.cli-artifact-prefix }} - - - name: Use prebuilt OpenShell gateway - if: inputs.gateway-artifact-prefix != '' - uses: ./.github/actions/setup-e2e-gateway - with: - artifact-prefix: ${{ inputs.gateway-artifact-prefix }} - - - name: Install mise - run: | - curl https://mise.run | MISE_VERSION=v2026.4.25 sh - echo "$HOME/.local/bin" >> "$GITHUB_PATH" - echo "$HOME/.local/share/mise/shims" >> "$GITHUB_PATH" - - - name: Install tools - run: mise install --locked - - - name: Install Podman and build dependencies - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - apparmor \ - build-essential \ - clang \ - fuse-overlayfs \ - libssl-dev \ - libz3-dev \ - openssh-client \ - passt \ - pkg-config \ - "conmon=${{ matrix.conmon_package_version }}" \ - "podman=${{ matrix.podman_package_version }}" \ - uidmap - # Hosted runners can place newer Podman and conmon binaries under - # /usr/local ahead of Ubuntu's packages. Select the distro CLI and - # use Podman's supported final config override for its conmon path. - podman_config="${RUNNER_TEMP}/openshell-containers.conf" - printf '%s\n' \ - '[engine]' \ - 'conmon_path = ["/usr/bin/conmon"]' \ - > "${podman_config}" - echo "/usr/bin" >> "${GITHUB_PATH}" - echo "CONTAINERS_CONF_OVERRIDE=${podman_config}" >> "${GITHUB_ENV}" - - - name: Allow pasta to receive Podman stop signals - # Ubuntu's packaged pasta profile currently blocks this signal, forcing - # Podman to wait for its SIGKILL fallback. Keep this narrow allowance - # until the distribution package includes the upstream profile fix. - run: | - set -euo pipefail - profile=/etc/apparmor.d/usr.bin.pasta - rule=' signal (receive) peer=podman,' - if ! sudo grep -Fqx "${rule}" "${profile}"; then - sudo sed -i '\|^ include $|a\ signal (receive) peer=podman,' "${profile}" - fi - sudo grep -Fqx "${rule}" "${profile}" - sudo apparmor_parser --replace "${profile}" - - - name: Configure rootless Podman - run: | - set -euo pipefail - if ! grep -q "^${USER}:" /etc/subuid; then - sudo usermod --add-subuids 100000-165535 "$USER" - fi - if ! grep -q "^${USER}:" /etc/subgid; then - sudo usermod --add-subgids 100000-165535 "$USER" - fi - runtime_dir="/run/user/$(id -u)" - sudo install -d -m 0700 -o "$(id -u)" -g "$(id -g)" "$runtime_dir" - echo "XDG_RUNTIME_DIR=$runtime_dir" >> "$GITHUB_ENV" - - - name: Verify rootless Podman environment - run: | - set -euo pipefail - podman_version="$(podman version --format '{{.Client.Version}}')" - case "$podman_version" in - "${{ matrix.podman_major }}".*) ;; - *) echo "ERROR: expected Podman ${{ matrix.podman_major }}.x, found $podman_version" >&2; exit 1 ;; - esac - test "$(dpkg-query -W -f='${Version}' podman)" = "${{ matrix.podman_package_version }}" - test "$(dpkg-query -W -f='${Version}' conmon)" = "${{ matrix.conmon_package_version }}" - test "$(command -v podman)" = "/usr/bin/podman" - test "$(podman info --format '{{.Host.Conmon.Path}}')" = "/usr/bin/conmon" - test "$(podman info --format '{{.Host.Security.Rootless}}')" = "true" - test "$(podman info --format '{{.Host.RootlessNetworkCmd}}')" = "pasta" - test "$(sudo sysctl -n kernel.apparmor_restrict_unprivileged_userns)" = "1" - echo "=== host ===" - uname -a - echo "=== AppArmor ===" - cat /proc/self/attr/current - sudo aa-status || true - echo "=== Podman ===" - podman version - podman info --debug - - - name: Probe rootless capability bounding set - run: | - set -euo pipefail - probe="$RUNNER_TEMP/openshell-capbset-probe" - cc -static -O2 -Wall -Wextra -Werror \ - e2e/support/capbset-probe.c \ - -o "$probe" - podman run --rm \ - --cap-add=SETPCAP \ - --volume "$probe:/openshell-capbset-probe:ro" \ - docker.io/library/alpine:3.22 \ - /openshell-capbset-probe - - - name: Log in to GHCR with Podman - run: echo "${{ secrets.GITHUB_TOKEN }}" | podman login ghcr.io -u "${{ github.actor }}" --password-stdin - - - name: Run Podman E2E - run: ${{ matrix.cmd }} - - - name: Print AppArmor denials - if: always() - run: sudo dmesg | grep -E 'apparmor=.*DENIED|profile="unprivileged_userns"' | tail -100 || true - - - name: Fail on pasta SIGTERM AppArmor denial - if: always() - run: | - set -euo pipefail - denials="$(sudo dmesg | grep -E 'profile="pasta".*requested_mask="receive".*signal=term.*peer="podman"' || true)" - if [ -n "${denials}" ]; then - echo "::error::pasta denied Podman's SIGTERM; Podman will use its SIGKILL fallback" - printf '%s\n' "${denials}" - exit 1 - fi - e2e-vm: name: E2E (rust-vm-${{ matrix.suite }}) # libkrun needs KVM, so this job must run directly on a GitHub-hosted diff --git a/CI.md b/CI.md index aae22f4c4f..9dd919d1bd 100644 --- a/CI.md +++ b/CI.md @@ -14,7 +14,7 @@ Merge queue validation is a second integration gate for `main`. After a PR has p Three opt-in labels enable the long-running E2E suites: -- `test:e2e` runs the standard Docker, rootless Podman, Kubernetes, and VM E2E +- `test:e2e` runs the standard Docker, Kubernetes, and VM E2E suites in `Branch E2E Checks` - `test:e2e-gpu` runs GPU E2E in `Branch E2E Checks` - `test:e2e-kubernetes` runs Kubernetes E2E with the HA Helm overlay diff --git a/e2e/rust/e2e-podman-rootless.sh b/e2e/rust/e2e-podman-rootless.sh deleted file mode 100755 index d7fb5acd3c..0000000000 --- a/e2e/rust/e2e-podman-rootless.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -# Run the Podman e2e suite and verify rootless mode. -# -# Identical to e2e-podman.sh but fails fast if Podman is not running -# rootless. Use this to explicitly validate the rootless networking -# path (pasta, host-gateway, bind address). - -set -euo pipefail - -rootless="$(podman info --format '{{.Host.Security.Rootless}}' 2>/dev/null || true)" -if [ "${rootless}" != "true" ]; then - echo "ERROR: podman is not running rootless; expected true, got '${rootless:-}'" >&2 - exit 2 -fi - -exec "$(dirname "${BASH_SOURCE[0]}")/e2e-podman.sh" diff --git a/tasks/test.toml b/tasks/test.toml index dfb632dda0..29e06b826a 100644 --- a/tasks/test.toml +++ b/tasks/test.toml @@ -136,10 +136,6 @@ run = [ "CONTAINER_RUNTIME=docker e2e/with-keycloak.sh env OPENSHELL_E2E_OIDC_GATEWAY=1 e2e/with-docker-gateway.sh uv run pytest -m 'not gpu' e2e/python/oidc", ] -["e2e:podman:rootless"] -description = "Run Rust CLI e2e tests against a rootless Podman-backed gateway" -run = "e2e/rust/e2e-podman-rootless.sh" - ["e2e:podman:gpu"] description = "Run GPU e2e against a standalone gateway with the Podman compute driver" env = { OPENSHELL_E2E_PODMAN_GPU = "1", OPENSHELL_E2E_PODMAN_TEST = "gpu", OPENSHELL_E2E_PODMAN_FEATURES = "e2e-podman-gpu" }