feat(staging): implement ephemeral staging VM provisioning and TrueNAS snapshot clone workflow

This commit is contained in:
Tibo De Peuter 2026-07-17 22:15:28 +02:00
parent 2386e1e942
commit ac224b7294
Signed by: tdpeuter
SSH key fingerprint: SHA256:u/h/LVoqKF1Iz02uOyxe6hcjmoZASCGV2HM0TG9ZMoU
4 changed files with 232 additions and 0 deletions

View file

@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
# This script creates a ZFS snapshot of a production dataset and clones it for a staging environment.
# Required environment variables:
# TRUENAS_IP: The IP address of the TrueNAS scale instance
# TRUENAS_API_KEY: The API token for TrueNAS
# POOL_NAME: The name of the ZFS pool (e.g., "tank")
# SOURCE_DATASET: The name of the production dataset (e.g., "apps/jellyfin")
# PR_NUMBER: The Pull Request number
if [[ -z "${TRUENAS_IP:-}" || -z "${TRUENAS_API_KEY:-}" || -z "${POOL_NAME:-}" || -z "${SOURCE_DATASET:-}" || -z "${PR_NUMBER:-}" ]]; then
echo "Error: Missing required environment variables."
exit 1
fi
BASE_URL="http://${TRUENAS_IP}/api/v2.0"
HEADERS=(
"-H" "Authorization: Bearer ${TRUENAS_API_KEY}"
"-H" "Content-Type: application/json"
)
DATASET_ID="${POOL_NAME}/${SOURCE_DATASET}"
SNAPSHOT_NAME="pr-${PR_NUMBER}-base"
CLONE_DATASET_NAME="${SOURCE_DATASET}-pr-${PR_NUMBER}"
echo "1. Creating snapshot of ${DATASET_ID} @ ${SNAPSHOT_NAME}..."
curl -s -X POST "${BASE_URL}/zfs/snapshot" "${HEADERS[@]}" \
-d "{\"dataset\": \"${DATASET_ID}\", \"name\": \"${SNAPSHOT_NAME}\"}" > /dev/null
echo "2. Cloning snapshot to ${POOL_NAME}/${CLONE_DATASET_NAME}..."
curl -s -X POST "${BASE_URL}/zfs/snapshot/clone" "${HEADERS[@]}" \
-d "{\"snapshot\": \"${DATASET_ID}@${SNAPSHOT_NAME}\", \"dataset_dst\": \"${CLONE_DATASET_NAME}\"}" > /dev/null
echo "Staging dataset cloned successfully."

View file

@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail
# This script destroys the ephemeral staging ZFS clone and the base snapshot.
# Required environment variables:
# TRUENAS_IP: The IP address of the TrueNAS scale instance
# TRUENAS_API_KEY: The API token for TrueNAS
# POOL_NAME: The name of the ZFS pool (e.g., "tank")
# SOURCE_DATASET: The name of the production dataset (e.g., "apps/jellyfin")
# PR_NUMBER: The Pull Request number
if [[ -z "${TRUENAS_IP:-}" || -z "${TRUENAS_API_KEY:-}" || -z "${POOL_NAME:-}" || -z "${SOURCE_DATASET:-}" || -z "${PR_NUMBER:-}" ]]; then
echo "Error: Missing required environment variables."
exit 1
fi
BASE_URL="http://${TRUENAS_IP}/api/v2.0"
HEADERS=(
"-H" "Authorization: Bearer ${TRUENAS_API_KEY}"
"-H" "Content-Type: application/json"
)
DATASET_ID="${POOL_NAME}/${SOURCE_DATASET}"
SNAPSHOT_NAME="pr-${PR_NUMBER}-base"
CLONE_DATASET_ID="${POOL_NAME}/${SOURCE_DATASET}-pr-${PR_NUMBER}"
# TrueNAS API requires the ID to be URL-encoded for dataset deletion
# URL encoding helper (replace / with %2F)
ENCODED_CLONE_ID="${CLONE_DATASET_ID//\//%2F}"
ENCODED_SNAPSHOT_ID="${DATASET_ID}@${SNAPSHOT_NAME}"
ENCODED_SNAPSHOT_ID="${ENCODED_SNAPSHOT_ID//\//%2F}"
echo "1. Destroying staging clone ${CLONE_DATASET_ID}..."
curl -s -X DELETE "${BASE_URL}/zfs/dataset/id/${ENCODED_CLONE_ID}" "${HEADERS[@]}" > /dev/null
echo "2. Destroying base snapshot ${DATASET_ID}@${SNAPSHOT_NAME}..."
curl -s -X DELETE "${BASE_URL}/zfs/snapshot/id/${ENCODED_SNAPSHOT_ID}" "${HEADERS[@]}" > /dev/null
echo "Staging dataset and snapshot cleaned up successfully."