Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
232 lines
8.4 KiB
Bash
Executable File
232 lines
8.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Deploy OpenClaw to Kubernetes.
|
|
#
|
|
# Secrets are generated in a temp directory and applied server-side.
|
|
# No secret material is ever written to the repo checkout.
|
|
#
|
|
# Usage:
|
|
# ./scripts/k8s/deploy.sh # Deploy (requires API key in env or secret already in cluster)
|
|
# ./scripts/k8s/deploy.sh --create-secret # Create or update the K8s Secret from env vars
|
|
# ./scripts/k8s/deploy.sh --show-token # Print the gateway token after deploy
|
|
# ./scripts/k8s/deploy.sh --delete # Tear down
|
|
#
|
|
# Environment:
|
|
# OPENCLAW_NAMESPACE Kubernetes namespace (default: openclaw)
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
MANIFESTS="$SCRIPT_DIR/manifests"
|
|
NS="${OPENCLAW_NAMESPACE:-openclaw}"
|
|
|
|
# Check prerequisites
|
|
for cmd in kubectl openssl; do
|
|
command -v "$cmd" &>/dev/null || { echo "Missing: $cmd" >&2; exit 1; }
|
|
done
|
|
kubectl cluster-info &>/dev/null || { echo "Cannot connect to cluster. Check kubeconfig." >&2; exit 1; }
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# -h / --help
|
|
# ---------------------------------------------------------------------------
|
|
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
|
cat <<'HELP'
|
|
Usage: ./scripts/k8s/deploy.sh [OPTION]
|
|
|
|
(no args) Deploy OpenClaw (creates secret from env if needed)
|
|
--create-secret Create or update the K8s Secret from env vars without deploying
|
|
--show-token Print the gateway token after deploy or secret creation
|
|
--delete Delete the namespace and all resources
|
|
-h, --help Show this help
|
|
|
|
Environment:
|
|
Export at least one provider API key:
|
|
ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY
|
|
|
|
OPENCLAW_NAMESPACE Kubernetes namespace (default: openclaw)
|
|
HELP
|
|
exit 0
|
|
fi
|
|
|
|
SHOW_TOKEN=false
|
|
MODE="deploy"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--create-secret)
|
|
MODE="create-secret"
|
|
;;
|
|
--delete)
|
|
MODE="delete"
|
|
;;
|
|
--show-token)
|
|
SHOW_TOKEN=true
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
echo "Run ./scripts/k8s/deploy.sh --help for usage." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# --delete
|
|
# ---------------------------------------------------------------------------
|
|
if [[ "$MODE" == "delete" ]]; then
|
|
echo "Deleting namespace '$NS' and all resources..."
|
|
kubectl delete namespace "$NS" --ignore-not-found
|
|
echo "Done."
|
|
exit 0
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Create and apply Secret to the cluster
|
|
# ---------------------------------------------------------------------------
|
|
_apply_secret() {
|
|
local TMP_DIR
|
|
local EXISTING_SECRET=false
|
|
local EXISTING_TOKEN=""
|
|
local ANTHROPIC_VALUE=""
|
|
local OPENAI_VALUE=""
|
|
local GEMINI_VALUE=""
|
|
local OPENROUTER_VALUE=""
|
|
local TOKEN
|
|
local SECRET_MANIFEST
|
|
TMP_DIR="$(mktemp -d)"
|
|
chmod 700 "$TMP_DIR"
|
|
trap 'rm -rf "$TMP_DIR"' EXIT
|
|
|
|
if kubectl get secret openclaw-secrets -n "$NS" &>/dev/null; then
|
|
EXISTING_SECRET=true
|
|
EXISTING_TOKEN="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d)"
|
|
ANTHROPIC_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.ANTHROPIC_API_KEY}' 2>/dev/null | base64 -d)"
|
|
OPENAI_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENAI_API_KEY}' 2>/dev/null | base64 -d)"
|
|
GEMINI_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.GEMINI_API_KEY}' 2>/dev/null | base64 -d)"
|
|
OPENROUTER_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENROUTER_API_KEY}' 2>/dev/null | base64 -d)"
|
|
fi
|
|
|
|
TOKEN="${EXISTING_TOKEN:-$(openssl rand -hex 32)}"
|
|
ANTHROPIC_VALUE="${ANTHROPIC_API_KEY:-$ANTHROPIC_VALUE}"
|
|
OPENAI_VALUE="${OPENAI_API_KEY:-$OPENAI_VALUE}"
|
|
GEMINI_VALUE="${GEMINI_API_KEY:-$GEMINI_VALUE}"
|
|
OPENROUTER_VALUE="${OPENROUTER_API_KEY:-$OPENROUTER_VALUE}"
|
|
SECRET_MANIFEST="$TMP_DIR/secrets.yaml"
|
|
|
|
# Write secret material to temp files so kubectl handles encoding safely.
|
|
printf '%s' "$TOKEN" > "$TMP_DIR/OPENCLAW_GATEWAY_TOKEN"
|
|
printf '%s' "$ANTHROPIC_VALUE" > "$TMP_DIR/ANTHROPIC_API_KEY"
|
|
printf '%s' "$OPENAI_VALUE" > "$TMP_DIR/OPENAI_API_KEY"
|
|
printf '%s' "$GEMINI_VALUE" > "$TMP_DIR/GEMINI_API_KEY"
|
|
printf '%s' "$OPENROUTER_VALUE" > "$TMP_DIR/OPENROUTER_API_KEY"
|
|
chmod 600 \
|
|
"$TMP_DIR/OPENCLAW_GATEWAY_TOKEN" \
|
|
"$TMP_DIR/ANTHROPIC_API_KEY" \
|
|
"$TMP_DIR/OPENAI_API_KEY" \
|
|
"$TMP_DIR/GEMINI_API_KEY" \
|
|
"$TMP_DIR/OPENROUTER_API_KEY"
|
|
|
|
kubectl create secret generic openclaw-secrets \
|
|
-n "$NS" \
|
|
--from-file=OPENCLAW_GATEWAY_TOKEN="$TMP_DIR/OPENCLAW_GATEWAY_TOKEN" \
|
|
--from-file=ANTHROPIC_API_KEY="$TMP_DIR/ANTHROPIC_API_KEY" \
|
|
--from-file=OPENAI_API_KEY="$TMP_DIR/OPENAI_API_KEY" \
|
|
--from-file=GEMINI_API_KEY="$TMP_DIR/GEMINI_API_KEY" \
|
|
--from-file=OPENROUTER_API_KEY="$TMP_DIR/OPENROUTER_API_KEY" \
|
|
--dry-run=client \
|
|
-o yaml > "$SECRET_MANIFEST"
|
|
chmod 600 "$SECRET_MANIFEST"
|
|
|
|
kubectl create namespace "$NS" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
|
|
kubectl apply --server-side --field-manager=openclaw -f "$SECRET_MANIFEST" >/dev/null
|
|
# Clean up any annotation left by older client-side apply runs.
|
|
kubectl annotate secret openclaw-secrets -n "$NS" kubectl.kubernetes.io/last-applied-configuration- >/dev/null 2>&1 || true
|
|
rm -rf "$TMP_DIR"
|
|
trap - EXIT
|
|
|
|
if $EXISTING_SECRET; then
|
|
echo "Secret updated in namespace '$NS'. Existing gateway token preserved."
|
|
else
|
|
echo "Secret created in namespace '$NS'."
|
|
fi
|
|
|
|
if $SHOW_TOKEN; then
|
|
echo "Gateway token: $TOKEN"
|
|
else
|
|
echo "Gateway token stored in Secret only."
|
|
echo "Retrieve it with:"
|
|
echo " kubectl get secret openclaw-secrets -n $NS -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d && echo"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# --create-secret
|
|
# ---------------------------------------------------------------------------
|
|
if [[ "$MODE" == "create-secret" ]]; then
|
|
HAS_KEY=false
|
|
for key in ANTHROPIC_API_KEY OPENAI_API_KEY GEMINI_API_KEY OPENROUTER_API_KEY; do
|
|
if [[ -n "${!key:-}" ]]; then
|
|
HAS_KEY=true
|
|
echo " Found $key in environment"
|
|
fi
|
|
done
|
|
|
|
if ! $HAS_KEY; then
|
|
echo "No API keys found in environment. Export at least one and re-run:"
|
|
echo " export <PROVIDER>_API_KEY=\"...\" (ANTHROPIC, GEMINI, OPENAI, or OPENROUTER)"
|
|
echo " ./scripts/k8s/deploy.sh --create-secret"
|
|
exit 1
|
|
fi
|
|
|
|
_apply_secret
|
|
echo ""
|
|
echo "Now run:"
|
|
echo " ./scripts/k8s/deploy.sh"
|
|
exit 0
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Check that the secret exists in the cluster
|
|
# ---------------------------------------------------------------------------
|
|
if ! kubectl get secret openclaw-secrets -n "$NS" &>/dev/null; then
|
|
HAS_KEY=false
|
|
for key in ANTHROPIC_API_KEY OPENAI_API_KEY GEMINI_API_KEY OPENROUTER_API_KEY; do
|
|
[[ -n "${!key:-}" ]] && HAS_KEY=true
|
|
done
|
|
|
|
if $HAS_KEY; then
|
|
echo "Creating secret from environment..."
|
|
_apply_secret
|
|
echo ""
|
|
else
|
|
echo "No secret found and no API keys in environment."
|
|
echo ""
|
|
echo "Export at least one provider API key and re-run:"
|
|
echo " export <PROVIDER>_API_KEY=\"...\" (ANTHROPIC, GEMINI, OPENAI, or OPENROUTER)"
|
|
echo " ./scripts/k8s/deploy.sh"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Deploy
|
|
# ---------------------------------------------------------------------------
|
|
echo "Deploying to namespace '$NS'..."
|
|
kubectl create namespace "$NS" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
|
|
kubectl apply -k "$MANIFESTS" -n "$NS"
|
|
kubectl rollout restart deployment/openclaw -n "$NS" 2>/dev/null || true
|
|
echo ""
|
|
echo "Waiting for rollout..."
|
|
kubectl rollout status deployment/openclaw -n "$NS" --timeout=300s
|
|
echo ""
|
|
echo "Done. Access the gateway:"
|
|
echo " kubectl port-forward svc/openclaw 18789:18789 -n $NS"
|
|
echo " open http://localhost:18789"
|
|
echo ""
|
|
if $SHOW_TOKEN; then
|
|
echo "Gateway token (paste into Control UI):"
|
|
echo " $(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d)"
|
|
echo ""
|
|
fi
|
|
echo "Retrieve the gateway token with:"
|
|
echo " kubectl get secret openclaw-secrets -n $NS -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d && echo"
|