Files
incus-backup-ui/deploy/install-agent.sh
T

265 lines
6.7 KiB
Bash
Executable File

#!/bin/sh
set -eu
SERVICE_NAME="incus-backup-agent"
DEFAULT_REPO_URL="https://forgejo.digital-droplets.de/philschlo/incus-backup-ui.git"
INSTALL_DIR="${INSTALL_DIR:-/opt/incus-backup-ui}"
REPO_URL="${INCUS_BACKUP_REPO_URL:-$DEFAULT_REPO_URL}"
BRANCH="${INCUS_BACKUP_BRANCH:-main}"
AGENT_DIR="$INSTALL_DIR/agent"
OLD_AGENT_DIR="$INSTALL_DIR/backend"
STATE_DIR="${STATE_DIR:-/var/lib/incus-backup-agent}"
CONFIG_DIR="${CONFIG_DIR:-/etc/incus-backup-agent}"
ENV_FILE="$AGENT_DIR/.env"
SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service"
log() {
printf '%s\n' "$*"
}
fail() {
printf 'ERROR: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"
}
node_major_version() {
node -p "Number(process.versions.node.split('.')[0])"
}
check_node_version() {
need_cmd node
major=$(node_major_version)
if [ "$major" -lt 22 ]; then
fail "Node.js 22.5 or newer is required. Found: $(node -v). Install a current Node.js release and rerun this installer."
fi
}
as_root() {
if [ "$(id -u)" -eq 0 ]; then
"$@"
return
fi
command -v sudo >/dev/null 2>&1 || fail "Run as root or install sudo."
sudo "$@"
}
copy_repo_from_script_location() {
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
repo_dir=$(CDPATH= cd -- "$script_dir/.." && pwd)
[ -d "$repo_dir/agent" ] || return 1
mkdir -p "$INSTALL_DIR"
if [ "$repo_dir" = "$INSTALL_DIR" ]; then
return 0
fi
command -v rsync >/dev/null 2>&1 || return 1
rsync -a --delete \
--exclude '.git' \
--exclude 'agent/.env' \
--exclude 'agent/node_modules' \
--exclude 'management/node_modules' \
--exclude 'frontend/node_modules' \
"$repo_dir/" "$INSTALL_DIR/"
}
checkout_or_update_repo() {
need_cmd git
if [ -d "$INSTALL_DIR/.git" ]; then
log "Updating $INSTALL_DIR from Git..."
git -C "$INSTALL_DIR" fetch --prune origin "$BRANCH"
git -C "$INSTALL_DIR" checkout "$BRANCH"
git -C "$INSTALL_DIR" pull --ff-only origin "$BRANCH"
return
fi
if copy_repo_from_script_location; then
log "Installed files from local checkout to $INSTALL_DIR."
return
fi
log "Cloning $REPO_URL to $INSTALL_DIR..."
mkdir -p "$(dirname "$INSTALL_DIR")"
git clone --branch "$BRANCH" "$REPO_URL" "$INSTALL_DIR"
}
move_old_agent_dir() {
if [ -d "$OLD_AGENT_DIR" ] && [ ! -d "$AGENT_DIR" ]; then
log "Renaming old backend directory to agent..."
mv "$OLD_AGENT_DIR" "$AGENT_DIR"
fi
if [ -f "$OLD_AGENT_DIR/.env" ] && [ ! -f "$ENV_FILE" ]; then
log "Moving existing backend .env to agent .env..."
mv "$OLD_AGENT_DIR/.env" "$ENV_FILE"
fi
}
ensure_env_file() {
[ -d "$AGENT_DIR" ] || fail "Agent directory not found: $AGENT_DIR"
if [ ! -f "$ENV_FILE" ]; then
cp "$AGENT_DIR/.env.example" "$ENV_FILE"
chmod 600 "$ENV_FILE"
fi
}
env_has_key() {
key="$1"
grep -Eq "^[[:space:]]*$key=" "$ENV_FILE"
}
append_env_if_missing() {
key="$1"
value="$2"
if ! env_has_key "$key"; then
printf '%s="%s"\n' "$key" "$value" >> "$ENV_FILE"
fi
}
replace_env_value() {
key="$1"
value="$2"
tmp_file="$ENV_FILE.tmp"
awk -v key="$key" -v value="$value" '
BEGIN { done = 0 }
$0 ~ "^[[:space:]]*" key "=" {
print key "=\"" value "\""
done = 1
next
}
{ print }
END {
if (!done) print key "=\"" value "\""
}
' "$ENV_FILE" > "$tmp_file"
mv "$tmp_file" "$ENV_FILE"
chmod 600 "$ENV_FILE"
}
current_env_value() {
key="$1"
awk -F= -v key="$key" '
$1 == key {
value = $0
sub("^[^=]*=", "", value)
gsub(/^"/, "", value)
gsub(/"$/, "", value)
print value
exit
}
' "$ENV_FILE"
}
random_token() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -hex 32
return
fi
need_cmd od
od -An -N32 -tx1 /dev/urandom | tr -d ' \n'
}
configure_env() {
append_env_if_missing "AGENT_DATABASE_PATH" "$STATE_DIR/agent.sqlite"
replace_env_value "AGENT_DATABASE_PATH" "${AGENT_DATABASE_PATH:-$STATE_DIR/agent.sqlite}"
if [ -n "${API_TOKEN:-}" ]; then
replace_env_value "API_TOKEN" "$API_TOKEN"
else
token=$(current_env_value API_TOKEN)
if [ ${#token} -lt 32 ] || [ "$token" = "change-me-to-at-least-32-characters" ]; then
generated_token=$(random_token)
replace_env_value "API_TOKEN" "$generated_token"
log "Generated API_TOKEN for this agent:"
log "$generated_token"
log "Store this token in the management node configuration."
fi
fi
[ -z "${HOST:-}" ] || replace_env_value "HOST" "$HOST"
[ -z "${PORT:-}" ] || replace_env_value "PORT" "$PORT"
[ -z "${ALLOWED_MANAGEMENT_IPS:-}" ] || replace_env_value "ALLOWED_MANAGEMENT_IPS" "$ALLOWED_MANAGEMENT_IPS"
[ -z "${HTTPS_ENABLED:-}" ] || replace_env_value "HTTPS_ENABLED" "$HTTPS_ENABLED"
[ -z "${TLS_CERT_FILE:-}" ] || replace_env_value "TLS_CERT_FILE" "$TLS_CERT_FILE"
[ -z "${TLS_KEY_FILE:-}" ] || replace_env_value "TLS_KEY_FILE" "$TLS_KEY_FILE"
}
install_dependencies() {
need_cmd npm
check_node_version
log "Installing agent dependencies..."
if [ -f "$AGENT_DIR/package-lock.json" ]; then
npm --prefix "$AGENT_DIR" ci --omit=dev
else
npm --prefix "$AGENT_DIR" install --omit=dev
fi
}
install_systemd_service() {
install -d -m 700 "$STATE_DIR"
install -d -m 700 "$CONFIG_DIR"
tmp_service="$SERVICE_FILE.tmp"
awk -v agent_dir="$AGENT_DIR" '
/^WorkingDirectory=/ {
print "WorkingDirectory=" agent_dir
next
}
{ print }
' "$INSTALL_DIR/deploy/systemd/$SERVICE_NAME.service" > "$tmp_service"
chmod 644 "$tmp_service"
mv "$tmp_service" "$SERVICE_FILE"
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
}
start_service() {
if [ "${SKIP_START:-false}" = "true" ]; then
log "SKIP_START=true; service was installed but not started."
return
fi
systemctl restart "$SERVICE_NAME"
systemctl --no-pager --full status "$SERVICE_NAME" || true
}
main() {
need_cmd awk
need_cmd grep
as_root sh -c "true"
if [ "$(id -u)" -ne 0 ]; then
exec sudo \
INSTALL_DIR="$INSTALL_DIR" \
INCUS_BACKUP_REPO_URL="$REPO_URL" \
INCUS_BACKUP_BRANCH="$BRANCH" \
STATE_DIR="$STATE_DIR" \
CONFIG_DIR="$CONFIG_DIR" \
API_TOKEN="${API_TOKEN:-}" \
AGENT_DATABASE_PATH="${AGENT_DATABASE_PATH:-}" \
HOST="${HOST:-}" \
PORT="${PORT:-}" \
ALLOWED_MANAGEMENT_IPS="${ALLOWED_MANAGEMENT_IPS:-}" \
HTTPS_ENABLED="${HTTPS_ENABLED:-}" \
TLS_CERT_FILE="${TLS_CERT_FILE:-}" \
TLS_KEY_FILE="${TLS_KEY_FILE:-}" \
SKIP_START="${SKIP_START:-false}" \
sh "$0"
fi
checkout_or_update_repo
move_old_agent_dir
ensure_env_file
configure_env
install_dependencies
install_systemd_service
start_service
log "Agent install/update complete."
log "Config: $ENV_FILE"
log "Service: $SERVICE_NAME"
}
main "$@"