mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-10-17 05:14:19 +02:00
Retire the dotted "N.O.M.A.D." styling everywhere in favor of "Project NOMAD" (no periods) ahead of the trademark filing, and remove the "Node for Offline Media, Archives, and Data" backronym from all copy except a single origin-story line in the About page (corrected to "Maps" rather than "Media"). Scope is display strings only: docs, admin UI labels/titles, install script output, Dockerfile labels, package.json description, and issue templates. Code identifiers, container/service names, env vars, CSS tokens, URLs, and the project-nomad slug are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
251 lines
9.8 KiB
Bash
Executable File
251 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Project NOMAD — Disk Collector Migration Script
|
|
#
|
|
# Script | Project NOMAD Disk Collector Migration Script
|
|
# Version | 1.0.0
|
|
# Author | Crosstalk Solutions, LLC
|
|
# Website | https://crosstalksolutions.com
|
|
#
|
|
# PURPOSE:
|
|
# One-time migration from the host-based disk info collector to the
|
|
# disk-collector Docker sidecar. The old approach used a nohup background
|
|
# process that wrote to /tmp/nomad-disk-info.json, which was bind-mounted
|
|
# into the admin container. This broke on host reboots because /tmp is
|
|
# cleared and Docker would create a directory at the mount point instead of a file.
|
|
#
|
|
# The new approach uses a disk-collector sidecar container that reads host
|
|
# disk info via the /:/host:ro,rslave bind-mount pattern (same pattern as Prometheus
|
|
# node-exporter, and no SYS_ADMIN or privileged capabilities required) and writes directly to
|
|
# /opt/project-nomad/storage/nomad-disk-info.json, which the admin container
|
|
# already reads via its existing storage bind-mount. Thus, no admin image update
|
|
# or new volume mounts required.
|
|
|
|
###############################################################################
|
|
# Color Codes
|
|
###############################################################################
|
|
|
|
RESET='\033[0m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[1;31m'
|
|
GREEN='\033[1;32m'
|
|
WHITE_R='\033[39m'
|
|
|
|
###############################################################################
|
|
# Constants
|
|
###############################################################################
|
|
|
|
NOMAD_DIR="/opt/project-nomad"
|
|
COMPOSE_FILE="${NOMAD_DIR}/compose.yml"
|
|
COMPOSE_PROJECT_NAME="project-nomad"
|
|
|
|
###############################################################################
|
|
# Pre-flight Checks
|
|
###############################################################################
|
|
|
|
check_is_bash() {
|
|
if [[ -z "$BASH_VERSION" ]]; then
|
|
echo -e "${RED}#${RESET} This script must be run with bash."
|
|
echo -e "${RED}#${RESET} Example: bash $(basename "$0")"
|
|
exit 1
|
|
fi
|
|
echo -e "${GREEN}#${RESET} Running in bash.\n"
|
|
}
|
|
|
|
check_has_sudo() {
|
|
if sudo -n true 2>/dev/null; then
|
|
echo -e "${GREEN}#${RESET} Sudo permissions confirmed.\n"
|
|
else
|
|
echo -e "${RED}#${RESET} This script requires sudo permissions."
|
|
echo -e "${RED}#${RESET} Example: sudo bash $(basename "$0")"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_confirmation() {
|
|
echo -e "${YELLOW}#${RESET} This script migrates your Project NOMAD installation from the"
|
|
echo -e "${YELLOW}#${RESET} host-based disk info collector to the new disk-collector sidecar."
|
|
echo -e "${YELLOW}#${RESET} It will modify compose.yml and restart the full compose stack"
|
|
echo -e "${YELLOW}#${RESET} to drop the old /tmp bind mount and start the disk-collector sidecar."
|
|
echo -e "${YELLOW}#${RESET} Please ensure you have a backup of your data before proceeding.\n"
|
|
|
|
echo -e "${RED}#${RESET} STOP: If you have customized your compose.yml or NOMAD's storage setup (not common), please make these changes manually instead of using this script!\n"
|
|
read -rp "Do you want to continue? (y/N) " response
|
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
|
echo -e "${RED}#${RESET} Aborting. No changes have been made."
|
|
exit 0
|
|
fi
|
|
echo -e "${GREEN}#${RESET} Confirmation received. Proceeding with migration...\n"
|
|
}
|
|
|
|
check_docker_running() {
|
|
if ! command -v docker &>/dev/null; then
|
|
echo -e "${RED}#${RESET} Docker is not installed. Cannot proceed."
|
|
exit 1
|
|
fi
|
|
if ! systemctl is-active --quiet docker; then
|
|
echo -e "${RED}#${RESET} Docker is not running. Please start Docker and try again."
|
|
exit 1
|
|
fi
|
|
echo -e "${GREEN}#${RESET} Docker is running.\n"
|
|
}
|
|
|
|
check_compose_file() {
|
|
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
|
echo -e "${RED}#${RESET} compose.yml not found at ${COMPOSE_FILE}."
|
|
echo -e "${RED}#${RESET} Project NOMAD does not appear to be installed or compose.yml is missing."
|
|
exit 1
|
|
fi
|
|
echo -e "${GREEN}#${RESET} Found compose.yml at ${COMPOSE_FILE}.\n"
|
|
}
|
|
|
|
# Step 1: Stop old host process
|
|
stop_old_host_process() {
|
|
local pid_file="${NOMAD_DIR}/nomad-collect-disk-info.pid"
|
|
|
|
if [[ -f "$pid_file" ]]; then
|
|
echo -e "${YELLOW}#${RESET} Stopping old collect-disk-info background process..."
|
|
local pid
|
|
pid=$(cat "$pid_file")
|
|
if kill "$pid" 2>/dev/null; then
|
|
echo -e "${GREEN}#${RESET} Process ${pid} stopped.\n"
|
|
else
|
|
echo -e "${YELLOW}#${RESET} Process ${pid} was not running (already stopped).\n"
|
|
fi
|
|
rm -f "$pid_file"
|
|
else
|
|
echo -e "${GREEN}#${RESET} No old collect-disk-info PID file found — nothing to stop.\n"
|
|
fi
|
|
}
|
|
|
|
# Step 2: Backup compose.yml
|
|
backup_compose_file() {
|
|
local backup="${COMPOSE_FILE}.bak.$(date +%Y%m%d%H%M%S)"
|
|
echo -e "${YELLOW}#${RESET} Backing up compose.yml to ${backup}..."
|
|
if cp "$COMPOSE_FILE" "$backup"; then
|
|
echo -e "${GREEN}#${RESET} Backup created at ${backup}.\n"
|
|
else
|
|
echo -e "${RED}#${RESET} Failed to create backup. Aborting."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Step 3: Remove old bind-mount from admin volumes
|
|
remove_old_bind_mount() {
|
|
if ! grep -q 'nomad-disk-info\.json' "$COMPOSE_FILE"; then
|
|
echo -e "${GREEN}#${RESET} Old /tmp/nomad-disk-info.json bind-mount not found — already removed.\n"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${YELLOW}#${RESET} Removing old /tmp/nomad-disk-info.json bind-mount from admin volumes..."
|
|
sed -i '/\/tmp\/nomad-disk-info\.json:\/app\/storage\/nomad-disk-info\.json/d' "$COMPOSE_FILE"
|
|
|
|
if grep -q 'nomad-disk-info\.json' "$COMPOSE_FILE"; then
|
|
echo -e "${RED}#${RESET} Failed to remove old bind-mount from compose.yml. Please remove it manually:"
|
|
echo -e "${WHITE_R} - /tmp/nomad-disk-info.json:/app/storage/nomad-disk-info.json${RESET}"
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${GREEN}#${RESET} Old bind-mount removed.\n"
|
|
}
|
|
|
|
# Step 4: Add disk-collector service block
|
|
add_disk_collector_service() {
|
|
if grep -q 'disk-collector:' "$COMPOSE_FILE"; then
|
|
echo -e "${GREEN}#${RESET} disk-collector service already present in compose.yml — skipping.\n"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${YELLOW}#${RESET} Adding disk-collector service to compose.yml..."
|
|
|
|
# Insert the disk-collector service block before the top-level `volumes:` key
|
|
awk '/^volumes:/{
|
|
print " disk-collector:"
|
|
print " image: ghcr.io/crosstalk-solutions/project-nomad-disk-collector:latest"
|
|
print " pull_policy: always"
|
|
print " container_name: nomad_disk_collector"
|
|
print " restart: unless-stopped"
|
|
print " volumes:"
|
|
print " - /:/host:ro,rslave # Read-only view of host FS with rslave propagation so /sys and /proc submounts are visible"
|
|
print " - /opt/project-nomad/storage:/storage # Shared storage dir — disk info written here is read by the admin container"
|
|
print ""
|
|
}
|
|
{print}' "$COMPOSE_FILE" > "${COMPOSE_FILE}.tmp" && mv "${COMPOSE_FILE}.tmp" "$COMPOSE_FILE"
|
|
|
|
if ! grep -q 'disk-collector:' "$COMPOSE_FILE"; then
|
|
echo -e "${RED}#${RESET} Failed to add disk-collector service. Please add it manually before the top-level volumes: key."
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${GREEN}#${RESET} disk-collector service added.\n"
|
|
}
|
|
|
|
# Step 5 — Pull new image and restart the full stack
|
|
# This will re-create the admin container and drop the old /tmp bind, and
|
|
# also starts the new disk-collector sidecar we just added to compose.yml
|
|
restart_stack() {
|
|
echo -e "${YELLOW}#${RESET} Pulling latest images (including disk-collector)..."
|
|
if ! docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" pull; then
|
|
echo -e "${RED}#${RESET} Failed to pull images. Check your network connection."
|
|
exit 1
|
|
fi
|
|
echo -e "${GREEN}#${RESET} Images pulled.\n"
|
|
|
|
echo -e "${YELLOW}#${RESET} Restarting stack..."
|
|
if ! docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" up -d; then
|
|
echo -e "${RED}#${RESET} Failed to bring the stack up."
|
|
exit 1
|
|
fi
|
|
echo -e "${GREEN}#${RESET} Stack restarted.\n"
|
|
}
|
|
|
|
# Step 6: Verify
|
|
verify_disk_collector_running() {
|
|
sleep 3
|
|
if docker ps --filter "name=^nomad_disk_collector$" --filter "status=running" --format '{{.Names}}' | grep -qx "nomad_disk_collector"; then
|
|
echo -e "${GREEN}#${RESET} disk-collector container is running.\n"
|
|
else
|
|
echo -e "${RED}#${RESET} disk-collector container does not appear to be running."
|
|
echo -e "${RED}#${RESET} Check its logs with: docker logs nomad_disk_collector"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Main
|
|
echo -e "${GREEN}#########################################################################${RESET}"
|
|
echo -e "${GREEN}#${RESET} Project NOMAD — Disk Collector Migration Script ${GREEN}#${RESET}"
|
|
echo -e "${GREEN}#########################################################################${RESET}\n"
|
|
|
|
check_is_bash
|
|
check_has_sudo
|
|
check_confirmation
|
|
check_docker_running
|
|
check_compose_file
|
|
|
|
echo -e "${YELLOW}#${RESET} Step 1: Stopping old host process...\n"
|
|
stop_old_host_process
|
|
|
|
echo -e "${YELLOW}#${RESET} Step 2: Backing up compose.yml...\n"
|
|
backup_compose_file
|
|
|
|
echo -e "${YELLOW}#${RESET} Step 3: Removing old bind-mount...\n"
|
|
remove_old_bind_mount
|
|
|
|
echo -e "${YELLOW}#${RESET} Step 4: Adding disk-collector service...\n"
|
|
add_disk_collector_service
|
|
|
|
echo -e "${YELLOW}#${RESET} Step 5: Pulling images and restarting stack...\n"
|
|
restart_stack
|
|
|
|
echo -e "${YELLOW}#${RESET} Step 6: Verifying disk-collector is running...\n"
|
|
verify_disk_collector_running
|
|
|
|
echo -e "${GREEN}#########################################################################${RESET}"
|
|
echo -e "${GREEN}#${RESET} Migration completed successfully!"
|
|
echo -e "${GREEN}#${RESET}"
|
|
echo -e "${GREEN}#${RESET} The disk-collector sidecar is now running and will update disk info"
|
|
echo -e "${GREEN}#${RESET} every 2 minutes. The /api/system/info endpoint will return disk data"
|
|
echo -e "${GREEN}#${RESET} after the first collector write (~5 seconds after startup)."
|
|
echo -e "${GREEN}#${RESET}"
|
|
echo -e "${GREEN}#########################################################################${RESET}\n"
|