Add non-deploy tooling: validate stacks, print plan, Makefile targets (bootstrap|validate|plan)
This commit is contained in:
12
Makefile
Normal file
12
Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
SHELL := /usr/bin/bash
|
||||
|
||||
.PHONY: bootstrap validate plan
|
||||
|
||||
bootstrap:
|
||||
bash stacks/scripts/bootstrap.sh
|
||||
|
||||
validate:
|
||||
bash stacks/scripts/validate.sh
|
||||
|
||||
plan:
|
||||
bash stacks/scripts/plan.sh
|
||||
36
stacks/scripts/plan.sh
Executable file
36
stacks/scripts/plan.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
printf "Planned deployment order (no actions performed):\n\n"
|
||||
cat <<ORDER
|
||||
1) Core
|
||||
- stacks/core/traefik.yml
|
||||
- stacks/databases/postgresql-primary.yml
|
||||
- stacks/databases/mariadb-primary.yml
|
||||
- stacks/databases/redis-cluster.yml
|
||||
|
||||
2) Monitoring
|
||||
- stacks/monitoring/netdata.yml
|
||||
|
||||
3) Apps (wave 1)
|
||||
- stacks/apps/homeassistant.yml
|
||||
- stacks/apps/immich.yml
|
||||
- stacks/apps/nextcloud.yml
|
||||
- stacks/apps/paperless.yml
|
||||
- stacks/apps/jellyfin.yml
|
||||
|
||||
4) Apps (wave 2)
|
||||
- stacks/apps/gitea.yml
|
||||
- stacks/apps/appflowy.yml
|
||||
- stacks/apps/vaultwarden.yml
|
||||
- stacks/apps/adguard.yml
|
||||
|
||||
5) Native/web
|
||||
- stacks/web/caddy.yml
|
||||
- stacks/ai/ollama.yml
|
||||
|
||||
Safety:
|
||||
- Use blue/green: keep legacy running until switchover verified.
|
||||
- DB: prefer replication options documented in WORLD_CLASS_MIGRATION_TODO.md.
|
||||
- DNS: lower TTL 48h before each cutover.
|
||||
ORDER
|
||||
49
stacks/scripts/validate.sh
Executable file
49
stacks/scripts/validate.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
red() { printf "\033[31m%s\033[0m\n" "$*"; }
|
||||
grn() { printf "\033[32m%s\033[0m\n" "$*"; }
|
||||
yel() { printf "\033[33m%s\033[0m\n" "$*"; }
|
||||
|
||||
ROOT_DIR=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
fail=0
|
||||
|
||||
# Validate YAML via docker stack config
|
||||
while IFS= read -r -d '' file; do
|
||||
yel "Validating: $file"
|
||||
if docker stack config -c "$file" >/dev/null 2>&1; then
|
||||
grn "OK: $file"
|
||||
else
|
||||
red "ERROR: docker stack config failed for $file"
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# Check external networks referenced exist
|
||||
nets=$(awk '/networks:/,/^$/' "$file" | awk '/external: true/{print prev}{prev=$1}' | sed 's/://g' | sed 's/^[[:space:]]*//g' | sort -u || true)
|
||||
for n in $nets; do
|
||||
if docker network inspect "$n" >/dev/null 2>&1; then
|
||||
grn " network OK: $n"
|
||||
else
|
||||
yel " network MISSING: $n"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check external secrets referenced exist
|
||||
secs=$(awk '/secrets:/,/^$/' "$file" | awk '/external: true/{print prev}{prev=$1}' | sed 's/://g' | sed 's/^[[:space:]]*//g' | sort -u || true)
|
||||
for s in $secs; do
|
||||
if docker secret inspect "$s" >/dev/null 2>&1; then
|
||||
grn " secret OK: $s"
|
||||
else
|
||||
yel " secret MISSING: $s"
|
||||
fi
|
||||
done
|
||||
|
||||
done < <(find stacks -type f -name "*.yml" -print0 | sort -z)
|
||||
|
||||
# List NFS volumes for operator verification
|
||||
yel "NFS volumes referenced (verify exports exist on omv800.local):"
|
||||
grep -R "device: :/export/" -n stacks || true
|
||||
|
||||
exit $fail
|
||||
Reference in New Issue
Block a user