Files
OpenShell/e2e/rust/e2e-openshift.sh
Saurabh Agarwal 5007042e79 feat(helm): add optional PostgreSQL backing store (#1579)
* feat(helm): add optional PostgreSQL backing store with Secret-based credentials

- Add postgres.enabled and postgres.deploy values to control database
  backend (SQLite vs PostgreSQL) and subchart deployment independently.
- Introduce db-secret.yaml template for Opaque Secret with assembled
  postgresql:// connection string injected via OPENSHELL_DB_URL env var.
- Add Bitnami PostgreSQL as optional subchart dependency keyed on
  postgres.deploy to prevent subchart deployment in external mode.
- Externalize JWT signing key file mode via sandboxJwt.secretDefaultMode
  with 0400 default matching upstream.
- Add validation guard for postgres.deploy=true without postgres.enabled.
- Add helm unit tests covering internal, external, URL-override, special
  character encoding, and misconfiguration error paths.
- Update README with Kubernetes and OpenShift install examples for
  bundled and external PostgreSQL configurations.
- Add helm dependency build to lint and unittest tasks.

* fix(helm): add database backend docs to README.md.gotmpl and regenerate

The helm-docs CI check failed because the Database backend section was
added directly to README.md instead of README.md.gotmpl. Move the
content to the template and regenerate so the check passes.

* fix(helm): use Secret-based DB credentials and support existingSecret

Replace the inline db-url stringData pattern with a proper Secret
containing individual fields plus a uri key.  When postgres.deploy=true
the Bitnami service-binding secret is referenced directly; when
deploy=false users can supply postgres.external.existingSecret to
bring their own Secret, or let the chart generate one from the external
field values.

Also restructures the README database section for clarity, adds
helm-unittest coverage for the new secret resolution paths, and
fixes a markdown lint issue in the root README.

* refactor(helm): move OpenShift e2e script to e2e/rust/ and add mise task

Move test-openshift-scenarios.sh from deploy/helm/openshell/ci/ to
e2e/rust/e2e-openshift.sh, matching the existing e2e script naming
convention. Register it as `e2e:openshift` in tasks/test.toml — not
wired into the `test` or `e2e` aggregates so it only runs on explicit
invocation against a live OpenShift cluster.

* feat(e2e): add database backend scenarios to Kubernetes e2e

Extend with-kube-gateway.sh with an optional multi-scenario loop gated
by OPENSHELL_E2E_KUBE_DB_SCENARIOS=1. When enabled, the script installs
the Helm chart three times — SQLite (default), bundled PostgreSQL, and
external PostgreSQL with existingSecret — running the full test suite
against each backend. When unset, existing single-install behavior is
unchanged.

Also adds helm dependency build before helm install, fixing CI failures
caused by the missing PostgreSQL subchart dependency.

* refactor(helm): simplify PostgreSQL config to two orthogonal controls

Replace postgres.deploy and postgres.external.* with two simple controls:
- postgres.enabled: deploy the bundled Bitnami PostgreSQL subchart
- server.externalDbSecret: name of a pre-existing Secret with a uri key

Delete db-secret.yaml — the chart no longer generates Secrets from
individual credential fields. Users either get the Bitnami service-binding
secret (bundled) or bring their own via server.externalDbSecret.

Add validation that postgres.serviceBindings.enabled must stay true
when using bundled PostgreSQL, preventing a confusing runtime failure.
2026-05-28 18:31:41 -07:00

193 lines
6.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Validates all OpenShift database-backend scenarios against a live cluster.
#
# Prerequisites:
# - oc CLI authenticated to an OpenShift cluster
# - helm 3.x installed
# - Chart dependencies built (helm dependency build deploy/helm/openshell)
#
# Usage:
# mise run e2e:openshift
# e2e/rust/e2e-openshift.sh [--chart-path ./deploy/helm/openshell] [--image-tag dev]
set -euo pipefail
CHART_PATH="${CHART_PATH:-./deploy/helm/openshell}"
NAMESPACE="openshell"
RELEASE="openshell"
IMAGE_TAG="${IMAGE_TAG:-dev}"
WAIT_TIMEOUT="120s"
PASSED=0
FAILED=0
SCENARIOS=()
while [[ $# -gt 0 ]]; do
case $1 in
--chart-path) CHART_PATH="$2"; shift 2 ;;
--image-tag) IMAGE_TAG="$2"; shift 2 ;;
--namespace) NAMESPACE="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
# --- helpers ----------------------------------------------------------------
log() { echo "==> $*"; }
pass() { log "PASS: $1"; PASSED=$((PASSED + 1)); SCENARIOS+=("PASS $1"); }
fail() { log "FAIL: $1 — $2"; FAILED=$((FAILED + 1)); SCENARIOS+=("FAIL $1: $2"); }
wait_for_ready() {
local label="$1" timeout="$2"
if oc wait pod -n "$NAMESPACE" -l "$label" --for=condition=Ready --timeout="$timeout" 2>/dev/null; then
return 0
fi
return 1
}
cleanup_release() {
log "Cleaning up release $RELEASE"
helm uninstall "$RELEASE" -n "$NAMESPACE" --wait 2>/dev/null || true
# Wait for pods to terminate
for i in $(seq 1 30); do
if [ -z "$(oc get pods -n "$NAMESPACE" -l "app.kubernetes.io/instance=$RELEASE" --no-headers 2>/dev/null)" ]; then
break
fi
sleep 2
done
# Clean up PVCs left by StatefulSets
oc delete pvc -n "$NAMESPACE" -l "app.kubernetes.io/instance=$RELEASE" --wait=false 2>/dev/null || true
}
verify_gateway() {
local scenario="$1"
if wait_for_ready "app.kubernetes.io/name=openshell,app.kubernetes.io/instance=$RELEASE" "$WAIT_TIMEOUT"; then
# Check the pod is actually running (not CrashLoopBackOff)
local phase
phase=$(oc get pod -n "$NAMESPACE" -l "app.kubernetes.io/name=openshell,app.kubernetes.io/instance=$RELEASE" \
-o jsonpath='{.items[0].status.phase}' 2>/dev/null)
if [ "$phase" = "Running" ]; then
pass "$scenario"
else
fail "$scenario" "pod phase is $phase, expected Running"
fi
else
local status
status=$(oc get pods -n "$NAMESPACE" -l "app.kubernetes.io/name=openshell" --no-headers 2>/dev/null || echo "no pods found")
fail "$scenario" "gateway pod not ready within $WAIT_TIMEOUT ($status)"
fi
}
# --- setup ------------------------------------------------------------------
log "Setting up namespace $NAMESPACE"
oc create ns "$NAMESPACE" 2>/dev/null || true
oc adm policy add-scc-to-user privileged -z "${RELEASE}-sandbox" -n "$NAMESPACE"
OPENSHIFT_FLAGS=(
--set server.disableTls=true
--set podSecurityContext.fsGroup=null
--set securityContext.runAsUser=null
--set image.tag="$IMAGE_TAG"
)
# --- scenario 1: SQLite (default, no postgres) -----------------------------
SCENARIO="SQLite (default)"
log "Testing: $SCENARIO"
cleanup_release
helm install "$RELEASE" "$CHART_PATH" -n "$NAMESPACE" \
"${OPENSHIFT_FLAGS[@]}"
verify_gateway "$SCENARIO"
cleanup_release
# --- scenario 2: Bundled PostgreSQL -------------------------------------------
SCENARIO="Bundled PostgreSQL"
log "Testing: $SCENARIO"
cleanup_release
helm install "$RELEASE" "$CHART_PATH" -n "$NAMESPACE" \
"${OPENSHIFT_FLAGS[@]}" \
--set postgres.enabled=true
# Wait for postgres to be ready first
log "Waiting for bundled PostgreSQL..."
wait_for_ready "app.kubernetes.io/name=postgres,app.kubernetes.io/instance=$RELEASE" "$WAIT_TIMEOUT" || true
verify_gateway "$SCENARIO"
cleanup_release
# --- scenario 3: External PostgreSQL with existing Secret -------------------
SCENARIO="External PostgreSQL (externalDbSecret)"
log "Testing: $SCENARIO"
cleanup_release
# Deploy a standalone Bitnami PostgreSQL as the "external" database
EXTERNAL_PG_RELEASE="pg-external"
EXTERNAL_PG_PASSWORD="ext-test-password"
EXTERNAL_PG_DATABASE="openshell"
EXTERNAL_PG_USERNAME="openshell"
log "Deploying standalone PostgreSQL as external database..."
helm install "$EXTERNAL_PG_RELEASE" oci://registry-1.docker.io/bitnamicharts/postgresql \
-n "$NAMESPACE" \
--set auth.username="$EXTERNAL_PG_USERNAME" \
--set auth.password="$EXTERNAL_PG_PASSWORD" \
--set auth.database="$EXTERNAL_PG_DATABASE" \
--set primary.podSecurityContext.fsGroup=null \
--set primary.containerSecurityContext.runAsUser=null \
--wait --timeout "$WAIT_TIMEOUT" 2>/dev/null || true
wait_for_ready "app.kubernetes.io/name=postgresql,app.kubernetes.io/instance=$EXTERNAL_PG_RELEASE" "$WAIT_TIMEOUT" || true
EXTERNAL_PG_HOST="${EXTERNAL_PG_RELEASE}-postgresql.${NAMESPACE}.svc.cluster.local"
EXTERNAL_PG_URI="postgresql://${EXTERNAL_PG_USERNAME}:${EXTERNAL_PG_PASSWORD}@${EXTERNAL_PG_HOST}:5432/${EXTERNAL_PG_DATABASE}"
# Create the existing Secret with the uri key
log "Creating existing Secret with PostgreSQL credentials..."
oc create secret generic my-pg-credentials -n "$NAMESPACE" \
--from-literal=uri="$EXTERNAL_PG_URI" \
2>/dev/null || true
# Install OpenShell pointing at the existing Secret
helm install "$RELEASE" "$CHART_PATH" -n "$NAMESPACE" \
"${OPENSHIFT_FLAGS[@]}" \
--set server.externalDbSecret=my-pg-credentials
verify_gateway "$SCENARIO"
# Cleanup external postgres and secret
cleanup_release
helm uninstall "$EXTERNAL_PG_RELEASE" -n "$NAMESPACE" --wait 2>/dev/null || true
oc delete secret my-pg-credentials -n "$NAMESPACE" 2>/dev/null || true
oc delete pvc -n "$NAMESPACE" -l "app.kubernetes.io/instance=$EXTERNAL_PG_RELEASE" --wait=false 2>/dev/null || true
# --- teardown ---------------------------------------------------------------
log "Removing SCC binding and namespace"
oc adm policy remove-scc-from-user privileged -z "${RELEASE}-sandbox" -n "$NAMESPACE" 2>/dev/null || true
oc delete ns "$NAMESPACE" --wait=false 2>/dev/null || true
# --- summary ----------------------------------------------------------------
echo ""
echo "========================================"
echo " Test Summary"
echo "========================================"
for s in "${SCENARIOS[@]}"; do
echo " $s"
done
echo "----------------------------------------"
echo " Passed: $PASSED Failed: $FAILED"
echo "========================================"
if [ "$FAILED" -gt 0 ]; then
exit 1
fi