#!/bin/sh set -eu # Atomic Nginx upstream switch with validation and automatic config rollback. # This script performs no action unless invoked with preflight, go, legacy, or status. action=${1:-status} active_include=${NGINX_ACTIVE_INCLUDE:-} go_upstream=${GO_UPSTREAM:-gateway-api:8080} legacy_upstream=${LEGACY_UPSTREAM:-legacy-gateway:8000} go_health=${GO_HEALTH_URL:-http://127.0.0.1:8080/readyz} legacy_health=${LEGACY_HEALTH_URL:-http://127.0.0.1:8000/readyz} public_health=${PUBLIC_HEALTH_URL:-} nginx_bin=${NGINX_BIN:-nginx} probe() { curl -fsS --max-time 5 "$1" >/dev/null } preflight() { probe "$go_health" probe "$legacy_health" "$nginx_bin" -t echo "preflight ok: Go and legacy are ready; Nginx configuration is valid" } status() { if [ -z "$active_include" ] || [ ! -f "$active_include" ]; then echo "active upstream include is not configured or does not exist" exit 1 fi sed -n '1,3p' "$active_include" } switch_to() { target_name=$1 target_address=$2 target_health=$3 if [ -z "$active_include" ]; then echo "NGINX_ACTIVE_INCLUDE is required" >&2 exit 2 fi probe "$target_health" directory=$(dirname "$active_include") timestamp=$(date -u +%Y%m%dT%H%M%SZ) backup="${active_include}.backup.${timestamp}" temporary="${directory}/.ai-gateway-upstream.${timestamp}.tmp" if [ -f "$active_include" ]; then cp -p "$active_include" "$backup" fi umask 027 { echo "# ai-gateway-active=${target_name} switched_at=${timestamp}" echo "upstream ai_gateway_active {" echo " server ${target_address};" echo " keepalive 128;" echo "}" } >"$temporary" mv "$temporary" "$active_include" if ! "$nginx_bin" -t; then if [ -f "$backup" ]; then mv "$backup" "$active_include"; fi echo "Nginx validation failed; previous include restored" >&2 exit 1 fi if ! "$nginx_bin" -s reload; then if [ -f "$backup" ]; then mv "$backup" "$active_include"; "$nginx_bin" -s reload || true; fi echo "Nginx reload failed; previous include restored" >&2 exit 1 fi if [ -n "$public_health" ] && ! probe "$public_health"; then if [ -f "$backup" ]; then mv "$backup" "$active_include"; "$nginx_bin" -t; "$nginx_bin" -s reload; fi echo "public health probe failed; traffic rolled back" >&2 exit 1 fi echo "active upstream switched to ${target_name}; backup: ${backup}" } case "$action" in preflight) preflight ;; status) status ;; go) switch_to go "$go_upstream" "$go_health" ;; legacy|rollback) switch_to legacy "$legacy_upstream" "$legacy_health" ;; *) echo "usage: $0 {preflight|status|go|legacy|rollback}" >&2; exit 2 ;; esac