Files
git.stella-ops.org/ops/orchestrator/build-airgap-bundle.sh
StellaOps Bot 7e7be4d2fd
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
up
2025-11-29 02:40:21 +02:00

277 lines
8.5 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# ORCH-SVC-34-004: Build air-gap bundle for Orchestrator service
# Packages container images, configs, and manifests for offline deployment.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
VERSION="${VERSION:-2025.10.0-edge}"
CHANNEL="${CHANNEL:-edge}"
BUNDLE_DIR="${BUNDLE_DIR:-$REPO_ROOT/out/bundles/orchestrator-${VERSION}}"
SRC_DIR="${SRC_DIR:-$REPO_ROOT/out/buildx/orchestrator}"
usage() {
cat <<EOF
Usage: $0 [options]
Build an air-gap bundle for StellaOps Orchestrator service.
Options:
--version VERSION Bundle version (default: $VERSION)
--channel CHANNEL Release channel (default: $CHANNEL)
--output DIR Output bundle directory (default: $BUNDLE_DIR)
--source DIR Source buildx directory (default: $SRC_DIR)
--skip-images Skip OCI image export (use existing)
--help Show this help
Environment variables:
VERSION, CHANNEL, BUNDLE_DIR, SRC_DIR
Examples:
$0 --version 2025.10.0 --channel stable
VERSION=2025.10.0 CHANNEL=stable $0
EOF
exit "${1:-0}"
}
SKIP_IMAGES=false
while [[ $# -gt 0 ]]; do
case "$1" in
--version) VERSION="$2"; shift 2 ;;
--channel) CHANNEL="$2"; shift 2 ;;
--output) BUNDLE_DIR="$2"; shift 2 ;;
--source) SRC_DIR="$2"; shift 2 ;;
--skip-images) SKIP_IMAGES=true; shift ;;
--help) usage 0 ;;
*) echo "Unknown option: $1" >&2; usage 64 ;;
esac
done
BUNDLE_DIR="${BUNDLE_DIR:-$REPO_ROOT/out/bundles/orchestrator-${VERSION}}"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "[orchestrator-airgap] Building bundle v${VERSION} (${CHANNEL})"
echo "[orchestrator-airgap] Output: ${BUNDLE_DIR}"
mkdir -p "$BUNDLE_DIR"/{images,configs,manifests,docs}
# ------------------------------------------------------------------------------
# Stage 1: Export container images as OCI archives
# ------------------------------------------------------------------------------
if [[ "$SKIP_IMAGES" == "false" ]]; then
echo "[orchestrator-airgap] Exporting container images..."
IMAGES=(
"orchestrator-web:${VERSION}"
"orchestrator-worker:${VERSION}"
)
for img in "${IMAGES[@]}"; do
img_name="${img%%:*}"
img_file="${BUNDLE_DIR}/images/${img_name}.oci.tar.gz"
if [[ -f "${SRC_DIR}/${img_name}/image.oci" ]]; then
echo "[orchestrator-airgap] Packaging ${img_name} from buildx output..."
gzip -c "${SRC_DIR}/${img_name}/image.oci" > "$img_file"
else
echo "[orchestrator-airgap] Exporting ${img_name} via docker save..."
docker save "registry.stella-ops.org/stellaops/${img}" | gzip > "$img_file"
fi
# Generate checksum
sha256sum "$img_file" | cut -d' ' -f1 > "${img_file}.sha256"
# Copy SBOM if available
if [[ -f "${SRC_DIR}/${img_name}/sbom.syft.json" ]]; then
cp "${SRC_DIR}/${img_name}/sbom.syft.json" "${BUNDLE_DIR}/manifests/${img_name}.sbom.json"
fi
done
else
echo "[orchestrator-airgap] Skipping image export (--skip-images)"
fi
# ------------------------------------------------------------------------------
# Stage 2: Copy configuration templates
# ------------------------------------------------------------------------------
echo "[orchestrator-airgap] Copying configuration templates..."
# Helm values overlay
if [[ -f "$REPO_ROOT/deploy/helm/stellaops/values-orchestrator.yaml" ]]; then
cp "$REPO_ROOT/deploy/helm/stellaops/values-orchestrator.yaml" \
"${BUNDLE_DIR}/configs/values-orchestrator.yaml"
fi
# Sample configuration
if [[ -f "$REPO_ROOT/etc/orchestrator.yaml.sample" ]]; then
cp "$REPO_ROOT/etc/orchestrator.yaml.sample" \
"${BUNDLE_DIR}/configs/orchestrator.yaml.sample"
fi
# PostgreSQL migration scripts
if [[ -d "$REPO_ROOT/src/Orchestrator/StellaOps.Orchestrator/migrations" ]]; then
mkdir -p "${BUNDLE_DIR}/configs/migrations"
cp "$REPO_ROOT/src/Orchestrator/StellaOps.Orchestrator/migrations/"*.sql \
"${BUNDLE_DIR}/configs/migrations/" 2>/dev/null || true
fi
# Bootstrap secrets template
cat > "${BUNDLE_DIR}/configs/secrets.env.example" <<'SECRETS_EOF'
# Orchestrator Secrets Template
# Copy to secrets.env and fill in values before deployment
# PostgreSQL password (required)
POSTGRES_PASSWORD=
# Authority JWT signing key (if using local Authority)
AUTHORITY_SIGNING_KEY=
# OpenTelemetry endpoint (optional)
OTEL_EXPORTER_OTLP_ENDPOINT=
# Tenant encryption key for multi-tenant isolation (optional)
TENANT_ENCRYPTION_KEY=
SECRETS_EOF
# ------------------------------------------------------------------------------
# Stage 3: Generate bundle manifest
# ------------------------------------------------------------------------------
echo "[orchestrator-airgap] Generating bundle manifest..."
# Calculate checksums for all bundle files
MANIFEST_FILE="${BUNDLE_DIR}/manifests/bundle-manifest.json"
# Build file list with checksums
FILES_JSON="[]"
while IFS= read -r -d '' file; do
rel_path="${file#$BUNDLE_DIR/}"
if [[ "$rel_path" != "manifests/bundle-manifest.json" ]]; then
sha=$(sha256sum "$file" | cut -d' ' -f1)
size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null || echo "0")
FILES_JSON=$(echo "$FILES_JSON" | jq --arg name "$rel_path" --arg sha "$sha" --arg size "$size" \
'. + [{"name": $name, "sha256": $sha, "size": ($size | tonumber)}]')
fi
done < <(find "$BUNDLE_DIR" -type f -print0 | sort -z)
cat > "$MANIFEST_FILE" <<EOF
{
"bundle": {
"name": "stellaops-orchestrator",
"version": "${VERSION}",
"channel": "${CHANNEL}",
"createdAt": "${TIMESTAMP}",
"components": [
{
"name": "orchestrator-web",
"type": "container",
"image": "registry.stella-ops.org/stellaops/orchestrator-web:${VERSION}"
},
{
"name": "orchestrator-worker",
"type": "container",
"image": "registry.stella-ops.org/stellaops/orchestrator-worker:${VERSION}"
},
{
"name": "orchestrator-postgres",
"type": "infrastructure",
"image": "docker.io/library/postgres:16-alpine"
}
]
},
"files": ${FILES_JSON}
}
EOF
# Checksum the manifest itself
sha256sum "$MANIFEST_FILE" | cut -d' ' -f1 > "${MANIFEST_FILE}.sha256"
# ------------------------------------------------------------------------------
# Stage 4: Copy documentation
# ------------------------------------------------------------------------------
echo "[orchestrator-airgap] Copying documentation..."
# Module architecture
if [[ -f "$REPO_ROOT/docs/modules/orchestrator/architecture.md" ]]; then
cp "$REPO_ROOT/docs/modules/orchestrator/architecture.md" \
"${BUNDLE_DIR}/docs/architecture.md"
fi
# GA checklist
if [[ -f "$REPO_ROOT/ops/orchestrator/GA_CHECKLIST.md" ]]; then
cp "$REPO_ROOT/ops/orchestrator/GA_CHECKLIST.md" \
"${BUNDLE_DIR}/docs/GA_CHECKLIST.md"
fi
# Quick deployment guide
cat > "${BUNDLE_DIR}/docs/DEPLOY.md" <<'DEPLOY_EOF'
# Orchestrator Air-Gap Deployment Guide
## Prerequisites
- Docker or containerd runtime
- Kubernetes 1.28+ (for Helm deployment) or Docker Compose
- PostgreSQL 16+ (included as container or external)
## Quick Start (Docker)
1. Load images:
```bash
for img in images/*.oci.tar.gz; do
gunzip -c "$img" | docker load
done
```
2. Configure secrets:
```bash
cp configs/secrets.env.example secrets.env
# Edit secrets.env with your values
```
3. Start services:
```bash
docker compose -f docker-compose.orchestrator.yaml up -d
```
## Helm Deployment
1. Import images to registry:
```bash
for img in images/*.oci.tar.gz; do
crane push "$img" your-registry.local/stellaops/$(basename "$img" .oci.tar.gz)
done
```
2. Install chart:
```bash
helm upgrade --install stellaops ./stellaops \
-f configs/values-orchestrator.yaml \
--set global.imageRegistry=your-registry.local
```
## Verification
Check health endpoints:
```bash
curl http://localhost:8080/healthz
curl http://localhost:8080/readyz
```
DEPLOY_EOF
# ------------------------------------------------------------------------------
# Stage 5: Create final tarball
# ------------------------------------------------------------------------------
echo "[orchestrator-airgap] Creating final tarball..."
TARBALL="${BUNDLE_DIR}.tar.gz"
tar -C "$(dirname "$BUNDLE_DIR")" -czf "$TARBALL" "$(basename "$BUNDLE_DIR")"
# Checksum the tarball
sha256sum "$TARBALL" | cut -d' ' -f1 > "${TARBALL}.sha256"
echo "[orchestrator-airgap] Bundle created successfully:"
echo " Tarball: ${TARBALL}"
echo " SHA256: $(cat "${TARBALL}.sha256")"
echo " Size: $(du -h "$TARBALL" | cut -f1)"