138 lines
5.1 KiB
Bash
Executable File
138 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
umask 077
|
|
|
|
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
source_target=${PRODUCTION_SSH_TARGET:-whoneedhelp}
|
|
monitor_target=${MONITOR_SSH_TARGET:-buyvm-maya}
|
|
production_env=${PRODUCTION_ENV_PATH:-/srv/who_need_help-production/.env}
|
|
remote_root=${MONITOR_REMOTE_ROOT:-/home/simple/.local/lib/who-need-help}
|
|
remote_config=${MONITOR_REMOTE_CONFIG:-/home/simple/.config/who-need-help/monitor.json}
|
|
remote_state=${MONITOR_REMOTE_STATE:-/home/simple/.local/state/who-need-help/monitor.json}
|
|
monitor_url=${MONITOR_URL:-https://whoneedhelp.com/healthz/ready}
|
|
monitor_calendar=${MONITOR_ON_CALENDAR:-'*:0/1'}
|
|
health_timeout=${MONITOR_HEALTH_TIMEOUT_SECONDS:-3}
|
|
|
|
for command in mktemp scp ssh; do
|
|
command -v "$command" >/dev/null 2>&1 || {
|
|
echo "Required command is unavailable: $command" >&2
|
|
exit 2
|
|
}
|
|
done
|
|
|
|
source_host=$(ssh -G "$source_target" | awk '$1 == "hostname" {print $2; exit}')
|
|
monitor_host=$(ssh -G "$monitor_target" | awk '$1 == "hostname" {print $2; exit}')
|
|
if [[ -z "$source_host" || -z "$monitor_host" || "$source_host" == "$monitor_host" ]]; then
|
|
echo "The external monitor must resolve and run on a host other than production." >&2
|
|
exit 2
|
|
fi
|
|
|
|
case "$health_timeout" in
|
|
'' | *[!0-9]*) echo "MONITOR_HEALTH_TIMEOUT_SECONDS must be a positive integer." >&2; exit 2 ;;
|
|
0) echo "MONITOR_HEALTH_TIMEOUT_SECONDS must be a positive integer." >&2; exit 2 ;;
|
|
esac
|
|
|
|
work_dir=$(mktemp -d "$ROOT/tmp/production-operations/monitor-install.XXXXXX")
|
|
config="$work_dir/monitor.json"
|
|
service="$work_dir/who-need-help-production-monitor.service"
|
|
timer="$work_dir/who-need-help-production-monitor.timer"
|
|
|
|
cleanup() {
|
|
rm -rf "$work_dir"
|
|
}
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
ssh -o BatchMode=yes "$source_target" \
|
|
"python3 - '$production_env' '$monitor_url' '$health_timeout'" >"$config" <<'PY'
|
|
import json
|
|
import shlex
|
|
import sys
|
|
|
|
path, health_url, timeout = sys.argv[1:]
|
|
wanted = {
|
|
"SMTP_RELAY",
|
|
"SMTP_PORT",
|
|
"SMTP_USERNAME",
|
|
"SMTP_PASSWORD",
|
|
"SMTP_TLS",
|
|
"SMTP_SSL",
|
|
"EMAIL_FROM_ADDRESS",
|
|
"EMAIL_FROM_NAME",
|
|
"SUPPORT_INBOX_ADDRESS",
|
|
}
|
|
values = {}
|
|
with open(path, encoding="utf-8") as handle:
|
|
for raw_line in handle:
|
|
line = raw_line.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
key, value = line.split("=", 1)
|
|
if key not in wanted:
|
|
continue
|
|
parsed = shlex.split(value, comments=False, posix=True)
|
|
values[key] = parsed[0] if parsed else ""
|
|
|
|
missing = sorted(key for key in wanted if not values.get(key))
|
|
if missing:
|
|
raise SystemExit("Missing production mail settings: " + ", ".join(missing))
|
|
|
|
payload = {
|
|
"health_timeout_seconds": int(timeout),
|
|
"health_url": health_url,
|
|
"smtp": {
|
|
"from_address": values["EMAIL_FROM_ADDRESS"],
|
|
"from_name": values["EMAIL_FROM_NAME"],
|
|
"implicit_ssl": values["SMTP_SSL"].lower() == "true",
|
|
"password": values["SMTP_PASSWORD"],
|
|
"port": int(values["SMTP_PORT"]),
|
|
"recipient": values["SUPPORT_INBOX_ADDRESS"],
|
|
"relay": values["SMTP_RELAY"],
|
|
"starttls": values["SMTP_TLS"].lower() == "always",
|
|
"username": values["SMTP_USERNAME"],
|
|
},
|
|
}
|
|
json.dump(payload, sys.stdout, ensure_ascii=False, indent=2, sort_keys=True)
|
|
sys.stdout.write("\n")
|
|
PY
|
|
chmod 600 "$config"
|
|
|
|
cat >"$service" <<EOF
|
|
[Unit]
|
|
Description=Who Need Help independent production readiness monitor
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/bin/python3 $remote_root/production-external-monitor.py --config $remote_config --state $remote_state check
|
|
EOF
|
|
|
|
cat >"$timer" <<EOF
|
|
[Unit]
|
|
Description=Run the Who Need Help independent production readiness monitor
|
|
|
|
[Timer]
|
|
OnCalendar=$monitor_calendar
|
|
Persistent=true
|
|
Unit=who-need-help-production-monitor.service
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
EOF
|
|
|
|
ssh -o BatchMode=yes "$monitor_target" \
|
|
"install -d -m 700 '$remote_root' /home/simple/.config/who-need-help /home/simple/.local/state/who-need-help /home/simple/.config/systemd/user"
|
|
scp -q "$ROOT/scripts/production-external-monitor.py" \
|
|
"$monitor_target:$remote_root/production-external-monitor.py"
|
|
scp -q "$config" "$monitor_target:$remote_config"
|
|
scp -q "$service" "$monitor_target:/home/simple/.config/systemd/user/who-need-help-production-monitor.service"
|
|
scp -q "$timer" "$monitor_target:/home/simple/.config/systemd/user/who-need-help-production-monitor.timer"
|
|
|
|
ssh -o BatchMode=yes "$monitor_target" \
|
|
"chmod 700 '$remote_root/production-external-monitor.py'; chmod 600 '$remote_config' /home/simple/.config/systemd/user/who-need-help-production-monitor.service /home/simple/.config/systemd/user/who-need-help-production-monitor.timer; systemctl --user daemon-reload; systemctl --user start who-need-help-production-monitor.service; systemctl --user enable --now who-need-help-production-monitor.timer"
|
|
|
|
printf 'External monitor installed on %s (%s).\n' "$monitor_target" "$monitor_host"
|
|
printf 'Health URL: %s\n' "$monitor_url"
|
|
printf 'Schedule: %s; request timeout: %ss.\n' "$monitor_calendar" "$health_timeout"
|
|
echo "The SMTP credential is stored only in a mode-0600 configuration on the external monitor host."
|