- netboot-base.nix with SSH key auth - Launch scripts for node01/02/03 - Node configuration.nix and disko.nix - Nix modules for first-boot automation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
89 lines
2.9 KiB
Bash
Executable file
89 lines
2.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
# bootstrap-detector.sh - Detects if node should bootstrap or join cluster
|
|
# Usage: bootstrap-detector.sh [config_file]
|
|
#
|
|
# Arguments:
|
|
# config_file - Path to cluster-config.json (default: /etc/nixos/secrets/cluster-config.json)
|
|
#
|
|
# Returns:
|
|
# 0 - Node should bootstrap (initialize new cluster)
|
|
# 1 - Node should join existing cluster
|
|
# 2 - Error (invalid config or missing file)
|
|
|
|
CONFIG_FILE="${1:-/etc/nixos/secrets/cluster-config.json}"
|
|
FIRST_BOOT_MARKER="/var/lib/first-boot-automation/.initialized"
|
|
|
|
# Logging function with JSON output
|
|
log() {
|
|
local level="$1"
|
|
local message="$2"
|
|
local timestamp
|
|
timestamp=$(date -Iseconds)
|
|
|
|
echo "{\"timestamp\":\"$timestamp\",\"level\":\"$level\",\"component\":\"bootstrap-detector\",\"message\":\"$message\"}" >&2
|
|
}
|
|
|
|
# Validate config file exists
|
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
log "ERROR" "Configuration file not found: $CONFIG_FILE"
|
|
exit 2
|
|
fi
|
|
|
|
# Parse JSON config
|
|
log "INFO" "Reading configuration from $CONFIG_FILE"
|
|
|
|
if ! CONFIG_JSON=$(cat "$CONFIG_FILE"); then
|
|
log "ERROR" "Failed to read configuration file"
|
|
exit 2
|
|
fi
|
|
|
|
# Extract bootstrap flag using jq (fallback to grep if jq not available)
|
|
if command -v jq &> /dev/null; then
|
|
BOOTSTRAP=$(echo "$CONFIG_JSON" | jq -r '.bootstrap // false')
|
|
NODE_ID=$(echo "$CONFIG_JSON" | jq -r '.node_id // "unknown"')
|
|
NODE_ROLE=$(echo "$CONFIG_JSON" | jq -r '.node_role // "unknown"')
|
|
else
|
|
# Fallback to grep/sed for minimal environments
|
|
BOOTSTRAP=$(echo "$CONFIG_JSON" | grep -oP '"bootstrap"\s*:\s*\K(true|false)' || echo "false")
|
|
NODE_ID=$(echo "$CONFIG_JSON" | grep -oP '"node_id"\s*:\s*"\K[^"]+' || echo "unknown")
|
|
NODE_ROLE=$(echo "$CONFIG_JSON" | grep -oP '"node_role"\s*:\s*"\K[^"]+' || echo "unknown")
|
|
fi
|
|
|
|
log "INFO" "Node configuration: id=$NODE_ID, role=$NODE_ROLE, bootstrap=$BOOTSTRAP"
|
|
|
|
# Check if this is a reboot (marker file exists)
|
|
if [[ -f "$FIRST_BOOT_MARKER" ]]; then
|
|
log "INFO" "First-boot marker found, this is a reboot - skipping cluster join"
|
|
|
|
# Read marker info
|
|
if [[ -r "$FIRST_BOOT_MARKER" ]]; then
|
|
MARKER_TIMESTAMP=$(cat "$FIRST_BOOT_MARKER")
|
|
log "INFO" "Node initialized at: $MARKER_TIMESTAMP"
|
|
fi
|
|
|
|
# Always join for reboots (clusters should already be initialized)
|
|
exit 1
|
|
fi
|
|
|
|
# First boot logic
|
|
log "INFO" "First boot detected (no marker file)"
|
|
|
|
# Decision based on bootstrap flag
|
|
if [[ "$BOOTSTRAP" == "true" ]]; then
|
|
log "INFO" "Bootstrap mode enabled - node will initialize new cluster"
|
|
|
|
# Create marker directory and file to track initialization
|
|
mkdir -p "$(dirname "$FIRST_BOOT_MARKER")"
|
|
date -Iseconds > "$FIRST_BOOT_MARKER"
|
|
|
|
exit 0 # Bootstrap
|
|
else
|
|
log "INFO" "Join mode enabled - node will join existing cluster"
|
|
|
|
# Create marker after successful join (done by cluster-join.sh)
|
|
# For now, just return join status
|
|
exit 1 # Join existing
|
|
fi
|