#!/usr/bin/env bash # scripts/run_uptime.sh — uptime monitoring deploy (extracted from run_platform.sh, P9/REQ-173). # Sourced by run_platform.sh (G-112: source, not invoke — shares CONTRACT/WORK/ROOT env). # Uses: $ROOT, $CONTRACT, $WORK, $DEPLOY_UPTIME, $CHECK_ONLY, $PLAN_ONLY, $QUIET. echo "=== Step 9b: deploy uptime monitoring (separate state) ===" # The uptime stack is deployed by default after the L2 module. It uses a # separate terraform state ($WORK/uptime-tf). Endpoints from the L2 outputs # are passed as monitored_endpoints. The feature flag (uptime_enabled, # default true) controls whether this step runs. # # P57 contract shape: uptime_enabled is a per-module input under # infrastructure..inputs.uptime_enabled (the old top-level # contract.inputs.uptime_enabled was removed). Scan every module's inputs; # any module setting uptime_enabled=false disables the uptime step (one # contract = one logical stack, so a single false wins). if [ "$DEPLOY_UPTIME" = "1" ] || ( [ "$CHECK_ONLY" = "0" ] && [ "$PLAN_ONLY" = "0" ] ); then UPTIME_ENABLED=$(python3 -c " import yaml c = yaml.safe_load(open('$CONTRACT')) infra = c.get('infrastructure', {}) # Default true; a module may override to false. for m, entry in infra.items(): if isinstance(entry, dict) and entry.get('inputs', {}).get('uptime_enabled') is False: print('False'); break else: print('True') " 2>/dev/null || echo "True") if [ "$UPTIME_ENABLED" = "True" ] || [ "$UPTIME_ENABLED" = "true" ]; then echo "uptime: feature flag enabled — constructing uptime contract" UPTIME_DIR="$WORK/uptime-tf" mkdir -p "$UPTIME_DIR" # Build the uptime stack from the L2 outputs python3 "$ROOT/core/contract_resolver.py" "$CONTRACT" "$WORK/stack.json" 2>/dev/null || true python3 -c " import json, sys, yaml sys.path.insert(0, '$ROOT') from core.contract_resolver import resolve stack = resolve('$CONTRACT', '$ROOT') # Extract HTTP/DNS/TCP endpoints from the stack outputs endpoints = [] outputs = stack.get('outputs', {}) for name, spec in outputs.items(): src_rid = spec.get('from', '') src_output = spec.get('output', name) if 'domain' in name.lower() or 'url' in name.lower() or 'endpoint' in name.lower(): endpoints.append({ 'name': name, 'url': f'ref:{src_rid}.{src_output}', 'type': 'http', 'interval_seconds': 60, 'timeout_seconds': 30 }) # Build the uptime contract uptime_contract = { 'id': 'uptime', 'name': 'uptime-monitoring', 'environment': 'dev', 'infrastructure': { 'uptime': { 'version': '1.0.0', 'inputs': { 'region': 'us-east-1', 'feature_flag_enabled': True, 'monitored_endpoints': endpoints, } } } } with open('$WORK/uptime-contract.yml', 'w') as f: yaml.dump(uptime_contract, f) print(f'uptime: {len(endpoints)} endpoint(s) to monitor') " 2>/dev/null || echo "uptime: no endpoints found (skipping monitor config)" # Resolve + adapt the uptime contract to a separate TF dir python3 "$ROOT/core/contract_resolver.py" "$WORK/uptime-contract.yml" "$WORK/uptime-stack.json" 2>/dev/null || true python3 "$ROOT/adapters/terraform/adapter.py" "$WORK/uptime-stack.json" "$UPTIME_DIR" 2>/dev/null || true if [ "$DEPLOY_UPTIME" = "1" ] && [ -f "$UPTIME_DIR/main.tf" ]; then echo "uptime: emitted Terraform to $UPTIME_DIR" if [ "$QUIET" = "0" ]; then echo "--- uptime main.tf ---" cat "$UPTIME_DIR/main.tf" echo "--- end uptime main.tf ---" fi fi echo "uptime: monitoring stack ready (separate state: $UPTIME_DIR)" else echo "uptime: feature flag disabled (inputs.uptime_enabled=false) — skipping" fi fi