mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-10 11:44:45 +08:00
* 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>
311 lines
8.6 KiB
Bash
311 lines
8.6 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# Start a standalone openshell-gateway using the detected compute driver.
|
|
#
|
|
# Auto-detection follows the gateway's runtime order:
|
|
# Kubernetes -> Podman -> Docker
|
|
#
|
|
# VM/MicroVM is intentionally explicit-only because it requires runtime setup.
|
|
# Use either:
|
|
# OPENSHELL_COMPUTE_DRIVER=vm mise run gateway
|
|
# mise run gateway:vm
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
# shellcheck source=tasks/scripts/gateway-pull-policy.sh
|
|
source "${ROOT}/tasks/scripts/gateway-pull-policy.sh"
|
|
GATEWAY_BIN="${OPENSHELL_GATEWAY_BIN:-${ROOT}/target/debug/openshell-gateway}"
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: mise run gateway [-- --driver DRIVER]
|
|
|
|
Start a local OpenShell gateway with the detected compute driver.
|
|
|
|
Driver detection order:
|
|
kubernetes -> podman -> docker
|
|
|
|
Options:
|
|
--driver DRIVER Override detection. Accepted values:
|
|
kubernetes, podman, docker, vm, microvm
|
|
-h, --help Show this help.
|
|
|
|
Environment:
|
|
OPENSHELL_COMPUTE_DRIVER Driver override used by openshell-gateway.
|
|
OPENSHELL_GATEWAY_NAME Gateway name for delegated or Kubernetes runs.
|
|
OPENSHELL_BIND_ADDRESS Gateway listener address. Defaults to 127.0.0.1,
|
|
or ::1 for Podman Machine on macOS.
|
|
OPENSHELL_SERVER_PORT Gateway port. Defaults to 8080 for Kubernetes,
|
|
18080 for Podman/Docker, and 18081 for VM.
|
|
Docker, Podman, and VM runs delegate to their gateway:<driver> setup scripts.
|
|
EOF
|
|
}
|
|
|
|
normalize_driver() {
|
|
local driver
|
|
driver="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
|
|
|
|
case "${driver}" in
|
|
kubernetes|k8s) echo "kubernetes" ;;
|
|
podman) echo "podman" ;;
|
|
docker) echo "docker" ;;
|
|
vm|microvm) echo "vm" ;;
|
|
"")
|
|
echo "ERROR: empty driver value" >&2
|
|
exit 2
|
|
;;
|
|
*)
|
|
echo "ERROR: unsupported driver '$1' (expected kubernetes, podman, docker, vm, or microvm)" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
}
|
|
|
|
command_available() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
require_mise() {
|
|
if ! command_available mise; then
|
|
echo "ERROR: mise is required to build local gateway artifacts" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
run_mise_task() {
|
|
require_mise
|
|
mise run "$@"
|
|
}
|
|
|
|
podman_available() {
|
|
command_available podman && podman info >/dev/null 2>&1
|
|
}
|
|
|
|
docker_available() {
|
|
command_available docker && docker info >/dev/null 2>&1
|
|
}
|
|
|
|
detect_driver() {
|
|
if [[ -n "${KUBERNETES_SERVICE_HOST:-}" ]]; then
|
|
echo "kubernetes"
|
|
return
|
|
fi
|
|
|
|
if podman_available; then
|
|
echo "podman"
|
|
return
|
|
fi
|
|
|
|
if docker_available; then
|
|
echo "docker"
|
|
return
|
|
fi
|
|
|
|
echo "ERROR: no compute driver detected." >&2
|
|
echo " Start Podman or Docker, run inside Kubernetes, or set OPENSHELL_COMPUTE_DRIVER." >&2
|
|
exit 2
|
|
}
|
|
|
|
port_is_in_use() {
|
|
local port=$1
|
|
if command_available lsof; then
|
|
lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1
|
|
return $?
|
|
fi
|
|
if command_available nc; then
|
|
nc -z 127.0.0.1 "${port}" >/dev/null 2>&1
|
|
return $?
|
|
fi
|
|
(echo >/dev/tcp/127.0.0.1/"${port}") >/dev/null 2>&1
|
|
}
|
|
|
|
register_gateway_metadata() {
|
|
local name=$1
|
|
local endpoint=$2
|
|
local port=$3
|
|
local config_home gateway_dir
|
|
|
|
config_home="${XDG_CONFIG_HOME:-${HOME}/.config}"
|
|
gateway_dir="${config_home}/openshell/gateways/${name}"
|
|
|
|
mkdir -p "${gateway_dir}"
|
|
cat >"${gateway_dir}/metadata.json" <<EOF
|
|
{
|
|
"name": "${name}",
|
|
"gateway_endpoint": "${endpoint}",
|
|
"is_remote": false,
|
|
"gateway_port": ${port},
|
|
"auth_mode": "plaintext"
|
|
}
|
|
EOF
|
|
printf '%s' "${name}" >"${config_home}/openshell/active_gateway"
|
|
}
|
|
|
|
explicit_driver=""
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case "$1" in
|
|
--driver)
|
|
if [[ "$#" -lt 2 ]]; then
|
|
echo "ERROR: --driver requires a value" >&2
|
|
exit 2
|
|
fi
|
|
explicit_driver="$(normalize_driver "$2")"
|
|
shift 2
|
|
;;
|
|
--driver=*)
|
|
explicit_driver="$(normalize_driver "${1#--driver=}")"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "ERROR: unknown gateway option '$1'" >&2
|
|
echo >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -n "${explicit_driver}" && -n "${OPENSHELL_COMPUTE_DRIVER:-}" ]]; then
|
|
echo "ERROR: use either --driver or OPENSHELL_COMPUTE_DRIVER, not both" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ -z "${explicit_driver}" && -n "${OPENSHELL_COMPUTE_DRIVER:-}" ]]; then
|
|
if [[ "${OPENSHELL_COMPUTE_DRIVER}" == *,* ]]; then
|
|
echo "ERROR: mise run gateway supports one driver; got OPENSHELL_COMPUTE_DRIVER=${OPENSHELL_COMPUTE_DRIVER}" >&2
|
|
exit 2
|
|
fi
|
|
explicit_driver="$(normalize_driver "${OPENSHELL_COMPUTE_DRIVER}")"
|
|
fi
|
|
|
|
DRIVER="${explicit_driver:-$(detect_driver)}"
|
|
|
|
case "${DRIVER}" in
|
|
docker)
|
|
export OPENSHELL_DOCKER_GATEWAY_NAME="${OPENSHELL_DOCKER_GATEWAY_NAME:-${OPENSHELL_GATEWAY_NAME:-docker-dev}}"
|
|
exec bash "${ROOT}/tasks/scripts/gateway-docker.sh"
|
|
;;
|
|
podman)
|
|
export OPENSHELL_PODMAN_GATEWAY_NAME="${OPENSHELL_PODMAN_GATEWAY_NAME:-${OPENSHELL_GATEWAY_NAME:-podman-dev}}"
|
|
exec bash "${ROOT}/tasks/scripts/gateway-podman.sh"
|
|
;;
|
|
vm)
|
|
export OPENSHELL_VM_GATEWAY_NAME="${OPENSHELL_VM_GATEWAY_NAME:-${OPENSHELL_GATEWAY_NAME:-vm-dev}}"
|
|
exec bash "${ROOT}/tasks/scripts/gateway-vm.sh"
|
|
;;
|
|
esac
|
|
|
|
PORT="${OPENSHELL_SERVER_PORT:-8080}"
|
|
GATEWAY_NAME="${OPENSHELL_GATEWAY_NAME:-${DRIVER}-dev}"
|
|
STATE_DIR="${OPENSHELL_GATEWAY_STATE_DIR:-${ROOT}/.cache/gateway-${DRIVER}}"
|
|
SANDBOX_NAMESPACE="${OPENSHELL_SANDBOX_NAMESPACE:-${DRIVER}-dev}"
|
|
SANDBOX_IMAGE="${OPENSHELL_SANDBOX_IMAGE:-ghcr.io/nvidia/openshell-community/sandboxes/base:latest}"
|
|
SANDBOX_IMAGE_PULL_POLICY="$(normalize_image_pull_policy "${OPENSHELL_SANDBOX_IMAGE_PULL_POLICY:-if_not_present}")"
|
|
GRPC_ENDPOINT="${OPENSHELL_GRPC_ENDPOINT:-}"
|
|
LOG_LEVEL="${OPENSHELL_LOG_LEVEL:-info}"
|
|
PRIMARY_BIND_IP="${OPENSHELL_BIND_ADDRESS:-127.0.0.1}"
|
|
|
|
if [[ ! "${GATEWAY_NAME}" =~ ^[A-Za-z0-9._-]+$ ]]; then
|
|
echo "ERROR: OPENSHELL_GATEWAY_NAME must contain only letters, numbers, dots, underscores, or dashes" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if port_is_in_use "${PORT}"; then
|
|
echo "ERROR: port ${PORT} is already in use; free it or set OPENSHELL_SERVER_PORT" >&2
|
|
exit 2
|
|
fi
|
|
|
|
echo "Building openshell-gateway..."
|
|
run_mise_task build:gateway
|
|
|
|
if [[ ! -x "${GATEWAY_BIN}" ]]; then
|
|
echo "ERROR: expected gateway binary at ${GATEWAY_BIN}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
TLS_DIR="${STATE_DIR}/tls"
|
|
echo "Generating local gateway credentials..."
|
|
"${GATEWAY_BIN}" generate-certs \
|
|
--output-dir "${TLS_DIR}" \
|
|
--server-san "127.0.0.1" \
|
|
--server-san "localhost" \
|
|
--server-san "host.openshell.internal"
|
|
|
|
mkdir -p "${STATE_DIR}"
|
|
CONFIG_PATH="${STATE_DIR}/gateway.toml"
|
|
# The config may reference credential-bearing material (e.g. proxy_auth_file);
|
|
# keep it owner-only regardless of the ambient umask.
|
|
install -m 600 /dev/null "${CONFIG_PATH}"
|
|
|
|
# Kubernetes is a shared deployment, so its sandbox JWTs must expire. Local
|
|
# drivers omit ttl_secs and inherit the gateway's non-expiring default, so a
|
|
# sandbox restarted while the gateway is down can still reconnect.
|
|
GATEWAY_JWT_TTL_CONFIG=""
|
|
if [[ "${DRIVER}" == "kubernetes" ]]; then
|
|
GATEWAY_JWT_TTL_CONFIG="ttl_secs = 3600"
|
|
fi
|
|
|
|
cat >"${CONFIG_PATH}" <<EOF
|
|
[openshell]
|
|
version = 2
|
|
|
|
[openshell.gateway]
|
|
name = "${GATEWAY_NAME}"
|
|
compute_driver = "${DRIVER}"
|
|
disable_tls = true
|
|
|
|
[openshell.gateway.otlp]
|
|
endpoint = "http://127.0.0.1:4317"
|
|
|
|
[openshell.gateway.auth]
|
|
allow_unauthenticated_users = true
|
|
|
|
[openshell.gateway.gateway_jwt]
|
|
signing_key_path = "${TLS_DIR}/jwt/signing.pem"
|
|
public_key_path = "${TLS_DIR}/jwt/public.pem"
|
|
kid_path = "${TLS_DIR}/jwt/kid"
|
|
gateway_id = "${GATEWAY_NAME}"
|
|
${GATEWAY_JWT_TTL_CONFIG}
|
|
EOF
|
|
|
|
cat >>"${CONFIG_PATH}" <<EOF
|
|
|
|
[openshell.drivers.kubernetes]
|
|
namespace = "${SANDBOX_NAMESPACE}"
|
|
default_image = "${SANDBOX_IMAGE}"
|
|
image_pull_policy = "${SANDBOX_IMAGE_PULL_POLICY}"
|
|
EOF
|
|
if [[ -n "${GRPC_ENDPOINT}" ]]; then
|
|
printf 'grpc_endpoint = "%s"\n' "${GRPC_ENDPOINT}" >>"${CONFIG_PATH}"
|
|
fi
|
|
|
|
GATEWAY_ENDPOINT="http://127.0.0.1:${PORT}"
|
|
register_gateway_metadata "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}"
|
|
|
|
echo "Starting standalone ${DRIVER} gateway..."
|
|
echo " gateway: ${GATEWAY_NAME}"
|
|
echo " endpoint: ${GATEWAY_ENDPOINT}"
|
|
echo " bind: ${PRIMARY_BIND_IP}:${PORT}"
|
|
echo " namespace: ${SANDBOX_NAMESPACE}"
|
|
echo " state dir: ${STATE_DIR}"
|
|
echo
|
|
echo "Active gateway set to '${GATEWAY_NAME}'. The CLI now targets this gateway by default."
|
|
echo
|
|
|
|
exec "${GATEWAY_BIN}" \
|
|
--config "${CONFIG_PATH}" \
|
|
--bind-address "${PRIMARY_BIND_IP}" \
|
|
--port "${PORT}" \
|
|
--log-level "${LOG_LEVEL}" \
|
|
--compute-driver "${DRIVER}" \
|
|
--disable-tls \
|
|
--db-url "sqlite:${STATE_DIR}/gateway.db?mode=rwc"
|