diff --git a/tasks/gateway.toml b/tasks/gateway.toml index bbf309c459..ceae01a68a 100644 --- a/tasks/gateway.toml +++ b/tasks/gateway.toml @@ -16,6 +16,10 @@ run = "bash tasks/scripts/gateway.sh" description = "Run a standalone gateway with the bundled Docker compute driver" run = "bash tasks/scripts/gateway-docker.sh" +["gateway:kubernetes"] +description = "Run a standalone gateway with the bundled Kubernetes compute driver" +run = "bash tasks/scripts/gateway-kubernetes.sh" + ["gateway:podman"] description = "Run a standalone gateway with the bundled Podman compute driver" run = "bash tasks/scripts/gateway-podman.sh" diff --git a/tasks/scripts/gateway-common.sh b/tasks/scripts/gateway-common.sh new file mode 100644 index 0000000000..883241927f --- /dev/null +++ b/tasks/scripts/gateway-common.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Shared plumbing for the local gateway launchers. Driver-specific configuration +# and runtime setup belong in gateway-.sh. + +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 "$@" +} + +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 +} + +append_local_otlp_config_if_available() { + local config_path=$1 + if ! port_is_in_use 4317; then + echo "OTLP collector not detected on 127.0.0.1:4317; trace export disabled." + return + fi + + cat >>"${config_path}" <<'EOF' + +[openshell.gateway.otlp] +endpoint = "http://127.0.0.1:4317" +EOF + echo "OTLP trace export enabled for http://127.0.0.1:4317." +} + +validate_gateway_name() { + local name=$1 + local variable_name=$2 + if [[ ! "${name}" =~ ^[A-Za-z0-9._-]+$ ]]; then + echo "ERROR: ${variable_name} must contain only letters, numbers, dots, underscores, or dashes" >&2 + exit 2 + fi +} + +register_local_gateway() { + local name=$1 + local endpoint=$2 + local port=$3 + local select_gateway=${4:-false} + 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" <"${config_home}/openshell/active_gateway" + fi +} + +ensure_container_runtime_image() { + local engine=$1 + local image=$2 + local configured_image=$3 + local build_target=$4 + local role=$5 + + if [[ -n "${configured_image}" ]]; then + if container_image_exists "${engine}" "${image}"; then + return + fi + echo "ERROR: ${role} image '${image}' not found locally." >&2 + echo " Build it with ${engine} or unset its image override to build the local :dev image." >&2 + exit 1 + fi + + echo "Refreshing ${engine} ${role} image (${image})..." + require_mise + CONTAINER_ENGINE="${engine}" IMAGE_TAG=dev mise run "build:docker:${build_target}" + + if ! container_image_exists "${engine}" "${image}"; then + echo "ERROR: expected ${role} image '${image}' after build" >&2 + exit 1 + fi +} + +container_image_exists() { + local engine=$1 + local image=$2 + case "${engine}" in + docker) docker image inspect "${image}" >/dev/null 2>&1 ;; + podman) podman image exists "${image}" >/dev/null 2>&1 ;; + *) + echo "ERROR: unsupported container engine '${engine}'" >&2 + return 2 + ;; + esac +} diff --git a/tasks/scripts/gateway-docker.sh b/tasks/scripts/gateway-docker.sh index a98ff3ea2b..a5ed554722 100644 --- a/tasks/scripts/gateway-docker.sh +++ b/tasks/scripts/gateway-docker.sh @@ -26,6 +26,8 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck source=tasks/scripts/gateway-common.sh +source "${ROOT}/tasks/scripts/gateway-common.sh" # shellcheck source=tasks/scripts/gateway-toml.sh source "${ROOT}/tasks/scripts/gateway-toml.sh" # shellcheck source=tasks/scripts/gateway-pull-policy.sh @@ -41,85 +43,7 @@ SANDBOX_IMAGE_PULL_POLICY="$(normalize_image_pull_policy "${OPENSHELL_SANDBOX_IM LOG_LEVEL="${OPENSHELL_LOG_LEVEL:-info}" GATEWAY_BIN="${ROOT}/target/debug/openshell-gateway" -port_is_in_use() { - local port=$1 - if command -v lsof >/dev/null 2>&1; then - lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1 - return $? - fi - if command -v nc >/dev/null 2>&1; 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 -} - -ensure_docker_runtime_image() { - local image=$1 - local configured_image=$2 - local build_target=$3 - local role=$4 - - if [[ -n "${configured_image}" ]]; then - if docker image inspect "${image}" >/dev/null 2>&1; then - return - fi - echo "ERROR: ${role} image '${image}' not found locally." >&2 - echo " Build it with Docker or unset its image override to build the local :dev image." >&2 - exit 1 - fi - - # Always run the build pipeline for default development images so source - # changes cannot leave a fixed :dev tag pointing at stale runtime code. - echo "Refreshing Docker ${role} image (${image})..." - CONTAINER_ENGINE=docker IMAGE_TAG=dev mise run "build:docker:${build_target}" - - if ! docker image inspect "${image}" >/dev/null 2>&1; then - echo "ERROR: expected ${role} image '${image}' after build" >&2 - exit 1 - fi -} - -append_local_otlp_config_if_available() { - local config_path=$1 - if ! port_is_in_use 4317; then - echo "OTLP collector not detected on 127.0.0.1:4317; trace export disabled." - return - fi - - cat >>"${config_path}" <<'EOF' - -[openshell.gateway.otlp] -endpoint = "http://127.0.0.1:4317" -EOF - echo "OTLP trace export enabled for http://127.0.0.1:4317." -} - -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" <&2 - exit 2 -fi +validate_gateway_name "${GATEWAY_NAME}" OPENSHELL_DOCKER_GATEWAY_NAME if ! command -v docker >/dev/null 2>&1; then echo "ERROR: docker CLI is required" >&2 @@ -135,12 +59,12 @@ if port_is_in_use "${PORT}"; then exit 2 fi -ensure_docker_runtime_image \ +ensure_container_runtime_image docker \ "${SUPERVISOR_IMAGE}" \ "${OPENSHELL_SUPERVISOR_IMAGE:-}" \ supervisor \ supervisor -ensure_docker_runtime_image \ +ensure_container_runtime_image docker \ "${SANDBOX_RUNTIME_IMAGE}" \ "${OPENSHELL_SANDBOX_RUNTIME_IMAGE:-}" \ sandbox \ @@ -221,7 +145,7 @@ fi append_local_otlp_config_if_available "${CONFIG_PATH}" GATEWAY_ENDPOINT="http://127.0.0.1:${PORT}" -register_gateway_metadata "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}" +register_local_gateway "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}" echo "Starting standalone Docker gateway..." echo " gateway: ${GATEWAY_NAME}" diff --git a/tasks/scripts/gateway-kubernetes.sh b/tasks/scripts/gateway-kubernetes.sh new file mode 100644 index 0000000000..a2ab1a5ecc --- /dev/null +++ b/tasks/scripts/gateway-kubernetes.sh @@ -0,0 +1,104 @@ +#!/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 backed by the Kubernetes compute driver. + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck source=tasks/scripts/gateway-common.sh +source "${ROOT}/tasks/scripts/gateway-common.sh" +# shellcheck source=tasks/scripts/gateway-pull-policy.sh +source "${ROOT}/tasks/scripts/gateway-pull-policy.sh" + +PORT="${OPENSHELL_SERVER_PORT:-8080}" +GATEWAY_NAME="${OPENSHELL_GATEWAY_NAME:-kubernetes-dev}" +STATE_DIR="${OPENSHELL_GATEWAY_STATE_DIR:-${ROOT}/.cache/gateway-kubernetes}" +SANDBOX_NAMESPACE="${OPENSHELL_SANDBOX_NAMESPACE:-kubernetes-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}" +GATEWAY_BIN="${OPENSHELL_GATEWAY_BIN:-${ROOT}/target/debug/openshell-gateway}" + +validate_gateway_name "${GATEWAY_NAME}" OPENSHELL_GATEWAY_NAME + +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" +install -m 600 /dev/null "${CONFIG_PATH}" +cat >"${CONFIG_PATH}" <>"${CONFIG_PATH}" +fi + +GATEWAY_ENDPOINT="http://127.0.0.1:${PORT}" +register_local_gateway "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}" true + +echo "Starting standalone Kubernetes 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 kubernetes \ + --disable-tls \ + --db-url "sqlite:${STATE_DIR}/gateway.db?mode=rwc" diff --git a/tasks/scripts/gateway-podman.sh b/tasks/scripts/gateway-podman.sh index 745dcd542f..fa39807f41 100644 --- a/tasks/scripts/gateway-podman.sh +++ b/tasks/scripts/gateway-podman.sh @@ -23,6 +23,8 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck source=tasks/scripts/gateway-common.sh +source "${ROOT}/tasks/scripts/gateway-common.sh" # shellcheck source=tasks/scripts/gateway-toml.sh source "${ROOT}/tasks/scripts/gateway-toml.sh" # shellcheck source=tasks/scripts/gateway-pull-policy.sh @@ -39,17 +41,6 @@ PRIMARY_BIND_IP="${OPENSHELL_BIND_ADDRESS:-127.0.0.1}" CLI_ENDPOINT_HOST="127.0.0.1" GATEWAY_BIN="${ROOT}/target/debug/openshell-gateway" -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 -} - podman_available() { command_available podman && podman info >/dev/null 2>&1 } @@ -71,88 +62,7 @@ require_podman_service() { fi } -ensure_podman_runtime_image() { - local image=$1 - local configured_image=$2 - local build_target=$3 - local role=$4 - - if [[ -n "${configured_image}" ]]; then - if podman image exists "${image}" >/dev/null 2>&1; then - return - fi - echo "ERROR: ${role} image '${image}' not found locally." >&2 - echo " Build it with Podman or unset its image override to build the local :dev image." >&2 - exit 1 - fi - - # Always run the build pipeline for the default development image so source - # changes cannot leave the fixed :dev tag pointing at a stale runtime. - # Cargo and BuildKit caches keep unchanged rebuilds incremental. - echo "Refreshing Podman ${role} image (${image})..." - require_mise - CONTAINER_ENGINE=podman IMAGE_TAG=dev mise run "build:docker:${build_target}" - - if ! podman image exists "${image}" >/dev/null 2>&1; then - echo "ERROR: expected ${role} image '${image}' after build" >&2 - exit 1 - fi -} - -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 -} - -append_local_otlp_config_if_available() { - local config_path=$1 - if ! port_is_in_use 4317; then - echo "OTLP collector not detected on 127.0.0.1:4317; trace export disabled." - return - fi - - cat >>"${config_path}" <<'EOF' - -[openshell.gateway.otlp] -endpoint = "http://127.0.0.1:4317" -EOF - echo "OTLP trace export enabled for http://127.0.0.1:4317." -} - -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" <"${config_home}/openshell/active_gateway" -} - -if [[ ! "${GATEWAY_NAME}" =~ ^[A-Za-z0-9._-]+$ ]]; then - echo "ERROR: OPENSHELL_PODMAN_GATEWAY_NAME must contain only letters, numbers, dots, underscores, or dashes" >&2 - exit 2 -fi +validate_gateway_name "${GATEWAY_NAME}" OPENSHELL_PODMAN_GATEWAY_NAME require_podman_service @@ -163,12 +73,12 @@ fi SUPERVISOR_IMAGE="${OPENSHELL_SUPERVISOR_IMAGE:-openshell/supervisor:dev}" SANDBOX_RUNTIME_IMAGE="${OPENSHELL_SANDBOX_RUNTIME_IMAGE:-openshell/sandbox:dev}" -ensure_podman_runtime_image \ +ensure_container_runtime_image podman \ "${SUPERVISOR_IMAGE}" \ "${OPENSHELL_SUPERVISOR_IMAGE:-}" \ supervisor \ supervisor -ensure_podman_runtime_image \ +ensure_container_runtime_image podman \ "${SANDBOX_RUNTIME_IMAGE}" \ "${OPENSHELL_SANDBOX_RUNTIME_IMAGE:-}" \ sandbox \ @@ -271,7 +181,7 @@ fi append_local_otlp_config_if_available "${CONFIG_PATH}" GATEWAY_ENDPOINT="http://${CLI_ENDPOINT_HOST}:${PORT}" -register_gateway_metadata "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}" +register_local_gateway "${GATEWAY_NAME}" "${GATEWAY_ENDPOINT}" "${PORT}" true echo "Starting standalone Podman gateway..." echo " gateway: ${GATEWAY_NAME}" diff --git a/tasks/scripts/gateway-vm.sh b/tasks/scripts/gateway-vm.sh index ff3f1e7dff..61685e8aa5 100755 --- a/tasks/scripts/gateway-vm.sh +++ b/tasks/scripts/gateway-vm.sh @@ -33,6 +33,8 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck source=tasks/scripts/gateway-common.sh +source "${ROOT}/tasks/scripts/gateway-common.sh" # shellcheck source=tasks/scripts/gateway-toml.sh source "${ROOT}/tasks/scripts/gateway-toml.sh" PORT="${OPENSHELL_SERVER_PORT:-18081}" @@ -74,34 +76,6 @@ normalize_bool() { esac } -port_is_in_use() { - local port=$1 - if command -v lsof >/dev/null 2>&1; then - lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1 - return $? - fi - if command -v nc >/dev/null 2>&1; 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 -} - -append_local_otlp_config_if_available() { - local config_path=$1 - if ! port_is_in_use 4317; then - echo "OTLP collector not detected on 127.0.0.1:4317; trace export disabled." - return - fi - - cat >>"${config_path}" <<'EOF' - -[openshell.gateway.otlp] -endpoint = "http://127.0.0.1:4317" -EOF - echo "OTLP trace export enabled for http://127.0.0.1:4317." -} - invoking_user() { if [ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER}" != "root" ]; then printf '%s\n' "${SUDO_USER}" @@ -268,10 +242,7 @@ else unset OPENSHELL_VM_GPU fi -if [[ ! "${GATEWAY_NAME}" =~ ^[A-Za-z0-9._-]+$ ]]; then - echo "ERROR: OPENSHELL_VM_GATEWAY_NAME must contain only letters, numbers, dots, underscores, or dashes" >&2 - exit 2 -fi +validate_gateway_name "${GATEWAY_NAME}" OPENSHELL_VM_GATEWAY_NAME if port_is_in_use "${PORT}"; then echo "ERROR: port ${PORT} is already in use; free it or set OPENSHELL_SERVER_PORT" >&2 diff --git a/tasks/scripts/gateway.sh b/tasks/scripts/gateway.sh index 34bc143fb6..98163a21e4 100644 --- a/tasks/scripts/gateway.sh +++ b/tasks/scripts/gateway.sh @@ -16,9 +16,8 @@ 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}" +# shellcheck source=tasks/scripts/gateway-common.sh +source "${ROOT}/tasks/scripts/gateway-common.sh" usage() { cat <<'EOF' @@ -41,7 +40,7 @@ Environment: 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: setup scripts. +Each run delegates to its gateway: setup script. EOF } @@ -65,22 +64,6 @@ normalize_driver() { 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 } @@ -110,41 +93,6 @@ detect_driver() { 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" <"${config_home}/openshell/active_gateway" -} - explicit_driver="" while [[ "$#" -gt 0 ]]; do case "$1" in @@ -189,6 +137,10 @@ fi DRIVER="${explicit_driver:-$(detect_driver)}" case "${DRIVER}" in + kubernetes) + export OPENSHELL_GATEWAY_NAME="${OPENSHELL_GATEWAY_NAME:-kubernetes-dev}" + exec bash "${ROOT}/tasks/scripts/gateway-kubernetes.sh" + ;; docker) export OPENSHELL_DOCKER_GATEWAY_NAME="${OPENSHELL_DOCKER_GATEWAY_NAME:-${OPENSHELL_GATEWAY_NAME:-docker-dev}}" exec bash "${ROOT}/tasks/scripts/gateway-docker.sh" @@ -202,109 +154,3 @@ case "${DRIVER}" in 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}" <>"${CONFIG_PATH}" <>"${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" diff --git a/tasks/scripts/test-gateway-config.sh b/tasks/scripts/test-gateway-config.sh index 9541aa68fc..c038054a23 100755 --- a/tasks/scripts/test-gateway-config.sh +++ b/tasks/scripts/test-gateway-config.sh @@ -27,7 +27,7 @@ printf '%s\n' '#!/usr/bin/env bash' 'config=""' 'while [ "$#" -gt 0 ]; do' ' if chmod +x "${WORK}/bin/gateway" CAPTURED_CONFIG="${WORK}/generated.toml" -CAPTURED_CONFIG="${WORK}/generated.toml" PATH="${WORK}/bin:${PATH}" KUBERNETES_SERVICE_HOST=fixture OPENSHELL_GATEWAY_BIN="${WORK}/bin/gateway" OPENSHELL_GATEWAY_STATE_DIR="${WORK}/state" OPENSHELL_SANDBOX_IMAGE_PULL_POLICY=IfNotPresent OPENSHELL_GRPC_ENDPOINT=https://callback.example.test:9443 bash "${ROOT}/tasks/scripts/gateway.sh" +CAPTURED_CONFIG="${WORK}/generated.toml" XDG_CONFIG_HOME="${WORK}/config" PATH="${WORK}/bin:${PATH}" KUBERNETES_SERVICE_HOST=fixture OPENSHELL_GATEWAY_BIN="${WORK}/bin/gateway" OPENSHELL_GATEWAY_STATE_DIR="${WORK}/state" OPENSHELL_SANDBOX_IMAGE_PULL_POLICY=IfNotPresent OPENSHELL_GRPC_ENDPOINT=https://callback.example.test:9443 bash "${ROOT}/tasks/scripts/gateway.sh" printf '%s\n' 'import sys, tomllib' 'from pathlib import Path' 'config = tomllib.loads(Path(sys.argv[1]).read_text())' 'gateway = config["openshell"]["gateway"]' 'driver = config["openshell"]["drivers"]["kubernetes"]' 'assert config["openshell"]["version"] == 2' 'assert gateway["compute_driver"] == "kubernetes"' 'assert "compute_drivers" not in gateway' 'assert driver["image_pull_policy"] == "if_not_present"' 'assert driver["grpc_endpoint"] == "https://callback.example.test:9443"' > "${WORK}/check_generated.py" "${UV}" run --no-project python "${WORK}/check_generated.py" "${CAPTURED_CONFIG}" diff --git a/tasks/scripts/test-gateway-pull-policy.sh b/tasks/scripts/test-gateway-pull-policy.sh index 7859b7c1d8..110f5f185b 100755 --- a/tasks/scripts/test-gateway-pull-policy.sh +++ b/tasks/scripts/test-gateway-pull-policy.sh @@ -37,7 +37,7 @@ if normalize_image_pull_policy sometimes >/dev/null 2>&1; then exit 1 fi -for script in gateway.sh gateway-docker.sh gateway-podman.sh; do +for script in gateway-kubernetes.sh gateway-docker.sh gateway-podman.sh; do if ! grep -q 'normalize_image_pull_policy' "${ROOT}/tasks/scripts/${script}"; then echo "${script} does not normalize image pull policy" >&2 exit 1