Files
OpenShell/e2e/with-podman-gateway.sh
T
Jesse JaggarsandDrew Newberry 02b664bb0d refactor(config): normalize and enforce gateway schema v2 (#2814)
* refactor(config): normalize compute driver field names

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* refactor(config): introduce canonical gateway fields

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* refactor(config): enforce gateway schema version 2

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): preserve compute driver runtime guarantees

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): address schema v2 review regressions

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): complete schema v2 migration safeguards

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(config): expand schema v2 regression coverage

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(config): add schema v2 parity manifest

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): correct parity manifest inventory

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* docs(config): record schema v2 intentional changes

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* docs(config): disposition schema v2 parity gaps

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): add dual schema parity harness

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): establish compute lifecycle parity baseline

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): preserve gateway option compatibility

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): record gateway option parity

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* docs(config): close gateway-wide parity gaps

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(podman): apply configured pids limit

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): validate Podman option parity

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): add Kubernetes option parity harness

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): record Kubernetes option parity

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): disposition VM parity lanes

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): add external driver parity lane

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(e2e): preserve external driver pull policy

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): attest parity artifacts and launches

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): require clean parity build sources

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): bind parity runtime artifacts

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(e2e): use isolated supervisor tags

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(e2e): qualify parity image tags

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(e2e): serve parity supervisor locally

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): isolate parity podman services

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): harden parity evidence provenance

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): pin parity sandbox artifacts

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): attest parity runtime inputs

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): bind parity runtime evidence

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): record compute boundary parity

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(e2e): disposition cross-cutting parity lanes

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(packaging): preflight gateway config upgrades

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): preserve rebase integration guarantees

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(ci): isolate temporary git signing config

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): update remaining schema v2 consumers

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(ci): provide e2fs tools to VM tests

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): align preflight with gateway startup

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(vm): preserve rootfs tar configuration

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* chore(config): adopt duration unit constructors

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(packaging): preflight RPM gateway config

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(config): address driver review findings

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(e2e): require fresh semantic parity evidence

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* fix(docker): update tests for renamed sandbox label

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>

* test(gateway): preserve selective driver coverage after rebase

Signed-off-by: Drew Newberry <anewberry@nvidia.com>

---------

Signed-off-by: Jesse Jaggars <jjaggars@redhat.com>
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
Co-authored-by: Drew Newberry <anewberry@nvidia.com>
2026-09-11 05:00:24 +00:00

828 lines
34 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# Run an e2e command against a Podman-backed OpenShell gateway.
#
# Modes:
# - OPENSHELL_GATEWAY_ENDPOINT unset:
# Build and start an ephemeral standalone gateway with the Podman compute
# driver, then run the command against that gateway.
# - OPENSHELL_GATEWAY_ENDPOINT=http://host:port:
# Use the existing plaintext gateway endpoint and run the command.
#
# HTTPS endpoint-only mode is intentionally unsupported here. Use a named
# gateway config when mTLS materials are needed.
#
# Set OPENSHELL_E2E_PODMAN_STOP_TIMEOUT_SECS to override the managed gateway's
# Podman sandbox stop timeout. The harness default is intentionally shorter
# than the production driver default to keep CI teardown bounded.
set -euo pipefail
if [ "$#" -eq 0 ]; then
echo "Usage: e2e/with-podman-gateway.sh <command> [args...]" >&2
exit 2
fi
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=e2e/support/gateway-common.sh
source "${ROOT}/e2e/support/gateway-common.sh"
# shellcheck source=e2e/support/podman-gateway-config.sh
source "${ROOT}/e2e/support/podman-gateway-config.sh"
require_container_engine_lane() {
local lane=$1
local label=$2
local selected_engine selected_driver
if [ -n "${OPENSHELL_E2E_CONTAINER_ENGINE:-}" ]; then
echo "ERROR: OPENSHELL_E2E_CONTAINER_ENGINE is no longer supported." >&2
echo " Set CONTAINER_ENGINE=${lane} for the ${label} e2e lane, or unset it." >&2
exit 2
fi
selected_engine="$(printf '%s' "${CONTAINER_ENGINE:-}" | tr '[:upper:]' '[:lower:]')"
selected_driver="$(printf '%s' "${OPENSHELL_E2E_DRIVER:-}" | tr '[:upper:]' '[:lower:]')"
if [ -n "${selected_engine}" ] && [ "${selected_engine}" != "${lane}" ]; then
echo "ERROR: CONTAINER_ENGINE=${CONTAINER_ENGINE} conflicts with the ${label} e2e lane." >&2
echo " Set CONTAINER_ENGINE=${lane} or unset CONTAINER_ENGINE." >&2
exit 2
fi
if [ -n "${selected_driver}" ] && [ "${selected_driver}" != "${lane}" ]; then
echo "ERROR: OPENSHELL_E2E_DRIVER=${OPENSHELL_E2E_DRIVER} conflicts with the ${label} e2e lane." >&2
echo " Set OPENSHELL_E2E_DRIVER=${lane} or unset OPENSHELL_E2E_DRIVER." >&2
exit 2
fi
export CONTAINER_ENGINE="${lane}"
export OPENSHELL_E2E_DRIVER="${lane}"
}
require_container_engine_lane podman Podman
PODMAN_XDG_CONFIG_HOME_WAS_SET=0
PODMAN_XDG_CONFIG_HOME=""
if [ "${XDG_CONFIG_HOME+x}" = x ]; then
PODMAN_XDG_CONFIG_HOME_WAS_SET=1
PODMAN_XDG_CONFIG_HOME="${XDG_CONFIG_HOME}"
export OPENSHELL_E2E_CONTAINER_ENGINE_XDG_CONFIG_HOME="${PODMAN_XDG_CONFIG_HOME}"
unset OPENSHELL_E2E_CONTAINER_ENGINE_UNSET_XDG_CONFIG_HOME
else
export OPENSHELL_E2E_CONTAINER_ENGINE_UNSET_XDG_CONFIG_HOME=1
unset OPENSHELL_E2E_CONTAINER_ENGINE_XDG_CONFIG_HOME
fi
with_podman_config() {
if [ "${PODMAN_XDG_CONFIG_HOME_WAS_SET}" = "1" ]; then
XDG_CONFIG_HOME="${PODMAN_XDG_CONFIG_HOME}" "$@"
else
env -u XDG_CONFIG_HOME "$@"
fi
}
podman_cmd() {
if [ -n "${OPENSHELL_PODMAN_SOCKET:-}" ]; then
with_podman_config podman --url "unix://${OPENSHELL_PODMAN_SOCKET}" "$@"
else
with_podman_config podman "$@"
fi
}
require_expected_sha256() {
local label=$1 path=$2 expected=$3 actual
[ -n "${expected}" ] || return 0
if [ ! -f "${path}" ]; then
echo "ERROR: ${label} is missing before execution: ${path}" >&2
exit 2
fi
actual="$(sha256sum "${path}" | cut -d' ' -f1)"
if [ "${actual}" != "${expected}" ]; then
echo "ERROR: ${label} hash changed before execution." >&2
exit 2
fi
}
WORKDIR_PARENT="${TMPDIR:-/tmp}"
WORKDIR_PARENT="${WORKDIR_PARENT%/}"
WORKDIR="$(mktemp -d "${WORKDIR_PARENT}/openshell-e2e-podman.XXXXXX")"
if [ "${OPENSHELL_E2E_SPIFFE_FIXTURE:-0}" = "1" ]; then
mkdir -p "${WORKDIR}/spiffe"
export OPENSHELL_E2E_GATEWAY_SPIFFE_SOCKET="${OPENSHELL_E2E_GATEWAY_SPIFFE_SOCKET:-${WORKDIR}/spiffe/gateway.sock}"
export OPENSHELL_GATEWAY_SPIFFE_WORKLOAD_API_SOCKET="${OPENSHELL_E2E_GATEWAY_SPIFFE_SOCKET}"
if [ -z "${OPENSHELL_E2E_PROVIDER_SPIFFE_SOCKET:-}" ]; then
OPENSHELL_E2E_PROVIDER_SPIFFE_PORT="$(e2e_pick_port)"
export OPENSHELL_E2E_PROVIDER_SPIFFE_LISTEN="0.0.0.0:${OPENSHELL_E2E_PROVIDER_SPIFFE_PORT}"
export OPENSHELL_E2E_PROVIDER_SPIFFE_SOCKET="tcp:169.254.1.2:${OPENSHELL_E2E_PROVIDER_SPIFFE_PORT}"
fi
fi
GATEWAY_BIN=""
CLI_BIN=""
GATEWAY_PID=""
GATEWAY_LOG="${WORKDIR}/gateway.log"
export OPENSHELL_E2E_GATEWAY_LOG="${GATEWAY_LOG}"
GATEWAY_PID_FILE="${WORKDIR}/gateway.pid"
GATEWAY_ARGS_FILE="${WORKDIR}/gateway.args"
DRIVER_BIN=""
DRIVER_PID=""
DRIVER_LOG="${OPENSHELL_PARITY_EXTERNAL_DRIVER_LOG_CAPTURE:-${WORKDIR}/podman-driver.log}"
mkdir -p "$(dirname "${DRIVER_LOG}")"
DRIVER_SOCKET="${WORKDIR}/compute-driver.sock"
E2E_NAMESPACE=""
PODMAN_NETWORK_NAME=""
PODMAN_NETWORK_MANAGED=0
PODMAN_SERVICE_PID=""
PODMAN_SERVICE_LOG="${WORKDIR}/podman-service.log"
PODMAN_SOCKET=""
GPU_MODE="${OPENSHELL_E2E_PODMAN_GPU:-0}"
OIDC_MODE="${OPENSHELL_E2E_OIDC_GATEWAY:-0}"
OIDC_ISSUER="${OPENSHELL_E2E_OIDC_ISSUER:-}"
if [ "${OIDC_MODE}" = "1" ] && [ -z "${OIDC_ISSUER}" ]; then
echo "ERROR: OPENSHELL_E2E_OIDC_ISSUER is required when OPENSHELL_E2E_OIDC_GATEWAY=1" >&2
exit 2
fi
# Isolate CLI/SDK gateway metadata from the developer's real config.
export XDG_CONFIG_HOME="${WORKDIR}/config"
cleanup() {
local exit_code=$?
e2e_stop_gateway "${GATEWAY_PID}" "${GATEWAY_PID_FILE}"
e2e_stop_process "${DRIVER_PID}" "external Podman compute driver"
local sandbox_ids=""
if command -v podman >/dev/null 2>&1; then
if [ -n "${PODMAN_NETWORK_NAME}" ]; then
sandbox_ids="$(podman_cmd ps -aq \
--filter "label=openshell.managed=true" \
--filter "network=${PODMAN_NETWORK_NAME}" \
2>/dev/null || true)"
elif [ -n "${E2E_NAMESPACE}" ]; then
sandbox_ids="$(podman_cmd ps -aq \
--filter "label=openshell.managed=true" \
--filter "label=openshell.ai/sandbox-namespace=${E2E_NAMESPACE}" \
2>/dev/null || true)"
fi
fi
if [ "${exit_code}" -ne 0 ] && [ -n "${sandbox_ids}" ]; then
echo "=== sandbox container logs (preserved for debugging) ==="
for id in ${sandbox_ids}; do
echo "--- container ${id} (inspect) ---"
podman_cmd inspect --format '{{.Name}} state={{.State.Status}} exit={{.State.ExitCode}} error={{.State.Error}}' "${id}" 2>/dev/null || true
echo "--- container ${id} (last 80 log lines) ---"
podman_cmd logs --tail 80 "${id}" 2>&1 || true
done
echo "=== end sandbox container logs ==="
fi
if [ -n "${sandbox_ids}" ]; then
for id in ${sandbox_ids}; do
local sandbox_id
sandbox_id="$(podman_cmd inspect --format '{{ index .Config.Labels "openshell.ai/sandbox-id" }}' "${id}" 2>/dev/null || true)"
podman_cmd rm -f "${id}" >/dev/null 2>&1 || true
if [ -n "${sandbox_id}" ] && [ "${sandbox_id}" != "<no value>" ]; then
podman_cmd volume rm -f "openshell-sandbox-${sandbox_id}-workspace" >/dev/null 2>&1 || true
fi
done
fi
if [ "${PODMAN_NETWORK_MANAGED}" = "1" ] \
&& [ -n "${PODMAN_NETWORK_NAME}" ] \
&& command -v podman >/dev/null 2>&1; then
podman_cmd network rm "${PODMAN_NETWORK_NAME}" >/dev/null 2>&1 || true
fi
e2e_print_gateway_log_on_failure "${exit_code}" "${GATEWAY_LOG}"
if [ "${exit_code}" -ne 0 ] && [ -f "${DRIVER_LOG}" ]; then
echo "=== external Podman compute driver log ==="
cat "${DRIVER_LOG}" || true
echo "=== end external Podman compute driver log ==="
fi
if [ "${exit_code}" -ne 0 ] && [ -f "${PODMAN_SERVICE_LOG}" ]; then
echo "=== podman service log (preserved for debugging) ==="
cat "${PODMAN_SERVICE_LOG}" || true
echo "=== end podman service log ==="
fi
if [ -n "${PODMAN_SERVICE_PID}" ]; then
kill "${PODMAN_SERVICE_PID}" >/dev/null 2>&1 || true
wait "${PODMAN_SERVICE_PID}" >/dev/null 2>&1 || true
fi
rm -rf "${WORKDIR}" 2>/dev/null || true
}
trap cleanup EXIT
ensure_e2e_podman_network() {
local network=$1
if podman_cmd network inspect "${network}" >/dev/null 2>&1; then
return 0
fi
podman_cmd network create \
--driver bridge \
--label openshell.managed=true \
--label "openshell.ai/sandbox-namespace=${E2E_NAMESPACE}" \
"${network}" >/dev/null
PODMAN_NETWORK_MANAGED=1
}
default_podman_socket_path() {
case "$(uname -s)" in
Darwin)
# On macOS the podman client talks to a VM; the API socket path is
# per-launch (under $TMPDIR) and reported by `podman machine inspect`.
# The legacy ~/.local/share/containers/podman/machine/podman.sock path
# is not created by podman >= 5.x with the applehv/libkrun providers.
podman_cmd machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}' 2>/dev/null \
| awk 'NF { print; exit }'
;;
Linux)
if [ -n "${XDG_RUNTIME_DIR:-}" ]; then
printf '%s\n' "${XDG_RUNTIME_DIR}/podman/podman.sock"
else
printf '%s\n' "/run/user/$(id -u)/podman/podman.sock"
fi
;;
*)
return 1
;;
esac
}
ensure_podman_api_socket() {
if [ "${OPENSHELL_E2E_FORCE_TEMP_PODMAN_SERVICE:-0}" != 1 ]; then
if [ -n "${OPENSHELL_PODMAN_SOCKET:-}" ]; then
export CONTAINER_HOST="${CONTAINER_HOST:-unix://${OPENSHELL_PODMAN_SOCKET}}"
return 0
fi
local default_socket
default_socket="$(default_podman_socket_path || true)"
if [ -n "${default_socket}" ] \
&& [ -S "${default_socket}" ] \
&& with_podman_config podman --url "unix://${default_socket}" info >/dev/null 2>&1; then
export OPENSHELL_PODMAN_SOCKET="${default_socket}"
export CONTAINER_HOST="${CONTAINER_HOST:-unix://${OPENSHELL_PODMAN_SOCKET}}"
return 0
fi
else
unset OPENSHELL_PODMAN_SOCKET CONTAINER_HOST
fi
# `podman system service` is a Linux-only subcommand — the macOS client
# delegates the API service to the VM, so we can't spin one up locally.
# If we got here on Darwin, the user's `podman machine` is either not
# running or its socket isn't reachable; surface that directly.
if [ "$(uname -s)" = "Darwin" ]; then
echo "ERROR: could not reach the Podman API socket on macOS." >&2
echo " Expected socket from 'podman machine inspect': ${default_socket:-<none>}" >&2
echo " Ensure 'podman machine start' has been run, or set" >&2
echo " OPENSHELL_PODMAN_SOCKET to a reachable unix socket path." >&2
exit 2
fi
PODMAN_SOCKET="${WORKDIR}/podman/podman.sock"
mkdir -p "$(dirname "${PODMAN_SOCKET}")"
echo "Starting temporary Podman API service at ${PODMAN_SOCKET}..."
with_podman_config podman system service --time=0 "unix://${PODMAN_SOCKET}" \
>"${PODMAN_SERVICE_LOG}" 2>&1 &
PODMAN_SERVICE_PID=$!
export OPENSHELL_PODMAN_SOCKET="${PODMAN_SOCKET}"
export CONTAINER_HOST="${CONTAINER_HOST:-unix://${OPENSHELL_PODMAN_SOCKET}}"
local elapsed=0
local timeout=30
while [ "${elapsed}" -lt "${timeout}" ]; do
if [ -S "${PODMAN_SOCKET}" ] \
&& podman_cmd info >/dev/null 2>&1; then
return 0
fi
if ! kill -0 "${PODMAN_SERVICE_PID}" 2>/dev/null; then
echo "ERROR: Podman API service exited before becoming reachable" >&2
cat "${PODMAN_SERVICE_LOG}" >&2 || true
exit 2
fi
sleep 1
elapsed=$((elapsed + 1))
done
echo "ERROR: Podman API service did not become reachable within ${timeout}s" >&2
cat "${PODMAN_SERVICE_LOG}" >&2 || true
exit 2
}
resolve_podman_supervisor_image() {
if [ -n "${OPENSHELL_SUPERVISOR_IMAGE:-}" ]; then
printf '%s\n' "${OPENSHELL_SUPERVISOR_IMAGE}"
return 0
fi
if [ -n "${CI:-}" ]; then
if [ -z "${IMAGE_TAG:-}" ]; then
echo "ERROR: IMAGE_TAG must be set in CI when no Podman supervisor image override is provided." >&2
exit 2
fi
local registry="${OPENSHELL_REGISTRY:-ghcr.io/nvidia/openshell}"
printf '%s/supervisor:%s\n' "${registry%/}" "${IMAGE_TAG}"
return 0
fi
printf '%s\n' "openshell/supervisor:dev"
}
ensure_podman_supervisor_image() {
local image=$1
if [ -n "${OPENSHELL_E2E_SUPERVISOR_BIN:-}" ]; then
local dockerfile=${OPENSHELL_E2E_SUPERVISOR_DOCKERFILE:-${ROOT}/deploy/docker/Dockerfile.supervisor}
local context="${WORKDIR}/supervisor-image" arch
case "${image}" in
*:dev|*:latest)
echo "ERROR: supplied supervisor binaries require a unique versioned image tag, not ${image}." >&2
exit 2
;;
*:*) ;;
*)
echo "ERROR: supplied supervisor binaries require an explicit versioned image tag: ${image}." >&2
exit 2
;;
esac
case "$(uname -m)" in
x86_64|amd64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "ERROR: unsupported supervisor image architecture: $(uname -m)" >&2; exit 2 ;;
esac
if [ ! -x "${OPENSHELL_E2E_SUPERVISOR_BIN}" ]; then
echo "ERROR: supplied supervisor binary is not executable: ${OPENSHELL_E2E_SUPERVISOR_BIN}" >&2
exit 2
fi
if [ ! -f "${dockerfile}" ]; then
echo "ERROR: supervisor Dockerfile not found: ${dockerfile}" >&2
exit 2
fi
require_expected_sha256 "supervisor binary" "${OPENSHELL_E2E_SUPERVISOR_BIN}" \
"${OPENSHELL_E2E_EXPECTED_SUPERVISOR_SHA256:-}"
require_expected_sha256 "supervisor Dockerfile" "${dockerfile}" \
"${OPENSHELL_E2E_EXPECTED_SUPERVISOR_DOCKERFILE_SHA256:-}"
mkdir -p "${context}/deploy/docker/.build/prebuilt-binaries/${arch}"
install -m 0555 "${OPENSHELL_E2E_SUPERVISOR_BIN}" \
"${context}/deploy/docker/.build/prebuilt-binaries/${arch}/openshell-sandbox"
cp "${dockerfile}" "${context}/deploy/docker/Dockerfile.supervisor"
local -a pull_option=()
if [ -n "${OPENSHELL_E2E_SUPERVISOR_BASE_RUNTIME_IMAGE:-}" ]; then
local dockerfile_base
dockerfile_base="$(awk '$1 == "FROM" { print $2; exit }' "${dockerfile}")"
if [ "${dockerfile_base}" != "${OPENSHELL_E2E_SUPERVISOR_BASE_IMAGE:-}" ] \
|| ! [[ "${OPENSHELL_E2E_SUPERVISOR_BASE_RUNTIME_IMAGE}" =~ ^[^@]+@sha256:[0-9a-f]{64}$ ]]; then
echo "ERROR: supervisor base-image attestation does not match the Dockerfile." >&2
exit 2
fi
echo "Pulling pinned supervisor base image ${OPENSHELL_E2E_SUPERVISOR_BASE_RUNTIME_IMAGE}..."
podman_cmd pull "${OPENSHELL_E2E_SUPERVISOR_BASE_RUNTIME_IMAGE}"
podman_cmd tag "${OPENSHELL_E2E_SUPERVISOR_BASE_RUNTIME_IMAGE}" "${dockerfile_base}"
pull_option=(--pull=never)
fi
echo "Building Podman supervisor image ${image} from supplied binary..."
(
cd "${context}"
podman_cmd build \
"${pull_option[@]}" \
--build-arg "TARGETARCH=${arch}" \
--file deploy/docker/Dockerfile.supervisor \
--target supervisor \
--tag "${image}" \
.
)
return 0
fi
if [ "${image}" = "openshell/supervisor:dev" ] \
&& [ -z "${OPENSHELL_SUPERVISOR_IMAGE:-}" ] \
&& [ -z "${CI:-}" ]; then
echo "Building local Podman supervisor image ${image}..."
with_podman_config env CONTAINER_ENGINE=podman IMAGE_TAG=dev \
bash "${ROOT}/tasks/scripts/docker-build-image.sh" supervisor
if podman_cmd image exists "${image}" 2>/dev/null; then
return 0
fi
echo "ERROR: expected supervisor image '${image}' after local build." >&2
exit 2
fi
if podman_cmd image exists "${image}" 2>/dev/null; then
return 0
fi
echo "Pulling Podman supervisor image ${image}..."
if podman_cmd pull "${image}"; then
return 0
fi
echo "ERROR: supervisor image '${image}' is not available." >&2
echo " Build it, push it, or set OPENSHELL_SUPERVISOR_IMAGE to a pullable image." >&2
exit 2
}
if [ -n "${OPENSHELL_GATEWAY_ENDPOINT:-}" ]; then
case "${OPENSHELL_GATEWAY_ENDPOINT}" in
http://*) ;;
https://*)
echo "ERROR: OPENSHELL_GATEWAY_ENDPOINT endpoint mode is HTTP-only for e2e." >&2
echo " Register a named gateway with mTLS config instead of using a raw HTTPS endpoint." >&2
exit 2
;;
*)
echo "ERROR: OPENSHELL_GATEWAY_ENDPOINT must start with http:// for e2e endpoint mode." >&2
exit 2
;;
esac
GATEWAY_NAME="${OPENSHELL_GATEWAY:-openshell-e2e-podman-endpoint}"
e2e_register_plaintext_gateway \
"${XDG_CONFIG_HOME}" \
"${GATEWAY_NAME}" \
"${OPENSHELL_GATEWAY_ENDPOINT}" \
"$(e2e_endpoint_port "${OPENSHELL_GATEWAY_ENDPOINT}")"
export OPENSHELL_GATEWAY="${GATEWAY_NAME}"
export OPENSHELL_PROVISION_TIMEOUT="${OPENSHELL_PROVISION_TIMEOUT:-300}"
export OPENSHELL_E2E_DRIVER="podman"
echo "Using existing Podman e2e gateway endpoint: ${OPENSHELL_GATEWAY_ENDPOINT}"
"$@"
exit $?
fi
# Validate the generated configuration dialect before creating runtime resources.
CONFIG_SCHEMA_VERSION="$(e2e_podman_config_schema_version)"
EXTERNAL_DRIVER_PULL_POLICY="$(e2e_podman_external_driver_pull_policy "${CONFIG_SCHEMA_VERSION}")"
# Validate the opt-in profile before building images or allocating runtime resources.
e2e_podman_option_profile >/dev/null
# Preflight for managed Podman gateway mode.
if ! command -v podman >/dev/null 2>&1; then
echo "ERROR: podman CLI is required to run Podman-backed e2e tests" >&2
exit 2
fi
ensure_podman_api_socket
if ! podman_cmd info >/dev/null 2>&1; then
echo "ERROR: podman service is not reachable (podman info failed)" >&2
echo " Start it with 'podman machine start' on macOS, or the user service on Linux." >&2
exit 2
fi
e2e_build_gateway_binaries "${ROOT}" TARGET_DIR GATEWAY_BIN CLI_BIN
export OPENSHELL_BIN="${CLI_BIN}"
if [ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" = "1" ]; then
e2e_build_external_driver \
"${ROOT}" openshell-driver-podman openshell-driver-podman DRIVER_BIN
fi
SUPERVISOR_IMAGE="$(resolve_podman_supervisor_image)"
ensure_podman_supervisor_image "${SUPERVISOR_IMAGE}"
SUPERVISOR_IMAGE_ID="$(podman_cmd image inspect --format '{{.Id}}' "${SUPERVISOR_IMAGE}")"
SUPERVISOR_IMAGE_ID="${SUPERVISOR_IMAGE_ID#sha256:}"
SUPERVISOR_IMAGE_DIGEST="$(podman_cmd image inspect --format '{{.Digest}}' "${SUPERVISOR_IMAGE}")"
if ! [[ "${SUPERVISOR_IMAGE_ID}" =~ ^[0-9a-f]{64}$ ]]; then
echo "ERROR: could not resolve immutable supervisor image ID for ${SUPERVISOR_IMAGE}." >&2
exit 2
fi
if ! [[ "${SUPERVISOR_IMAGE_DIGEST}" =~ ^sha256:[0-9a-f]{64}$ ]]; then
echo "ERROR: could not resolve supervisor image digest for ${SUPERVISOR_IMAGE}." >&2
exit 2
fi
# The parity harness forces a temporary Podman API service into the same
# isolated XDG store where this image was built. Address the local image by its
# immutable manifest digest so policy=missing cannot resolve a mutable tag or
# contact a registry for a different artifact.
SUPERVISOR_IMAGE_REPOSITORY="${SUPERVISOR_IMAGE%:*}"
SUPERVISOR_RUNTIME_IMAGE="${SUPERVISOR_IMAGE_REPOSITORY}@${SUPERVISOR_IMAGE_DIGEST}"
if ! [[ "${SUPERVISOR_RUNTIME_IMAGE}" =~ ^[^@]+@sha256:[0-9a-f]{64}$ ]]; then
echo "ERROR: supervisor runtime image is not digest-pinned: ${SUPERVISOR_RUNTIME_IMAGE}" >&2
exit 2
fi
SUPERVISOR_BASE_IMAGE="$(awk '$1 == "FROM" { print $2; exit }' "${OPENSHELL_E2E_SUPERVISOR_DOCKERFILE:-${ROOT}/deploy/docker/Dockerfile.supervisor}")"
SUPERVISOR_BASE_IMAGE_ID="$(podman_cmd image inspect --format '{{.Id}}' "${SUPERVISOR_BASE_IMAGE}")"
SUPERVISOR_BASE_IMAGE_ID="${SUPERVISOR_BASE_IMAGE_ID#sha256:}"
SUPERVISOR_BASE_IMAGE_DIGEST="$(podman_cmd image inspect --format '{{.Digest}}' "${SUPERVISOR_BASE_IMAGE}")"
if ! [[ "${SUPERVISOR_BASE_IMAGE_ID}" =~ ^[0-9a-f]{64}$ ]] \
|| ! [[ "${SUPERVISOR_BASE_IMAGE_DIGEST}" =~ ^sha256:[0-9a-f]{64}$ ]]; then
echo "ERROR: could not resolve supervisor base-image provenance for ${SUPERVISOR_BASE_IMAGE}." >&2
exit 2
fi
SUPERVISOR_PACKAGE_MANIFEST="${OPENSHELL_PARITY_SUPERVISOR_PACKAGE_CAPTURE:-${WORKDIR}/supervisor.packages.txt}"
mkdir -p "$(dirname "${SUPERVISOR_PACKAGE_MANIFEST}")"
podman_cmd run --rm --network none --entrypoint /sbin/apk \
"${SUPERVISOR_RUNTIME_IMAGE}" info -v | LC_ALL=C sort >"${SUPERVISOR_PACKAGE_MANIFEST}"
SUPERVISOR_PACKAGE_MANIFEST_SHA256="$(sha256sum "${SUPERVISOR_PACKAGE_MANIFEST}" | cut -d' ' -f1)"
echo "Using Podman supervisor image: ${SUPERVISOR_RUNTIME_IMAGE} (ID ${SUPERVISOR_IMAGE_ID}, digest ${SUPERVISOR_IMAGE_DIGEST}, base ${SUPERVISOR_BASE_IMAGE} ID ${SUPERVISOR_BASE_IMAGE_ID} digest ${SUPERVISOR_BASE_IMAGE_DIGEST}, packages ${SUPERVISOR_PACKAGE_MANIFEST_SHA256})"
DEFAULT_SANDBOX_IMAGE="ghcr.io/nvidia/openshell-community/sandboxes/base:latest"
SANDBOX_IMAGE_REQUEST="${OPENSHELL_E2E_PODMAN_SANDBOX_IMAGE:-${OPENSHELL_SANDBOX_IMAGE:-${DEFAULT_SANDBOX_IMAGE}}}"
if [ "${OPENSHELL_E2E_REQUIRE_DIGEST_PINNED_SANDBOX_IMAGE:-0}" = "1" ] \
&& ! [[ "${SANDBOX_IMAGE_REQUEST}" =~ ^[^@]+@sha256:[0-9a-f]{64}$ ]]; then
echo "ERROR: this e2e invocation requires a digest-pinned sandbox image: ${SANDBOX_IMAGE_REQUEST}" >&2
exit 2
fi
PODMAN_STOP_TIMEOUT_SECS="${OPENSHELL_E2E_PODMAN_STOP_TIMEOUT_SECS:-15}"
if ! [[ "${PODMAN_STOP_TIMEOUT_SECS}" =~ ^[0-9]+$ ]]; then
echo "ERROR: OPENSHELL_E2E_PODMAN_STOP_TIMEOUT_SECS must be a non-negative integer." >&2
exit 2
fi
if ! podman_cmd image exists "${SANDBOX_IMAGE_REQUEST}" 2>/dev/null; then
echo "Pulling ${SANDBOX_IMAGE_REQUEST}..."
podman_cmd pull "${SANDBOX_IMAGE_REQUEST}"
fi
SANDBOX_IMAGE_ID="$(podman_cmd image inspect --format '{{.Id}}' "${SANDBOX_IMAGE_REQUEST}")"
SANDBOX_IMAGE_ID="${SANDBOX_IMAGE_ID#sha256:}"
SANDBOX_IMAGE_DIGEST="$(podman_cmd image inspect --format '{{.Digest}}' "${SANDBOX_IMAGE_REQUEST}")"
SANDBOX_IMAGE_REPOSITORY="${SANDBOX_IMAGE_REQUEST%%@*}"
case "${SANDBOX_IMAGE_REPOSITORY##*/}" in
*:*) SANDBOX_IMAGE_REPOSITORY="${SANDBOX_IMAGE_REPOSITORY%:*}" ;;
esac
SANDBOX_RUNTIME_IMAGE="${SANDBOX_IMAGE_REPOSITORY}@${SANDBOX_IMAGE_DIGEST}"
if ! [[ "${SANDBOX_IMAGE_ID}" =~ ^[0-9a-f]{64}$ ]] \
|| ! [[ "${SANDBOX_RUNTIME_IMAGE}" =~ ^[^@]+@sha256:[0-9a-f]{64}$ ]]; then
echo "ERROR: could not resolve an immutable sandbox image for ${SANDBOX_IMAGE_REQUEST}." >&2
exit 2
fi
if [ "${OPENSHELL_E2E_REQUIRE_DIGEST_PINNED_SANDBOX_IMAGE:-0}" = "1" ] \
&& [ "${SANDBOX_IMAGE_REQUEST}" != "${SANDBOX_RUNTIME_IMAGE}" ]; then
echo "ERROR: sandbox image digest changed while resolving ${SANDBOX_IMAGE_REQUEST}." >&2
exit 2
fi
SANDBOX_CLIENT_IMAGE_ALIAS=""
SANDBOX_CLIENT_IMAGE_ALIAS_ID=""
if [ "${OPENSHELL_E2E_REQUIRE_DIGEST_PINNED_SANDBOX_IMAGE:-0}" = "1" ]; then
SANDBOX_CLIENT_IMAGE_ALIAS="${SANDBOX_IMAGE_REPOSITORY}:latest"
podman_cmd tag "${SANDBOX_RUNTIME_IMAGE}" "${SANDBOX_CLIENT_IMAGE_ALIAS}"
SANDBOX_CLIENT_IMAGE_ALIAS_ID="$(podman_cmd image inspect --format '{{.Id}}' "${SANDBOX_CLIENT_IMAGE_ALIAS}")"
SANDBOX_CLIENT_IMAGE_ALIAS_ID="${SANDBOX_CLIENT_IMAGE_ALIAS_ID#sha256:}"
if [ "${SANDBOX_CLIENT_IMAGE_ALIAS_ID}" != "${SANDBOX_IMAGE_ID}" ]; then
echo "ERROR: sandbox client alias does not resolve to the pinned sandbox image." >&2
exit 2
fi
fi
echo "Using Podman sandbox image: ${SANDBOX_RUNTIME_IMAGE} (ID ${SANDBOX_IMAGE_ID}, digest ${SANDBOX_IMAGE_DIGEST}, client alias ${SANDBOX_CLIENT_IMAGE_ALIAS:-none} ID ${SANDBOX_CLIENT_IMAGE_ALIAS_ID:-none})"
PKI_DIR="${WORKDIR}/pki"
e2e_generate_pki "${GATEWAY_BIN}" "${PKI_DIR}" "host.containers.internal"
export OPENSHELL_E2E_GATEWAY_CA_CERT="${PKI_DIR}/ca.crt"
HOST_PORT=$(e2e_pick_port)
HEALTH_PORT=$(e2e_pick_port)
if [ "$(uname -s)" = "Darwin" ]; then
# Podman Machine reserves IPv4 loopback for its callback-only listener.
PRIMARY_BIND_IP="::1"
CLI_ENDPOINT_HOST="localhost"
HEALTH_ENDPOINT_HOST="[::1]"
else
PRIMARY_BIND_IP="127.0.0.1"
CLI_ENDPOINT_HOST="127.0.0.1"
HEALTH_ENDPOINT_HOST="127.0.0.1"
fi
STATE_DIR="${WORKDIR}/state"
mkdir -p "${STATE_DIR}"
export XDG_STATE_HOME="${STATE_DIR}"
JWT_DIR="${STATE_DIR}/jwt"
E2E_NAMESPACE="e2e-podman-$$-${HOST_PORT}"
PODMAN_NETWORK_NAME="${E2E_NAMESPACE}"
ensure_e2e_podman_network "${PODMAN_NETWORK_NAME}"
export OPENSHELL_E2E_DRIVER="podman"
export OPENSHELL_E2E_NETWORK_NAME="${PODMAN_NETWORK_NAME}"
export OPENSHELL_E2E_SANDBOX_NAMESPACE="${E2E_NAMESPACE}"
echo "Starting openshell-gateway on port ${HOST_PORT} (namespace: ${E2E_NAMESPACE})..."
e2e_generate_gateway_jwt "${JWT_DIR}"
GATEWAY_CONFIG="${STATE_DIR}/gateway.toml"
e2e_write_podman_gateway_config \
"${GATEWAY_CONFIG}" \
"${CONFIG_SCHEMA_VERSION}" \
"${ROOT}" \
"${PKI_DIR}" \
"${JWT_DIR}" \
"openshell-e2e-podman-${HOST_PORT}" \
"${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" \
"${DRIVER_SOCKET}" \
"${PODMAN_NETWORK_NAME}" \
"${HOST_PORT}" \
"${SANDBOX_RUNTIME_IMAGE}" \
"${PODMAN_STOP_TIMEOUT_SECS}" \
"${SUPERVISOR_RUNTIME_IMAGE}" \
"${OPENSHELL_E2E_PROVIDER_SPIFFE_SOCKET:-}" \
"${OPENSHELL_PODMAN_SOCKET:-}" \
"${OIDC_MODE}" \
"${OPENSHELL_OIDC_ISSUER:-}"
if [ -n "${OPENSHELL_PARITY_GATEWAY_CONFIG_CAPTURE:-}" ]; then
cp "${GATEWAY_CONFIG}" "${OPENSHELL_PARITY_GATEWAY_CONFIG_CAPTURE}"
fi
EXTERNAL_DRIVER_CALLBACK_ENDPOINT="https://host.containers.internal:${HOST_PORT}"
EXTERNAL_DRIVER_HEALTH_CHECK_INTERVAL_SECS=10
EXTERNAL_DRIVER_ENABLE_BIND_MOUNTS=true
EXTERNAL_DRIVER_TLS_CA="${PKI_DIR}/ca.crt"
EXTERNAL_DRIVER_TLS_CERT="${PKI_DIR}/client/tls.crt"
EXTERNAL_DRIVER_TLS_KEY="${PKI_DIR}/client/tls.key"
if [ -n "${OPENSHELL_PARITY_LAUNCH_MANIFEST_CAPTURE:-}" ]; then
driver_transport=in_tree
external_driver_grpc_endpoint=null
external_driver_host_gateway_ip=null
external_driver_userns=null
external_driver_spiffe=false
external_driver_proxy=false
external_driver_app_armor=false
external_driver_environment=null
if [ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" = "1" ]; then
driver_transport=remote_uds
external_driver_grpc_endpoint="\"${EXTERNAL_DRIVER_CALLBACK_ENDPOINT}\""
external_driver_host_gateway_ip='"host-gateway"'
driver_tls_ca_sha256="$(sha256sum "${EXTERNAL_DRIVER_TLS_CA}" | cut -d' ' -f1)"
driver_tls_cert_sha256="$(sha256sum "${EXTERNAL_DRIVER_TLS_CERT}" | cut -d' ' -f1)"
driver_tls_key_sha256="$(sha256sum "${EXTERNAL_DRIVER_TLS_KEY}" | cut -d' ' -f1)"
external_driver_environment="$(printf '{\"OPENSHELL_COMPUTE_DRIVER_SOCKET\":\"%s\",\"OPENSHELL_PODMAN_SOCKET\":\"%s\",\"OPENSHELL_SANDBOX_IMAGE\":\"%s\",\"OPENSHELL_SANDBOX_IMAGE_PULL_POLICY\":\"%s\",\"OPENSHELL_HEALTH_CHECK_INTERVAL_SECS\":%s,\"OPENSHELL_GRPC_ENDPOINT\":\"%s\",\"OPENSHELL_GATEWAY_PORT\":%s,\"OPENSHELL_NETWORK_NAME\":\"%s\",\"OPENSHELL_STOP_TIMEOUT\":%s,\"OPENSHELL_SUPERVISOR_IMAGE\":\"%s\",\"OPENSHELL_PODMAN_TLS_CA\":{\"path\":\"%s\",\"sha256\":\"%s\"},\"OPENSHELL_PODMAN_TLS_CERT\":{\"path\":\"%s\",\"sha256\":\"%s\"},\"OPENSHELL_PODMAN_TLS_KEY\":{\"path\":\"%s\",\"sha256\":\"%s\"},\"OPENSHELL_ENABLE_BIND_MOUNTS\":%s}' \
"${DRIVER_SOCKET}" \
"${OPENSHELL_PODMAN_SOCKET:-}" \
"${SANDBOX_RUNTIME_IMAGE}" \
"${EXTERNAL_DRIVER_PULL_POLICY}" \
"${EXTERNAL_DRIVER_HEALTH_CHECK_INTERVAL_SECS}" \
"${EXTERNAL_DRIVER_CALLBACK_ENDPOINT}" \
"${HOST_PORT}" \
"${PODMAN_NETWORK_NAME}" \
"${PODMAN_STOP_TIMEOUT_SECS}" \
"${SUPERVISOR_RUNTIME_IMAGE}" \
"${EXTERNAL_DRIVER_TLS_CA}" \
"${driver_tls_ca_sha256}" \
"${EXTERNAL_DRIVER_TLS_CERT}" \
"${driver_tls_cert_sha256}" \
"${EXTERNAL_DRIVER_TLS_KEY}" \
"${driver_tls_key_sha256}" \
"${EXTERNAL_DRIVER_ENABLE_BIND_MOUNTS}")"
fi
printf '{"schema_version":%s,"gateway_port":%s,"external_compute_driver":%s,"compute_driver_transport":"%s","external_driver_pull_policy":"%s","supervisor_image":"%s","supervisor_image_id":"%s","supervisor_image_digest":"%s","supervisor_runtime_image":"%s","supervisor_base_image":"%s","supervisor_base_image_id":"%s","supervisor_base_image_digest":"%s","supervisor_base_runtime_image":"%s","supervisor_package_manifest_sha256":"%s","sandbox_image_request":"%s","sandbox_image_id":"%s","sandbox_image_digest":"%s","sandbox_runtime_image":"%s","sandbox_client_image_alias":"%s","sandbox_client_image_alias_id":"%s","gateway_sha256_before_execution":"%s","cli_sha256_before_execution":"%s","conformance_sha256_before_execution":"%s","external_driver_sha256_before_execution":"%s","supervisor_sha256_before_execution":"%s","supervisor_dockerfile_sha256_before_execution":"%s","cli_trace_wrapper_sha256_before_execution":"%s","external_driver_grpc_endpoint":%s,"external_driver_host_gateway_ip":%s,"external_driver_userns":%s,"external_driver_spiffe":%s,"external_driver_proxy":%s,"external_driver_app_armor":%s,"external_driver_environment":%s}\n' \
"${CONFIG_SCHEMA_VERSION}" \
"${HOST_PORT}" \
"$([ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" = "1" ] && printf true || printf false)" \
"${driver_transport}" \
"${EXTERNAL_DRIVER_PULL_POLICY}" \
"${SUPERVISOR_IMAGE}" \
"${SUPERVISOR_IMAGE_ID}" \
"${SUPERVISOR_IMAGE_DIGEST}" \
"${SUPERVISOR_RUNTIME_IMAGE}" \
"${SUPERVISOR_BASE_IMAGE}" \
"${SUPERVISOR_BASE_IMAGE_ID}" \
"${SUPERVISOR_BASE_IMAGE_DIGEST}" \
"${OPENSHELL_E2E_SUPERVISOR_BASE_RUNTIME_IMAGE:-${SUPERVISOR_BASE_IMAGE}@${SUPERVISOR_BASE_IMAGE_DIGEST}}" \
"${SUPERVISOR_PACKAGE_MANIFEST_SHA256}" \
"${SANDBOX_IMAGE_REQUEST}" \
"${SANDBOX_IMAGE_ID}" \
"${SANDBOX_IMAGE_DIGEST}" \
"${SANDBOX_RUNTIME_IMAGE}" \
"${SANDBOX_CLIENT_IMAGE_ALIAS}" \
"${SANDBOX_CLIENT_IMAGE_ALIAS_ID}" \
"${OPENSHELL_E2E_EXPECTED_GATEWAY_SHA256:-}" \
"${OPENSHELL_E2E_EXPECTED_CLI_SHA256:-}" \
"${OPENSHELL_E2E_EXPECTED_CONFORMANCE_SHA256:-}" \
"${OPENSHELL_E2E_EXPECTED_EXTERNAL_DRIVER_SHA256:-}" \
"${OPENSHELL_E2E_EXPECTED_SUPERVISOR_SHA256:-}" \
"${OPENSHELL_E2E_EXPECTED_SUPERVISOR_DOCKERFILE_SHA256:-}" \
"${OPENSHELL_E2E_EXPECTED_CLI_TRACE_WRAPPER_SHA256:-}" \
"${external_driver_grpc_endpoint}" \
"${external_driver_host_gateway_ip}" \
"${external_driver_userns}" \
"${external_driver_spiffe}" \
"${external_driver_proxy}" \
"${external_driver_app_armor}" \
"${external_driver_environment}" \
>"${OPENSHELL_PARITY_LAUNCH_MANIFEST_CAPTURE}"
fi
if [ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" = "1" ]; then
require_expected_sha256 "external compute driver" "${DRIVER_BIN}" \
"${OPENSHELL_E2E_EXPECTED_EXTERNAL_DRIVER_SHA256:-}"
env -i \
OPENSHELL_COMPUTE_DRIVER_SOCKET="${DRIVER_SOCKET}" \
OPENSHELL_PODMAN_SOCKET="${OPENSHELL_PODMAN_SOCKET:-}" \
OPENSHELL_SANDBOX_IMAGE="${SANDBOX_RUNTIME_IMAGE}" \
OPENSHELL_SANDBOX_IMAGE_PULL_POLICY="${EXTERNAL_DRIVER_PULL_POLICY}" \
OPENSHELL_HEALTH_CHECK_INTERVAL_SECS="${EXTERNAL_DRIVER_HEALTH_CHECK_INTERVAL_SECS}" \
OPENSHELL_GRPC_ENDPOINT="${EXTERNAL_DRIVER_CALLBACK_ENDPOINT}" \
OPENSHELL_GATEWAY_PORT="${HOST_PORT}" \
OPENSHELL_NETWORK_NAME="${PODMAN_NETWORK_NAME}" \
OPENSHELL_STOP_TIMEOUT="${PODMAN_STOP_TIMEOUT_SECS}" \
OPENSHELL_SUPERVISOR_IMAGE="${SUPERVISOR_RUNTIME_IMAGE}" \
OPENSHELL_PODMAN_TLS_CA="${EXTERNAL_DRIVER_TLS_CA}" \
OPENSHELL_PODMAN_TLS_CERT="${EXTERNAL_DRIVER_TLS_CERT}" \
OPENSHELL_PODMAN_TLS_KEY="${EXTERNAL_DRIVER_TLS_KEY}" \
OPENSHELL_ENABLE_BIND_MOUNTS="${EXTERNAL_DRIVER_ENABLE_BIND_MOUNTS}" \
"${DRIVER_BIN}" >"${DRIVER_LOG}" 2>&1 &
DRIVER_PID=$!
e2e_wait_for_socket \
"${DRIVER_SOCKET}" "${DRIVER_PID}" "external Podman compute driver"
fi
GATEWAY_ARGS=(
--config "${GATEWAY_CONFIG}"
# compute_driver comes from the RPM template. Override the loopback address
# and port so Podman Machine can keep its IPv4 callback listener distinct.
--bind-address "${PRIMARY_BIND_IP}"
--port "${HOST_PORT}"
--health-port "${HEALTH_PORT}"
--tls-cert "${PKI_DIR}/server/tls.crt"
--tls-key "${PKI_DIR}/server/tls.key"
--db-url "sqlite:${STATE_DIR}/gateway.db?mode=rwc"
--log-level info
)
if [ "${OIDC_MODE}" = "1" ]; then
GATEWAY_ARGS+=(
--oidc-issuer "${OIDC_ISSUER}"
--oidc-audience openshell-cli
--oidc-scopes-claim scope
)
else
GATEWAY_ARGS+=(
--tls-client-ca "${PKI_DIR}/ca.crt"
)
fi
e2e_write_gateway_args_file "${GATEWAY_ARGS_FILE}" "${GATEWAY_ARGS[@]}"
e2e_export_gateway_restart_metadata \
"${GATEWAY_BIN}" \
"${GATEWAY_ARGS_FILE}" \
"${GATEWAY_LOG}" \
"${GATEWAY_PID_FILE}"
require_expected_sha256 "gateway binary" "${GATEWAY_BIN}" \
"${OPENSHELL_E2E_EXPECTED_GATEWAY_SHA256:-}"
OPENSHELL_LOCAL_TLS_DIR="${PKI_DIR}" \
OPENSHELL_SUPERVISOR_IMAGE="${SUPERVISOR_RUNTIME_IMAGE}" \
OPENSHELL_NETWORK_NAME="${PODMAN_NETWORK_NAME}" \
"${GATEWAY_BIN}" "${GATEWAY_ARGS[@]}" >"${GATEWAY_LOG}" 2>&1 &
GATEWAY_PID=$!
printf '%s\n' "${GATEWAY_PID}" >"${GATEWAY_PID_FILE}"
GATEWAY_NAME="openshell-e2e-podman-${HOST_PORT}"
if [ "${OIDC_MODE}" = "1" ]; then
CLI_GATEWAY_ENDPOINT="https://${CLI_ENDPOINT_HOST}:${HOST_PORT}"
export OPENSHELL_E2E_OIDC_GATEWAY_ENDPOINT="${CLI_GATEWAY_ENDPOINT}"
else
CLI_GATEWAY_ENDPOINT="https://${CLI_ENDPOINT_HOST}:${HOST_PORT}"
e2e_register_mtls_gateway \
"${XDG_CONFIG_HOME}" \
"${GATEWAY_NAME}" \
"${CLI_GATEWAY_ENDPOINT}" \
"${HOST_PORT}" \
"${PKI_DIR}" \
"${OPENSHELL_OIDC_ISSUER:-}"
fi
export OPENSHELL_GATEWAY="${GATEWAY_NAME}"
export OPENSHELL_PROVISION_TIMEOUT="${OPENSHELL_PROVISION_TIMEOUT:-300}"
if [ "${OIDC_MODE}" = "1" ] || [ -n "${OPENSHELL_OIDC_ISSUER:-}" ]; then
export OPENSHELL_E2E_OIDC=1
export OPENSHELL_E2E_OIDC_SCOPES=1
fi
echo "Waiting for gateway to become healthy..."
elapsed=0
timeout=120
while [ "${elapsed}" -lt "${timeout}" ]; do
if ! kill -0 "${GATEWAY_PID}" 2>/dev/null; then
echo "ERROR: openshell-gateway exited before becoming healthy"
exit 1
fi
# Keep this loopback probe direct even when ::1 is absent from NO_PROXY.
if curl --noproxy '*' -sf "http://${HEALTH_ENDPOINT_HOST}:${HEALTH_PORT}/healthz" >/dev/null 2>&1; then
echo "Gateway healthy after ${elapsed}s."
break
fi
sleep 2
elapsed=$((elapsed + 2))
done
if [ "${elapsed}" -ge "${timeout}" ]; then
echo "ERROR: gateway did not become healthy within ${timeout}s"
exit 1
fi
require_expected_sha256 "OpenShell CLI" "${CLI_BIN}" \
"${OPENSHELL_E2E_EXPECTED_CLI_SHA256:-}"
if [ -n "${OPENSHELL_E2E_EXPECTED_CONFORMANCE_SHA256:-}" ]; then
require_expected_sha256 "conformance CLI" "${OPENSHELL_CONFORMANCE_BIN}" \
"${OPENSHELL_E2E_EXPECTED_CONFORMANCE_SHA256}"
fi
echo "Running e2e command against ${CLI_GATEWAY_ENDPOINT}: $*"
"$@"