feat(install): macOS + Windows support
install.sh / uninstall.sh (Linux + macOS): - Detect OS with uname -s; root check is Linux-only (Docker/Podman Desktop on macOS runs rootless, no sudo needed). - Arch detection adds arm64 case for Apple Silicon (uname -m returns "arm64" on macOS, "aarch64" on Linux). - sed_inplace() wrapper handles BSD sed on macOS (requires empty -i suffix). - Fix --image flag to append TRITON_MANAGE_IMAGE rather than sed-replace a line that is commented out in env.template. - uninstall: re-apply --purge-data fixes (rm installer dir, drop interactive prompt, use $RUNTIME for raw cleanup instead of hardcoded podman). install.ps1 / uninstall.ps1 (Windows): - Equivalent logic for Docker Desktop / Podman Desktop on Windows. - Arch via RuntimeInformation.OSArchitecture (X64 → amd64, Arm64 → arm64). - Secrets via RandomNumberGenerator (no openssl dependency). - Parameters: -GatewayHostname, -ManageHostIP, -Image, -NoTls / -PurgeData. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fe90753a37
commit
5cb8ce2f63
4 changed files with 261 additions and 26 deletions
145
manage-server/install.ps1
Normal file
145
manage-server/install.ps1
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#Requires -Version 5.1
|
||||||
|
# install.ps1 — Triton Manage Server installer for Windows.
|
||||||
|
#
|
||||||
|
# Idempotent. Generates secrets on first run, reuses .env afterwards.
|
||||||
|
# Container-based via Docker Desktop or Podman Desktop (auto-detected).
|
||||||
|
# Requires Docker Desktop in Linux container mode (the default).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# .\install.ps1
|
||||||
|
#
|
||||||
|
# Parameters (all optional):
|
||||||
|
# -GatewayHostname HOST Agent mTLS hostname (defaults to current FQDN).
|
||||||
|
# -ManageHostIP IP Host LAN IP — used for "+ This machine".
|
||||||
|
# -Image TAG Pin a specific manage-server image tag.
|
||||||
|
# -NoTls Skip the TLS-required sanity check (dev).
|
||||||
|
param(
|
||||||
|
[string]$GatewayHostname = '',
|
||||||
|
[string]$ManageHostIP = '',
|
||||||
|
[string]$Image = '',
|
||||||
|
[switch]$NoTls
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
Set-Location $ScriptDir
|
||||||
|
|
||||||
|
function Write-Info([string]$msg) { Write-Host "[manage-server] $msg" }
|
||||||
|
function Write-Die([string]$msg) { Write-Error "[manage-server] error: $msg"; exit 1 }
|
||||||
|
|
||||||
|
# ── architecture detection ───────────────────────────────────────────────
|
||||||
|
$cpuArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
|
||||||
|
$arch = switch ($cpuArch.ToString()) {
|
||||||
|
'X64' { 'amd64' }
|
||||||
|
'Arm64' { 'arm64' }
|
||||||
|
default { Write-Die "unsupported architecture: $cpuArch (supported: X64, Arm64)"; 'unknown' }
|
||||||
|
}
|
||||||
|
Write-Info "architecture: windows/$arch"
|
||||||
|
|
||||||
|
# ── runtime detection ────────────────────────────────────────────────────
|
||||||
|
$composeCmd = $null
|
||||||
|
$runtime = $null
|
||||||
|
|
||||||
|
if (Get-Command docker -ErrorAction SilentlyContinue) {
|
||||||
|
& docker compose version 2>&1 | Out-Null
|
||||||
|
if ($LASTEXITCODE -eq 0) { $composeCmd = @('docker','compose'); $runtime = 'docker' }
|
||||||
|
}
|
||||||
|
if (-not $composeCmd -and (Get-Command podman -ErrorAction SilentlyContinue)) {
|
||||||
|
& podman compose version 2>&1 | Out-Null
|
||||||
|
if ($LASTEXITCODE -eq 0) { $composeCmd = @('podman','compose'); $runtime = 'podman' }
|
||||||
|
}
|
||||||
|
if (-not $composeCmd) {
|
||||||
|
Write-Die "no compose runtime found. Install Docker Desktop or Podman Desktop."
|
||||||
|
}
|
||||||
|
Write-Info "using runtime: $runtime"
|
||||||
|
|
||||||
|
# ── random secret generation ─────────────────────────────────────────────
|
||||||
|
function New-RandomHex([int]$byteCount) {
|
||||||
|
$buf = [byte[]]::new($byteCount)
|
||||||
|
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($buf)
|
||||||
|
return ($buf | ForEach-Object { $_.ToString('x2') }) -join ''
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── .env bootstrap ───────────────────────────────────────────────────────
|
||||||
|
$envFile = Join-Path $ScriptDir '.env'
|
||||||
|
|
||||||
|
if (-not (Test-Path $envFile)) {
|
||||||
|
Write-Info "writing .env from env.template"
|
||||||
|
$template = Join-Path $ScriptDir 'env.template'
|
||||||
|
if (-not (Test-Path $template)) { Write-Die "env.template not found in $ScriptDir" }
|
||||||
|
Copy-Item $template $envFile
|
||||||
|
|
||||||
|
$pgPass = New-RandomHex 24
|
||||||
|
$jwtKey = New-RandomHex 32
|
||||||
|
$workerKey = New-RandomHex 16
|
||||||
|
$vaultKey = New-RandomHex 32
|
||||||
|
|
||||||
|
# Read-modify-write in one pass so line endings are preserved.
|
||||||
|
$content = [System.IO.File]::ReadAllText($envFile)
|
||||||
|
$content = $content -replace '(?m)^POSTGRES_PASSWORD=.*', "POSTGRES_PASSWORD=$pgPass"
|
||||||
|
$content = $content -replace '(?m)^TRITON_MANAGE_JWT_SIGNING_KEY=.*', "TRITON_MANAGE_JWT_SIGNING_KEY=$jwtKey"
|
||||||
|
$content = $content -replace '(?m)^TRITON_MANAGE_WORKER_KEY=.*', "TRITON_MANAGE_WORKER_KEY=$workerKey"
|
||||||
|
$content = $content -replace '(?m)^TRITON_VAULT_KEY=.*', "TRITON_VAULT_KEY=$vaultKey"
|
||||||
|
[System.IO.File]::WriteAllText($envFile, $content)
|
||||||
|
Write-Info "vault key generated (PostgreSQL AES-256-GCM)"
|
||||||
|
|
||||||
|
if ($GatewayHostname) {
|
||||||
|
$content = [System.IO.File]::ReadAllText($envFile)
|
||||||
|
$content = $content -replace '(?m)^TRITON_MANAGE_GATEWAY_HOSTNAME=.*', "TRITON_MANAGE_GATEWAY_HOSTNAME=$GatewayHostname"
|
||||||
|
[System.IO.File]::WriteAllText($envFile, $content)
|
||||||
|
}
|
||||||
|
if ($ManageHostIP) {
|
||||||
|
$content = [System.IO.File]::ReadAllText($envFile)
|
||||||
|
$content = $content -replace '(?m)^TRITON_MANAGE_HOST_IP=.*', "TRITON_MANAGE_HOST_IP=$ManageHostIP"
|
||||||
|
[System.IO.File]::WriteAllText($envFile, $content)
|
||||||
|
}
|
||||||
|
# IMAGE has no placeholder line in env.template — append it directly.
|
||||||
|
if ($Image) {
|
||||||
|
Add-Content $envFile "`nTRITON_MANAGE_IMAGE=$Image"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info ".env created at $envFile"
|
||||||
|
Write-Info " back this up — it contains the JWT signing key, worker key, and vault key"
|
||||||
|
} else {
|
||||||
|
Write-Info "reusing existing .env at $envFile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── start ────────────────────────────────────────────────────────────────
|
||||||
|
Write-Info "starting containers..."
|
||||||
|
& $composeCmd[0] $composeCmd[1] --env-file $envFile up -d
|
||||||
|
if ($LASTEXITCODE -ne 0) { Write-Die "compose up failed (exit $LASTEXITCODE)" }
|
||||||
|
|
||||||
|
# ── wait for health ──────────────────────────────────────────────────────
|
||||||
|
$portLine = Get-Content $envFile | Where-Object { $_ -match '^TRITON_MANAGE_HOST_PORT=' } | Select-Object -First 1
|
||||||
|
$hostPort = if ($portLine) { $portLine -replace '^TRITON_MANAGE_HOST_PORT=', '' } else { '8082' }
|
||||||
|
|
||||||
|
Write-Info "waiting for manage server to become healthy on :$hostPort..."
|
||||||
|
$up = $false
|
||||||
|
for ($i = 1; $i -le 30; $i++) {
|
||||||
|
try {
|
||||||
|
$resp = Invoke-WebRequest "http://localhost:$hostPort/" -MaximumRedirection 0 -UseBasicParsing -ErrorAction Stop
|
||||||
|
if ($resp.StatusCode -in @(200, 302)) { $up = $true; break }
|
||||||
|
} catch [System.Net.WebException] {
|
||||||
|
$code = [int]$_.Exception.Response.StatusCode
|
||||||
|
if ($code -in @(200, 302)) { $up = $true; break }
|
||||||
|
} catch { <# connection refused — keep polling #> }
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($up) {
|
||||||
|
Write-Info "manage server is up"
|
||||||
|
} else {
|
||||||
|
Write-Info "warning: health check timed out — check logs with: $($composeCmd -join ' ') logs manage-server"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info ""
|
||||||
|
Write-Info "Installation complete. Next steps:"
|
||||||
|
Write-Info " 1. Open http://localhost:$hostPort (or your public URL)"
|
||||||
|
Write-Info " 2. Complete the setup wizard:"
|
||||||
|
Write-Info " - Set your manage server name"
|
||||||
|
Write-Info " - Enter your Triton licence server URL and licence ID"
|
||||||
|
Write-Info " - Or upload an air-gap licence file"
|
||||||
|
Write-Info " 3. Configure TLS via reverse proxy (see docs)"
|
||||||
|
Write-Info ""
|
||||||
|
|
@ -4,8 +4,12 @@
|
||||||
# Idempotent. Generates secrets on first run, reuses .env afterwards.
|
# Idempotent. Generates secrets on first run, reuses .env afterwards.
|
||||||
# Container-based via Podman or Docker (auto-detected).
|
# Container-based via Podman or Docker (auto-detected).
|
||||||
#
|
#
|
||||||
|
# Supported: Linux (amd64/arm64), macOS (Intel/Apple Silicon).
|
||||||
|
# For Windows use install.ps1 instead.
|
||||||
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# sudo bash install.sh
|
# Linux: sudo bash install.sh
|
||||||
|
# macOS: bash install.sh
|
||||||
#
|
#
|
||||||
# Flags (all optional):
|
# Flags (all optional):
|
||||||
# --gateway-hostname HOST Agent mTLS hostname (defaults to current FQDN).
|
# --gateway-hostname HOST Agent mTLS hostname (defaults to current FQDN).
|
||||||
|
|
@ -20,6 +24,14 @@ cd "$SCRIPT_DIR"
|
||||||
info() { printf '[manage-server] %s\n' "$*"; }
|
info() { printf '[manage-server] %s\n' "$*"; }
|
||||||
die() { printf '[manage-server] error: %s\n' "$*" >&2; exit 1; }
|
die() { printf '[manage-server] error: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Portable in-place sed: BSD sed (macOS) requires an explicit empty backup suffix.
|
||||||
|
OS=$(uname -s)
|
||||||
|
if [[ "$OS" == "Darwin" ]]; then
|
||||||
|
sed_inplace() { sed -i '' "$@"; }
|
||||||
|
else
|
||||||
|
sed_inplace() { sed -i "$@"; }
|
||||||
|
fi
|
||||||
|
|
||||||
# ── arg parsing ──────────────────────────────────────────────────────────
|
# ── arg parsing ──────────────────────────────────────────────────────────
|
||||||
GATEWAY_HOST=""
|
GATEWAY_HOST=""
|
||||||
HOST_IP=""
|
HOST_IP=""
|
||||||
|
|
@ -36,15 +48,17 @@ while [[ $# -gt 0 ]]; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[[ $EUID -eq 0 ]] || die "must run as root"
|
# Docker/Podman Desktop on macOS runs rootless; only Linux needs root.
|
||||||
|
[[ "$OS" != "Linux" || $EUID -eq 0 ]] || die "must run as root (on Linux use: sudo bash install.sh)"
|
||||||
|
|
||||||
# ── architecture detection ───────────────────────────────────────────────
|
# ── architecture detection ───────────────────────────────────────────────
|
||||||
case "$(uname -m)" in
|
case "$(uname -m)" in
|
||||||
x86_64) ARCH=amd64 ;;
|
x86_64) ARCH=amd64 ;;
|
||||||
aarch64) ARCH=arm64 ;;
|
aarch64|arm64) ARCH=arm64 ;; # aarch64 = Linux, arm64 = macOS Apple Silicon
|
||||||
*) die "unsupported architecture: $(uname -m) (supported: x86_64, aarch64)" ;;
|
*) die "unsupported architecture: $(uname -m) (supported: x86_64, aarch64, arm64)" ;;
|
||||||
esac
|
esac
|
||||||
info "architecture: linux/$ARCH"
|
OS_LABEL=$(echo "$OS" | tr '[:upper:]' '[:lower:]')
|
||||||
|
info "architecture: ${OS_LABEL}/$ARCH"
|
||||||
|
|
||||||
# ── runtime detection ────────────────────────────────────────────────────
|
# ── runtime detection ────────────────────────────────────────────────────
|
||||||
if command -v podman-compose >/dev/null 2>&1; then
|
if command -v podman-compose >/dev/null 2>&1; then
|
||||||
|
|
@ -57,7 +71,7 @@ elif docker compose version >/dev/null 2>&1; then
|
||||||
COMPOSE=(docker compose)
|
COMPOSE=(docker compose)
|
||||||
RUNTIME=docker
|
RUNTIME=docker
|
||||||
else
|
else
|
||||||
die "no compose runtime found. Install podman-compose or docker compose."
|
die "no compose runtime found. Install Docker Desktop or podman-compose."
|
||||||
fi
|
fi
|
||||||
info "using runtime: $RUNTIME"
|
info "using runtime: $RUNTIME"
|
||||||
|
|
||||||
|
|
@ -73,7 +87,7 @@ if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
WORKER_KEY=$(openssl rand -hex 16)
|
WORKER_KEY=$(openssl rand -hex 16)
|
||||||
VAULT_KEY=$(openssl rand -hex 32)
|
VAULT_KEY=$(openssl rand -hex 32)
|
||||||
|
|
||||||
sed -i \
|
sed_inplace \
|
||||||
-e "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$PG_PASS|" \
|
-e "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$PG_PASS|" \
|
||||||
-e "s|^TRITON_MANAGE_JWT_SIGNING_KEY=.*|TRITON_MANAGE_JWT_SIGNING_KEY=$JWT_KEY|" \
|
-e "s|^TRITON_MANAGE_JWT_SIGNING_KEY=.*|TRITON_MANAGE_JWT_SIGNING_KEY=$JWT_KEY|" \
|
||||||
-e "s|^TRITON_MANAGE_WORKER_KEY=.*|TRITON_MANAGE_WORKER_KEY=$WORKER_KEY|" \
|
-e "s|^TRITON_MANAGE_WORKER_KEY=.*|TRITON_MANAGE_WORKER_KEY=$WORKER_KEY|" \
|
||||||
|
|
@ -81,9 +95,10 @@ if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
"$ENV_FILE"
|
"$ENV_FILE"
|
||||||
info "vault key generated (PostgreSQL AES-256-GCM)"
|
info "vault key generated (PostgreSQL AES-256-GCM)"
|
||||||
|
|
||||||
[[ -n "$GATEWAY_HOST" ]] && sed -i "s|^TRITON_MANAGE_GATEWAY_HOSTNAME=.*|TRITON_MANAGE_GATEWAY_HOSTNAME=$GATEWAY_HOST|" "$ENV_FILE"
|
[[ -n "$GATEWAY_HOST" ]] && sed_inplace "s|^TRITON_MANAGE_GATEWAY_HOSTNAME=.*|TRITON_MANAGE_GATEWAY_HOSTNAME=$GATEWAY_HOST|" "$ENV_FILE"
|
||||||
[[ -n "$HOST_IP" ]] && sed -i "s|^TRITON_MANAGE_HOST_IP=.*|TRITON_MANAGE_HOST_IP=$HOST_IP|" "$ENV_FILE"
|
[[ -n "$HOST_IP" ]] && sed_inplace "s|^TRITON_MANAGE_HOST_IP=.*|TRITON_MANAGE_HOST_IP=$HOST_IP|" "$ENV_FILE"
|
||||||
[[ -n "$IMAGE" ]] && sed -i "s|^TRITON_MANAGE_IMAGE=.*|TRITON_MANAGE_IMAGE=$IMAGE|" "$ENV_FILE"
|
# IMAGE has no placeholder line in env.template — append it directly.
|
||||||
|
[[ -n "$IMAGE" ]] && printf '\nTRITON_MANAGE_IMAGE=%s\n' "$IMAGE" >> "$ENV_FILE"
|
||||||
|
|
||||||
info ".env created at $ENV_FILE"
|
info ".env created at $ENV_FILE"
|
||||||
info " back this up — it contains the JWT signing key, worker key, and vault key"
|
info " back this up — it contains the JWT signing key, worker key, and vault key"
|
||||||
|
|
|
||||||
68
manage-server/uninstall.ps1
Normal file
68
manage-server/uninstall.ps1
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#Requires -Version 5.1
|
||||||
|
# uninstall.ps1 — stop and remove Manage Server containers on Windows.
|
||||||
|
#
|
||||||
|
# By default, KEEPS the PostgreSQL volume (scan history, hosts, users)
|
||||||
|
# and the installer directory (preserves .env secrets for reinstall).
|
||||||
|
# Pass -PurgeData to delete volumes + installer directory — irreversible.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# .\uninstall.ps1 # stop + remove containers, keep DB + .env
|
||||||
|
# .\uninstall.ps1 -PurgeData # also delete DB, volumes, and installer dir
|
||||||
|
param(
|
||||||
|
[switch]$PurgeData
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
Set-Location $ScriptDir
|
||||||
|
|
||||||
|
function Write-Info([string]$msg) { Write-Host "[manage-server] $msg" }
|
||||||
|
function Write-Die([string]$msg) { Write-Error "[manage-server] error: $msg"; exit 1 }
|
||||||
|
|
||||||
|
# ── runtime detection ────────────────────────────────────────────────────
|
||||||
|
$composeCmd = $null
|
||||||
|
$runtime = $null
|
||||||
|
|
||||||
|
if (Get-Command docker -ErrorAction SilentlyContinue) {
|
||||||
|
& docker compose version 2>&1 | Out-Null
|
||||||
|
if ($LASTEXITCODE -eq 0) { $composeCmd = @('docker','compose'); $runtime = 'docker' }
|
||||||
|
}
|
||||||
|
if (-not $composeCmd -and (Get-Command podman -ErrorAction SilentlyContinue)) {
|
||||||
|
& podman compose version 2>&1 | Out-Null
|
||||||
|
if ($LASTEXITCODE -eq 0) { $composeCmd = @('podman','compose'); $runtime = 'podman' }
|
||||||
|
}
|
||||||
|
if (-not $composeCmd) { Write-Die "no compose runtime found" }
|
||||||
|
|
||||||
|
# ── stop containers ──────────────────────────────────────────────────────
|
||||||
|
$envFile = Join-Path $ScriptDir '.env'
|
||||||
|
if (Test-Path $envFile) {
|
||||||
|
Write-Info "stopping containers..."
|
||||||
|
& $composeCmd[0] $composeCmd[1] --env-file $envFile down
|
||||||
|
} else {
|
||||||
|
Write-Info ".env not found, attempting raw container cleanup..."
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $runtime rm -f triton-manageserver triton-manage-db 2>$null
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── purge ────────────────────────────────────────────────────────────────
|
||||||
|
if ($PurgeData) {
|
||||||
|
Write-Info "DESTRUCTIVE: removing manage server volumes..."
|
||||||
|
Write-Info " this deletes: scan history, hosts, users, worker binaries"
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
foreach ($vol in @('triton-manage-db-data', 'triton-manage-bins')) {
|
||||||
|
& $runtime volume rm -f $vol 2>$null
|
||||||
|
}
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
Write-Info " volumes removed"
|
||||||
|
Write-Info " removing installer directory $ScriptDir..."
|
||||||
|
Remove-Item -Recurse -Force $ScriptDir
|
||||||
|
Write-Info " installer directory removed"
|
||||||
|
} else {
|
||||||
|
Write-Info "DB + bins volumes retained (run with -PurgeData to delete)"
|
||||||
|
Write-Info ".env preserved at $envFile — secrets reused on reinstall"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info "uninstall complete"
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# uninstall.sh — stop and remove Manage Server containers.
|
# uninstall.sh — stop and remove Manage Server containers.
|
||||||
#
|
#
|
||||||
# By default, KEEPS the PostgreSQL volume (scan history, hosts, users).
|
# By default, KEEPS the PostgreSQL volume (scan history, hosts, users)
|
||||||
# Pass --purge-data to delete the volumes as well — irreversible.
|
# and the installer directory (preserves .env secrets for reinstall).
|
||||||
|
# Pass --purge-data to delete volumes + installer directory — irreversible.
|
||||||
|
#
|
||||||
|
# Supported: Linux (amd64/arm64), macOS (Intel/Apple Silicon).
|
||||||
|
# For Windows use uninstall.ps1 instead.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# sudo bash uninstall.sh # stop + remove containers, keep DB
|
# Linux: sudo bash uninstall.sh # stop + remove containers, keep DB + .env
|
||||||
# sudo bash uninstall.sh --purge-data # also delete DB + binaries volume
|
# Linux: sudo bash uninstall.sh --purge-data # also delete DB, volumes, and installer dir
|
||||||
|
# macOS: bash uninstall.sh [--purge-data]
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||||
|
|
@ -15,7 +20,10 @@ cd "$SCRIPT_DIR"
|
||||||
info() { printf '[manage-server] %s\n' "$*"; }
|
info() { printf '[manage-server] %s\n' "$*"; }
|
||||||
die() { printf '[manage-server] error: %s\n' "$*" >&2; exit 1; }
|
die() { printf '[manage-server] error: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
[[ $EUID -eq 0 ]] || die "must run as root"
|
OS=$(uname -s)
|
||||||
|
|
||||||
|
# Docker/Podman Desktop on macOS runs rootless; only Linux needs root.
|
||||||
|
[[ "$OS" != "Linux" || $EUID -eq 0 ]] || die "must run as root (on Linux use: sudo bash uninstall.sh)"
|
||||||
|
|
||||||
PURGE=0
|
PURGE=0
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
|
|
@ -26,9 +34,9 @@ while [[ $# -gt 0 ]]; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if command -v podman-compose >/dev/null 2>&1; then COMPOSE=(podman-compose)
|
if command -v podman-compose >/dev/null 2>&1; then COMPOSE=(podman-compose); RUNTIME=podman
|
||||||
elif podman compose version >/dev/null 2>&1; then COMPOSE=(podman compose)
|
elif podman compose version >/dev/null 2>&1; then COMPOSE=(podman compose); RUNTIME=podman
|
||||||
elif docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose)
|
elif docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose); RUNTIME=docker
|
||||||
else die "no compose runtime found"; fi
|
else die "no compose runtime found"; fi
|
||||||
|
|
||||||
if [[ -f .env ]]; then
|
if [[ -f .env ]]; then
|
||||||
|
|
@ -36,23 +44,22 @@ if [[ -f .env ]]; then
|
||||||
"${COMPOSE[@]}" --env-file .env down
|
"${COMPOSE[@]}" --env-file .env down
|
||||||
else
|
else
|
||||||
info ".env not found, attempting raw container cleanup..."
|
info ".env not found, attempting raw container cleanup..."
|
||||||
podman rm -f triton-manageserver triton-manage-db 2>/dev/null || true
|
"$RUNTIME" rm -f triton-manageserver triton-manage-db 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $PURGE -eq 1 ]]; then
|
if [[ $PURGE -eq 1 ]]; then
|
||||||
info "DESTRUCTIVE: removing manage server volumes..."
|
info "DESTRUCTIVE: removing manage server volumes..."
|
||||||
info " this deletes: scan history, hosts, users, worker binaries"
|
info " this deletes: scan history, hosts, users, worker binaries"
|
||||||
read -r -p " Are you sure? Type 'yes' to confirm: " CONFIRM
|
|
||||||
[[ "$CONFIRM" == "yes" ]] || die "aborted"
|
|
||||||
for vol in triton-manage-db-data triton-manage-bins; do
|
for vol in triton-manage-db-data triton-manage-bins; do
|
||||||
podman volume rm -f "$vol" 2>/dev/null \
|
"$RUNTIME" volume rm -f "$vol" 2>/dev/null || true
|
||||||
|| docker volume rm -f "$vol" 2>/dev/null \
|
|
||||||
|| true
|
|
||||||
done
|
done
|
||||||
info " volumes removed"
|
info " volumes removed"
|
||||||
info " .env still on disk at $SCRIPT_DIR/.env — delete manually if desired"
|
info " removing installer directory $SCRIPT_DIR..."
|
||||||
|
rm -rf "$SCRIPT_DIR"
|
||||||
|
info " installer directory removed"
|
||||||
else
|
else
|
||||||
info "DB + bins volumes retained (run with --purge-data to delete)"
|
info "DB + bins volumes retained (run with --purge-data to delete)"
|
||||||
|
info ".env preserved at $SCRIPT_DIR/.env — secrets reused on reinstall"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "uninstall complete"
|
info "uninstall complete"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue