204 lines
7.7 KiB
Bash
Executable File
204 lines
7.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Exercises the registry through an isolated Compose project. An optional WORKSPACE_GIT_REMOTE
|
|
# is contacted read-only as a connectivity preflight; all pull/fallback mutations target a fresh
|
|
# temporary bare repository so this smoke test can never alter an operator's shared registry.
|
|
set -euo pipefail
|
|
|
|
root="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
tmp="$(mktemp -d "${TMPDIR:-/tmp}/thoth-workspace-registry-smoke.XXXXXX")"
|
|
tmp_slug="$(basename "$tmp" | tr '[:upper:]._' '[:lower:]--' | tr -cd 'a-z0-9-')"
|
|
project="thoth-workspace-registry-smoke-${tmp_slug}-$$"
|
|
image="thothii-workspace-registry-smoke:${project}"
|
|
remote="$tmp/remote.git"
|
|
seed="$tmp/seed"
|
|
branch="workspace-registry-smoke"
|
|
core_remote="/fixtures/remote.git"
|
|
|
|
cleanup_smoke_image() {
|
|
docker image rm -f "$image" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
workspace_registry_smoke_leftovers() {
|
|
{
|
|
docker ps -a --filter "label=com.docker.compose.project=$project" -q
|
|
docker volume ls --filter "label=com.docker.compose.project=$project" -q
|
|
docker network ls --filter "label=com.docker.compose.project=$project" -q
|
|
docker image inspect --format '{{.Id}}' "$image" 2>/dev/null || true
|
|
} | sed '/^$/d'
|
|
}
|
|
|
|
workspace_registry_smoke_self_test_image_cleanup_identity() {
|
|
local calls exact_image foreign_project foreign_tag leftovers
|
|
calls="$(mktemp "${TMPDIR:-/tmp}/thoth-workspace-registry-smoke-image-contract.XXXXXX")"
|
|
project="thoth-workspace-registry-smoke-selftest-123"
|
|
exact_image="thothii-workspace-registry-smoke:${project}"
|
|
foreign_project="thothii-workspace-registry-smoke:thoth-workspace-registry-smoke-foreign-456"
|
|
foreign_tag="thothii-workspace-registry-smoke:local"
|
|
image="$exact_image"
|
|
|
|
docker() {
|
|
printf '%s\n' "docker $*" >>"$calls"
|
|
case "$1 $2" in
|
|
"image rm")
|
|
[[ "$3" == "-f" ]] || return 41
|
|
[[ "$4" == "$exact_image" ]] || return 42
|
|
return 0
|
|
;;
|
|
"image inspect")
|
|
[[ "$3" == "--format" ]] || return 43
|
|
[[ "$5" == "$exact_image" ]] || return 44
|
|
return 1
|
|
;;
|
|
"ps -a"|"volume ls"|"network ls")
|
|
[[ "$*" == *"label=com.docker.compose.project=$project"* ]] || return 45
|
|
return 0
|
|
;;
|
|
*)
|
|
return 46
|
|
;;
|
|
esac
|
|
}
|
|
|
|
cleanup_smoke_image
|
|
leftovers="$(workspace_registry_smoke_leftovers)"
|
|
[[ -z "$leftovers" ]] || {
|
|
echo "self-test observed leftovers for the per-run image" >&2
|
|
printf '%s\n' "$leftovers" >&2
|
|
return 1
|
|
}
|
|
grep -Fq "docker image rm -f $exact_image" "$calls" \
|
|
|| { echo "self-test did not remove the exact per-run image reference" >&2; return 1; }
|
|
if grep -Fq "$foreign_project" "$calls" || grep -Fq "$foreign_tag" "$calls"; then
|
|
echo "self-test cleanup touched a foreign workspace-registry smoke image reference" >&2
|
|
return 1
|
|
fi
|
|
echo "workspace registry smoke image cleanup identity self-test passed"
|
|
}
|
|
|
|
if [[ "${WORKSPACE_REGISTRY_SMOKE_SELF_TEST:-}" == "image-cleanup-identity" ]]; then
|
|
workspace_registry_smoke_self_test_image_cleanup_identity
|
|
exit 0
|
|
fi
|
|
|
|
cleanup() {
|
|
local cleanup_status=$?
|
|
compose down --volumes --remove-orphans >/dev/null 2>&1 || true
|
|
cleanup_smoke_image
|
|
if [[ "$cleanup_status" -eq 0 ]]; then
|
|
local leftovers
|
|
leftovers="$(workspace_registry_smoke_leftovers)"
|
|
if [[ -n "$leftovers" ]]; then
|
|
echo "workspace registry cleanup left owned Docker resources:" >&2
|
|
printf '%s\n' "$leftovers" >&2
|
|
cleanup_status=1
|
|
else
|
|
echo "workspace registry cleanup proof: no compose containers, volumes, networks, or image remain for $project."
|
|
fi
|
|
fi
|
|
rm -rf "$tmp"
|
|
exit "$cleanup_status"
|
|
}
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
compose() {
|
|
docker compose --project-name "$project" -f - "$@" <<EOF
|
|
services:
|
|
core:
|
|
build:
|
|
context: $root
|
|
dockerfile: docker/core.Dockerfile
|
|
image: $image
|
|
environment:
|
|
HOST: 0.0.0.0
|
|
PORT: "8787"
|
|
AUTH_MODE: none
|
|
THT_HARNESS_DIR: /app/harness
|
|
THT_BIN: /opt/venv/bin/tht
|
|
SETTINGS_FILE: /tmp/settings.json
|
|
THT_WORKSPACE_REGISTRY_ROOT: /data/workspace-registry
|
|
THT_WORKSPACE_GIT_REMOTE: $core_remote
|
|
THT_WORKSPACE_GIT_BRANCH: $branch
|
|
THT_WORKSPACE_INSTALLATION_ID: smoke
|
|
THT_WORKSPACE_SECRET_ROOTS: /run/secrets
|
|
volumes:
|
|
- workspace-registry:/data/workspace-registry
|
|
- $remote:/fixtures/remote.git:ro
|
|
volumes:
|
|
workspace-registry: {}
|
|
EOF
|
|
}
|
|
|
|
wait_for_core() {
|
|
local attempt
|
|
for attempt in $(seq 1 30); do
|
|
if compose exec -T core curl -fsS http://127.0.0.1:8787/health >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
compose logs core >&2 || true
|
|
return 1
|
|
}
|
|
|
|
if [[ -n "${WORKSPACE_GIT_REMOTE:-}" ]]; then
|
|
echo "== Read-only Git remote preflight =="
|
|
git ls-remote --heads "$WORKSPACE_GIT_REMOTE" >/dev/null
|
|
fi
|
|
|
|
echo "== Seed isolated workspace registry =="
|
|
git init --bare --initial-branch=main "$remote" >/dev/null
|
|
git clone "$remote" "$seed" >/dev/null
|
|
git -C "$seed" checkout -b "$branch" >/dev/null
|
|
npm --prefix "$root/backend" run build >/dev/null
|
|
node "$root/backend/dist/workspaces/migrate-legacy.js" \
|
|
--input "$root/harness/workspaces/local.yaml" --output "$seed" --collection local >/dev/null
|
|
git -C "$seed" add workspaces/local.yaml
|
|
git -C "$seed" -c user.name='Workspace Registry Smoke' -c user.email='workspace-registry-smoke@example.invalid' \
|
|
commit -m 'Seed workspace registry smoke' >/dev/null
|
|
git -C "$seed" push origin "HEAD:$branch" >/dev/null
|
|
|
|
echo "== Build and start isolated Compose core =="
|
|
compose up -d --build
|
|
wait_for_core
|
|
initial_status="$(compose exec -T core curl -fsS http://127.0.0.1:8787/workspace-registry/status)"
|
|
printf '%s' "$initial_status" | grep -Eq '"head":"[0-9a-f]{40}"'
|
|
compose exec -T core test -f /data/workspace-registry/state/active.json
|
|
|
|
echo "== Recreate offline and prove registry-volume fallback =="
|
|
core_remote="/fixtures/offline.git"
|
|
compose up -d --force-recreate
|
|
wait_for_core
|
|
recreated_status="$(compose exec -T core curl -fsS http://127.0.0.1:8787/workspace-registry/status)"
|
|
initial_head="$(printf '%s' "$initial_status" | sed -n 's/.*"head":"\([0-9a-f]*\)".*/\1/p')"
|
|
recreated_head="$(printf '%s' "$recreated_status" | sed -n 's/.*"head":"\([0-9a-f]*\)".*/\1/p')"
|
|
test -n "$initial_head" && test "$initial_head" = "$recreated_head"
|
|
printf '%s' "$recreated_status" | grep -Fq '"degraded":true'
|
|
compose exec -T core curl -fsS http://127.0.0.1:8787/workspaces | grep -Fq 'Local'
|
|
|
|
echo "== Pull a valid remote update =="
|
|
sed -i.bak 's/name: Local/name: Local Updated/' "$seed/workspaces/local.yaml"
|
|
rm "$seed/workspaces/local.yaml.bak"
|
|
git -C "$seed" add workspaces/local.yaml
|
|
git -C "$seed" -c user.name='Workspace Registry Smoke' -c user.email='workspace-registry-smoke@example.invalid' \
|
|
commit -m 'Update workspace registry smoke' >/dev/null
|
|
git -C "$seed" push origin "HEAD:$branch" >/dev/null
|
|
core_remote="/fixtures/remote.git"
|
|
compose up -d --force-recreate
|
|
wait_for_core
|
|
compose exec -T core curl -fsS -X POST http://127.0.0.1:8787/workspace-registry/pull | grep -Eq '"head":"[0-9a-f]{40}"'
|
|
compose exec -T core curl -fsS http://127.0.0.1:8787/workspaces | grep -Fq 'Local Updated'
|
|
|
|
echo "== Reject invalid remote content and retain the last valid snapshot =="
|
|
printf '%s\n' 'workspace: invalid' >"$seed/workspaces/local.yaml"
|
|
git -C "$seed" add workspaces/local.yaml
|
|
git -C "$seed" -c user.name='Workspace Registry Smoke' -c user.email='workspace-registry-smoke@example.invalid' \
|
|
commit -m 'Invalid workspace registry smoke fixture' >/dev/null
|
|
git -C "$seed" push origin "HEAD:$branch" >/dev/null
|
|
if compose exec -T core curl -fsS -X POST http://127.0.0.1:8787/workspace-registry/pull >/dev/null; then
|
|
echo "registry accepted invalid remote workspace content" >&2
|
|
exit 1
|
|
fi
|
|
compose exec -T core curl -fsS http://127.0.0.1:8787/workspaces | grep -Fq 'Local Updated'
|
|
|
|
echo "workspace registry smoke passed"
|