diff --git a/docs/content/install/maas-setup.md b/docs/content/install/maas-setup.md index 91c028f8a..f175eb475 100644 --- a/docs/content/install/maas-setup.md +++ b/docs/content/install/maas-setup.md @@ -44,6 +44,13 @@ postgresql://USERNAME:PASSWORD@HOSTNAME:PORT/DATABASE?sslmode=require The full `scripts/deploy.sh` script also creates PostgreSQL automatically when deploying MaaS. +!!! note "Using deploy.sh with an external database" + If you use `scripts/deploy.sh`, you can supply your own PostgreSQL connection string with the `--postgres-connection` flag. This skips the built-in POC PostgreSQL deployment and creates the `maas-db-config` Secret automatically: + + ```bash + ./scripts/deploy.sh --postgres-connection 'postgresql://username:password@hostname:5432/database?sslmode=require' + ``` + !!! note "Restarting maas-api" If you add or update the Secret after the DataScienceCluster already has modelsAsService in managed state, restart the maas-api deployment to pick up the config: diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 768d17aab..f85b94f65 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -60,6 +60,9 @@ # # Test custom MaaS API image # MAAS_API_IMAGE=quay.io/myuser/maas-api:pr-123 ./scripts/deploy.sh # +# # Use external PostgreSQL (production) +# ./scripts/deploy.sh --postgres-connection 'postgresql://user:pass@db.example.com:5432/maas?sslmode=require' +# # For detailed documentation, see: # https://opendatahub-io.github.io/models-as-a-service/latest/install/maas-setup/ ################################################################################ @@ -108,6 +111,7 @@ MAAS_API_IMAGE="${MAAS_API_IMAGE:-}" MAAS_CONTROLLER_IMAGE="${MAAS_CONTROLLER_IMAGE:-}" KUSTOMIZE_FORCE_CONFLICTS="${KUSTOMIZE_FORCE_CONFLICTS:-false}" EXTERNAL_OIDC="${EXTERNAL_OIDC:-false}" +POSTGRES_CONNECTION="${POSTGRES_CONNECTION:-}" #────────────────────────────────────────────────────────────── # HELP TEXT @@ -144,6 +148,11 @@ OPTIONS: Creates keycloak-system namespace and deploys Keycloak operator See docs/samples/install/keycloak/ for configuration guide + --postgres-connection + Use an external PostgreSQL database instead of deploying a POC instance. + Format: postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=require + When set, skips the built-in PostgreSQL deployment entirely. + --namespace Target namespace for deployment Default: redhat-ods-applications (RHOAI) or opendatahub (ODH) @@ -195,6 +204,7 @@ ENVIRONMENT VARIABLES: OIDC_ISSUER_URL External OIDC issuer URL for maas-api AuthPolicy patching LOG_LEVEL Logging verbosity (DEBUG, INFO, WARN, ERROR) KUSTOMIZE_FORCE_CONFLICTS When true, pass --force-conflicts to kubectl apply in kustomize mode (default: false) + POSTGRES_CONNECTION External PostgreSQL connection string (same as --postgres-connection) TIMEOUT CONFIGURATION (all values in seconds): Customize timeouts for slow clusters or CI/CD environments: @@ -229,6 +239,9 @@ EXAMPLES: --operator-catalog quay.io/opendatahub/opendatahub-operator-catalog:pr-456 \\ --operator-image quay.io/opendatahub/opendatahub-operator:pr-456 + # Use an external PostgreSQL database + ./scripts/deploy.sh --postgres-connection 'postgresql://user:pass@rds.example.com:5432/maas?sslmode=require' + For more information, see: https://github.com/opendatahub-io/models-as-a-service EOF } @@ -314,6 +327,11 @@ parse_arguments() { OPERATOR_CHANNEL="$2" shift 2 ;; + --postgres-connection) + require_flag_value "$1" "${2:-}" + POSTGRES_CONNECTION="$2" + shift 2 + ;; --external-oidc) EXTERNAL_OIDC="true" shift @@ -697,8 +715,30 @@ deploy_via_kustomize() { # POSTGRESQL DEPLOYMENT #────────────────────────────────────────────────────────────── +validate_postgres_connection() { + local conn="$1" + if [[ ! "$conn" =~ ^postgres(ql)?:// ]]; then + log_error "Invalid PostgreSQL connection string format" + log_error "Expected: postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=require" + return 1 + fi +} + deploy_postgresql() { - NAMESPACE="$NAMESPACE" "${SCRIPT_DIR}/setup-database.sh" + if [[ -n "$POSTGRES_CONNECTION" ]]; then + validate_postgres_connection "$POSTGRES_CONNECTION" || exit 1 + log_info "Using external PostgreSQL connection" + create_maas_db_config_secret "$NAMESPACE" "$POSTGRES_CONNECTION" + log_info "Created maas-db-config secret with external connection" + else + log_warn "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + log_warn " DEPLOYING POC POSTGRESQL — NOT INTENDED FOR PRODUCTION USE" + log_warn " Data is stored in ephemeral storage and will be lost on pod restart." + log_warn " For production, use --postgres-connection with an external database" + log_warn " (AWS RDS, Crunchy Operator, Azure Database, etc.)" + log_warn "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + NAMESPACE="$NAMESPACE" "${SCRIPT_DIR}/setup-database.sh" + fi } #────────────────────────────────────────────────────────────── diff --git a/scripts/deployment-helpers.sh b/scripts/deployment-helpers.sh index 6800f1aea..80d3f3a7a 100755 --- a/scripts/deployment-helpers.sh +++ b/scripts/deployment-helpers.sh @@ -1456,3 +1456,35 @@ wait_authorino_ready() { echo " WARNING: Auth request verification timed out, continuing anyway" return 0 } + +# ========================================== +# Database Secret Helpers +# ========================================== + +# create_maas_db_config_secret +# Creates the maas-db-config Secret containing DB_CONNECTION_URL. +# This secret is read by maas-api at startup to connect to PostgreSQL. +# +# Usage: +# create_maas_db_config_secret "opendatahub" "postgresql://user:pass@host:5432/db?sslmode=require" +create_maas_db_config_secret() { + local namespace="$1" + local connection_url="$2" + + if [[ -z "$namespace" ]]; then + log_error "create_maas_db_config_secret: namespace is required" + return 1 + fi + if [[ -z "$connection_url" ]]; then + log_error "create_maas_db_config_secret: connection_url is required" + return 1 + fi + + # Pass the connection URL via stdin to avoid exposing credentials in process arguments + printf '%s' "$connection_url" | \ + kubectl create secret generic maas-db-config \ + --from-file=DB_CONNECTION_URL=/dev/stdin \ + --dry-run=client -o yaml | \ + kubectl label --local -f - app=maas-api --dry-run=client -o yaml | \ + kubectl apply -n "$namespace" -f - +} diff --git a/scripts/setup-database.sh b/scripts/setup-database.sh index 88d111ed5..17fc2cc2f 100755 --- a/scripts/setup-database.sh +++ b/scripts/setup-database.sh @@ -25,6 +25,11 @@ set -euo pipefail +# Source helpers +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=deployment-helpers.sh +source "${SCRIPT_DIR}/deployment-helpers.sh" + # Default namespace for ODH; use redhat-ods-applications for RHOAI : "${NAMESPACE:=opendatahub}" @@ -34,6 +39,15 @@ if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then kubectl create namespace "$NAMESPACE" fi +echo "" +echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓" +echo "┃ ⚠️ WARNING FOR PRODUCTION USE. ⚠️ ┃" +echo "┃ This deploys PostgreSQL with ephemeral storage (emptyDir). ┃" +echo "┃ Data WILL be lost on pod restart. ┃" +echo "┃ For production, use an external database: ┃" +echo "┃ deploy.sh --postgres-connection postgresql://... ┃" +echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛" +echo "" echo "🔧 Deploying PostgreSQL for API key storage in namespace '$NAMESPACE'..." # Check if PostgreSQL already exists @@ -142,18 +156,21 @@ spec: ports: - port: 5432 targetPort: 5432 ---- -apiVersion: v1 -kind: Secret -metadata: - name: maas-db-config - labels: - app: maas-api - purpose: poc -stringData: - DB_CONNECTION_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable" EOF +# Create the maas-db-config secret used by maas-api +# URL-encode the password in case it contains reserved characters (@, :, /, ?, etc.) +# 1. printf '%s' outputs the raw password bytes (no trailing newline) +# 2. od -An -tx1 converts each byte to space-separated two-digit hex (e.g., "a" -> " 61") +# 3. tr -d ' \n' strips spaces and newlines to produce a continuous hex string +# 4. sed inserts a "%" before every hex pair, producing percent-encoding (e.g., "61" -> "%61") +# This encodes all characters (including safe ones like letters), which is more aggressive +# than strictly necessary but is always correct per RFC 3986 — %61 is equivalent to "a". +# Uses od (POSIX) instead of xxd which may not be available in all environments. +ENCODED_PASSWORD=$(printf '%s' "$POSTGRES_PASSWORD" | od -An -tx1 | tr -d ' \n' | sed 's/../%&/g') +DB_CONNECTION_URL="postgresql://${POSTGRES_USER}:${ENCODED_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable" +create_maas_db_config_secret "$NAMESPACE" "$DB_CONNECTION_URL" + echo " Waiting for PostgreSQL to be ready..." if ! kubectl wait -n "$NAMESPACE" --for=condition=available deployment/postgres --timeout=120s; then echo "❌ PostgreSQL deployment failed to become ready" >&2