From dd22ca103c667e14f85f848ad371fbf06cdebf2a Mon Sep 17 00:00:00 2001 From: Wenxi Date: Mon, 14 Sep 2026 19:23:15 +0000 Subject: [PATCH] chore(dev): improve local kind development and add Zed launcher (#14683) --- .vscode/.env.k8s.template | 2 +- .zed/README.md | 13 + .zed/process-compose.k8s.yaml | 156 +++++++++ .zed/run-all-k8s.sh | 312 ++++++++++++++++++ .zed/tasks.json | 10 + deployment/helm/charts/onyx/Chart.yaml | 16 +- deployment/helm/charts/onyx/values.yaml | 8 +- deployment/helm/dev/k8s-up.sh | 3 +- .../{charts/onyx => dev}/values-localdev.yaml | 7 +- docs/craft/dev/local-kubernetes.md | 43 ++- 10 files changed, 546 insertions(+), 24 deletions(-) create mode 100644 .zed/README.md create mode 100644 .zed/process-compose.k8s.yaml create mode 100755 .zed/run-all-k8s.sh rename deployment/helm/{charts/onyx => dev}/values-localdev.yaml (94%) diff --git a/.vscode/.env.k8s.template b/.vscode/.env.k8s.template index 7854c7621f..eeda210aa1 100644 --- a/.vscode/.env.k8s.template +++ b/.vscode/.env.k8s.template @@ -73,7 +73,7 @@ POSTGRES_PASSWORD=postgres # Decrypts secrets the cluster wrote to Postgres (LLM keys, connector + craft # credentials). MUST match the cluster's key in -# deployment/helm/charts/onyx/values-localdev.yaml — a mismatch surfaces as +# deployment/helm/dev/values-localdev.yaml — a mismatch surfaces as # "Data is not valid UTF-8 — likely encrypted with a different key" on reads. ENCRYPTION_KEY_SECRET=6767676767676767 diff --git a/.zed/README.md b/.zed/README.md new file mode 100644 index 0000000000..2c395167cc --- /dev/null +++ b/.zed/README.md @@ -0,0 +1,13 @@ +# Kubernetes development in Zed + +1. Install Process Compose: `brew install f1bonacc1/tap/process-compose`. +2. Run `make craft-up` from the repository root, then fill in `.vscode/.env.k8s`. +3. Stop any local services using ports 3000 or 8080. +4. Open the task picker (`Cmd+Shift+R`) and run `run: all services (k8s)`. + +Use `Ctrl+R` to restart a service and `F10` to stop the stack. +Logs are available in Process Compose and `.zed/logs/`. +For CLI access, use `process-compose --port 18080`. + +For VPN or proxy certificate errors, see +[manual CA setup](../docs/craft/dev/local-kubernetes.md#vpn-or-proxy-certificate-errors). diff --git a/.zed/process-compose.k8s.yaml b/.zed/process-compose.k8s.yaml new file mode 100644 index 0000000000..cd5725b495 --- /dev/null +++ b/.zed/process-compose.k8s.yaml @@ -0,0 +1,156 @@ +version: "0.5" + +log_level: info +log_length: 2000 +log_configuration: + rotation: + max_size_mb: 20 + max_age_days: 7 + max_backups: 3 + compress: true + disable_json: true + add_timestamp: true + no_color: true + flush_each_line: true + +processes: + web: + command: bun run dev + working_dir: ${ONYX_ZED_ROOT}/web + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.web + log_location: ${ONYX_ZED_ROOT}/.zed/logs/web.log + namespace: frontend + + api: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python -m uvicorn onyx.main:app + --reload + --reload-exclude ${ONYX_ZED_ROOT}/backend/onyx/skills/builtin + --reload-exclude ${ONYX_ZED_ROOT}/backend/onyx/server/features/build/sandbox/image + --port 8080 + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: + - LOG_LEVEL=DEBUG + - PYTHONUNBUFFERED=1 + - SANDBOX_BACKEND=kubernetes + readiness_probe: + http_get: + host: 127.0.0.1 + port: 8080 + path: /health + initial_delay_seconds: 5 + period_seconds: 2 + failure_threshold: 60 + log_location: ${ONYX_ZED_ROOT}/.zed/logs/api.log + namespace: backend + + celery_primary: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.primary worker + --pool=threads --concurrency=4 --prefetch-multiplier=1 --loglevel=INFO + --hostname=primary@%n -Q celery + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: &celery_environment + - LOG_LEVEL=INFO + - PYTHONUNBUFFERED=1 + - PYTHONPATH=. + - SANDBOX_BACKEND=kubernetes + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-primary.log + namespace: celery + + celery_light: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.light worker + --pool=threads --concurrency=64 --prefetch-multiplier=8 --loglevel=INFO + --hostname=light@%n + -Q vespa_metadata_sync,connector_deletion,doc_permissions_upsert,checkpoint_cleanup,index_attempt_cleanup,index_reclaim,opensearch_migration + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: *celery_environment + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-light.log + namespace: celery + + celery_heavy: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.heavy worker + --pool=threads --concurrency=4 --prefetch-multiplier=1 --loglevel=INFO + --hostname=heavy@%n + -Q connector_pruning,connector_doc_permissions_sync,connector_external_group_sync,csv_generation,sandbox + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: *celery_environment + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-heavy.log + namespace: celery + + celery_docfetching: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.docfetching worker + --pool=threads --prefetch-multiplier=1 --loglevel=INFO + --hostname=docfetching@%n -Q connector_doc_fetching + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: &celery_debug_environment + - LOG_LEVEL=DEBUG + - PYTHONUNBUFFERED=1 + - PYTHONPATH=. + - SANDBOX_BACKEND=kubernetes + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-docfetching.log + namespace: celery + + celery_docprocessing: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.docprocessing worker + --pool=threads --prefetch-multiplier=1 --loglevel=INFO + --hostname=docprocessing@%n -Q docprocessing,port + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: + - ENABLE_MULTIPASS_INDEXING=false + - LOG_LEVEL=DEBUG + - PYTHONUNBUFFERED=1 + - PYTHONPATH=. + - SANDBOX_BACKEND=kubernetes + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-docprocessing.log + namespace: celery + + celery_user_file_processing: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.user_file_processing worker + --pool=threads --concurrency=2 --prefetch-multiplier=1 --loglevel=INFO + --hostname=user_file_processing@%n + -Q user_file_processing,user_file_project_sync,user_file_delete,user_file_port + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: *celery_environment + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-user-file-processing.log + namespace: celery + + celery_scheduled_tasks: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.scheduled_tasks worker + --pool=threads --concurrency=4 --prefetch-multiplier=1 --loglevel=INFO + --hostname=scheduled_tasks@%n -Q scheduled_tasks + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: *celery_environment + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-scheduled-tasks.log + namespace: celery + + celery_beat: + command: >- + ${ONYX_ZED_ROOT}/.venv/bin/python ${ONYX_ZED_ROOT}/backend/scripts/dev_celery_reload.py + -A onyx.background.celery.versioned_apps.beat beat --loglevel=INFO + working_dir: ${ONYX_ZED_ROOT}/backend + env_file: ${ONYX_ZED_ROOT}/.vscode/.env.k8s + environment: *celery_debug_environment + log_location: ${ONYX_ZED_ROOT}/.zed/logs/celery-beat.log + namespace: celery diff --git a/.zed/run-all-k8s.sh b/.zed/run-all-k8s.sh new file mode 100755 index 0000000000..1382c04535 --- /dev/null +++ b/.zed/run-all-k8s.sh @@ -0,0 +1,312 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ONYX_ZED_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +readonly ONYX_ZED_ROOT +readonly ONYX_ZED_ENV_FILE="${ONYX_ZED_ROOT}/.vscode/.env.k8s" +readonly ONYX_ZED_WEB_ENV_FILE="${ONYX_ZED_ROOT}/.vscode/.env.web" +readonly ONYX_ZED_COMPOSE_FILE="${ONYX_ZED_ROOT}/.zed/process-compose.k8s.yaml" +readonly ONYX_ZED_CONTROL_PORT=18080 +readonly ONYX_ZED_API_PORT=8080 +readonly ONYX_ZED_WEB_PORT=3000 + +ONYX_ZED_STACK_STARTED=false +ONYX_ZED_INTERCEPT_CREATED=false +ONYX_ZED_CLEANING_UP=false +ONYX_ZED_PROCESS_TREE_PIDS=() + +require_command() { + local command_name="$1" + local installation_hint="$2" + + if ! command -v "$command_name" >/dev/null 2>&1; then + echo "error: ${command_name} was not found; ${installation_hint}" >&2 + exit 1 + fi +} + +require_file() { + local file_path="$1" + local creation_hint="$2" + + if [[ ! -f "$file_path" ]]; then + echo "error: ${file_path} was not found; ${creation_hint}" >&2 + exit 1 + fi +} + +listener_pids() { + local port="$1" + + lsof -nP -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null || true +} + +process_command() { + local pid="$1" + + ps -p "$pid" -o command= 2>/dev/null || true +} + +is_managed_process_compose() { + local pid="$1" + local command + + command="$(process_command "$pid")" + [[ "$command" == *"process-compose"* ]] && + [[ "$command" == *"--port ${ONYX_ZED_CONTROL_PORT}"* ]] && + [[ "$command" == *"--config ${ONYX_ZED_COMPOSE_FILE}"* ]] +} + +collect_process_tree() { + local root_pid="$1" + local child_pid + + if ! kill -0 "$root_pid" 2>/dev/null; then + return + fi + + ONYX_ZED_PROCESS_TREE_PIDS+=("$root_pid") + while IFS= read -r child_pid; do + [[ -n "$child_pid" ]] && collect_process_tree "$child_pid" + done < <(pgrep -P "$root_pid" 2>/dev/null || true) +} + +terminate_process_trees() { + local root_pid + local pid + local remaining_pids=() + + ONYX_ZED_PROCESS_TREE_PIDS=() + for root_pid in "$@"; do + collect_process_tree "$root_pid" + done + + if [[ "${#ONYX_ZED_PROCESS_TREE_PIDS[@]}" -eq 0 ]]; then + return + fi + + kill -TERM "${ONYX_ZED_PROCESS_TREE_PIDS[@]}" 2>/dev/null || true + for _ in 1 2 3 4 5; do + remaining_pids=() + for pid in "${ONYX_ZED_PROCESS_TREE_PIDS[@]}"; do + kill -0 "$pid" 2>/dev/null && remaining_pids+=("$pid") + done + [[ "${#remaining_pids[@]}" -eq 0 ]] && return + sleep 1 + done + + kill -KILL "${remaining_pids[@]}" 2>/dev/null || true +} + +# Bound cleanup commands without macOS `timeout`. Ignore failures; return 124 on timeout. +run_bounded() { + local deadline_seconds="$1" + shift + + local bounded_pid + "$@" >/dev/null 2>&1 & + bounded_pid="$!" + + local _ + for _ in $(seq 1 "$deadline_seconds"); do + if ! kill -0 "$bounded_pid" 2>/dev/null; then + wait "$bounded_pid" 2>/dev/null || true + return 0 + fi + sleep 1 + done + + terminate_process_trees "$bounded_pid" + wait "$bounded_pid" 2>/dev/null || true + return 124 +} + +request_process_compose_down() { + run_bounded 5 process-compose --port "$ONYX_ZED_CONTROL_PORT" down || true +} + +assert_port_available() { + local port="$1" + local purpose="$2" + local pid + local command + local occupied=false + + while IFS= read -r pid; do + [[ -z "$pid" ]] && continue + occupied=true + command="$(process_command "$pid")" + echo "error: port ${port} (${purpose}) is owned by PID ${pid}: ${command}" >&2 + done < <(listener_pids "$port") + + if [[ "$occupied" == true ]]; then + echo "error: refusing to stop a process that was not started by this worktree" >&2 + return 1 + fi +} + +stop_managed_stack() { + local pid + local control_listener_pids=() + + while IFS= read -r pid; do + [[ -n "$pid" ]] && control_listener_pids+=("$pid") + done < <(listener_pids "$ONYX_ZED_CONTROL_PORT") + + # Bash 3.2 requires a count check before expanding empty arrays under set -u. + if [[ "${#control_listener_pids[@]}" -gt 0 ]]; then + for pid in "${control_listener_pids[@]}"; do + if ! is_managed_process_compose "$pid"; then + echo "error: port ${ONYX_ZED_CONTROL_PORT} is owned by an unrelated process:" >&2 + echo " PID ${pid}: $(process_command "$pid")" >&2 + return 1 + fi + done + + echo "==> stopping the existing Onyx Process Compose stack" + request_process_compose_down + terminate_process_trees "${control_listener_pids[@]}" + fi + + assert_port_available "$ONYX_ZED_CONTROL_PORT" "Process Compose control server" + assert_port_available "$ONYX_ZED_API_PORT" "Onyx API" +} + +update_env_value() { + local key="$1" + local value="$2" + local temporary_file + + # Single quotes preserve backslashes and dollar signs in Process Compose env files. + case "$value" in + *"'"*|*$'\n'*|*$'\r'*) + echo "error: $key contains a quote or newline that cannot be stored safely" >&2 + return 1 + ;; + esac + + temporary_file="$(mktemp "${ONYX_ZED_ENV_FILE}.tmp.XXXXXX")" + ONYX_ZED_ENV_VALUE="'$value'" awk -v key="$key" ' + BEGIN { found = 0; value = ENVIRON["ONYX_ZED_ENV_VALUE"] } + index($0, key "=") == 1 { print key "=" value; found = 1; next } + { print } + END { if (!found) print key "=" value } + ' "$ONYX_ZED_ENV_FILE" >"$temporary_file" + chmod 600 "$temporary_file" + mv "$temporary_file" "$ONYX_ZED_ENV_FILE" +} + +cleanup() { + if [[ "$ONYX_ZED_CLEANING_UP" == true ]]; then + return + fi + ONYX_ZED_CLEANING_UP=true + + trap - EXIT INT TERM + if [[ "$ONYX_ZED_STACK_STARTED" == true ]]; then + stop_managed_stack || true + fi + if [[ "$ONYX_ZED_INTERCEPT_CREATED" == true ]]; then + run_bounded 10 telepresence leave onyx-api-server || true + fi +} + +require_command process-compose "install it with 'brew install f1bonacc1/tap/process-compose'" +require_command telepresence "install Telepresence before using the local kind cluster" +require_command lsof "install lsof before starting local services" + +if [[ "${1:-}" == "--stop" ]]; then + if [[ "$#" -gt 1 ]]; then + echo "error: --stop does not accept additional arguments" >&2 + exit 1 + fi + stop_managed_stack + run_bounded 10 telepresence leave onyx-api-server || true + echo "==> Onyx local services stopped" + exit 0 +fi + +if [[ "$#" -gt 0 ]]; then + echo "error: unsupported argument: $1" >&2 + exit 1 +fi + +require_command bun "install Bun before starting the web server" +require_command kubectl "install kubectl before using the local kind cluster" +require_file "$ONYX_ZED_ENV_FILE" "copy .vscode/.env.k8s.template and fill in its required values" +require_file "$ONYX_ZED_WEB_ENV_FILE" "run make craft-up to create the web environment file" +require_file "${ONYX_ZED_ROOT}/.venv/bin/python" "run 'uv sync --frozen' from the repository root" + +if ! kubectl config get-contexts kind-onyx-dev -o name >/dev/null 2>&1; then + echo "error: kubectl context kind-onyx-dev was not found; run make craft-up first" >&2 + exit 1 +fi + +echo "==> reading cluster secrets into .vscode/.env.k8s" +readonly ONYX_ZED_KUBECTL=(kubectl --context kind-onyx-dev --namespace onyx) +opensearch_password="$( + "${ONYX_ZED_KUBECTL[@]}" get secret onyx-opensearch \ + -o jsonpath='{.data.opensearch_admin_password}' 2>/dev/null | base64 -d || true +)" +if [[ -z "$opensearch_password" ]]; then + echo "error: could not read the onyx-opensearch admin password; is the cluster running?" >&2 + exit 1 +fi + +encryption_key="$( + "${ONYX_ZED_KUBECTL[@]}" get secret onyx-encryption-key \ + -o jsonpath='{.data.encryption_key_secret}' 2>/dev/null | base64 -d || true +)" +if [[ -z "$encryption_key" ]]; then + echo "error: could not read the onyx-encryption-key value; is the cluster running?" >&2 + exit 1 +fi + +update_env_value OPENSEARCH_ADMIN_PASSWORD "$opensearch_password" +update_env_value ENCRYPTION_KEY_SECRET "$encryption_key" +unset opensearch_password encryption_key + +mkdir -p "${ONYX_ZED_ROOT}/.zed/logs" + +stop_managed_stack +assert_port_available "$ONYX_ZED_WEB_PORT" "Onyx web server" + +echo "==> connecting Telepresence to kind-onyx-dev" +# Clear any previous context; bound the call because Telepresence can hang. +if ! run_bounded 15 telepresence quit; then + echo " 'telepresence quit' did not exit in 15s; killed it and continuing" +fi +telepresence connect --context kind-onyx-dev --namespace onyx + +echo "==> intercepting onyx-api-server on port ${ONYX_ZED_API_PORT}" +run_bounded 10 telepresence leave onyx-api-server || true +telepresence intercept onyx-api-server \ + --namespace onyx \ + --port 8080:8080 \ + --mount=false +ONYX_ZED_INTERCEPT_CREATED=true + +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + +process_compose_args=( + --port "$ONYX_ZED_CONTROL_PORT" + --ordered-shutdown + --disable-dotenv + --config "$ONYX_ZED_COMPOSE_FILE" +) + +if [[ -t 1 ]]; then + echo "==> starting the service stack in Process Compose" +else + echo "==> no terminal detected; starting the service stack without the TUI" + echo " logs: ${ONYX_ZED_ROOT}/.zed/logs" + echo " attach: process-compose --port ${ONYX_ZED_CONTROL_PORT} attach" + process_compose_args+=(--tui=false) +fi + +export ONYX_ZED_ROOT +ONYX_ZED_STACK_STARTED=true +process-compose "${process_compose_args[@]}" up diff --git a/.zed/tasks.json b/.zed/tasks.json index 6d01edca3e..40abe7b15e 100644 --- a/.zed/tasks.json +++ b/.zed/tasks.json @@ -609,4 +609,14 @@ }, "reevaluate_context": true, }, + + // --- 7. Run against the local kind cluster (see .zed/README.md) --- + { + "label": "run: all services (k8s)", + "command": "$ZED_WORKTREE_ROOT/.zed/run-all-k8s.sh", + "cwd": "$ZED_WORKTREE_ROOT", + "use_new_terminal": false, + "allow_concurrent_runs": false, + "reveal": "always", + }, ] diff --git a/deployment/helm/charts/onyx/Chart.yaml b/deployment/helm/charts/onyx/Chart.yaml index 885aba2af2..4056a3b44c 100644 --- a/deployment/helm/charts/onyx/Chart.yaml +++ b/deployment/helm/charts/onyx/Chart.yaml @@ -5,7 +5,7 @@ home: https://www.onyx.app/ sources: - "https://github.com/onyx-dot-app/onyx" type: application -version: 0.8.24 +version: 0.8.25 appVersion: latest annotations: category: Productivity @@ -20,17 +20,9 @@ annotations: - name: background image: docker.io/onyxdotapp/onyx-backend:latest artifacthub.io/changes: | - - kind: added - description: Optional verified TLS for an external Redis. Set - `redisTls.enabled=true` and supply the Redis server CA in a Secret - (`redisTls.caSecretName`) or a ConfigMap (`redisTls.caConfigMapName`). - The chart then mounts the CA read-only into every Redis client workload - and sets `REDIS_SSL`, `REDIS_SSL_CERT_REQS=required`, - `REDIS_SSL_CA_CERTS`, and `REDIS_SSL_CHECK_HOSTNAME`. Hostname - verification defaults to true; set `redisTls.checkHostname=false` only - for an endpoint whose certificate cannot match its host. Rendering fails - if `redis.enabled=true`, or if both CA sources are set, or if neither - is. Default deployments keep their current Redis configuration. + - kind: changed + description: Move the local development values file out of the packaged chart + to deployment/helm/dev/values-localdev.yaml in the source repository. dependencies: # Helm uses the first condition path that exists: `postgresqlOperator.enabled` # is unset by default, so this falls through to `postgresql.enabled`. Set it diff --git a/deployment/helm/charts/onyx/values.yaml b/deployment/helm/charts/onyx/values.yaml index ca68be995e..dfad48f333 100644 --- a/deployment/helm/charts/onyx/values.yaml +++ b/deployment/helm/charts/onyx/values.yaml @@ -2,10 +2,10 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. # -# Companion overlays in this directory: -# - values-lite.yaml Minimal deploy (no vector DB / Redis / model servers). -# - values-localdev.yaml Laptop-sized overlay for kind/minikube; see -# /docs/craft/dev/local-kubernetes.md for the full workflow. +# Overlays: +# - values-lite.yaml: minimal deployment without vector DB, Redis, or model servers. +# - ../../dev/values-localdev.yaml: local Kubernetes development (source repository only). +# See /docs/craft/dev/local-kubernetes.md. global: # Global version for all Onyx components (overrides .Chart.AppVersion) diff --git a/deployment/helm/dev/k8s-up.sh b/deployment/helm/dev/k8s-up.sh index 0a5ccab4a7..1596d4921e 100755 --- a/deployment/helm/dev/k8s-up.sh +++ b/deployment/helm/dev/k8s-up.sh @@ -15,6 +15,7 @@ # (default: generated, printed at the end) # --skip-cluster-create skip kind create (use an existing cluster) # --skip-helm only create the cluster, don't install Onyx +# set -euo pipefail @@ -27,7 +28,7 @@ KIND_NODE_IMAGE="${KIND_NODE_IMAGE:-kindest/node:v1.33.1}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CHART_DIR="$(cd "$SCRIPT_DIR/../charts/onyx" && pwd)" -VALUES_OVERLAY="$CHART_DIR/values-localdev.yaml" +VALUES_OVERLAY="$SCRIPT_DIR/values-localdev.yaml" require() { local bin="$1" diff --git a/deployment/helm/charts/onyx/values-localdev.yaml b/deployment/helm/dev/values-localdev.yaml similarity index 94% rename from deployment/helm/charts/onyx/values-localdev.yaml rename to deployment/helm/dev/values-localdev.yaml index 754811f51b..3405504217 100644 --- a/deployment/helm/charts/onyx/values-localdev.yaml +++ b/deployment/helm/dev/values-localdev.yaml @@ -82,6 +82,8 @@ configMap: # Full base URL, path prefix included — the Next dev server proxies the API under /api. ONYX_SERVER_URL: "http://host.docker.internal:3000/api" ENABLE_PAID_ENTERPRISE_EDITION_FEATURES: "true" + # Allow the local API to start without USER_AUTH_SECRET. + DEV_MODE: "true" sandboxProxy: replicaCount: 1 @@ -134,10 +136,7 @@ sandboxPushPolicy: # ---- App pods ---- # -# Scaled to 0 since you run these locally via telepresence intercept. -# api stays at 1: `telepresence intercept onyx-api-server` needs a Deployment -# with a running pod template to inject the traffic-agent sidecar (the main -# container is free to CrashLoopBackOff). +# Run app services locally. Keep one API pod for the Telepresence traffic agent. api: replicaCount: 1 diff --git a/docs/craft/dev/local-kubernetes.md b/docs/craft/dev/local-kubernetes.md index 0d73b857dc..e6ed162c32 100644 --- a/docs/craft/dev/local-kubernetes.md +++ b/docs/craft/dev/local-kubernetes.md @@ -127,7 +127,7 @@ kubectl -n onyx get pods -w ``` The chart pins images to the `:edge` tag in -[`values-localdev.yaml`](/deployment/helm/charts/onyx/values-localdev.yaml) +[`values-localdev.yaml`](/deployment/helm/dev/values-localdev.yaml) with `pullPolicy: Always`, so in-cluster pods track nightly builds off `main` rather than the released `:latest`. @@ -311,7 +311,7 @@ kind load docker-image onyxdotapp/onyx-backend:dev --name onyx-dev # so the nightly :edge tag refreshes. helm upgrade onyx deployment/helm/charts/onyx \ -n onyx \ - -f deployment/helm/charts/onyx/values-localdev.yaml \ + -f deployment/helm/dev/values-localdev.yaml \ --set global.pullPolicy=IfNotPresent \ --set api.image.tag=dev \ --set celery_shared.image.tag=dev @@ -382,6 +382,45 @@ before launching the api_server. ## Troubleshooting +### VPN or proxy certificate errors + +For `x509: certificate signed by unknown authority` or `CERTIFICATE_VERIFY_FAILED`, +obtain your proxy's root CA from IT. Save it as a PEM `.crt` outside the repository. +Setup scripts do not install certificates. Keep TLS verification enabled. + +If `docker pull` fails, follow [Docker's CA setup](https://docs.docker.com/engine/network/ca-certs/). +If only kind image pulls fail, run this in Bash with your CA path: + +```bash +set -euo pipefail +onyx_local_ca="/absolute/path/to/company-root.crt" +deployment/helm/dev/k8s-up.sh --skip-helm +onyx_local_nodes="$(kind get nodes --name onyx-dev)" +for onyx_local_node in $onyx_local_nodes; do + docker cp "$onyx_local_ca" "$onyx_local_node:/usr/local/share/ca-certificates/onyx-local-proxy.crt" + docker exec "$onyx_local_node" update-ca-certificates + docker exec "$onyx_local_node" systemctl restart containerd +done +make craft-up +``` + +For local client errors, build a bundle from the repository root: + +```bash +set -euo pipefail +onyx_local_ca="/absolute/path/to/company-root.crt" +mkdir -p "$HOME/.onyx-dev" +onyx_public_ca="$(.venv/bin/python -m certifi)" +cat "$onyx_public_ca" "$onyx_local_ca" > "$HOME/.onyx-dev/manual-ca-bundle.crt" +``` + +Set `SSL_CERT_FILE` and `REQUESTS_CA_BUNDLE` to the bundle's absolute path in `.vscode/.env.k8s`. +Set `NODE_EXTRA_CA_CERTS` to the root CA's absolute path in `.vscode/.env.web`. Restart the services. + +After CA rotation or removal, replace or remove the node certificate, run `update-ca-certificates --fresh`, and restart containerd. +Rebuild the local bundle and update client settings. Repeat node setup after cluster recreation. +Node trust does not configure certificates inside application pods. + ### Sandbox pods stuck in `ImagePullBackOff` **Symptoms:** Pods in the `onyx-sandboxes` namespace fail to start, with