dotenvy aborted on the production .env (a quote in an unquoted value makes it
read to the end of the file) and echoed the offending line, password
included. envfile.rs ports python-dotenv 1.1.1 (parser + ${VAR} interpolation,
no override): unparsable statements are skipped with a warning that names only
the line number. Verified against python-dotenv on 614 generated inputs
(testdata/). The .env is loaded before the tokio runtime starts.
install_collector.sh now runs the dry run through systemd-run with the unit's
User, WorkingDirectory and EnvironmentFile, i.e. exactly what the service sees.
89 lines
3.9 KiB
Bash
Executable File
89 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Install the Rust collector (rust/collector, `ping-collector`) and switch
|
|
# data collection over to it, or back.
|
|
#
|
|
# sudo bash scripts/install_collector.sh # install binary + unit, dry-run check
|
|
# sudo bash scripts/install_collector.sh --cutover # install, then Rust collects:
|
|
# # COLLECTION_ENABLED=false, start
|
|
# # ping-collector, restart water-monitor
|
|
# sudo bash scripts/install_collector.sh --rollback # Python collects again
|
|
#
|
|
# The binary comes from BIN=/path/to/ping-collector (e.g. the "ping-collector-
|
|
# linux-x86_64" CI artifact) or from rust/target/release/ping-collector, built
|
|
# beforehand as a normal user:
|
|
# cargo build --release --locked --manifest-path rust/Cargo.toml
|
|
# (needs Rust >= 1.88 from rustup -- distro cargo is too old -- plus pkg-config
|
|
# and libssl-dev; see rust/README.md).
|
|
set -euo pipefail
|
|
|
|
APP_DIR="${APP_DIR:-/opt/thailand-water-monitor}"
|
|
SERVICE_USER="${SERVICE_USER:-water-monitor}" # as in scripts/ping-collector.service
|
|
ENV_FILE="${APP_DIR}/.env"
|
|
STATUS_FILE="${APP_DIR}/collector-status.json"
|
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
MODE="${1:-install}"
|
|
|
|
log() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
|
|
die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
[ "$(id -u)" -eq 0 ] || die "run as root (sudo)"
|
|
[ -f "$ENV_FILE" ] || die "$ENV_FILE not found"
|
|
|
|
set_env() {
|
|
local key="$1" value="$2"
|
|
if grep -q "^${key}=" "$ENV_FILE"; then
|
|
sed -i "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
|
|
else
|
|
printf '%s=%s\n' "$key" "$value" >>"$ENV_FILE"
|
|
fi
|
|
}
|
|
|
|
if [ "$MODE" = "--rollback" ]; then
|
|
log "Stopping ping-collector; the Python app collects again"
|
|
systemctl disable --now ping-collector.service 2>/dev/null || true
|
|
set_env COLLECTION_ENABLED true
|
|
systemctl restart water-monitor.service
|
|
log "Done. Check: journalctl -u water-monitor -n 50"
|
|
exit 0
|
|
fi
|
|
case "$MODE" in install | --cutover) ;; *) die "unknown option: $MODE" ;; esac
|
|
|
|
BIN="${BIN:-${REPO_DIR}/rust/target/release/ping-collector}"
|
|
[ -x "$BIN" ] || die "no binary at $BIN: build it (see header) or pass BIN=/path/to/ping-collector"
|
|
|
|
log "Installing $BIN -> ${APP_DIR}/bin/ping-collector ($("$BIN" --version))"
|
|
install -D -m 0755 -o root -g root "$BIN" "${APP_DIR}/bin/ping-collector"
|
|
install -m 0644 "${REPO_DIR}/scripts/ping-collector.service" /etc/systemd/system/ping-collector.service
|
|
systemctl daemon-reload
|
|
|
|
# Both services read and write /scraping/status through this file (inside
|
|
# the app dir: the units use PrivateTmp).
|
|
set_env SCRAPING_STATUS_FILE "$STATUS_FILE"
|
|
touch "$STATUS_FILE"
|
|
chown "${SERVICE_USER}:$(id -gn "$SERVICE_USER")" "$STATUS_FILE"
|
|
|
|
log "Dry run against the configured database (writes nothing)"
|
|
# Run it the way the service will: same user, working directory and
|
|
# EnvironmentFile parsing (systemd's, not a .env library's).
|
|
if systemd-run --version >/dev/null 2>&1; then
|
|
systemd-run --quiet --pipe --wait --collect \
|
|
-p User="$SERVICE_USER" -p Group="$(id -gn "$SERVICE_USER")" \
|
|
-p WorkingDirectory="$APP_DIR" -p EnvironmentFile="$ENV_FILE" \
|
|
"${APP_DIR}/bin/ping-collector" once --dry-run
|
|
else
|
|
(cd "$APP_DIR" && "${APP_DIR}/bin/ping-collector" --env-file "$ENV_FILE" once --dry-run)
|
|
fi
|
|
|
|
if [ "$MODE" = "--cutover" ]; then
|
|
log "Cutover: ping-collector collects, the Python app forecasts and notifies"
|
|
set_env COLLECTION_ENABLED false
|
|
systemctl enable --now ping-collector.service
|
|
systemctl restart water-monitor.service
|
|
sleep 20
|
|
systemctl --no-pager --lines=0 status ping-collector.service || true
|
|
journalctl -u ping-collector -n 20 --no-pager || true
|
|
log "Rollback if needed: sudo bash scripts/install_collector.sh --rollback"
|
|
else
|
|
log "Installed, not started. Switch over with: sudo bash scripts/install_collector.sh --cutover"
|
|
fi
|