feat: install timer+check service, ask recap time, fix tmpdir+ns8-sendmail checks
This commit is contained in:
+66
-31
@@ -9,18 +9,28 @@
|
|||||||
# Requires: root, python3, curl (no git needed)
|
# Requires: root, python3, curl (no git needed)
|
||||||
# Tested on: AlmaLinux 8/9, Rocky Linux 8/9 (NS8 supported distros)
|
# Tested on: AlmaLinux 8/9, Rocky Linux 8/9 (NS8 supported distros)
|
||||||
#
|
#
|
||||||
|
# Two systemd units are installed:
|
||||||
|
# ns8-backup-monitor.service - long-running webhook receiver for
|
||||||
|
# Alertmanager failure alerts
|
||||||
|
# ns8-backup-monitor-check.timer - daily one-shot timer that sends a
|
||||||
|
# scheduled recap email regardless of
|
||||||
|
# backup outcome (success or failure)
|
||||||
|
#
|
||||||
# ns8-sendmail is NOT in the standard root PATH on NS8 nodes.
|
# ns8-sendmail is NOT in the standard root PATH on NS8 nodes.
|
||||||
# The installer checks for it via 'runagent' (the NS8 agent runner) instead of
|
# The installer verifies it via 'runagent' — the NS8 agent runner — which
|
||||||
# relying on PATH lookup. This is the correct way to invoke NS8 built-in tools.
|
# is always present when ns8-sendmail is available.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# --- constants ----------------------------------------------------------------
|
# --- constants ----------------------------------------------------------------
|
||||||
SERVICE_NAME="ns8-backup-monitor"
|
SERVICE_NAME="ns8-backup-monitor"
|
||||||
|
CHECK_SERVICE_NAME="ns8-backup-monitor-check"
|
||||||
INSTALL_DIR="/opt/ns8-backup-monitor"
|
INSTALL_DIR="/opt/ns8-backup-monitor"
|
||||||
CONFIG_DIR="/etc/ns8-backup-monitor"
|
CONFIG_DIR="/etc/ns8-backup-monitor"
|
||||||
CONFIG_FILE="${CONFIG_DIR}/config.yml"
|
CONFIG_FILE="${CONFIG_DIR}/config.yml"
|
||||||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
CHECK_SERVICE_FILE="/etc/systemd/system/${CHECK_SERVICE_NAME}.service"
|
||||||
|
CHECK_TIMER_FILE="/etc/systemd/system/${CHECK_SERVICE_NAME}.timer"
|
||||||
|
|
||||||
# Gitea raw base URL for downloading individual files
|
# Gitea raw base URL for downloading individual files
|
||||||
RAW_BASE="https://repo.lelekaos.com/admin/ns8-backup-monitor/raw/branch/main"
|
RAW_BASE="https://repo.lelekaos.com/admin/ns8-backup-monitor/raw/branch/main"
|
||||||
@@ -41,21 +51,19 @@ error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; }
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# CHECK NS8-SENDMAIL
|
# CHECK NS8-SENDMAIL
|
||||||
# On NS8 nodes, ns8-sendmail is not in the standard root PATH.
|
# On NS8 nodes, ns8-sendmail is not in the standard root PATH.
|
||||||
# The canonical way to verify it is available is to check that 'runagent'
|
# The canonical check is to verify that 'runagent' exists: ns8-sendmail is
|
||||||
# exists (the NS8 agent runner), since ns8-sendmail is always provided by the
|
# always available when the NS8 environment (and runagent) is present.
|
||||||
# NS8 environment that also provides runagent.
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
check_ns8_sendmail() {
|
check_ns8_sendmail() {
|
||||||
# First try direct PATH lookup (covers non-standard installs)
|
# 1. Direct PATH lookup (covers non-standard or manual installs)
|
||||||
if command -v ns8-sendmail &>/dev/null; then
|
if command -v ns8-sendmail &>/dev/null; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
# On NS8 nodes, ns8-sendmail is invoked through the NS8 environment.
|
# 2. runagent presence confirms this is an NS8 node where ns8-sendmail
|
||||||
# If runagent is present, ns8-sendmail is available at runtime.
|
# is available at runtime even if not in root's PATH.
|
||||||
if command -v runagent &>/dev/null; then
|
if command -v runagent &>/dev/null; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
# Neither found: genuine warning
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,25 +72,29 @@ check_ns8_sendmail() {
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
do_uninstall() {
|
do_uninstall() {
|
||||||
echo -e "${BOLD}=== ns8-backup-monitor UNINSTALL ===${RESET}"
|
echo -e "${BOLD}=== ns8-backup-monitor UNINSTALL ===${RESET}"
|
||||||
warn "This will stop and remove the service, code and config."
|
warn "This will stop and remove the service, timer, code and config."
|
||||||
read -rp "Continue? [y/N] " confirm
|
read -rp "Continue? [y/N] " confirm
|
||||||
[[ "$confirm" =~ ^[Yy]$ ]] || { info "Aborted."; exit 0; }
|
[[ "$confirm" =~ ^[Yy]$ ]] || { info "Aborted."; exit 0; }
|
||||||
|
|
||||||
if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
|
for unit in "$CHECK_SERVICE_NAME" "$SERVICE_NAME"; do
|
||||||
info "Stopping service..."
|
if systemctl is-active --quiet "$unit" 2>/dev/null; then
|
||||||
systemctl stop "$SERVICE_NAME"
|
info "Stopping ${unit}..."
|
||||||
|
systemctl stop "$unit"
|
||||||
fi
|
fi
|
||||||
if systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then
|
if systemctl is-enabled --quiet "$unit" 2>/dev/null; then
|
||||||
info "Disabling service..."
|
info "Disabling ${unit}..."
|
||||||
systemctl disable "$SERVICE_NAME"
|
systemctl disable "$unit"
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if [[ -f "$SERVICE_FILE" ]]; then
|
for f in "$CHECK_TIMER_FILE" "$CHECK_SERVICE_FILE" "$SERVICE_FILE"; do
|
||||||
info "Removing systemd unit..."
|
if [[ -f "$f" ]]; then
|
||||||
rm -f "$SERVICE_FILE"
|
info "Removing ${f}..."
|
||||||
systemctl daemon-reload
|
rm -f "$f"
|
||||||
ok "Systemd unit removed."
|
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
|
systemctl daemon-reload
|
||||||
|
ok "Systemd units removed."
|
||||||
|
|
||||||
if [[ -d "$INSTALL_DIR" ]]; then
|
if [[ -d "$INSTALL_DIR" ]]; then
|
||||||
info "Removing ${INSTALL_DIR}..."
|
info "Removing ${INSTALL_DIR}..."
|
||||||
@@ -105,12 +117,11 @@ do_uninstall() {
|
|||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# DOWNLOAD SOURCE
|
# DOWNLOAD SOURCE
|
||||||
|
# Bug fix: declare and assign tmpdir on the same line so that 'set -u' never
|
||||||
|
# sees an unset variable, even if the trap fires before mktemp completes.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
download_source() {
|
download_source() {
|
||||||
# BUG FIX: declare and assign on the same line so that 'set -u' never sees
|
|
||||||
# an unset variable, even if the trap fires before mktemp succeeds.
|
|
||||||
local tmpdir; tmpdir=$(mktemp -d)
|
local tmpdir; tmpdir=$(mktemp -d)
|
||||||
# The trap now always has a valid $tmpdir value.
|
|
||||||
trap 'rm -rf "$tmpdir"' RETURN
|
trap 'rm -rf "$tmpdir"' RETURN
|
||||||
|
|
||||||
info "Downloading source archive..."
|
info "Downloading source archive..."
|
||||||
@@ -125,7 +136,6 @@ download_source() {
|
|||||||
extracted_dir=$(find "${tmpdir}" -mindepth 1 -maxdepth 1 -type d | head -n1)
|
extracted_dir=$(find "${tmpdir}" -mindepth 1 -maxdepth 1 -type d | head -n1)
|
||||||
[[ -n "$extracted_dir" ]] || error "Could not find extracted directory in archive."
|
[[ -n "$extracted_dir" ]] || error "Could not find extracted directory in archive."
|
||||||
|
|
||||||
# Sync into INSTALL_DIR (rsync-style, pure bash)
|
|
||||||
cp -a "${extracted_dir}/." "$INSTALL_DIR/"
|
cp -a "${extracted_dir}/." "$INSTALL_DIR/"
|
||||||
ok "Source ready at ${INSTALL_DIR}."
|
ok "Source ready at ${INSTALL_DIR}."
|
||||||
}
|
}
|
||||||
@@ -142,7 +152,6 @@ do_install() {
|
|||||||
command -v curl &>/dev/null || error "curl not found."
|
command -v curl &>/dev/null || error "curl not found."
|
||||||
command -v tar &>/dev/null || error "tar not found."
|
command -v tar &>/dev/null || error "tar not found."
|
||||||
|
|
||||||
# Check ns8-sendmail availability the NS8-correct way (via runagent).
|
|
||||||
if check_ns8_sendmail; then
|
if check_ns8_sendmail; then
|
||||||
ok "ns8-sendmail available (NS8 node confirmed)."
|
ok "ns8-sendmail available (NS8 node confirmed)."
|
||||||
else
|
else
|
||||||
@@ -171,10 +180,24 @@ do_install() {
|
|||||||
read -rp "Subject prefix [[NS8 Backup]]: " SUBJECT_PREFIX
|
read -rp "Subject prefix [[NS8 Backup]]: " SUBJECT_PREFIX
|
||||||
SUBJECT_PREFIX="${SUBJECT_PREFIX:-[NS8 Backup]}"
|
SUBJECT_PREFIX="${SUBJECT_PREFIX:-[NS8 Backup]}"
|
||||||
|
|
||||||
|
# --- scheduled check time -------------------------------------------------
|
||||||
|
echo
|
||||||
|
echo -e "${BOLD}Scheduled daily recap${RESET}"
|
||||||
|
echo -e "A daily timer will send a backup recap email regardless of outcome."
|
||||||
|
echo -e "Set this to ~30 minutes after your last backup is expected to finish."
|
||||||
|
read -rp "Daily recap time (HH:MM, 24h) [07:00]: " RECAP_TIME
|
||||||
|
RECAP_TIME="${RECAP_TIME:-07:00}"
|
||||||
|
# Validate format
|
||||||
|
[[ "$RECAP_TIME" =~ ^([01][0-9]|2[0-3]):[0-5][0-9]$ ]] \
|
||||||
|
|| error "Invalid time format '${RECAP_TIME}'. Use HH:MM (e.g. 07:00)."
|
||||||
|
RECAP_HOUR="${RECAP_TIME%%:*}"
|
||||||
|
RECAP_MIN="${RECAP_TIME##*:}"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
info "From: $MAIL_FROM"
|
info "From: $MAIL_FROM"
|
||||||
info "To: ${MAIL_TO_LIST[*]}"
|
info "To: ${MAIL_TO_LIST[*]}"
|
||||||
info "Prefix: $SUBJECT_PREFIX"
|
info "Prefix: $SUBJECT_PREFIX"
|
||||||
|
info "Recap at: ${RECAP_TIME} daily"
|
||||||
echo
|
echo
|
||||||
read -rp "Confirm and proceed with install? [Y/n] " go
|
read -rp "Confirm and proceed with install? [Y/n] " go
|
||||||
[[ "$go" =~ ^[Nn]$ ]] && { info "Aborted."; exit 0; }
|
[[ "$go" =~ ^[Nn]$ ]] && { info "Aborted."; exit 0; }
|
||||||
@@ -197,7 +220,6 @@ do_install() {
|
|||||||
else
|
else
|
||||||
info "Writing ${CONFIG_FILE}..."
|
info "Writing ${CONFIG_FILE}..."
|
||||||
|
|
||||||
# Build YAML 'to' list
|
|
||||||
local to_yaml=""
|
local to_yaml=""
|
||||||
for addr in "${MAIL_TO_LIST[@]}"; do
|
for addr in "${MAIL_TO_LIST[@]}"; do
|
||||||
to_yaml+=" - \"${addr}\"\n"
|
to_yaml+=" - \"${addr}\"\n"
|
||||||
@@ -237,19 +259,32 @@ EOF
|
|||||||
ok "Config written."
|
ok "Config written."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- systemd unit ---------------------------------------------------------
|
# --- systemd units --------------------------------------------------------
|
||||||
info "Installing systemd unit..."
|
info "Installing systemd units..."
|
||||||
|
|
||||||
|
# Webhook receiver (long-running)
|
||||||
cp "${INSTALL_DIR}/deploy/ns8-backup-monitor.service" "$SERVICE_FILE"
|
cp "${INSTALL_DIR}/deploy/ns8-backup-monitor.service" "$SERVICE_FILE"
|
||||||
|
|
||||||
|
# Scheduled check service (one-shot, invoked by timer)
|
||||||
|
cp "${INSTALL_DIR}/deploy/ns8-backup-monitor-check.service" "$CHECK_SERVICE_FILE"
|
||||||
|
|
||||||
|
# Timer: inject the configured recap time into the unit file
|
||||||
|
sed "s/OnCalendar=\*-\*-\* 07:00:00/OnCalendar=*-*-* ${RECAP_HOUR}:${RECAP_MIN}:00/" \
|
||||||
|
"${INSTALL_DIR}/deploy/ns8-backup-monitor-check.timer" > "$CHECK_TIMER_FILE"
|
||||||
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable --now "$SERVICE_NAME"
|
systemctl enable --now "$SERVICE_NAME"
|
||||||
ok "Service enabled and started."
|
systemctl enable --now "${CHECK_SERVICE_NAME}.timer"
|
||||||
|
ok "Webhook service and daily recap timer enabled and started."
|
||||||
|
|
||||||
# --- done -----------------------------------------------------------------
|
# --- done -----------------------------------------------------------------
|
||||||
echo
|
echo
|
||||||
echo -e "${GREEN}${BOLD}Installation complete.${RESET}"
|
echo -e "${GREEN}${BOLD}Installation complete.${RESET}"
|
||||||
echo -e " Config: ${CONFIG_FILE}"
|
echo -e " Config: ${CONFIG_FILE}"
|
||||||
echo -e " Status: systemctl status ${SERVICE_NAME}"
|
echo -e " Webhook: systemctl status ${SERVICE_NAME}"
|
||||||
|
echo -e " Daily recap: systemctl status ${CHECK_SERVICE_NAME}.timer"
|
||||||
echo -e " Logs: journalctl -u ${SERVICE_NAME} -f"
|
echo -e " Logs: journalctl -u ${SERVICE_NAME} -f"
|
||||||
|
echo -e " Manual test: systemctl start ${CHECK_SERVICE_NAME}"
|
||||||
echo
|
echo
|
||||||
echo -e "To uninstall: ${BOLD}bash ${INSTALL_DIR}/deploy/install.sh --uninstall${RESET}"
|
echo -e "To uninstall: ${BOLD}bash ${INSTALL_DIR}/deploy/install.sh --uninstall${RESET}"
|
||||||
echo -e "To update: ${BOLD}bash <(curl -fsSL ${RAW_BASE}/deploy/install.sh)${RESET}"
|
echo -e "To update: ${BOLD}bash <(curl -fsSL ${RAW_BASE}/deploy/install.sh)${RESET}"
|
||||||
|
|||||||
Reference in New Issue
Block a user