Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
console-runner-image / build-runner-image (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
132 lines
3.9 KiB
Bash
132 lines
3.9 KiB
Bash
#!/usr/bin/env bash
|
|
# Package console for offline/airgap deployment
|
|
# Usage: ./package-offline-bundle.sh [image] [output-dir]
|
|
# Example: ./package-offline-bundle.sh registry.stella-ops.org/stellaops/console:2025.10.0 ./offline-bundle
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
|
|
|
IMAGE="${1:-registry.stella-ops.org/stellaops/console:latest}"
|
|
OUTPUT_DIR="${2:-${SCRIPT_DIR}/../artifacts/console/offline-bundle}"
|
|
BUNDLE_NAME="console-offline-$(date +%Y%m%dT%H%M%S)"
|
|
|
|
# Freeze timestamps
|
|
export SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH:-1704067200}
|
|
|
|
echo "==> Creating offline bundle for: ${IMAGE}"
|
|
mkdir -p "${OUTPUT_DIR}"
|
|
|
|
# Save image as tarball
|
|
IMAGE_TAR="${OUTPUT_DIR}/${BUNDLE_NAME}.tar"
|
|
echo "==> Saving image to ${IMAGE_TAR}..."
|
|
docker save "${IMAGE}" -o "${IMAGE_TAR}"
|
|
|
|
# Calculate checksums
|
|
echo "==> Generating checksums..."
|
|
cd "${OUTPUT_DIR}"
|
|
sha256sum "${BUNDLE_NAME}.tar" > "${BUNDLE_NAME}.tar.sha256"
|
|
|
|
# Copy Helm values
|
|
echo "==> Including Helm values overlay..."
|
|
cp "${REPO_ROOT}/deploy/helm/stellaops/values-console.yaml" "${OUTPUT_DIR}/"
|
|
|
|
# Copy Dockerfile for reference
|
|
cp "${REPO_ROOT}/ops/devops/docker/Dockerfile.console" "${OUTPUT_DIR}/"
|
|
|
|
# Generate SBOMs if syft available
|
|
if command -v syft &>/dev/null; then
|
|
echo "==> Generating SBOMs..."
|
|
syft "${IMAGE}" -o spdx-json > "${OUTPUT_DIR}/${BUNDLE_NAME}.spdx.json"
|
|
syft "${IMAGE}" -o cyclonedx-json > "${OUTPUT_DIR}/${BUNDLE_NAME}.cdx.json"
|
|
fi
|
|
|
|
# Create manifest
|
|
cat > "${OUTPUT_DIR}/manifest.json" <<EOF
|
|
{
|
|
"bundle": "${BUNDLE_NAME}",
|
|
"image": "${IMAGE}",
|
|
"imageTarball": "${BUNDLE_NAME}.tar",
|
|
"checksumFile": "${BUNDLE_NAME}.tar.sha256",
|
|
"helmValues": "values-console.yaml",
|
|
"dockerfile": "Dockerfile.console",
|
|
"sbom": {
|
|
"spdx": "${BUNDLE_NAME}.spdx.json",
|
|
"cyclonedx": "${BUNDLE_NAME}.cdx.json"
|
|
},
|
|
"createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"sourceDateEpoch": "${SOURCE_DATE_EPOCH}"
|
|
}
|
|
EOF
|
|
|
|
# Create load script
|
|
cat > "${OUTPUT_DIR}/load.sh" <<'LOAD'
|
|
#!/usr/bin/env bash
|
|
# Load console image into local Docker daemon
|
|
set -euo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
MANIFEST="${SCRIPT_DIR}/manifest.json"
|
|
|
|
if [[ ! -f "${MANIFEST}" ]]; then
|
|
echo "ERROR: manifest.json not found" >&2
|
|
exit 1
|
|
fi
|
|
|
|
TARBALL=$(jq -r '.imageTarball' "${MANIFEST}")
|
|
CHECKSUM_FILE=$(jq -r '.checksumFile' "${MANIFEST}")
|
|
|
|
echo "==> Verifying checksum..."
|
|
cd "${SCRIPT_DIR}"
|
|
sha256sum -c "${CHECKSUM_FILE}"
|
|
|
|
echo "==> Loading image..."
|
|
docker load -i "${TARBALL}"
|
|
|
|
IMAGE=$(jq -r '.image' "${MANIFEST}")
|
|
echo "==> Image loaded: ${IMAGE}"
|
|
LOAD
|
|
chmod +x "${OUTPUT_DIR}/load.sh"
|
|
|
|
# Create README
|
|
cat > "${OUTPUT_DIR}/README.md" <<EOF
|
|
# Console Offline Bundle
|
|
|
|
This bundle contains the StellaOps Console container image and deployment assets
|
|
for air-gapped environments.
|
|
|
|
## Contents
|
|
|
|
- \`${BUNDLE_NAME}.tar\` - Docker image tarball
|
|
- \`${BUNDLE_NAME}.tar.sha256\` - SHA-256 checksum
|
|
- \`values-console.yaml\` - Helm values overlay
|
|
- \`Dockerfile.console\` - Reference Dockerfile
|
|
- \`${BUNDLE_NAME}.spdx.json\` - SPDX SBOM (if generated)
|
|
- \`${BUNDLE_NAME}.cdx.json\` - CycloneDX SBOM (if generated)
|
|
- \`manifest.json\` - Bundle manifest
|
|
- \`load.sh\` - Image load helper script
|
|
|
|
## Usage
|
|
|
|
1. Transfer this bundle to the air-gapped environment
|
|
2. Verify checksums: \`sha256sum -c ${BUNDLE_NAME}.tar.sha256\`
|
|
3. Load image: \`./load.sh\` or \`docker load -i ${BUNDLE_NAME}.tar\`
|
|
4. Deploy with Helm: \`helm install stellaops ../stellaops -f values-console.yaml\`
|
|
|
|
## Image Details
|
|
|
|
- Image: \`${IMAGE}\`
|
|
- Created: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
- Non-root user: UID 101 (nginx-unprivileged)
|
|
- Port: 8080
|
|
|
|
## Verification
|
|
|
|
The image runs as non-root and supports read-only root filesystem.
|
|
Enable \`readOnlyRootFilesystem: true\` in your security context.
|
|
EOF
|
|
|
|
echo "==> Offline bundle created at: ${OUTPUT_DIR}"
|
|
echo "==> Contents:"
|
|
ls -la "${OUTPUT_DIR}"
|