mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
9d662f9889
* feat: bake aws-ebs-csi-driver addon + mdev by-id bridge Add the aws-ebs-csi-driver entry (v1.40.1) to the eks addon catalog and ship its baked manifests (RBAC, IRSA-wired controller, node DaemonSet, default gp3 StorageClass) under the eks-node image. The node plugin locates EBS volumes via /dev/disk/by-id, which busybox mdev does not populate. Add mulga-ebs-byid: an mdev rule + helper that symlinks nvme-Amazon_Elastic_Block_Store_<serial> from the virtio-blk serial, plus a boot oneshot that points the kernel hotplug helper at mdev and coldplugs volumes present at boot. * test: e2e EBS CSI volume — PVC bind + mount + reschedule survival Add a TestEKS/EBSCSIVolume subtest that installs the aws-ebs-csi-driver managed addon bound to an IRSA role, provisions a default gp3 PVC + pod, and asserts a marker written by the first pod survives a reschedule onto a fresh pod backed by the same PVC. Exercises the full data path: CreateVolume(Viperblock)/AttachVolume virtio-blk hotplug with serial set to the volume-id, mdev /dev/disk/by-id symlink, and ext4 mount. * feat: eks-node provider-id + topology/instance-type labels for EBS CSI mulga-eks-provider-id boot oneshot writes a K3s config.yaml.d drop-in from IMDS before K3s starts: kubelet provider-id plus the region, zone and instance-type node labels. The aws-ebs-csi-driver node plugin reads this metadata for NodeGetInfo (instance id from providerID, region/zone from topology labels, instance type for the volume-limit family lookup). k3s runs no AWS cloud-provider so none of it is set otherwise, which left the node plugin's metadata init failing, NodeGetInfo panicking in IsNitroInstanceType, and the CSINode carrying no topology key. Wire the service into the image manifest (INSTALL_FILES + ENABLE_SERVICES) and chmod it in setup. * test: e2e EBS CSI controller IRSA policy + failure diagnostics awsgw enforces IAM on assumed-role EC2 calls, so the controller's CreateVolume/AttachVolume need a permission policy on the IRSA role. Attach a customer-managed EC2 policy (AWS-managed ARNs are opaque and grant nothing) covering the EBS CSI action set; CreateVolume then succeeds. Add an OnFailure dump of the CSI control path (provisioner and plugin logs, pvc/pod describe, events, csinode) to the EBSCSIVolume subtest so provisioning/attach failures are diagnosable post-teardown. * feat: EBS CSI obeys control-plane taint; e2e worker nodegroup Drop the CriticalAddonsOnly/NoExecute tolerations from the EBS CSI controller + node manifests so the driver runs only on customer worker nodes, never the system control-plane VM. The e2e creates a 1-node worker nodegroup and the writer/reader pods drop their CP tolerations so the customer-owned volume attaches in the customer space. * fix: EBS CSI attach — decouple hotplug PCIe port from device name; iothread rollback virtio-blk-pci hot-plug bus was derived from the AWS device-name letter (/dev/sd[f-p]); the EBS CSI driver sends arbitrary names (/dev/xvdaa) that map to no slot, so device_add fell back to pcie.0 — which rejects hot-plug — and the attach hung. A failed attach also left the per-volume iothread object behind, so every retry died on object-add 'duplicate property'. nextHotplugEBSPort allocates the lowest free hotplug-ebs port from live query-block state, independent of the device name. delIothreadBestEffort removes the iothread on every attach rollback path. * fix: EBS CSI node discovery — un-shadow mdev by-id rule, mint nvme link Alpine eks-node runs busybox mdev (no eudev), which stops at the first matching /etc/mdev.conf rule. setup.sh appended the mulga-ebs-byid rule after the stock 'vd[a-z].* -> persistent-storage' line, so it never fired on hot-attach: only /dev/disk/by-id/virtio-<serial> was created, while the upstream aws-ebs-csi-driver resolves nvme-Amazon_Elastic_Block_Store_<volid>. Swap the stock vd* rule in place to route through mulga-ebs-byid, which now delegates to persistent-storage (preserving stock links) before minting the nvme-style link the node plugin expects. * test: EKS e2e — NAT Gateway egress for private worker nodegroup The customer-VPC worker is private; AttachIGW programs no SNAT (mirrors AWS: an IGW serves only public-IP instances), so an IGW-only fixture left the worker unable to reach the public NLB endpoint or pull public CSI images — the nodegroup never joined. createWorkerEgress now builds the real topology: an IGW-fronted public subnet hosting a NAT Gateway, with the worker subnet default-routed to it. Associating the worker route table triggers the subnet-wide SNAT egress reroute. Teardown drains the NAT Gateway before releasing the public subnet + EIP it pins.
122 lines
6.1 KiB
Bash
Executable File
122 lines
6.1 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
# setup.sh — guest customisation for the unified eks-node AMI (server + agent).
|
|
#
|
|
# Runs inside the libguestfs appliance (via virt-customize --run) under
|
|
# build-system-image.sh after packages and binaries are installed. Downloads the
|
|
# pinned K3s binary, verifies its SHA256, drops it into /usr/local/bin, sets
|
|
# executable bits on all role init scripts + cron entries, and writes the K3s
|
|
# server config skeleton. The role (server vs agent) is selected per-instance at
|
|
# first boot by eks-node-role.
|
|
#
|
|
# Network access inside the appliance is provided by virt-customize --network.
|
|
# curl is in APK_PACKAGES.
|
|
|
|
K3S_VERSION="v1.32.5+k3s1"
|
|
K3S_URL_BASE="https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}"
|
|
|
|
# Pull the upstream signed checksums file and pin the amd64 line. A tampered
|
|
# release replacing both files would still produce a self-consistent download,
|
|
# so the SHA file URL is the trust anchor — anyone forging a Mulga AMI build
|
|
# would need to compromise the k3s-io GitHub release artefacts.
|
|
echo "[eks-node-setup] fetching k3s checksums ${K3S_VERSION}"
|
|
curl -fsSL -o /tmp/k3s-checksums.txt "${K3S_URL_BASE}/sha256sum-amd64.txt"
|
|
K3S_SHA256=$(awk '/[ \t]k3s$/{print $1; exit}' /tmp/k3s-checksums.txt)
|
|
if [ -z "${K3S_SHA256}" ]; then
|
|
echo "[eks-node-setup] could not parse k3s sha256 from upstream checksums"
|
|
cat /tmp/k3s-checksums.txt
|
|
exit 1
|
|
fi
|
|
echo "[eks-node-setup] downloading k3s ${K3S_VERSION} (sha256=${K3S_SHA256})"
|
|
curl -fsSL -o /usr/local/bin/k3s "${K3S_URL_BASE}/k3s"
|
|
echo "${K3S_SHA256} /usr/local/bin/k3s" > /tmp/k3s.sha256
|
|
if ! sha256sum -c /tmp/k3s.sha256; then
|
|
echo "[eks-node-setup] k3s SHA256 mismatch — refusing to bake AMI"
|
|
exit 1
|
|
fi
|
|
chmod 0755 /usr/local/bin/k3s
|
|
ln -sf /usr/local/bin/k3s /usr/local/bin/kubectl
|
|
ln -sf /usr/local/bin/k3s /usr/local/bin/crictl
|
|
ln -sf /usr/local/bin/k3s /usr/local/bin/ctr
|
|
|
|
# Init scripts ship as 0644 from INSTALL_FILES; OpenRC requires 0755. Every
|
|
# role's services are baked; the selector enables the right ones at first boot.
|
|
chmod 0755 /etc/init.d/eks-node-role /etc/init.d/k3s /etc/init.d/k3s-agent \
|
|
/etc/init.d/eks-token-webhook /etc/init.d/k3s-first-boot /etc/init.d/mulga-eks-state-report \
|
|
/etc/init.d/mulga-eks-addon-sync
|
|
chmod 0755 /usr/local/sbin/eks-node-role /usr/local/sbin/k3s-first-boot \
|
|
/usr/local/sbin/mulga-eks-state-report /usr/local/sbin/mulga-eks-addon-sync
|
|
chmod 0755 /etc/init.d/mulga-ebs-byid /usr/local/sbin/mulga-ebs-byid
|
|
chmod 0755 /etc/init.d/mulga-eks-provider-id /usr/local/sbin/mulga-eks-provider-id
|
|
chmod 0755 /etc/periodic/daily/mulga-eks-etcd-snapshot
|
|
|
|
# EBS by-id bridge: route every virtio-blk event through mulga-ebs-byid, which
|
|
# delegates to the stock persistent-storage helper and then mints the
|
|
# nvme-Amazon_Elastic_Block_Store_<serial> link the EBS CSI node plugin resolves.
|
|
# busybox mdev stops at the first matching rule, so the stock vd* persistent-
|
|
# storage line is replaced in place — appending a second vd* rule would be
|
|
# shadowed and never fire. The leading '*' runs the command on add and remove.
|
|
sed -i \
|
|
's#^vd\[a-z\]\.\*[[:space:]].*persistent-storage#vd[a-z].*\troot:disk 0660 */usr/local/sbin/mulga-ebs-byid#' \
|
|
/etc/mdev.conf
|
|
grep -q 'mulga-ebs-byid' /etc/mdev.conf || {
|
|
echo "[eks-node-setup] failed to wire mulga-ebs-byid into /etc/mdev.conf"
|
|
exit 1
|
|
}
|
|
|
|
# K3s server config — skeleton; cloud-init / first-boot fills in the
|
|
# per-cluster fields (cluster-cidr, service-cidr, token-file, etc). Agents
|
|
# ignore this file (they run `k3s agent`).
|
|
# IAM token-auth is wired here via kube-apiserver-arg: the eks-token-webhook
|
|
# service (ordered `before k3s`) writes its kubeconfig to
|
|
# /etc/spinifex-eks/token-webhook.kubeconfig before the apiserver reads it.
|
|
# This only affects bearer-token requests; anonymous and client-cert paths
|
|
# (the first-boot /readyz probe uses the admin kubeconfig) are unaffected.
|
|
mkdir -p /etc/rancher/k3s
|
|
cat > /etc/rancher/k3s/config.yaml.skel <<'EOF'
|
|
# Populated at first boot by cloud-init user-data via k3s-first-boot.sh.
|
|
# Fields documented at https://docs.k3s.io/installation/configuration.
|
|
# cluster-init selects the embedded etcd datastore. cloud-init write_files
|
|
# normally overrides this skeleton; kept consistent as the boot fallback.
|
|
cluster-init: true
|
|
disable:
|
|
- traefik
|
|
- servicelb
|
|
- local-storage
|
|
kube-apiserver-arg:
|
|
- "authentication-token-webhook-config-file=/etc/spinifex-eks/token-webhook.kubeconfig"
|
|
- "authentication-token-webhook-cache-ttl=5m"
|
|
EOF
|
|
|
|
# Shared state dir for both roles (agent.env / role marker live here).
|
|
mkdir -p /etc/spinifex-eks
|
|
|
|
# Sentinel file marker — k3s-first-boot (server role) self-disables after first
|
|
# success by checking for this path. Initial state is "not yet run".
|
|
touch /var/lib/spinifex-eks/first-boot.pending 2>/dev/null || {
|
|
mkdir -p /var/lib/spinifex-eks
|
|
touch /var/lib/spinifex-eks/first-boot.pending
|
|
}
|
|
|
|
# Bind /dev/console to the serial port so userspace boot output — OpenRC
|
|
# service starts, cloud-init, role selection, and k3s-first-boot diagnostics —
|
|
# reaches ttyS0, which the orchestrator captures to a host-side log. The stock
|
|
# Alpine cloud image lists `console=tty0` LAST in default_kernel_opts; Linux
|
|
# makes the last console= the controlling /dev/console, so userspace logs to
|
|
# the framebuffer and the serial capture sees only kernel dmesg. Reorder so
|
|
# ttyS0 is last in both the generator config and the rendered extlinux.conf.
|
|
sed -i \
|
|
's|console=ttyS0,115200n8 console=ttyAMA0,115200n8 console=tty0|console=tty0 console=ttyAMA0,115200n8 console=ttyS0,115200n8|' \
|
|
/etc/update-extlinux.conf /boot/extlinux.conf
|
|
|
|
# Cut the boot-menu countdown from 10s to ~1s. The stock cloud image waits 10s at
|
|
# the SYSLINUX menu before auto-booting — a fixed, network-independent tax on every
|
|
# VM start. Patch the generator config (timeout in seconds) and the rendered output
|
|
# (TIMEOUT in 1/10s) so a regenerate keeps the short value. A small nonzero value is
|
|
# kept so the menu stays interruptible over serial; TIMEOUT 0 would wait forever.
|
|
sed -i 's/^timeout=.*/timeout=1/' /etc/update-extlinux.conf
|
|
sed -i 's/^TIMEOUT[[:space:]].*/TIMEOUT 10/' /boot/extlinux.conf
|
|
|
|
echo "[eks-node-setup] done"
|