44# Proto Fleet Installation and Setup Script
55# ============================================================================
66
7- PROJECT_ROOT=" $( pwd) "
7+ PROJECT_ROOT=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd) "
88COMPOSE_FILE=" $PROJECT_ROOT /docker-compose.yaml"
99COMPOSE_ALERTS_FILE=" $PROJECT_ROOT /docker-compose.alerts.yaml"
1010COMPOSE_SYSTEM_MONITORING_FILE=" $PROJECT_ROOT /docker-compose.system-monitoring.yaml"
1111COMPOSE_TRACING_FILE=" $PROJECT_ROOT /docker-compose.tracing.yaml"
12+ COMPOSE_UPDATER_FILE=" $PROJECT_ROOT /docker-compose.updater.yaml"
1213ENV_FILE=" $PROJECT_ROOT /.env"
1314
1415ENABLE_BETA_ALERTS=false
1516ENABLE_SYSTEM_MONITORING=false
1617ENABLE_TRACING=false
18+ ENABLE_ONE_CLICK_UPDATES=false
19+ NON_INTERACTIVE=false
20+ PREFLIGHT_ONLY=false
21+ SKIP_BUILD=false
1722
1823# How long the post-start steps wait for fleet-api to finish its migrations.
1924# 300 x 2s = 10 minutes: a first boot on SD-card-class hardware (Raspberry Pi)
@@ -49,6 +54,17 @@ Options:
4954 the .env file. Off by default. Can also be
5055 enabled by setting ENABLE_TRACING=true in
5156 the .env file.
57+ --enable-one-click-updates Connect fleet-api to the host updater Unix
58+ socket. The installer sets this after the
59+ systemd updater is installed successfully.
60+ --non-interactive Reuse complete persisted configuration and
61+ fail instead of prompting. Intended for the
62+ host updater, not first-time setup.
63+ --preflight-only Validate configuration, load release images,
64+ and build the new stack without stopping the
65+ running deployment.
66+ --skip-build Skip image preparation because a successful
67+ preflight already prepared this exact release.
5268 -h, --help Show this help and exit.
5369EOF
5470}
@@ -67,6 +83,22 @@ while [ $# -gt 0 ]; do
6783 ENABLE_TRACING=true
6884 shift
6985 ;;
86+ --enable-one-click-updates)
87+ ENABLE_ONE_CLICK_UPDATES=true
88+ shift
89+ ;;
90+ --non-interactive)
91+ NON_INTERACTIVE=true
92+ shift
93+ ;;
94+ --preflight-only)
95+ PREFLIGHT_ONLY=true
96+ shift
97+ ;;
98+ --skip-build)
99+ SKIP_BUILD=true
100+ shift
101+ ;;
70102 -h|--help)
71103 usage
72104 exit 0
@@ -83,17 +115,25 @@ while [ $# -gt 0 ]; do
83115 esac
84116done
85117
118+ if [ " $PREFLIGHT_ONLY " = " true" ] && [ " $SKIP_BUILD " = " true" ]; then
119+ echo " Error: --preflight-only and --skip-build cannot be combined." >&2
120+ exit 1
121+ fi
122+
86123# Also honor ENABLE_BETA_ALERTS=true from the .env file.
87- if grep -Eqi " ^ENABLE_BETA_ALERTS=true$" " $ENV_FILE " 2> /dev/null; then
124+ if grep -Eqi " ^ENABLE_BETA_ALERTS=true[[:space:]]* $" " $ENV_FILE " 2> /dev/null; then
88125 ENABLE_BETA_ALERTS=true
89126fi
90- if grep -Eqi " ^ENABLE_SYSTEM_MONITORING=true$" " $ENV_FILE " 2> /dev/null; then
127+ if grep -Eqi " ^ENABLE_SYSTEM_MONITORING=true[[:space:]]* $" " $ENV_FILE " 2> /dev/null; then
91128 ENABLE_SYSTEM_MONITORING=true
92129fi
93130# [[:space:]]*$ tolerates CRLF line endings from Windows/WSL-edited .env files.
94131if grep -Eqi " ^ENABLE_TRACING=true[[:space:]]*$" " $ENV_FILE " 2> /dev/null; then
95132 ENABLE_TRACING=true
96133fi
134+ if grep -Eqi " ^ENABLE_ONE_CLICK_UPDATES=true[[:space:]]*$" " $ENV_FILE " 2> /dev/null; then
135+ ENABLE_ONE_CLICK_UPDATES=true
136+ fi
97137
98138# System monitoring rides the alerts stack (the in-process metrics writer,
99139# Grafana rule evaluation, and webhook delivery are all alerts-gated), so it
@@ -131,6 +171,13 @@ refresh_compose_files() {
131171 if [ " $ENABLE_TRACING " = " true" ] && [ -f " $COMPOSE_TRACING_FILE " ]; then
132172 COMPOSE_FILES+=(-f " $COMPOSE_TRACING_FILE " )
133173 fi
174+ if [ " $ENABLE_ONE_CLICK_UPDATES " = " true" ]; then
175+ if [ ! -f " $COMPOSE_UPDATER_FILE " ]; then
176+ echo " Error: one-click updates are enabled but $COMPOSE_UPDATER_FILE is missing." >&2
177+ exit 1
178+ fi
179+ COMPOSE_FILES+=(-f " $COMPOSE_UPDATER_FILE " )
180+ fi
134181}
135182refresh_compose_files
136183
@@ -386,6 +433,10 @@ fix_wsl_networking() {
386433# ----------------------------------------------------------------------------
387434
388435if ! command -v docker & > /dev/null; then
436+ if [ " $NON_INTERACTIVE " = " true" ]; then
437+ echo " Error: Docker is not installed; non-interactive upgrade cannot install host prerequisites." >&2
438+ exit 1
439+ fi
389440 echo " Docker is not installed. Attempting to install Docker..."
390441
391442 if [ " $( uname) " == " Linux" ]; then
409460if [ " $( uname) " == " Linux" ]; then
410461 # Check if Docker is set to start on boot
411462 if ! systemctl is-enabled docker & > /dev/null; then
463+ if [ " $NON_INTERACTIVE " = " true" ]; then
464+ echo " Error: Docker is not enabled at boot; fix the host before retrying the upgrade." >&2
465+ exit 1
466+ fi
412467 echo " Configuring Docker to start on system boot..."
413468 sudo systemctl enable docker
414469 fi
@@ -420,6 +475,10 @@ if [ "$(uname)" == "Linux" ]; then
420475 # for the sudo-mismatch detection in install.sh) would exit here telling
421476 # the user to log out and back in, leaving the upgrade half-applied.
422477 if [ " $( id -u) " -ne 0 ] && ! groups $USER | grep -q ' \bdocker\b' ; then
478+ if [ " $NON_INTERACTIVE " = " true" ]; then
479+ echo " Error: the upgrade user cannot access Docker." >&2
480+ exit 1
481+ fi
423482 echo " Adding current user to the docker group for passwordless Docker usage..."
424483 sudo usermod -aG docker $USER
425484 echo " Please log out and log back in to apply group changes, then re-run this script."
432491# ----------------------------------------------------------------------------
433492
434493if ! docker info > /dev/null 2>&1 ; then
494+ if [ " $NON_INTERACTIVE " = " true" ]; then
495+ echo " Error: Docker daemon is not available; non-interactive upgrade will not mutate host services." >&2
496+ exit 1
497+ fi
435498 echo " Docker daemon is not running. Starting Docker..."
436499
437500 # For macOS, attempt to start Docker Desktop
485548# ----------------------------------------------------------------------------
486549
487550if ! docker compose version & > /dev/null; then
551+ if [ " $NON_INTERACTIVE " = " true" ]; then
552+ echo " Error: docker compose is not installed; non-interactive upgrade cannot install host prerequisites." >&2
553+ exit 1
554+ fi
488555 echo " docker compose is not installed. Attempting to install it..."
489556
490557 if [ " $( uname) " == " Linux" ]; then
@@ -582,7 +649,9 @@ prompt_fleet_profile() {
582649# curl | bash installs reach prompts with stdin at EOF; never let an
583650# unanswered prompt persist a profile
584651maybe_prompt_fleet_profile () {
585- if [ -t 0 ]; then
652+ if [ " $NON_INTERACTIVE " = " true" ]; then
653+ echo " No FLEET_PROFILE is persisted; keeping the existing conservative defaults."
654+ elif [ -t 0 ]; then
586655 prompt_fleet_profile
587656 else
588657 echo " Hint: host profiles are available; set FLEET_PROFILE=standard|mini|max in $ENV_FILE and re-run to tune for this hardware."
@@ -610,8 +679,12 @@ if [ -f "$ENV_FILE" ]; then
610679 done
611680
612681 if [ $missing_keys -eq 0 ]; then
613- echo -n " Existing environment file found with all required keys. Use it? (Y/n): "
614- read use_existing_creds
682+ if [ " $NON_INTERACTIVE " = " true" ]; then
683+ use_existing_creds=" y"
684+ else
685+ echo -n " Existing environment file found with all required keys. Use it? (Y/n): "
686+ read use_existing_creds
687+ fi
615688 if [[ -z " $use_existing_creds " || $use_existing_creds =~ ^[Yy]$ ]]; then
616689 use_existing=" yes"
617690 echo " Using existing environment file."
@@ -623,11 +696,20 @@ if [ -f "$ENV_FILE" ]; then
623696 prompt_store_reinit || { echo " Aborting due to existing data volume." ; exit 1; }
624697 fi
625698 else
699+ if [ " $NON_INTERACTIVE " = " true" ]; then
700+ echo " Error: existing environment file is incomplete; refusing to regenerate secrets during an upgrade." >&2
701+ exit 1
702+ fi
626703 echo " Existing environment file is incomplete. Regenerating…"
627704 prompt_store_reinit || { echo " Cannot proceed with incomplete env + existing data." ; exit 1; }
628705 fi
629706fi
630707
708+ if [ " $NON_INTERACTIVE " = " true" ] && [ " $use_existing " = " no" ]; then
709+ echo " Error: non-interactive mode requires an existing complete $ENV_FILE ." >&2
710+ exit 1
711+ fi
712+
631713# ----------------------------------------------------------------------------
632714# Generate New Environment Configuration
633715# ----------------------------------------------------------------------------
@@ -708,6 +790,31 @@ if [ "$use_existing" == "no" ]; then
708790 echo " Environment variables saved to $ENV_FILE "
709791fi
710792
793+ # Persist every deployment overlay as explicit state. Historically, flags were
794+ # process-only, so the next upgrade could silently disable alerts, monitoring,
795+ # or tracing. Last value wins, matching Compose's .env behavior.
796+ persist_boolean_setting () {
797+ local key=" $1 "
798+ local value=" $2 "
799+ local temp
800+ temp=$( mktemp)
801+ grep -v " ^${key} =" " $ENV_FILE " > " $temp " || true
802+ if [ -s " $temp " ] && [ -n " $( tail -c1 " $temp " ) " ]; then
803+ echo >> " $temp "
804+ fi
805+ echo " ${key} =${value} " >> " $temp "
806+ cat " $temp " > " $ENV_FILE "
807+ rm -f " $temp "
808+ }
809+
810+ persist_boolean_setting ENABLE_BETA_ALERTS " $ENABLE_BETA_ALERTS "
811+ persist_boolean_setting ENABLE_SYSTEM_MONITORING " $ENABLE_SYSTEM_MONITORING "
812+ persist_boolean_setting ENABLE_TRACING " $ENABLE_TRACING "
813+ persist_boolean_setting ENABLE_ONE_CLICK_UPDATES " $ENABLE_ONE_CLICK_UPDATES "
814+ chmod 600 " $ENV_FILE "
815+ refresh_compose_files
816+ refresh_compose_env_args
817+
711818# ----------------------------------------------------------------------------
712819# Docker Compose File Validation
713820# ----------------------------------------------------------------------------
@@ -807,6 +914,14 @@ if [ -f "$SSL_CERT" ] && [ -f "$SSL_KEY" ]; then
807914 echo " Private Key: $SSL_KEY "
808915 PROTOCOL_MODE=" https"
809916else
917+ if [ " $NON_INTERACTIVE " = " true" ]; then
918+ if grep -Eqi " ^SESSION_COOKIE_SECURE=true[[:space:]]*$" " $ENV_FILE " ; then
919+ echo " Error: HTTPS is persisted but its certificate/key are missing; refusing to switch protocol during upgrade." >&2
920+ exit 1
921+ fi
922+ echo " No SSL certificates found; preserving HTTP mode from $ENV_FILE ."
923+ PROTOCOL_MODE=" http"
924+ else
810925 echo " "
811926 echo " No SSL certificates found in $SSL_DIR "
812927 echo " "
842957 PROTOCOL_MODE=" http"
843958 ;;
844959 esac
960+ fi
845961fi
846962
847963echo " "
@@ -882,26 +998,38 @@ refresh_compose_env_args
882998# Docker Image Preparation
883999# ----------------------------------------------------------------------------
8841000
885- echo " Pulling latest Docker images..."
886- compose pull
1001+ if [ " $SKIP_BUILD " != " true" ]; then
1002+ echo " Pulling latest Docker images..."
1003+ if ! compose pull; then
1004+ echo " Error: Failed to pull required Docker images."
1005+ exit 1
1006+ fi
8871007
888- # Load pre-built TimescaleDB image if available (built in CI for the target architecture)
889- TSDB_IMAGE=" $PROJECT_ROOT /images/timescaledb.tar.gz"
890- if [ -f " $TSDB_IMAGE " ]; then
891- echo " Loading pre-built TimescaleDB image..."
892- if gunzip -c " $TSDB_IMAGE " | docker load; then
893- echo " TimescaleDB image loaded successfully."
1008+ # Load pre-built TimescaleDB image if available (built in CI for the target architecture)
1009+ TSDB_IMAGE=" $PROJECT_ROOT /images/timescaledb.tar.gz"
1010+ if [ -f " $TSDB_IMAGE " ]; then
1011+ echo " Loading pre-built TimescaleDB image..."
1012+ if gunzip -c " $TSDB_IMAGE " | docker load; then
1013+ echo " TimescaleDB image loaded successfully."
1014+ else
1015+ echo " Error: Failed to load TimescaleDB image from $TSDB_IMAGE "
1016+ exit 1
1017+ fi
8941018 else
895- echo " Error: Failed to load TimescaleDB image from $TSDB_IMAGE "
896- exit 1
1019+ echo " Warning: Pre-built TimescaleDB image not found at $TSDB_IMAGE . "
1020+ echo " The deployment will fail unless the image 'proto-fleet-timescaledb:latest' already exists locally. "
8971021 fi
1022+
1023+ # Build Docker images (fleet-api and fleet-client only; TimescaleDB uses pre-built image)
1024+ compose build --no-cache || { echo " Error: Build failed. Exiting." ; exit 1; }
8981025else
899- echo " Warning: Pre-built TimescaleDB image not found at $TSDB_IMAGE ."
900- echo " The deployment will fail unless the image 'proto-fleet-timescaledb:latest' already exists locally."
1026+ echo " Skipping image preparation; this release already passed updater preflight."
9011027fi
9021028
903- # Build Docker images (fleet-api and fleet-client only; TimescaleDB uses pre-built image)
904- compose build --no-cache || { echo " Error: Build failed. Exiting." ; exit 1; }
1029+ if [ " $PREFLIGHT_ONLY " = " true" ]; then
1030+ echo " Upgrade preflight completed successfully; the running stack was not stopped."
1031+ exit 0
1032+ fi
9051033
9061034# ----------------------------------------------------------------------------
9071035# Service Management
0 commit comments