diff --git a/deployment/.gitignore b/deployment/.gitignore new file mode 100644 index 0000000..a319647 --- /dev/null +++ b/deployment/.gitignore @@ -0,0 +1 @@ +artifact/ diff --git a/deployment/build/README.md b/deployment/build/README.md new file mode 100644 index 0000000..4ecac62 --- /dev/null +++ b/deployment/build/README.md @@ -0,0 +1,16 @@ +# Deployment Build Toolkit + +This folder provides scripts to produce offline server/client packages and publish the client package to FTP. + +Commands +- build_server_package.sh [--version YYYYMMDD] +- build_client_package.sh [--version YYYYMMDD] +- publish_client.sh --version YYYYMMDD --server --user ftpuser --password [--port 21] + +Outputs +- deployment/artifact/server// +- deployment/artifact/client// + +Notes +- Server package contains docker images (single all-images.tar.gz), compose/, scripts/, docs/, private/ skeleton. +- Client package reuses all-in-one-full artifact, repacked as argus-metric_.tar.gz (compatible with setup.sh). diff --git a/deployment/build/build_client_package.sh b/deployment/build/build_client_package.sh new file mode 100644 index 0000000..1a7124d --- /dev/null +++ b/deployment/build/build_client_package.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +BUILD_DIR="$ROOT_DIR/deployment/build" +ART_ROOT="$ROOT_DIR/deployment/artifact" + +. "$BUILD_DIR/common.sh" + +usage() { cat <<'EOF' +Build Argus Client Offline Package + +Usage: build_client_package.sh [--version YYYYMMDD] [--out DIR] + +Produces: deployment/artifact/client//argus-metric_.tar.gz +EOF +} + +VERSION="$(today_version)" +OUT_DIR="" +while [[ $# -gt 0 ]]; do + case "$1" in + --version) VERSION="$2"; shift 2;; + --out) OUT_DIR="$2"; shift 2;; + -h|--help) usage; exit 0;; + *) err "unknown arg: $1"; usage; exit 1;; + esac +done + +PKG_DIR="${OUT_DIR:-$ART_ROOT/client/$VERSION}" +make_dir "$PKG_DIR" + +log "Packaging client from all-in-one-full artifact" +PLUGIN_DIR="$ROOT_DIR/src/metric/client-plugins/all-in-one-full" +require_cmd bash tar gzip + +(cd "$PLUGIN_DIR" && bash scripts/package_artifact.sh --force) + +# pick latest artifact dir +ART_BASE="$PLUGIN_DIR/artifact" +latest_dir=$(ls -1dt "$ART_BASE"/*/ 2>/dev/null | head -n1 || true) +[[ -n "$latest_dir" ]] || { err "no client artifact found in $ART_BASE"; exit 1; } + +tmpdir=$(mktemp -d) +trap 'rm -rf "$tmpdir"' EXIT +rsync -a "$latest_dir" "$tmpdir/src" >/dev/null 2>&1 || cp -r "$latest_dir" "$tmpdir/src" + +out_name="argus-metric_$(echo "$VERSION" | sed 's/\./_/g').tar.gz" + +(cd "$tmpdir/src" && tar -czf "$PKG_DIR/$out_name" .) + +log "Client package ready: $PKG_DIR/$out_name" +echo "$VERSION" > "$PKG_DIR/LATEST_VERSION" + +exit 0 + diff --git a/deployment/build/build_server_package.sh b/deployment/build/build_server_package.sh new file mode 100644 index 0000000..dc4a6a9 --- /dev/null +++ b/deployment/build/build_server_package.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +BUILD_DIR="$ROOT_DIR/deployment/build" +ART_ROOT="$ROOT_DIR/deployment/artifact" + +. "$BUILD_DIR/common.sh" + +usage() { cat <<'EOF' +Build Argus Server Offline Package + +Usage: build_server_package.sh [--version YYYYMMDD] [--out DIR] [--resave-image] + +Outputs into deployment/artifact/server// by default. +EOF +} + +VERSION="$(today_version)" +OUT_DIR="" +RESAVE_IMAGE=false +while [[ $# -gt 0 ]]; do + case "$1" in + --version) VERSION="$2"; shift 2;; + --out) OUT_DIR="$2"; shift 2;; + --resave-image) RESAVE_IMAGE=true; shift;; + -h|--help) usage; exit 0;; + *) err "unknown arg: $1"; usage; exit 1;; + esac +done + +PKG_DIR="${OUT_DIR:-$ART_ROOT/server/$VERSION}" +STAGE="$(mktemp -d)" +trap 'rm -rf "$STAGE"' EXIT + +log "Version: $VERSION" +log "Staging: $STAGE" + +# 1) Layout +make_dir "$STAGE/images" +make_dir "$STAGE/compose" +make_dir "$STAGE/scripts" +make_dir "$STAGE/docs" +make_dir "$STAGE/private/argus" + +# 2) Compose: derive from sys/tests by removing test-only services +SRC_COMPOSE="$ROOT_DIR/src/sys/tests/docker-compose.yml" +[[ -f "$SRC_COMPOSE" ]] || { err "missing $SRC_COMPOSE"; exit 1; } +awk -f "$BUILD_DIR/templates/docker-compose.filter.awk" -v remove="node-a,node-b,test-node,test-gpu-node" "$SRC_COMPOSE" > "$STAGE/compose/docker-compose.yml" +cp "$BUILD_DIR/templates/.env.example" "$STAGE/compose/.env.example" + +# 3) Images (reuse if already exported unless --resave-image) +existing_images_tar="$PKG_DIR/images/all-images.tar.gz" +if [[ "$RESAVE_IMAGE" == false && -f "$existing_images_tar" ]]; then + log "Reusing existing images tar: $existing_images_tar" + cp "$existing_images_tar" "$STAGE/images/" +else + require_cmd docker gzip + images=( + argus-bind9:latest + argus-master:latest + argus-elasticsearch:latest + argus-kibana:latest + argus-metric-ftp:latest + argus-metric-prometheus:latest + argus-metric-grafana:latest + argus-alertmanager:latest + argus-web-frontend:latest + argus-web-proxy:latest + ) + log "Saving images: ${#images[@]}" + tarfile="$STAGE/images/all-images.tar" + docker save -o "$tarfile" "${images[@]}" + gzip -f "$tarfile" +fi + +# 4) Scripts & Docs +copy_tree "$BUILD_DIR/templates/scripts" "$STAGE/scripts" +cat > "$STAGE/docs/INSTALL_SERVER.md" << 'MD' +# Argus Server Offline Installation + +## Prerequisites +- Ubuntu 22.04 x86_64 +- Docker & Docker Compose installed +- Open ports: 32300,9200,5601,9090,9093,8080..8085,21,20,21100-21110 (or auto-fallback to high ports) + +## Steps +1. Extract this package to /opt/argus-deploy/versions/ +2. cd scripts && sudo ./server-install.sh +3. Check status: ./server-status.sh +4. Uninstall: ./server-uninstall.sh + +## Notes +- Selfcheck result is written to logs/selfcheck.json +- DNS will be managed by internal bind; FTP dns.conf is auto-published to share/dns.conf +MD + +# 5) Manifests +gen_manifest "$STAGE" "$STAGE/manifest.txt" +checksum_dir "$STAGE" "$STAGE/checksums.txt" + +# 6) Move to artifact +make_dir "$PKG_DIR" +rsync -a "$STAGE/" "$PKG_DIR/" 2>/dev/null || cp -r "$STAGE/." "$PKG_DIR/" +log "Server package ready: $PKG_DIR" + +echo "$VERSION" > "$PKG_DIR/version.json" + +# 7) Create distributable tarball +OUT_TAR_DIR="$(dirname "$PKG_DIR")" +OUT_TAR="$OUT_TAR_DIR/server_${VERSION}.tar.gz" +log "Creating tarball: $OUT_TAR" +(cd "$PKG_DIR/.." && tar -czf "$OUT_TAR" "$(basename "$PKG_DIR")") +log "Tarball ready: $OUT_TAR" + +exit 0 diff --git a/deployment/build/common.sh b/deployment/build/common.sh new file mode 100644 index 0000000..7bb3fb0 --- /dev/null +++ b/deployment/build/common.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +log() { echo -e "\033[0;34m[INFO]\033[0m $*"; } +warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; } +err() { echo -e "\033[0;31m[ERR ]\033[0m $*" >&2; } + +require_cmd() { + for c in "$@"; do + command -v "$c" >/dev/null 2>&1 || { err "missing command: $c"; exit 1; } + done +} + +today_version() { + date +%Y%m%d +} + +checksum_dir() { + local dir="$1"; local out="$2"; : > "$out"; + (cd "$dir" && find . -type f -print0 | sort -z | xargs -0 sha256sum) >> "$out" +} + +make_dir() { mkdir -p "$1"; } + +copy_tree() { + local src="$1" dst="$2"; rsync -a --delete "$src/" "$dst/" 2>/dev/null || cp -r "$src/." "$dst/"; +} + +gen_manifest() { + local root="$1"; local out="$2"; : > "$out"; + (cd "$root" && find . -maxdepth 3 -type f -printf "%p\n" | sort) >> "$out" +} + diff --git a/deployment/build/publish_client.sh b/deployment/build/publish_client.sh new file mode 100644 index 0000000..cfd896f --- /dev/null +++ b/deployment/build/publish_client.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +. "$ROOT_DIR/build/common.sh" + +usage() { cat <<'EOF' +Publish client package to FTP server + +Usage: publish_client.sh --version YYYYMMDD --server HOST --user USER --password PASS [--port 21] + +It uploads: setup.sh, argus-metric_.tar.gz, LATEST_VERSION to the FTP share root. +EOF +} + +VERSION=""; HOST=""; USERNAME=""; PASSWORD=""; PORT=21 +while [[ $# -gt 0 ]]; do + case "$1" in + --version) VERSION="$2"; shift 2;; + --server) HOST="$2"; shift 2;; + --user) USERNAME="$2"; shift 2;; + --password) PASSWORD="$2"; shift 2;; + --port) PORT="$2"; shift 2;; + -h|--help) usage; exit 0;; + *) err "unknown arg: $1"; usage; exit 1;; + esac +done + +[[ -n "$VERSION" && -n "$HOST" && -n "$USERNAME" && -n "$PASSWORD" ]] || { usage; exit 1; } + +CLIENT_DIR="$ROOT_DIR/artifact/client/$VERSION" +TAR_NAME="argus-metric_${VERSION}.tar.gz" +PKG="$CLIENT_DIR/$TAR_NAME" +SETUP_SRC="$ROOT_DIR/../src/sys/tests/private/argus/metric/ftp/share/setup.sh" +ALT_SETUP="$ROOT_DIR/../src/metric/client-plugins/all-in-one-full/scripts/setup.sh" + +[[ -f "$PKG" ]] || { err "missing client package: $PKG"; exit 1; } +if [[ ! -f "$SETUP_SRC" ]]; then + if [[ -f "$ALT_SETUP" ]]; then + SETUP_SRC="$ALT_SETUP" + else + err "missing setup.sh (checked $SETUP_SRC and $ALT_SETUP)"; exit 1 + fi +fi + +log "Uploading setup.sh" +curl -u "$USERNAME:$PASSWORD" -sfT "$SETUP_SRC" "ftp://$HOST:$PORT/setup.sh" + +log "Uploading client tar: $TAR_NAME" +curl -u "$USERNAME:$PASSWORD" -sfT "$PKG" "ftp://$HOST:$PORT/$TAR_NAME" + +log "Updating LATEST_VERSION -> $VERSION" +printf "%s" "$VERSION" | curl -u "$USERNAME:$PASSWORD" -sfT - "ftp://$HOST:$PORT/LATEST_VERSION" + +log "Publish done" + +exit 0 diff --git a/deployment/build/templates/.env.example b/deployment/build/templates/.env.example new file mode 100644 index 0000000..0af566c --- /dev/null +++ b/deployment/build/templates/.env.example @@ -0,0 +1,29 @@ +# UID/GID for service processes +ARGUS_BUILD_UID=1000 +ARGUS_BUILD_GID=1000 + +# Host ports (adjust if occupied) +MASTER_PORT=32300 +ES_HTTP_PORT=9200 +KIBANA_PORT=5601 +NODE_A_PORT=2020 +NODE_B_PORT=2021 +PROMETHEUS_PORT=9090 +GRAFANA_PORT=3000 +ALERTMANAGER_PORT=9093 +WEB_PROXY_PORT_8080=8080 +WEB_PROXY_PORT_8081=8081 +WEB_PROXY_PORT_8082=8082 +WEB_PROXY_PORT_8083=8083 +WEB_PROXY_PORT_8084=8084 +WEB_PROXY_PORT_8085=8085 + +# FTP +FTP_PORT=21 +FTP_DATA_PORT=20 +FTP_PASSIVE_HOST_RANGE=21100-21110 +FTP_PASSWORD=ZGClab1234! +FTP_DOMAIN=ftp.metric.argus.com + +# GPU profile disabled by default +ENABLE_GPU=false diff --git a/deployment/build/templates/docker-compose.filter.awk b/deployment/build/templates/docker-compose.filter.awk new file mode 100644 index 0000000..ae2c7f4 --- /dev/null +++ b/deployment/build/templates/docker-compose.filter.awk @@ -0,0 +1,41 @@ +#!/usr/bin/awk -f +# Remove specific service blocks from a docker-compose.yml by service name. +# Usage: awk -f docker-compose.filter.awk -v remove="node-a,node-b,test-node,test-gpu-node" input.yml > output.yml + +BEGIN{ + split(remove, rm, ","); + for(i in rm) skipname[rm[i]] = 1; +} + +function starts_service_line(line, name) { + if (match(line, /^\s{2}([a-zA-Z0-9_-]+):\s*$/, m)) { + name = m[1]; + return name; + } + return ""; +} + +{ + name = starts_service_line($0); + if (name != "") { + # detect top-level keys (networks:, services:, etc.) + if ($0 ~ /^services:\s*$/) { in_services=1; print; next; } + if ($0 ~ /^[a-zA-Z0-9_-]+:\s*$/ && $0 !~ /^\s/) { + in_services= ($0 ~ /^services:\s*$/); + } + + if (in_services && (name in skipname)) { + skipping=1; next; + } + } + + # end skipping when next top-level service appears + if (skipping) { + if (starts_service_line($0) != "") { skipping=0; } + else if ($0 ~ /^(networks|volumes):\s*$/) { skipping=0; } + else { next; } + } + + print; +} + diff --git a/deployment/build/templates/scripts/server-install.sh b/deployment/build/templates/scripts/server-install.sh new file mode 100644 index 0000000..8605031 --- /dev/null +++ b/deployment/build/templates/scripts/server-install.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PKG_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # version root + +PROJECT_NAME="argus-sys" + +log() { echo -e "\033[0;34m[INSTALL]\033[0m $*"; } +err() { echo -e "\033[0;31m[ERROR ]\033[0m $*" >&2; } + +require() { command -v "$1" >/dev/null 2>&1 || { err "missing command: $1"; exit 1; }; } + +require docker +if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); else require docker-compose; COMPOSE=(docker-compose); fi + +ENV_FILE="$PKG_ROOT/compose/.env" +ENV_TEMPLATE="$PKG_ROOT/compose/.env.example" + +find_free_port() { + local prefer="$1"; local start=${2:-20000}; local max=${3:-65000}; + if ! ss -ltnH 2>/dev/null | awk -v pat=":"$prefer"$" '$4 ~ pat{f=1} END{exit f?0:1}'; then echo "$prefer"; return; fi + for ((p=start; p<=max; p++)); do + if ! ss -ltnH 2>/dev/null | awk -v pat=":"$p"$" '$4 ~ pat{f=1} END{exit f?0:1}'; then echo "$p"; return; fi + done + return 1 +} + +prepare_env() { + if [[ -f "$ENV_FILE" ]]; then log ".env exists, keep as-is"; return; fi + [[ -f "$ENV_TEMPLATE" ]] || { err "missing $ENV_TEMPLATE"; exit 1; } + cp "$ENV_TEMPLATE" "$ENV_FILE" + # auto-assign ports if busy + for key in MASTER_PORT ES_HTTP_PORT KIBANA_PORT NODE_A_PORT NODE_B_PORT PROMETHEUS_PORT GRAFANA_PORT ALERTMANAGER_PORT \ + WEB_PROXY_PORT_8080 WEB_PROXY_PORT_8081 WEB_PROXY_PORT_8082 WEB_PROXY_PORT_8083 WEB_PROXY_PORT_8084 WEB_PROXY_PORT_8085 \ + FTP_PORT FTP_DATA_PORT; do + val=$(grep -E "^${key}=" "$ENV_FILE" | tail -1 | cut -d= -f2) + new=$(find_free_port "$val") || true + if [[ -n "${new:-}" && "$new" != "$val" ]]; then + sed -i "s/^${key}=.*/${key}=${new}/" "$ENV_FILE" + log "port ${key} busy -> ${new}" + fi + done +} + +load_images() { + local tar="$PKG_ROOT/images/all-images.tar.gz" + [[ -f "$tar" ]] || { err "missing images tar: $tar"; exit 1; } + log "loading images from $(basename "$tar") (may take minutes)" + gunzip -c "$tar" | docker load >/dev/null +} + +bring_up() { + log "starting services via compose" + (cd "$PKG_ROOT/compose" && "${COMPOSE[@]}" -p "$PROJECT_NAME" up -d) +} + +selfcheck() { + log "running selfcheck" + bash "$PKG_ROOT/scripts/server-selfcheck.sh" || { err "selfcheck failed"; exit 1; } +} + +main() { + prepare_env + load_images + bring_up + selfcheck + log "install completed. See logs in $PKG_ROOT/logs/" +} + +main "$@" + diff --git a/deployment/build/templates/scripts/server-selfcheck.sh b/deployment/build/templates/scripts/server-selfcheck.sh new file mode 100644 index 0000000..145f709 --- /dev/null +++ b/deployment/build/templates/scripts/server-selfcheck.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +log() { echo -e "\033[0;34m[CHECK]\033[0m $*"; } +err() { echo -e "\033[0;31m[ERROR]\033[0m $*" >&2; } + +ENV_FILE="$ROOT/compose/.env"; [[ -f "$ENV_FILE" ]] && set -a && source "$ENV_FILE" && set +a + +wait_http() { local url="$1"; local attempts=${2:-120}; local i=1; while ((i<=attempts)); do curl -fsS "$url" >/dev/null 2>&1 && return 0; echo "[..] waiting $url ($i/$attempts)"; sleep 5; ((i++)); done; return 1; } +code_for() { curl -s -o /dev/null -w "%{http_code}" "$1" || echo 000; } +header_val() { curl -s -D - -o /dev/null "$@" | awk -F': ' 'BEGIN{IGNORECASE=1}$1=="Access-Control-Allow-Origin"{gsub("\r","",$2);print $2}'; } + +mkdir -p "$ROOT/logs" +OUT_JSON="$ROOT/logs/selfcheck.json" +tmp=$(mktemp) + +ok=1 + +log "checking Elasticsearch" +if curl -fsS "http://localhost:${ES_HTTP_PORT:-9200}/_cluster/health?wait_for_status=yellow&timeout=1s" >/dev/null 2>&1; then es_ok=true; else es_ok=false; ok=0; fi + +log "checking Kibana" +kb_code=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${KIBANA_PORT:-5601}/api/status" || echo 000) +kb_ok=false +if [[ "$kb_code" == "200" ]]; then body=$(curl -sS "http://localhost:${KIBANA_PORT:-5601}/api/status"); echo "$body" | grep -q '"level":"available"' && kb_ok=true; fi +[[ "$kb_ok" == true ]] || ok=0 + +log "checking Master" +wait_http "http://localhost:${MASTER_PORT:-32300}/readyz" 60 || ok=0 + +log "checking FTP" +ftp_root="$ROOT/private/argus/metric/ftp/share"; [[ -d "$ftp_root" && -w "$ftp_root" ]] && ftp_ok=true || { ftp_ok=false; ok=0; } + +log "checking Prometheus" +wait_http "http://localhost:${PROMETHEUS_PORT:-9090}/-/ready" 60 || ok=0 + +log "checking Grafana" +gf_code=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${GRAFANA_PORT:-3000}/api/health" || echo 000) +gf_ok=false; if [[ "$gf_code" == "200" ]]; then body=$(curl -sS "http://localhost:${GRAFANA_PORT:-3000}/api/health"); echo "$body" | grep -q '"database"\s*:\s*"ok"' && gf_ok=true; fi +[[ "$gf_ok" == true ]] || ok=0 + +log "checking Alertmanager" +wait_http "http://localhost:${ALERTMANAGER_PORT:-9093}/api/v2/status" 60 || ok=0 + +log "checking Web-Proxy" +p8080=$(code_for "http://localhost:${WEB_PROXY_PORT_8080:-8080}/") +p8083=$(code_for "http://localhost:${WEB_PROXY_PORT_8083:-8083}/") +cors8084=$(header_val -H "Origin: http://localhost:${WEB_PROXY_PORT_8080:-8080}" "http://localhost:${WEB_PROXY_PORT_8084:-8084}/api/v2/status" || true) +cors8085=$(header_val -H "Origin: http://localhost:${WEB_PROXY_PORT_8080:-8080}" "http://localhost:${WEB_PROXY_PORT_8085:-8085}/api/v1/master/nodes" || true) +wp_ok=true +[[ "$p8080" == 200 ]] || wp_ok=false +([[ "$p8083" == 200 || "$p8083" == 302 ]]) || wp_ok=false +[[ -n "$cors8084" && -n "$cors8085" ]] || wp_ok=false +[[ "$wp_ok" == true ]] || ok=0 + +cat > "$tmp" </dev/null 2>&1; then COMPOSE=(docker compose); else COMPOSE=(docker-compose); fi + +echo "== Containers ==" +(cd "$ROOT/compose" && "${COMPOSE[@]}" -p "$PROJECT_NAME" ps) + +echo +echo "== Key Endpoints ==" +ENV_FILE="$ROOT/compose/.env"; [[ -f "$ENV_FILE" ]] && set -a && source "$ENV_FILE" && set +a +printf "master http://localhost:%s/readyz\n" "${MASTER_PORT:-32300}" +printf "es http://localhost:%s/_cluster/health\n" "${ES_HTTP_PORT:-9200}" +printf "kibana http://localhost:%s/api/status\n" "${KIBANA_PORT:-5601}" +printf "prom http://localhost:%s/-/ready\n" "${PROMETHEUS_PORT:-9090}" +printf "grafana http://localhost:%s/api/health\n" "${GRAFANA_PORT:-3000}" +printf "alert http://localhost:%s/api/v2/status\n" "${ALERTMANAGER_PORT:-9093}" +printf "web http://localhost:%s/ (8080)\n" "${WEB_PROXY_PORT_8080:-8080}" + +echo +echo "== Selfcheck result ==" +cat "$ROOT/logs/selfcheck.json" 2>/dev/null || echo "(no selfcheck yet)" + diff --git a/deployment/build/templates/scripts/server-uninstall.sh b/deployment/build/templates/scripts/server-uninstall.sh new file mode 100644 index 0000000..86c7688 --- /dev/null +++ b/deployment/build/templates/scripts/server-uninstall.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PKG_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +PROJECT_NAME="argus-sys" + +log() { echo -e "\033[0;34m[UNINSTALL]\033[0m $*"; } + +if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); else COMPOSE=(docker-compose); fi + +(cd "$PKG_ROOT/compose" && "${COMPOSE[@]}" -p "$PROJECT_NAME" down -v || true) +log "compose stack removed" +log "you may remove data under $PKG_ROOT/private if you want a clean slate" +