mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
179 lines
7.2 KiB
Bash
Executable File
179 lines
7.2 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
# eks-node-role — first-boot role selector for the unified eks-node AMI.
|
|
#
|
|
# The same image boots either as a K3s control-plane server or a worker agent;
|
|
# the role is chosen per-instance at first boot from SPINIFEX_K3S_ROLE in the
|
|
# cloud-init env file. This script enables + starts the matching services
|
|
# under whichever init the image ships (none are enabled at bake time),
|
|
# records the resolved role to ROLE_FILE so later boots and the etcd-snapshot
|
|
# cron can branch without re-parsing, then disables itself so it runs exactly
|
|
# once.
|
|
#
|
|
# server → eks-token-webhook, k3s (server), k3s-first-boot (bootstrap
|
|
# publish), mulga-eks-state-report
|
|
# server-join → eks-token-webhook, k3s (server, joins the first server's etcd
|
|
# quorum), mulga-eks-state-report. NO k3s-first-boot: the first
|
|
# server already publishes the cluster-identical bootstrap
|
|
# artifacts; a join re-publish only races the bootstrap bus.
|
|
# agent → k3s-agent
|
|
#
|
|
# Paths are overridable via env so the selection logic is unit-testable (bats)
|
|
# without root or a real /etc.
|
|
|
|
ROLE_FILE="${EKS_NODE_ROLE_FILE:-/etc/spinifex-eks/role}"
|
|
ENVFILE="${EKS_NODE_ENVFILE:-/etc/spinifex-eks/first-boot.env}"
|
|
AGENT_ENVFILE="${EKS_NODE_AGENT_ENVFILE:-/etc/spinifex-eks/agent.env}"
|
|
LOGTAG="eks-node-role"
|
|
|
|
log() { echo "[${LOGTAG}] $*"; }
|
|
die() { log "ERROR: $*"; exit 1; }
|
|
|
|
# This script is delivered unmodified to both the legacy Alpine/OpenRC image
|
|
# and the mkosi Ubuntu/systemd image, so the enable/start/disable calls below
|
|
# must dispatch on whichever init actually owns the box. EKS_NODE_INIT lets
|
|
# the env knob win outright (same override idiom as the paths above, and the
|
|
# only way to exercise both branches from one test run); detection then
|
|
# prefers rc-update so an Alpine host with a stray systemctl shim still
|
|
# resolves to openrc.
|
|
INIT_SYSTEM="${EKS_NODE_INIT:-}"
|
|
if [ -z "${INIT_SYSTEM}" ]; then
|
|
if command -v rc-update >/dev/null 2>&1; then
|
|
INIT_SYSTEM=openrc
|
|
elif command -v systemctl >/dev/null 2>&1; then
|
|
INIT_SYSTEM=systemd
|
|
else
|
|
die "no supported init system found (need rc-update or systemctl)"
|
|
fi
|
|
fi
|
|
|
|
# svc_enable/svc_start/svc_disable: thin wrappers so every call site below
|
|
# reads the same regardless of init. k3s-first-boot.sh duplicates this same
|
|
# dispatch rather than sourcing it from here — both are single-file
|
|
# deliverables installed straight to /usr/local/sbin with no shared
|
|
# library-path convention, and scripts/images/ is deleted at image-build
|
|
# cutover, so a shared file would outlive its usefulness.
|
|
svc_enable() {
|
|
case "${INIT_SYSTEM}" in
|
|
openrc) rc-update add "$1" default ;;
|
|
systemd) systemctl enable "$1.service" ;;
|
|
esac
|
|
}
|
|
|
|
# --no-block is load-bearing on systemd, not a tuning knob. This unit declares
|
|
# Before=k3s.service, and mulga-eks-k3s-recovery declares After=eks-node-role,
|
|
# so a blocking `systemctl start` deadlocks: systemd will not run the queued job
|
|
# until this unit finishes, and this unit cannot finish until the job it is
|
|
# waiting on runs. Type=oneshot defaults TimeoutStartSec=infinity, so nothing
|
|
# ever breaks the tie and the node wedges mid-boot with the role chosen but no
|
|
# control plane. Enqueue instead and let systemd sequence the units by the
|
|
# After=/Before= they already declare between themselves.
|
|
#
|
|
# OpenRC has no such constraint — rc-service runs the script directly rather
|
|
# than asking a supervisor to schedule it — so that branch still blocks and
|
|
# still surfaces a start failure via set -e.
|
|
svc_start() {
|
|
case "${INIT_SYSTEM}" in
|
|
openrc) rc-service "$1" start ;;
|
|
systemd) systemctl start --no-block "$1.service" ;;
|
|
esac
|
|
}
|
|
|
|
svc_disable() {
|
|
case "${INIT_SYSTEM}" in
|
|
openrc) rc-update del "$1" default 2>/dev/null || true ;;
|
|
systemd) systemctl disable "$1.service" 2>/dev/null || true ;;
|
|
esac
|
|
}
|
|
|
|
# Already resolved on a prior boot — the role services are in the runlevel and
|
|
# this service should have been pulled, but guard anyway so a stray re-run is a
|
|
# no-op rather than a double-enable.
|
|
if [ -f "${ROLE_FILE}" ]; then
|
|
log "role already resolved ($(cat "${ROLE_FILE}")); nothing to do"
|
|
exit 0
|
|
fi
|
|
|
|
# Resolve the role. Server seeds SPINIFEX_K3S_ROLE in first-boot.env; a worker
|
|
# seed that predates the explicit var is inferred from the presence of agent.env
|
|
# (K3S_URL/K3S_TOKEN live there).
|
|
ROLE=""
|
|
if [ -f "${ENVFILE}" ]; then
|
|
# ENVFILE is a runtime-resolved, test-overridable path.
|
|
# shellcheck disable=SC1090
|
|
ROLE=$(. "${ENVFILE}"; printf '%s' "${SPINIFEX_K3S_ROLE:-}")
|
|
fi
|
|
if [ -z "${ROLE}" ] && [ -f "${AGENT_ENVFILE}" ]; then
|
|
ROLE="agent"
|
|
fi
|
|
|
|
case "${ROLE}" in
|
|
server | server-join | agent) ;;
|
|
*)
|
|
die "SPINIFEX_K3S_ROLE missing or invalid: '${ROLE}'"
|
|
;;
|
|
esac
|
|
|
|
# Written before the rc-service calls below: mulga-eks-k3s-recovery, started
|
|
# inline for server/server-join, reads ROLE_FILE at the top of its own logic
|
|
# and must find it already in place, not empty.
|
|
mkdir -p "$(dirname "${ROLE_FILE}")"
|
|
printf '%s\n' "${ROLE}" > "${ROLE_FILE}"
|
|
|
|
case "${ROLE}" in
|
|
server)
|
|
log "configuring server role"
|
|
svc_enable eks-token-webhook
|
|
# Pre-k3s etcd recovery: enabled for later boots and started inline
|
|
# now too, so a restore-snapshot DR seed's directive applies before
|
|
# k3s starts on this first boot. A plain create has no directive, so it's a no-op.
|
|
svc_enable mulga-eks-k3s-recovery
|
|
svc_enable k3s
|
|
# konnectivity-server fronts apiserver egress; every server replica runs
|
|
# one so the agent's per-replica tunnel always lands a live server.
|
|
svc_enable konnectivity-server
|
|
svc_enable k3s-first-boot
|
|
svc_enable mulga-eks-state-report
|
|
# Managed-addon delivery runs only on the primary server: it renders
|
|
# staged addon bundles into the K3s auto-deploy dir, which a single
|
|
# writer owns. HA multi-server addon delivery is tracked separately
|
|
# Server-join nodes do not run this setup.
|
|
svc_enable mulga-eks-addon-sync
|
|
svc_start eks-token-webhook
|
|
svc_start mulga-eks-k3s-recovery
|
|
svc_start k3s
|
|
svc_start konnectivity-server
|
|
svc_start k3s-first-boot
|
|
svc_start mulga-eks-state-report
|
|
svc_start mulga-eks-addon-sync
|
|
;;
|
|
server-join)
|
|
log "configuring server-join role"
|
|
svc_enable eks-token-webhook
|
|
# Pre-k3s etcd recovery (see server role): server-join members are the
|
|
# wipe-rejoin followers of a cluster-reset, so they need it too, and for
|
|
# the same first-boot reason must be started here, not deferred.
|
|
svc_enable mulga-eks-k3s-recovery
|
|
svc_enable k3s
|
|
svc_enable konnectivity-server
|
|
svc_enable mulga-eks-state-report
|
|
svc_start eks-token-webhook
|
|
svc_start mulga-eks-k3s-recovery
|
|
svc_start k3s
|
|
svc_start konnectivity-server
|
|
svc_start mulga-eks-state-report
|
|
;;
|
|
agent)
|
|
log "configuring agent role"
|
|
svc_enable k3s-agent
|
|
svc_start k3s-agent
|
|
;;
|
|
*)
|
|
die "SPINIFEX_K3S_ROLE missing or invalid: '${ROLE}'"
|
|
;;
|
|
esac
|
|
|
|
svc_disable eks-node-role
|
|
log "role '${ROLE}' configured"
|