mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-20 03:11:46 +00:00
Merge branch 'feat/iso-installer' of github.com:mulgadc/spinifex into feat/iso-installer
This commit is contained in:
+20
-3
@@ -4,6 +4,14 @@
|
||||
# (user creation, directories, systemd units), initializes the cluster,
|
||||
# and starts services via systemd.
|
||||
#
|
||||
# Environment variables:
|
||||
# SPINIFEX_REGION Region for spx admin init (default: ap-southeast-2)
|
||||
# SPINIFEX_AZ AZ for spx admin init (default: ${SPINIFEX_REGION}a)
|
||||
# DEV_INSTALL_SKIP_INIT Internal flag used by reset-dev-env.sh. When 1,
|
||||
# stops after setup.sh and skips setup-ovn, init,
|
||||
# CA trust, service start, and LB image import —
|
||||
# caller owns those steps. Do not set manually.
|
||||
#
|
||||
# Usage: ./scripts/dev-install.sh
|
||||
set -euo pipefail
|
||||
|
||||
@@ -31,7 +39,7 @@ tar czf /tmp/spinifex-local.tar.gz -C "$STAGING" .
|
||||
echo "=== Cleaning stale state from previous installs ==="
|
||||
# Stop any running services before modifying files
|
||||
sudo systemctl stop spinifex.target 2>/dev/null || true
|
||||
sudo systemctl reset-failed 2>/dev/null || true
|
||||
sudo systemctl reset-failed 'spinifex-*' 2>/dev/null || true
|
||||
|
||||
# Remove stale files owned by the dev user in production paths.
|
||||
# Previous dev-mode installs (admin init without sudo, start-dev.sh) leave
|
||||
@@ -54,11 +62,20 @@ echo "=== Running setup.sh (creates users, dirs, systemd units) ==="
|
||||
sudo INSTALL_SPINIFEX_TARBALL=/tmp/spinifex-local.tar.gz INSTALL_SPINIFEX_SKIP_NEWGRP=1 bash "$PROJECT_ROOT/scripts/setup.sh"
|
||||
rm -f /tmp/spinifex-local.tar.gz
|
||||
|
||||
if [ "${DEV_INSTALL_SKIP_INIT:-0}" = "1" ]; then
|
||||
echo "=== DEV_INSTALL_SKIP_INIT=1 — stopping after setup.sh ==="
|
||||
echo "Caller owns: setup-ovn, spx admin init, CA trust, service start, LB image."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "=== Setting up OVN ==="
|
||||
sudo /usr/local/share/spinifex/setup-ovn.sh --management
|
||||
|
||||
echo "=== Initializing ==="
|
||||
sudo spx admin init --force --region ap-southeast-2 --az ap-southeast-2a --node node1 --nodes 1
|
||||
SPINIFEX_REGION="${SPINIFEX_REGION:-ap-southeast-2}"
|
||||
SPINIFEX_AZ="${SPINIFEX_AZ:-${SPINIFEX_REGION}a}"
|
||||
|
||||
echo "=== Initializing (region=$SPINIFEX_REGION az=$SPINIFEX_AZ) ==="
|
||||
sudo spx admin init --force --region "$SPINIFEX_REGION" --az "$SPINIFEX_AZ" --node node1 --nodes 1
|
||||
|
||||
echo "=== Installing CA certificate ==="
|
||||
sudo cp /etc/spinifex/ca.pem /usr/local/share/ca-certificates/spinifex-ca.crt
|
||||
|
||||
+231
-207
@@ -1,15 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reset dev environment (will purge all data)
|
||||
# This script is for single-node dev environments only.
|
||||
# reset-dev-env.sh — Single-node dev environment reset.
|
||||
#
|
||||
# Stops services, wipes production state (/etc/spinifex, /var/lib/spinifex,
|
||||
# /var/log/spinifex, /run/spinifex), rebuilds from source, reinstalls via
|
||||
# dev-install.sh, and relaunches a smoke-test instance.
|
||||
#
|
||||
# Preserves node/network settings from the existing /etc/spinifex/spinifex.toml
|
||||
# so the reset restores the current topology (region, AZ, external mode,
|
||||
# pool range) rather than applying defaults. On a fresh box with no config,
|
||||
# falls back to ap-southeast-2 + pool mode derived from the WAN subnet.
|
||||
#
|
||||
# WARNING: setup-ovn.sh converts the WAN NIC into an OVS bridge. On hosts
|
||||
# where the WAN NIC is also the SSH NIC, SSH will drop mid-run. Run from
|
||||
# the console or via a separate management NIC.
|
||||
#
|
||||
# Single-node only. Refuses on multi-node clusters.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE="$HOME/spinifex/config/spinifex.toml"
|
||||
# --- Paths (production layout from setup.sh) ---
|
||||
ETC_DIR=/etc/spinifex
|
||||
DATA_DIR=/var/lib/spinifex
|
||||
LOG_DIR=/var/log/spinifex
|
||||
RUN_DIR=/run/spinifex
|
||||
CONFIG_FILE="$ETC_DIR/spinifex.toml"
|
||||
|
||||
# --- Script context ---
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Resolve invoking user's HOME. $HOME is unsafe under sudo (resolves to /root).
|
||||
INVOKING_USER="${SUDO_USER:-$(id -un)}"
|
||||
INVOKING_HOME=$(getent passwd "$INVOKING_USER" | cut -d: -f6)
|
||||
if [ -z "$INVOKING_HOME" ]; then
|
||||
echo "❌ Could not resolve home directory for user: $INVOKING_USER"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Guard: refuse to run on multi-node clusters ---
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
NODE_COUNT=$(grep -cE '^\[nodes\.[^.]+\]' "$CONFIG_FILE")
|
||||
if sudo test -f "$CONFIG_FILE"; then
|
||||
NODE_COUNT=$(sudo grep -cE '^\[nodes\.[^.]+\]' "$CONFIG_FILE" 2>/dev/null || echo 0)
|
||||
if [ "$NODE_COUNT" -gt 1 ]; then
|
||||
echo "❌ Multi-node cluster detected ($NODE_COUNT nodes in $CONFIG_FILE)."
|
||||
echo " This script only supports single-node dev environments."
|
||||
@@ -18,266 +48,260 @@ if [ -f "$CONFIG_FILE" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Save region from existing config before we delete everything
|
||||
REGION="ap-southeast-2"
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
SAVED_REGION=$(grep -E '^\s*region\s*=' "$CONFIG_FILE" | tail -1 | sed 's/.*=\s*"\(.*\)"/\1/')
|
||||
if [ -n "$SAVED_REGION" ]; then
|
||||
REGION="$SAVED_REGION"
|
||||
fi
|
||||
# --- Capture settings from existing config ---
|
||||
# Parse before the wipe. Missing fields fall back to defaults below.
|
||||
REGION=""
|
||||
AZ=""
|
||||
EXT_MODE=""
|
||||
POOL_START=""
|
||||
POOL_END=""
|
||||
EXT_GATEWAY=""
|
||||
EXT_PREFIX=""
|
||||
|
||||
if sudo test -f "$CONFIG_FILE"; then
|
||||
# Copy config to a temp file we can read without sudo for cleaner parsing.
|
||||
TMP_CFG=$(mktemp)
|
||||
trap 'rm -f "$TMP_CFG"' EXIT
|
||||
sudo cat "$CONFIG_FILE" > "$TMP_CFG"
|
||||
|
||||
# toml_scalar <section-regex> <key> — grabs first matching scalar from the
|
||||
# first matching section. Strips surrounding quotes if present.
|
||||
toml_scalar() {
|
||||
local section_re="$1"
|
||||
local key="$2"
|
||||
awk -v sec="$section_re" -v key="$key" '
|
||||
$0 ~ sec { in_sec=1; next }
|
||||
/^\[/ { in_sec=0 }
|
||||
in_sec && $0 ~ "^[[:space:]]*" key "[[:space:]]*=" {
|
||||
sub(/^[^=]*=[[:space:]]*/, "")
|
||||
gsub(/[[:space:]]*#.*$/, "")
|
||||
gsub(/^"/, ""); gsub(/"$/, "")
|
||||
print; exit
|
||||
}' "$TMP_CFG"
|
||||
}
|
||||
|
||||
REGION=$(toml_scalar '^\[nodes\.' 'region')
|
||||
AZ=$(toml_scalar '^\[nodes\.' 'az')
|
||||
EXT_MODE=$(toml_scalar '^\[network\]' 'external_mode')
|
||||
POOL_START=$(toml_scalar '^\[\[network\.external_pools\]\]' 'range_start')
|
||||
POOL_END=$(toml_scalar '^\[\[network\.external_pools\]\]' 'range_end')
|
||||
EXT_GATEWAY=$(toml_scalar '^\[\[network\.external_pools\]\]' 'gateway')
|
||||
EXT_PREFIX=$(toml_scalar '^\[\[network\.external_pools\]\]' 'prefix_len')
|
||||
fi
|
||||
|
||||
# Defaults
|
||||
REGION="${REGION:-ap-southeast-2}"
|
||||
AZ="${AZ:-${REGION}a}"
|
||||
EXT_MODE="${EXT_MODE:-pool}"
|
||||
|
||||
echo "Preserving: region=$REGION az=$AZ external_mode=$EXT_MODE"
|
||||
if [ "$EXT_MODE" = "pool" ] && [ -n "$POOL_START" ]; then
|
||||
echo " pool: $POOL_START - $POOL_END gw=$EXT_GATEWAY prefix=$EXT_PREFIX"
|
||||
fi
|
||||
echo "Using region: $REGION"
|
||||
|
||||
# --- Shutdown services ---
|
||||
echo "Shutting down services"
|
||||
echo "==> Stopping services"
|
||||
sudo systemctl stop spinifex.target 2>/dev/null || true
|
||||
sudo systemctl reset-failed 'spinifex-*' 2>/dev/null || true
|
||||
sudo pkill -x qemu-system-x86_64 2>/dev/null || true
|
||||
sudo pkill -x qemu-system-aarch64 2>/dev/null || true
|
||||
|
||||
if ! ./scripts/stop-dev.sh; then
|
||||
echo "❌ Failed to stop services. Aborting reset to prevent data loss."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify no spinifex-related processes are still running
|
||||
PROCESS_PATTERN='(bin/spx|spinifex-ui|nats-server|predastore|viperblock|vpcd)'
|
||||
remaining=$(pgrep -af "$PROCESS_PATTERN" | grep -v "reset-dev-env.sh" || true)
|
||||
if [ -n "$remaining" ]; then
|
||||
echo "Waiting for remaining processes to exit..."
|
||||
timeout=30
|
||||
elapsed=0
|
||||
while pgrep -af "$PROCESS_PATTERN" | grep -qv "reset-dev-env.sh" 2>/dev/null; do
|
||||
if [ $elapsed -ge $timeout ]; then
|
||||
echo "❌ Processes still running after ${timeout}s:"
|
||||
pgrep -af "$PROCESS_PATTERN" | grep -v "reset-dev-env.sh" || true
|
||||
echo " Kill them manually and re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
elapsed=$((elapsed + 1))
|
||||
done
|
||||
fi
|
||||
|
||||
# Verify no QEMU VMs are running
|
||||
if pgrep -x qemu-system-x86_64 > /dev/null 2>&1; then
|
||||
echo "❌ QEMU instances still running. Cannot reset while VMs are active."
|
||||
echo " Run './scripts/stop-dev.sh' or kill them manually."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All services confirmed stopped"
|
||||
|
||||
# --- Clean OVS/OVN ---
|
||||
echo "Removing OVS bridges and config"
|
||||
# Wait for QEMU to fully exit before tearing down viperblock state.
|
||||
timeout=30
|
||||
elapsed=0
|
||||
while pgrep -x 'qemu-system-x86_64|qemu-system-aarch64' > /dev/null 2>&1; do
|
||||
if [ "$elapsed" -ge "$timeout" ]; then
|
||||
echo "❌ QEMU still running after ${timeout}s:"
|
||||
pgrep -af 'qemu-system-' || true
|
||||
echo " Kill them manually and re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
elapsed=$((elapsed + 1))
|
||||
done
|
||||
|
||||
# --- Clean OVS / OVN ---
|
||||
echo "==> Cleaning OVS bridges"
|
||||
if command -v ovs-vsctl >/dev/null 2>&1; then
|
||||
# Ensure OVS is running so we can clean up
|
||||
sudo systemctl start openvswitch-switch 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Delete all OVS bridges (br-int, br-external, etc.)
|
||||
for br in $(sudo ovs-vsctl list-br 2>/dev/null); do
|
||||
echo " Deleting bridge: $br"
|
||||
sudo ovs-vsctl --if-exists del-br "$br"
|
||||
done
|
||||
|
||||
# Clear OVN external_ids
|
||||
sudo ovs-vsctl --if-exists clear Open_vSwitch . external_ids 2>/dev/null || true
|
||||
echo " Cleared OVS external_ids"
|
||||
|
||||
# Stop OVS again after cleanup
|
||||
sudo systemctl stop openvswitch-switch 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Clean OVN databases (both Northbound and Southbound)
|
||||
# Delete the DB files outright — setup-ovn.sh will restart ovn-central with fresh
|
||||
# empty databases. This eliminates stale SB state (chassis entries, port bindings,
|
||||
# datapath bindings) that accumulates across resets and causes ovn-controller to
|
||||
# enter a commit failure loop ("OVNSB commit failed, force recompute").
|
||||
echo "Removing OVN database files"
|
||||
# Delete OVN DB files outright — setup-ovn.sh will restart ovn-central with
|
||||
# fresh empty DBs. Eliminates stale SB state (chassis entries, port bindings)
|
||||
# that accumulates across resets and triggers ovn-controller commit loops.
|
||||
echo "==> Removing OVN database files"
|
||||
sudo systemctl stop ovn-central 2>/dev/null || true
|
||||
sudo systemctl stop ovn-controller 2>/dev/null || true
|
||||
if [ -d /var/lib/ovn ]; then
|
||||
sudo rm -f /var/lib/ovn/ovnnb_db.db /var/lib/ovn/ovnsb_db.db
|
||||
echo " Deleted /var/lib/ovn/ovn{nb,sb}_db.db"
|
||||
else
|
||||
echo " /var/lib/ovn not found, skipping OVN DB cleanup"
|
||||
fi
|
||||
|
||||
# Remove veth pair created by setup-ovn.sh (veth mode — Linux bridge ↔ OVS bridge)
|
||||
# veth pair created by setup-ovn.sh (Linux bridge ↔ OVS bridge)
|
||||
if ip link show veth-wan-br >/dev/null 2>&1; then
|
||||
echo " Deleting veth pair: veth-wan-br ↔ veth-wan-ovs"
|
||||
sudo ip link del veth-wan-br 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Remove macvlan interfaces created by setup-ovn.sh
|
||||
for iface in $(ip -o link show type macvlan 2>/dev/null | awk -F': ' '{print $2}' | grep '^spx-ext-'); do
|
||||
echo " Deleting macvlan: $iface"
|
||||
sudo ip link del "$iface" 2>/dev/null || true
|
||||
done
|
||||
# --- Wipe production state ---
|
||||
echo "==> Wiping $ETC_DIR $DATA_DIR $LOG_DIR $RUN_DIR"
|
||||
sudo rm -rf "$ETC_DIR" "$DATA_DIR" "$LOG_DIR" "$RUN_DIR"
|
||||
|
||||
# --- Wipe data ---
|
||||
echo "Removing ~/spinifex"
|
||||
rm -rf ~/spinifex
|
||||
|
||||
# --- Re-initialize ---
|
||||
# Detect WAN interface (default route).
|
||||
WAN_IFACE=$(ip -4 route show default | awk '{print $5}' | head -1)
|
||||
WAN_GW=$(ip -4 route show default | awk '{print $3}' | head -1)
|
||||
|
||||
echo "Detected WAN interface: ${WAN_IFACE:-none}, gateway: ${WAN_GW:-none}"
|
||||
|
||||
# Check if WAN interface is already a bridge (tofu-cluster / production setup).
|
||||
# If so, setup-ovn.sh auto-detects it. If it's a physical NIC, we need to
|
||||
# decide between direct bridge and macvlan based on SSH safety.
|
||||
WAN_IS_BRIDGE=false
|
||||
if [ -n "$WAN_IFACE" ]; then
|
||||
if ip -d link show "$WAN_IFACE" 2>/dev/null | grep -q "bridge"; then
|
||||
WAN_IS_BRIDGE=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build setup-ovn.sh flags based on detected topology
|
||||
SETUP_OVN_FLAGS=""
|
||||
if [ "$WAN_IS_BRIDGE" = true ]; then
|
||||
# WAN is already a bridge (e.g. br-wan from cloud-init) — setup-ovn.sh
|
||||
# auto-detects it, no explicit flags needed.
|
||||
echo " WAN is a bridge: $WAN_IFACE (auto-detected)"
|
||||
elif [ -n "$WAN_IFACE" ]; then
|
||||
# WAN is a physical NIC. Detect SSH NIC to decide bridge strategy.
|
||||
SSH_NIC=""
|
||||
if [ -n "${SSH_CONNECTION:-}" ]; then
|
||||
SSH_IP=$(echo "$SSH_CONNECTION" | awk '{print $3}')
|
||||
SSH_NIC=$(ip -o -4 addr show | awk -v ip="$SSH_IP" '$0 ~ ip"/" {print $2}' | head -1)
|
||||
fi
|
||||
if [ -z "$SSH_NIC" ]; then
|
||||
SSH_NIC="$WAN_IFACE"
|
||||
fi
|
||||
|
||||
if [ "$WAN_IFACE" != "$SSH_NIC" ]; then
|
||||
SETUP_OVN_FLAGS="--wan-bridge=br-wan --wan-iface=$WAN_IFACE"
|
||||
echo " Bridge mode: direct (WAN=$WAN_IFACE != SSH=$SSH_NIC)"
|
||||
else
|
||||
SETUP_OVN_FLAGS="--macvlan --wan-iface=$WAN_IFACE"
|
||||
echo " Bridge mode: macvlan (WAN=$WAN_IFACE == SSH=$SSH_NIC)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Chassis ID — let setup-ovn.sh auto-detect from hostname. This ensures the
|
||||
# system-id matches what ovn-controller registers in the SBDB, which is what
|
||||
# vpcd discovers at startup for gateway scheduling.
|
||||
|
||||
echo "Re-initializing OVN"
|
||||
./scripts/setup-ovn.sh --management $SETUP_OVN_FLAGS
|
||||
|
||||
echo "Initializing platform"
|
||||
ADMIN_INIT_ARGS="--region $REGION --az ${REGION}a --node node1 --nodes 1"
|
||||
|
||||
# External networking mode: set EXTERNAL_MODE=nat for NAT/DHCP mode (outbound-only),
|
||||
# or leave unset / EXTERNAL_MODE=pool for pool mode (per-VM public IPs).
|
||||
EXTERNAL_MODE="${EXTERNAL_MODE:-pool}"
|
||||
|
||||
if [ -n "$WAN_IFACE" ] && [ -n "$WAN_GW" ]; then
|
||||
WAN_IP=$(ip -4 -o addr show "$WAN_IFACE" 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1)
|
||||
WAN_PREFIX=$(ip -4 -o addr show "$WAN_IFACE" 2>/dev/null | awk '{print $4}' | cut -d/ -f2 | head -1)
|
||||
if [ -z "$WAN_PREFIX" ]; then WAN_PREFIX=24; fi
|
||||
|
||||
if [ "$EXTERNAL_MODE" = "nat" ]; then
|
||||
# NAT mode: outbound-only via shared SNAT. Use a single IP from the
|
||||
# high end of the WAN subnet as the gateway IP. No per-VM public IPs.
|
||||
IFS='.' read -r o1 o2 o3 o4 <<< "$WAN_GW"
|
||||
GATEWAY_IP="${GATEWAY_IP:-${o1}.${o2}.${o3}.200}"
|
||||
echo " External mode: nat (gateway IP: $GATEWAY_IP, WAN gateway: $WAN_GW)"
|
||||
ADMIN_INIT_ARGS="$ADMIN_INIT_ARGS --external-mode=nat --gateway-ip=${GATEWAY_IP} --external-gateway=${WAN_GW} --external-prefix-len=${WAN_PREFIX}"
|
||||
else
|
||||
# Pool mode: per-VM public IPs from a static range
|
||||
IFS='.' read -r o1 o2 o3 o4 <<< "$WAN_GW"
|
||||
POOL_START="${o1}.${o2}.${o3}.200"
|
||||
POOL_END="${o1}.${o2}.${o3}.250"
|
||||
echo " External pool: $POOL_START - $POOL_END (gateway: $WAN_GW)"
|
||||
ADMIN_INIT_ARGS="$ADMIN_INIT_ARGS --external-mode=pool --external-pool=${POOL_START}-${POOL_END} --external-gateway=${WAN_GW} --external-prefix-len=${WAN_PREFIX}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Re-initialize with --force to regenerate credentials, certs, config, and
|
||||
# update ~/.aws/credentials. Must carry the same args so external networking
|
||||
# config isn't lost.
|
||||
echo "Re-initializing platform..."
|
||||
./bin/spx admin init --force $ADMIN_INIT_ARGS
|
||||
|
||||
# Generate SSH key if it doesn't exist
|
||||
if [ ! -f ~/.ssh/spinifex-key.pub ]; then
|
||||
echo "Generating SSH key pair..."
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/spinifex-key -N ""
|
||||
fi
|
||||
|
||||
# Trust the Spinifex CA certificate so TLS connections to predastore/awsgw work
|
||||
if [ -f "$HOME/spinifex/config/ca.pem" ]; then
|
||||
echo "Adding Spinifex CA certificate to system trust store..."
|
||||
sudo cp "$HOME/spinifex/config/ca.pem" /usr/local/share/ca-certificates/spinifex-ca.crt
|
||||
# Drop the old CA cert from the system trust store. The new init writes a
|
||||
# fresh CA which we re-install below.
|
||||
if [ -f /usr/local/share/ca-certificates/spinifex-ca.crt ]; then
|
||||
echo "==> Removing stale CA from trust store"
|
||||
sudo rm -f /usr/local/share/ca-certificates/spinifex-ca.crt
|
||||
sudo update-ca-certificates
|
||||
fi
|
||||
|
||||
# Enable pprof for development
|
||||
#PPROF_ENABLED=1 PPROF_OUTPUT=/tmp/spinifex-vm.prof ./scripts/start-dev.sh --build
|
||||
./scripts/start-dev.sh --build
|
||||
# --- Rebuild + setup.sh scaffolding (no init / start) ---
|
||||
echo "==> Rebuilding and reinstalling (dev-install.sh, setup-only mode)"
|
||||
DEV_INSTALL_SKIP_INIT=1 "$SCRIPT_DIR/dev-install.sh"
|
||||
|
||||
# --- Detect WAN topology (bridge only — no macvlan) ---
|
||||
WAN_IFACE=$(ip -4 route show default | awk '{print $5}' | head -1)
|
||||
WAN_GW=$(ip -4 route show default | awk '{print $3}' | head -1)
|
||||
WAN_PREFIX_HOST=""
|
||||
if [ -n "$WAN_IFACE" ]; then
|
||||
WAN_PREFIX_HOST=$(ip -4 -o addr show "$WAN_IFACE" 2>/dev/null | awk '{print $4}' | cut -d/ -f2 | head -1)
|
||||
fi
|
||||
WAN_PREFIX_HOST="${WAN_PREFIX_HOST:-24}"
|
||||
|
||||
SETUP_OVN_FLAGS=""
|
||||
if [ -n "$WAN_IFACE" ]; then
|
||||
if ip -d link show "$WAN_IFACE" 2>/dev/null | grep -q "bridge"; then
|
||||
echo "==> WAN is a bridge: $WAN_IFACE (setup-ovn.sh auto-detects)"
|
||||
else
|
||||
SETUP_OVN_FLAGS="--wan-bridge=br-wan --wan-iface=$WAN_IFACE"
|
||||
echo "==> WAN is physical: $WAN_IFACE (direct bridge br-wan)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "==> Running setup-ovn.sh"
|
||||
# shellcheck disable=SC2086 # intentional word-splitting for flag list
|
||||
sudo /usr/local/share/spinifex/setup-ovn.sh --management $SETUP_OVN_FLAGS
|
||||
|
||||
# --- Assemble external-networking args for spx admin init ---
|
||||
EXTERNAL_INIT_ARGS=""
|
||||
case "$EXT_MODE" in
|
||||
nat)
|
||||
# NAT gateway IP isn't captured from config (not stored in a
|
||||
# predictable location); derive from WAN subnet — same rule as
|
||||
# a fresh install. Operators who hand-edited the gateway IP will
|
||||
# need to re-apply it after reset.
|
||||
if [ -n "$WAN_GW" ]; then
|
||||
IFS='.' read -r o1 o2 o3 _ <<< "$WAN_GW"
|
||||
GATEWAY_IP="${o1}.${o2}.${o3}.200"
|
||||
EXTERNAL_INIT_ARGS="--external-mode=nat --gateway-ip=$GATEWAY_IP --external-gateway=$WAN_GW --external-prefix-len=${EXT_PREFIX:-$WAN_PREFIX_HOST}"
|
||||
echo "==> External mode: nat (gateway IP $GATEWAY_IP)"
|
||||
fi
|
||||
;;
|
||||
pool|*)
|
||||
if [ -n "$POOL_START" ] && [ -n "$POOL_END" ]; then
|
||||
EXTERNAL_INIT_ARGS="--external-mode=pool --external-pool=${POOL_START}-${POOL_END} --external-gateway=${EXT_GATEWAY:-$WAN_GW} --external-prefix-len=${EXT_PREFIX:-$WAN_PREFIX_HOST}"
|
||||
echo "==> External mode: pool (preserved $POOL_START-$POOL_END)"
|
||||
elif [ -n "$WAN_GW" ]; then
|
||||
# Fresh box: derive pool from WAN subnet.
|
||||
IFS='.' read -r o1 o2 o3 _ <<< "$WAN_GW"
|
||||
POOL_START="${o1}.${o2}.${o3}.200"
|
||||
POOL_END="${o1}.${o2}.${o3}.250"
|
||||
EXTERNAL_INIT_ARGS="--external-mode=pool --external-pool=${POOL_START}-${POOL_END} --external-gateway=$WAN_GW --external-prefix-len=$WAN_PREFIX_HOST"
|
||||
echo "==> External mode: pool (default $POOL_START-$POOL_END)"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Initialize platform ---
|
||||
echo "==> Initializing platform (region=$REGION az=$AZ)"
|
||||
# shellcheck disable=SC2086 # intentional word-splitting for arg list
|
||||
sudo /usr/local/bin/spx admin init --force \
|
||||
--region "$REGION" --az "$AZ" --node node1 --nodes 1 \
|
||||
$EXTERNAL_INIT_ARGS
|
||||
|
||||
# --- Install CA cert into system trust store ---
|
||||
echo "==> Installing CA certificate"
|
||||
sudo cp "$ETC_DIR/ca.pem" /usr/local/share/ca-certificates/spinifex-ca.crt
|
||||
sudo update-ca-certificates
|
||||
|
||||
# --- Start services ---
|
||||
echo "==> Starting spinifex.target"
|
||||
sudo systemctl start spinifex.target
|
||||
|
||||
# --- Build + import LB image (needs services running) ---
|
||||
echo "==> Building and importing LB image"
|
||||
cd "$PROJECT_ROOT" && make build-lb-agent
|
||||
"$PROJECT_ROOT/scripts/build-system-image.sh" "$PROJECT_ROOT/scripts/images/lb.conf" --import --quiet
|
||||
|
||||
# --- SSH key (generated as invoking user, not root) ---
|
||||
SSH_KEY="$INVOKING_HOME/.ssh/spinifex-key"
|
||||
if [ ! -f "$SSH_KEY.pub" ]; then
|
||||
echo "==> Generating SSH key pair ($SSH_KEY)"
|
||||
sudo -u "$INVOKING_USER" mkdir -p "$INVOKING_HOME/.ssh"
|
||||
sudo -u "$INVOKING_USER" ssh-keygen -t ed25519 -f "$SSH_KEY" -N ""
|
||||
fi
|
||||
|
||||
export AWS_PROFILE=spinifex
|
||||
|
||||
# Import SSH key
|
||||
echo "Importing SSH key"
|
||||
aws ec2 import-key-pair --key-name "spinifex-key" --public-key-material fileb://~/.ssh/spinifex-key.pub
|
||||
echo "==> Importing SSH key"
|
||||
aws ec2 import-key-pair --key-name spinifex-key \
|
||||
--public-key-material "fileb://$SSH_KEY.pub"
|
||||
aws ec2 describe-key-pairs
|
||||
|
||||
# Import AMI
|
||||
echo "Importing AMI"
|
||||
|
||||
LOCAL_IMAGE="$HOME/images/ubuntu-24.04.img"
|
||||
if [ -f "$LOCAL_IMAGE" ]; then
|
||||
echo "Using local image: $LOCAL_IMAGE"
|
||||
ARCH=$(uname -m)
|
||||
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||
IMG_ARCH="arm64"
|
||||
else
|
||||
# --- Import AMI ---
|
||||
echo "==> Importing AMI"
|
||||
LOCAL_IMAGE="$INVOKING_HOME/images/ubuntu-24.04.img"
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
IMG_ARCH="x86_64"
|
||||
fi
|
||||
./bin/spx admin images import --file "$LOCAL_IMAGE" --distro ubuntu --version 24.04 --arch "$IMG_ARCH"
|
||||
else
|
||||
# Download by name based on architecture
|
||||
ARCH=$(uname -m)
|
||||
if [ "$ARCH" = "x86_64" ]; then
|
||||
IMAGE_NAME="ubuntu-24.04-x86_64"
|
||||
elif [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||
;;
|
||||
aarch64|arm64)
|
||||
IMG_ARCH="arm64"
|
||||
IMAGE_NAME="ubuntu-24.04-arm64"
|
||||
else
|
||||
echo "Warning: Unknown architecture $ARCH, defaulting to x86_64"
|
||||
;;
|
||||
*)
|
||||
echo " Warning: unknown arch $ARCH, defaulting to x86_64"
|
||||
IMG_ARCH="x86_64"
|
||||
IMAGE_NAME="ubuntu-24.04-x86_64"
|
||||
fi
|
||||
echo "Downloading image: $IMAGE_NAME"
|
||||
./bin/spx admin images import --name "$IMAGE_NAME"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -f "$LOCAL_IMAGE" ]; then
|
||||
echo " Using local image: $LOCAL_IMAGE"
|
||||
sudo /usr/local/bin/spx admin images import \
|
||||
--file "$LOCAL_IMAGE" --distro ubuntu --version 24.04 --arch "$IMG_ARCH"
|
||||
else
|
||||
echo " Downloading image: $IMAGE_NAME"
|
||||
sudo /usr/local/bin/spx admin images import --name "$IMAGE_NAME"
|
||||
fi
|
||||
|
||||
aws ec2 describe-images
|
||||
|
||||
# --- Launch a smoke-test instance ---
|
||||
echo "Launching smoke-test instance..."
|
||||
|
||||
# Pick instance type based on CPU vendor
|
||||
# --- Smoke-test instance ---
|
||||
echo "==> Launching smoke-test instance"
|
||||
if grep -q 'AuthenticAMD' /proc/cpuinfo; then
|
||||
INSTANCE_TYPE="t3a.small"
|
||||
else
|
||||
INSTANCE_TYPE="t3.small"
|
||||
fi
|
||||
|
||||
# Get the AMI we just imported
|
||||
AMI_ID=$(aws ec2 describe-images --query "Images[0].ImageId" --output text)
|
||||
if [ -z "$AMI_ID" ] || [ "$AMI_ID" = "None" ]; then
|
||||
echo "❌ No AMI found, skipping instance launch"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Find a public subnet (first available)
|
||||
SUBNET_ID=$(aws ec2 describe-subnets \
|
||||
--filters "Name=map-public-ip-on-launch,Values=true" \
|
||||
--query "Subnets[0].SubnetId" --output text 2>/dev/null)
|
||||
# Fallback: just grab the first subnet if no public-tagged one exists
|
||||
if [ -z "$SUBNET_ID" ] || [ "$SUBNET_ID" = "None" ]; then
|
||||
SUBNET_ID=$(aws ec2 describe-subnets --query "Subnets[0].SubnetId" --output text)
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user