diff --git a/hack/install-ax.sh b/hack/install-ax.sh index 45360f01..4734a834 100755 --- a/hack/install-ax.sh +++ b/hack/install-ax.sh @@ -73,6 +73,7 @@ function usage() { echo "Options:" echo " --deploy-ax-server Build images and deploy AX server and components" echo " --delete-ax-server Delete AX server and components, preserving the event-log database" + echo " --deploy-postgres With --deploy-ax-server: also deploy a bundled Postgres for testing (default: connect to an existing Postgres via AX_EVENTLOG_DSN)" echo " -h, --help Show this help message" } @@ -196,6 +197,11 @@ deploy_ax_server() { echo "Error: AX_SNAPSHOTS_BUCKET environment variable must be set" >&2 exit 1 fi + # The default (external Postgres) path needs a DSN; fail fast before building. + if [[ "${DEPLOY_POSTGRES}" != "true" && -z "${AX_EVENTLOG_DSN:-}" ]]; then + echo "Error: AX_EVENTLOG_DSN must be set to your Postgres DSN, or pass --deploy-postgres to deploy a bundled test Postgres." >&2 + exit 1 + fi echo "Using GCS Bucket: ${AX_SNAPSHOTS_BUCKET}" @@ -204,24 +210,36 @@ deploy_ax_server() { ax_image=$(build_ax_image) ateom_image=$(build_ateom_image) - # Resolve a stable Postgres password for the event log. - local pg_password="${POSTGRES_PASSWORD:-}" - local existing_pw - existing_pw="$(run_kubectl -n ax get secret ax-eventlog-postgres -o go-template='{{.data.password | base64decode}}' 2>/dev/null || true)" - if [[ -n "${existing_pw}" ]]; then - pg_password="${existing_pw}" - elif [[ -z "${pg_password}" ]]; then - pg_password="$(openssl rand -hex 16)" + # Resolve the event-log Postgres DSN. By default ax-server connects to an + # existing Postgres via AX_EVENTLOG_DSN; --deploy-postgres creates a bundled + # Postgres in-cluster (for testing) and derives the DSN from it. + local pg_dsn pg_password="" + if [[ "${DEPLOY_POSTGRES}" == "true" ]]; then + # Reuse the existing bundled-Postgres password if present, else POSTGRES_PASSWORD, + # else generate one. + local existing_pw + existing_pw="$(run_kubectl -n ax get secret ax-eventlog-postgres -o go-template='{{.data.password | base64decode}}' 2>/dev/null || true)" + if [[ -n "${existing_pw}" ]]; then + pg_password="${existing_pw}" + else + pg_password="${POSTGRES_PASSWORD:-$(openssl rand -hex 16)}" + fi + pg_dsn="postgres://axuser:${pg_password}@ax-eventlog-postgres.ax.svc:5432/axeventlog?sslmode=disable" + else + pg_dsn="${AX_EVENTLOG_DSN}" + echo "Using existing Postgres via AX_EVENTLOG_DSN." >&2 fi - # Render the manifest and apply it. - if ! sed -e "s|\${GEMINI_API_KEY}|${GEMINI_API_KEY}|g" \ - -e "s|\${AX_SNAPSHOTS_BUCKET}|${AX_SNAPSHOTS_BUCKET}|g" \ - -e "s|\${AX_IMAGE}|${ax_image}|g" \ - -e "s|\${ATEOM_IMAGE}|${ateom_image}|g" \ - -e "s|\${POSTGRES_PASSWORD}|${pg_password}|g" \ - manifests/ax-deployment.yaml \ - | run_kubectl apply -f -; then + # Common substitutions applied to every rendered manifest. + local render_sed=( + -e "s|\${GEMINI_API_KEY}|${GEMINI_API_KEY}|g" + -e "s|\${AX_SNAPSHOTS_BUCKET}|${AX_SNAPSHOTS_BUCKET}|g" + -e "s|\${AX_IMAGE}|${ax_image}|g" + -e "s|\${ATEOM_IMAGE}|${ateom_image}|g" + ) + + # Render and apply the core manifest (namespace, harnesses, ax-server, ConfigMap). + if ! sed "${render_sed[@]}" manifests/ax-deployment.yaml | run_kubectl apply -f -; then echo >&2 echo "Error: cluster rejected the manifest. An \"unknown field\" error usually means the" >&2 echo "cluster's substrate is incompatible with AX's go.mod pin — see" >&2 @@ -229,11 +247,24 @@ deploy_ax_server() { exit 1 fi - # Wait for the event-log Postgres to be ready before ax-server relies on it. - log_step "wait for statefulset/ax-eventlog-postgres to be ready" - wait_with_spinner "waiting for postgres (timeout ${AX_WAIT_TIMEOUT:-5m})" \ - run_kubectl -n ax rollout status statefulset/ax-eventlog-postgres \ - --timeout="${AX_WAIT_TIMEOUT:-5m}" + # Create/update the event-log Secret with the DSN (and, for the bundled Postgres, + # its password). ax-server reads AX_EVENTLOG_DSN from this Secret's dsn key. + local secret_args=(--from-literal=dsn="${pg_dsn}") + if [[ "${DEPLOY_POSTGRES}" == "true" ]]; then + secret_args+=(--from-literal=password="${pg_password}") + fi + run_kubectl -n ax create secret generic ax-eventlog-postgres "${secret_args[@]}" \ + --dry-run=client -o yaml | run_kubectl apply -f - + + # With --deploy-postgres, create the bundled Postgres and wait for it to be + # ready before ax-server relies on it. + if [[ "${DEPLOY_POSTGRES}" == "true" ]]; then + run_kubectl apply -f manifests/ax-postgres.yaml + log_step "wait for statefulset/ax-eventlog-postgres to be ready" + wait_with_spinner "waiting for postgres (timeout ${AX_WAIT_TIMEOUT:-5m})" \ + run_kubectl -n ax rollout status statefulset/ax-eventlog-postgres \ + --timeout="${AX_WAIT_TIMEOUT:-5m}" + fi # Wait for the antigravity ActorTemplate's golden snapshot to be ready. log_step "wait for actortemplate/ax-harness-template to be Ready" @@ -265,6 +296,11 @@ if [ "$#" -eq 0 ]; then exit 1 fi +# Event-log Postgres: by default ax-server connects to an existing Postgres via +# the AX_EVENTLOG_DSN env var. --deploy-postgres additionally creates a bundled +# Postgres in-cluster (for testing on Substrate). +DEPLOY_POSTGRES=false + # If -h or --help appears anywhere in the command line, print the usage and exit. for arg in "$@"; do case "$arg" in @@ -272,6 +308,9 @@ for arg in "$@"; do usage exit 0 ;; + --deploy-postgres) + DEPLOY_POSTGRES=true + ;; esac done @@ -279,6 +318,7 @@ while [[ "$#" -gt 0 ]]; do case $1 in --deploy-ax-server) deploy_ax_server ;; --delete-ax-server) delete_ax_server ;; + --deploy-postgres) ;; # resolved in the pre-scan above *) echo "Error: unknown option: $1" >&2 echo "" diff --git a/manifests/README.md b/manifests/README.md index 0b1b152c..448b4dcf 100644 --- a/manifests/README.md +++ b/manifests/README.md @@ -56,12 +56,27 @@ gcloud auth configure-docker # set up the gcr.io credential helper #### Deploy +The event log is stored in Postgres. By default ax-server connects to an +**existing** Postgres that you provide via the `AX_EVENTLOG_DSN` env var (bring your own database). Pass `--deploy-postgres` to also +create a **bundled** Postgres in-cluster instead (for testing). + ```bash export PROJECT_ID="ax-substrate" # Your GCP project ID export GEMINI_API_KEY="your-api-key" export AX_SNAPSHOTS_BUCKET="snapshot-substrate-test-$PROJECT_ID" +# Connect to your existing Postgres: +export AX_EVENTLOG_DSN="postgres://user:pass@host:5432/db?sslmode=require" ./hack/install-ax.sh --deploy-ax-server + +# Or deploy a bundled Postgres for testing: +./hack/install-ax.sh --deploy-ax-server --deploy-postgres +``` + +The bundled Postgres uses an auto-generated password. To get its DSN: + +```bash +kubectl get secret ax-eventlog-postgres -n ax -o go-template='{{.data.dsn | base64decode}}' ``` ### 2. Port-Forward Services @@ -114,7 +129,7 @@ Use the **`kubectl ate`** CLI tool to inspect the live states of active actors and allocated standby worker pool instances: ```bash -kubectl ate get actors +kubectl ate get actors -a ax kubectl ate get workers ``` diff --git a/manifests/ax-deployment.yaml b/manifests/ax-deployment.yaml index eb71bf1c..4790d0c5 100644 --- a/manifests/ax-deployment.yaml +++ b/manifests/ax-deployment.yaml @@ -71,86 +71,6 @@ spec: location: gs://${AX_SNAPSHOTS_BUCKET}/antigravity/ --- -# --------------------------------------------------------------------------- -# Event log storage (PostgreSQL) -# --------------------------------------------------------------------------- -apiVersion: v1 -kind: Service -metadata: - name: ax-eventlog-postgres - namespace: ax - labels: - app: ax-eventlog-postgres -spec: - selector: - app: ax-eventlog-postgres - ports: - - port: 5432 - targetPort: 5432 ---- -apiVersion: v1 -kind: Secret -metadata: - name: ax-eventlog-postgres - namespace: ax -type: Opaque -stringData: - password: "${POSTGRES_PASSWORD}" - dsn: "postgres://axuser:${POSTGRES_PASSWORD}@ax-eventlog-postgres.ax.svc:5432/axeventlog?sslmode=disable" ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: ax-eventlog-postgres - namespace: ax - labels: - app: ax-eventlog-postgres -spec: - serviceName: ax-eventlog-postgres - replicas: 1 - selector: - matchLabels: - app: ax-eventlog-postgres - template: - metadata: - labels: - app: ax-eventlog-postgres - spec: - containers: - - name: postgres - image: postgres:16 - ports: - - containerPort: 5432 - env: - - name: POSTGRES_DB - value: axeventlog - - name: POSTGRES_USER - value: axuser - - name: POSTGRES_PASSWORD - valueFrom: - secretKeyRef: - name: ax-eventlog-postgres - key: password - - name: PGDATA - value: /var/lib/postgresql/data/pgdata - volumeMounts: - - name: data - mountPath: /var/lib/postgresql/data - readinessProbe: - exec: - command: ["pg_isready", "-U", "axuser", "-d", "axeventlog"] - initialDelaySeconds: 5 - periodSeconds: 5 - volumeClaimTemplates: - - metadata: - name: data - spec: - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 10Gi ---- - # --------------------------------------------------------------------------- # AX Server # --------------------------------------------------------------------------- diff --git a/manifests/ax-postgres.yaml b/manifests/ax-postgres.yaml new file mode 100644 index 00000000..3009e2e2 --- /dev/null +++ b/manifests/ax-postgres.yaml @@ -0,0 +1,87 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# --------------------------------------------------------------------------- +# Event log storage (PostgreSQL) +# +# Applied by hack/install-ax.sh only when --deploy-postgres is passed. By default +# ax-server connects to an existing Postgres via AX_EVENTLOG_DSN and these +# resources are not deployed. The ax-eventlog-postgres Secret (holding the +# password used below and the DSN) is created by install-ax.sh. +# --------------------------------------------------------------------------- +apiVersion: v1 +kind: Service +metadata: + name: ax-eventlog-postgres + namespace: ax + labels: + app: ax-eventlog-postgres +spec: + selector: + app: ax-eventlog-postgres + ports: + - port: 5432 + targetPort: 5432 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: ax-eventlog-postgres + namespace: ax + labels: + app: ax-eventlog-postgres +spec: + serviceName: ax-eventlog-postgres + replicas: 1 + selector: + matchLabels: + app: ax-eventlog-postgres + template: + metadata: + labels: + app: ax-eventlog-postgres + spec: + containers: + - name: postgres + image: postgres:16 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_DB + value: axeventlog + - name: POSTGRES_USER + value: axuser + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: ax-eventlog-postgres + key: password + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + readinessProbe: + exec: + command: ["pg_isready", "-U", "axuser", "-d", "axeventlog"] + initialDelaySeconds: 5 + periodSeconds: 5 + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 10Gi