#!/usr/bin/env bash # Create the planned Scaleway backup bucket. Reads bootstrap creds from # OpenBao. Never prints secret values. Writes only non-secret attributes # into substrate/object-stores/platform-audit-storage.yaml. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" ATTR="$ROOT/substrate/object-stores/platform-audit-storage.yaml" BAO_ADDR="${BAO_ADDR:-${VAULT_ADDR:-https://bao.coulomb.social}}" BOOTSTRAP_PATH="platform/workloads/railiance/scaleway/bootstrap" SCOPED_PATH="platform/workloads/railiance/backup/object-storage" REGION="nl-ams" BUCKET="${BUCKET:-railiance-platform-pg-backup}" PREFIX="${PREFIX:-platform-pg/}" ENDPOINT="https://s3.nl-ams.scw.cloud" need() { command -v "$1" >/dev/null || { echo "missing $1" >&2; exit 2; }; } export PATH="${HOME}/.local/bin:${PATH}" need python3 need curl need scw TOKEN="${OPENBAO_TOKEN:-${VAULT_TOKEN:-}}" if [[ -z "$TOKEN" && -f "$HOME/.vault-token" ]]; then TOKEN="$(cat "$HOME/.vault-token")" fi [[ -n "$TOKEN" ]] || { echo "no OpenBao token" >&2; exit 2; } read_kv() { local path="$1" curl -fsS -H "X-Vault-Token: $TOKEN" \ "$BAO_ADDR/v1/platform/data/${path#platform/}" \ | python3 -c 'import json,sys; d=json.load(sys.stdin); print(json.dumps(d["data"]["data"]))' } if ! BOOTSTRAP_JSON="$(read_kv workloads/railiance/scaleway/bootstrap 2>/dev/null)"; then echo "Bootstrap secret missing at $BOOTSTRAP_PATH" >&2 echo "Founder: put ACCESS_KEY SECRET_KEY DEFAULT_ORGANIZATION_ID DEFAULT_PROJECT_ID locally (not in chat)." >&2 echo "See docs/put-scaleway-bootstrap.md" >&2 exit 3 fi eval "$(BOOTSTRAP_JSON="$BOOTSTRAP_JSON" python3 - <<'PY' import json, os, sys data = json.loads(os.environ["BOOTSTRAP_JSON"]) needed = ("ACCESS_KEY", "SECRET_KEY", "DEFAULT_ORGANIZATION_ID", "DEFAULT_PROJECT_ID") missing = [k for k in needed if not (data.get(k) or data.get(k.lower()))] if missing: sys.stderr.write("bootstrap fields missing: " + ",".join(missing) + "\n") sys.exit(4) placeholders = [k for k in needed if (data.get(k) or data.get(k.lower()) or "") in {"xxx", "redacted", "changeme"}] if placeholders: sys.stderr.write("bootstrap still has example placeholders; replace them in the OpenBao UI first\n") sys.exit(5) def g(k): return data.get(k) or data.get(k.lower()) print("export SCW_ACCESS_KEY=" + json.dumps(g("ACCESS_KEY"))) print("export SCW_SECRET_KEY=" + json.dumps(g("SECRET_KEY"))) print("export SCW_DEFAULT_ORGANIZATION_ID=" + json.dumps(g("DEFAULT_ORGANIZATION_ID"))) print("export SCW_DEFAULT_PROJECT_ID=" + json.dumps(g("DEFAULT_PROJECT_ID"))) PY )" export SCW_DEFAULT_REGION="$REGION" if scw object bucket get "$BUCKET" region="$REGION" -o json >/dev/null 2>&1; then echo "bucket $BUCKET already exists in $REGION; skipping create" else echo "creating private bucket $BUCKET in $REGION (versioning on)" scw object bucket create "$BUCKET" region="$REGION" acl=private enable-versioning=true -o json fi echo "applying 30-day lifecycle (current + noncurrent versions)" VENV="${TMPDIR:-/tmp}/reef-storage-boto3" if [[ ! -x "$VENV/bin/python" ]]; then python3 -m venv "$VENV" "$VENV/bin/pip" -q install boto3 fi BUCKET="$BUCKET" AWS_ACCESS_KEY_ID="$SCW_ACCESS_KEY" AWS_SECRET_ACCESS_KEY="$SCW_SECRET_KEY" \ "$VENV/bin/python" - <<'PY' import os import boto3 client = boto3.client( "s3", region_name="nl-ams", endpoint_url="https://s3.nl-ams.scw.cloud", aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"], aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"], ) client.put_bucket_lifecycle_configuration( Bucket=os.environ["BUCKET"], LifecycleConfiguration={ "Rules": [ { "ID": "retain-30-days", "Status": "Enabled", "Filter": {"Prefix": ""}, "Expiration": {"Days": 30}, "NoncurrentVersionExpiration": {"NoncurrentDays": 30}, } ] }, ) print("lifecycle applied") PY echo "creating a 20 EUR monthly budget alert if none exists" scw billing budget create consumption-limit=20 enabled=true -o json | python3 -c ' import json,sys b=json.load(sys.stdin) print("budget_id", b.get("id") or b.get("budget",{}).get("id")) print("limit", b.get("consumption_limit") or b.get("budget",{}).get("consumption_limit")) ' || echo "budget create skipped (permissions or API shape)" echo "writing non-secret attributes (no keys)" python3 - <