project-nomad/install/sidecar-updater/update-watcher.sh
Matt Van Horn 3cbf7e0acf
feat(docker): replace Redis with Valkey for open-source compliance
Replace redis:7-alpine with valkey/valkey:8-alpine across the management
compose stack. Valkey is the Linux Foundation-backed BSD-3-Clause fork,
maintaining full API compatibility with Redis 7.x.

Changes:
- Swap Docker image to valkey/valkey:8-alpine
- Rename service and container (redis -> valkey, nomad_redis -> nomad_valkey)
- Update health check to use valkey-cli
- Update REDIS_HOST env var to point to new service name
- Update sidecar-updater service list

AdonisJS reads REDIS_HOST from env, so zero application code changes
are needed. BullMQ job queues work identically on Valkey.

Closes #276

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 16:19:00 -07:00

151 lines
4.8 KiB
Bash

#!/bin/bash
# Project N.O.M.A.D. Update Sidecar - Polls for update requests and executes them
SHARED_DIR="/shared"
REQUEST_FILE="${SHARED_DIR}/update-request"
STATUS_FILE="${SHARED_DIR}/update-status"
LOG_FILE="${SHARED_DIR}/update-log"
COMPOSE_FILE="/opt/project-nomad/compose.yml"
COMPOSE_PROJECT_NAME="project-nomad"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
write_status() {
local stage="$1"
local progress="$2"
local message="$3"
cat > "$STATUS_FILE" <<EOF
{
"stage": "$stage",
"progress": $progress,
"message": "$message",
"timestamp": "$(date -Iseconds)"
}
EOF
}
perform_update() {
local target_tag="$1"
log "Update request received - starting system update (target tag: ${target_tag})"
# Clear old logs
> "$LOG_FILE"
# Stage 1: Starting
write_status "starting" 0 "System update initiated"
log "System update initiated"
sleep 1
# Apply target image tag to compose.yml before pulling
log "Applying image tag '${target_tag}' to compose.yml..."
if sed -i "s|\(image: ghcr\.io/crosstalk-solutions/project-nomad\):.*|\1:${target_tag}|" "$COMPOSE_FILE" 2>> "$LOG_FILE"; then
log "Successfully updated compose.yml admin image tag to '${target_tag}'"
else
log "ERROR: Failed to update compose.yml image tag"
write_status "error" 0 "Failed to update compose.yml image tag - check logs"
return 1
fi
# Stage 2: Pulling images
write_status "pulling" 20 "Pulling latest Docker images..."
log "Pulling latest Docker images..."
if docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" pull >> "$LOG_FILE" 2>&1; then
log "Successfully pulled latest images"
write_status "pulled" 60 "Images pulled successfully"
else
log "ERROR: Failed to pull images"
write_status "error" 0 "Failed to pull Docker images - check logs"
return 1
fi
sleep 2
# Stage 3: Recreating containers individually (excluding updater)
write_status "recreating" 65 "Recreating containers individually..."
log "Recreating containers individually (excluding updater)..."
# List of services to update (excluding updater)
SERVICES_TO_UPDATE="admin mysql valkey dozzle"
local current_progress=65
local progress_per_service=8 # (95 - 65) / 4 services ≈ 8% per service
for service in $SERVICES_TO_UPDATE; do
log "Updating service: $service"
write_status "recreating" $current_progress "Recreating $service..."
# Stop the service
log " Stopping $service..."
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" stop "$service" >> "$LOG_FILE" 2>&1 || log " WARNING: Failed to stop $service"
# Remove the container
log " Removing old $service container..."
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" rm -f "$service" >> "$LOG_FILE" 2>&1 || log " WARNING: Failed to remove $service"
# Recreate and start with new image
log " Starting new $service container..."
if docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" up -d --no-deps "$service" >> "$LOG_FILE" 2>&1; then
log " ✓ Successfully recreated $service"
else
log " ERROR: Failed to recreate $service"
write_status "error" $current_progress "Failed to recreate $service - check logs"
return 1
fi
current_progress=$((current_progress + progress_per_service))
done
log "Successfully recreated all containers"
write_status "complete" 100 "System update completed successfully"
log "System update completed successfully"
return 0
}
cleanup() {
log "Update sidecar shutting down"
exit 0
}
trap cleanup SIGTERM SIGINT
# Main watch loop
log "Update sidecar started - watching for update requests"
write_status "idle" 0 "Ready for update requests"
while true; do
# Check if an update request file exists
if [ -f "$REQUEST_FILE" ]; then
log "Found update request file"
# Read request details
REQUEST_DATA=$(cat "$REQUEST_FILE" 2>/dev/null || echo "{}")
log "Request data: $REQUEST_DATA"
# Extract target tag from request (defaults to "latest" if not provided)
TARGET_TAG=$(echo "$REQUEST_DATA" | jq -r '.target_tag // "latest"')
log "Target image tag: ${TARGET_TAG}"
# Remove the request file to prevent re-processing
rm -f "$REQUEST_FILE"
if perform_update "$TARGET_TAG"; then
log "Update completed successfully"
else
log "Update failed - see logs for details"
fi
sleep 5
write_status "idle" 0 "Ready for update requests"
fi
# Sleep before next check (1 second polling)
sleep 1
done