mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-11-02 09:13:33 +00:00
b4dd606104
* feat: Vultr reserved-IP provider and floating external-address pool Adds a source="vultr" external pool backed by Vultr reserved IPs: an API client pinned by captured responses, an in-memory fake with the measured semantics, and a floating allocator that creates, claims, reconciles and releases labelled addresses. OCI and Vultr claims now share one AddressClaimer pass. Also fixes the host firewall dropping guest IMDS when imds_host_meta_ip is set, by matching the pre-DNAT destination, and arms the Spinifex firewall in place of an active ufw. * fix: detach a Vultr reserved IP before deleting it Vultr was measured refusing delete of an attached address with 400 "Unable to detach subnet", so release, rollback and reconcile now detach first. A first claim after allocation logs at info rather than warn. * fix: release the auto-assigned public IP an Elastic IP displaces AssociateAddress restamped the ENI with the EIP and never handed back the address launch had assigned. Nothing referred to it afterwards, so it stayed allocated for good, and on a cloud pool every associate left a billed address. AWS releases it on associate; withdraw its NAT rule and release it, scoped to the ENI, leaving any address another associated EIP holds alone. * fix: retry Vultr's refused detach on reserved-IP detach and delete Vultr has answered a detach with 400 "Unable to detach subnet" and accepted the same detach moments later. Surfacing it left the address for reconcile to collect, up to twenty billed minutes later; retry it like a 5xx instead. * fix: stop reporting the released launch address after an EIP is disassociated Associating an EIP now releases the auto-assigned public IP, but the stored instance kept its launch stamp, and DescribeInstances fell back to it once the EIP was disassociated. The public-IP change handler now moves the stamp with the runtime address. * feat: add a Vultr deploy script for a single-node Spinifex on a VX VM deploy.sh up/destroy/status drives Terraform for the VPC and VM, installs and forms Spinifex over SSH, copies the API key to the node rather than through user-data, and sweeps boot volumes and reserved IPs Terraform does not own. Cloud-init keeps needrestart off the Spinifex, OVS and OVN units and disables unattended upgrades on these disposable hosts. * feat: form a three-node Vultr deployment with install-node.sh over the VPC Each node comes up as an armed single node, the state install-node.sh forms from, with the cluster and Geneve planes on the VPC NIC. The pool and every node's provider_host_id go on each node after formation. * fix: read the pinned host key with ssh-keygen -F when forming a Vultr cluster ssh_config hashes known_hosts entries by default on Debian, so a grep for the address found nothing and set -e ended the run without a message. * docs: add the Vultr guide, one command to deploy one or three nodes and one to remove them * docs: drop the failover write note from the Vultr guide * fix: keep needrestart from restarting the Spinifex, OVS and OVN units Guest QEMU runs in spinifex-daemon's cgroup, so needrestart charged QEMU's replaced libraries to the daemon and restarted it after every dpkg run. The restart never cleared them, so an unattended upgrade ran the unit into its start limit and left the control plane down. setup.sh now writes /etc/needrestart/conf.d/90-spinifex.conf, deselecting spinifex-, ovs-, ovsdb-, ovn- and openvswitch- units. needrestart still lists them, so restarting after an upgrade stays the operator's choice. * chore: drop the Vultr cloud-init needrestart override now setup.sh writes it * docs: replace the Vultr guide's apt upgrade warning now the installer keeps needrestart off the stack * docs: install the Vultr guide's build from the dev channel until Vultr support is released * fix: answer ARP for Vultr reserved IPs on bare metal Vultr routes a reserved IP to a VM by MAC but ARPs for it on a bare-metal server's public segment, and a Vultr pool gave vpcd neither a gateway nor a bind bridge to proxy-ARP on, so nothing answered and no traffic arrived. A Vultr pool now proxy-ARPs on the interface the node routes the internet through. setup.sh installs arping, which the gratuitous ARP already called. * feat: deploy Spinifex on Vultr bare metal, renamed deploy-vultr.sh A vbm- plan creates bare-metal servers, one or three. Without --name the deployment writes the AWS profile spinifex, up prints every node's address and the API key allowlist, credentials and ssh subcommands are added, no AMI is imported unless --image is given, and OpenTofu or Terraform is used. The host key is pinned only once cloud-init is done, since bare metal first answers SSH from Vultr's installer with a different key. * docs: rework the Vultr guide into the OCI guide's flow, with bare metal * fix: restamp LaunchTime when a stopped or recovered instance relaunches The pending watchdog times a launch from LaunchTime, which still held the original launch. An instance recovered from a failed node, or started more than five minutes after it was first launched, was terminated as stuck in pending seconds after QEMU started. AWS restamps LaunchTime on every start. * feat: spread the nginx-alb workbook across nodes, and power Vultr nodes off and on from deploy-vultr.sh The workbook takes server_count and placement_strategy, so three web servers can run one per node. Its README now matches the template (public subnets, no NAT gateway) and the diagram shows the spread. deploy-vultr.sh gains stop and start, which halt and start a node through the Vultr API. * fix: let a terminate finish tearing down before the reaper calls it wedged OrphanQEMUReaper finalized any shutting-down instance whose QEMU had exited, on the premise that a live terminate still has a live QEMU. A terminate is still deleting volumes, releasing its IP and leaving its placement group for tens of seconds after QEMU exits, so the instance was reported terminated first. Terraform then deleted the placement group and got InvalidPlacementGroup.InUse. The reconcile now waits two minutes from shutting-down, well inside the ten-minute stuck-terminate backstop. * fix: release an ovs-vswitchd left in flow-restore-wait after a node boots ovn-controller clears flow-restore-wait once its flows are installed and sets it again straight after. When both writes reach ovs-vswitchd as one update, it only sees true, starts no upcall threads and forwards nothing. Seen on Vultr bare metal after a hard power-off: instances recovered onto the rebooted node ran but were unreachable. vpcd now clears the flag on any node held that way for a minute, longer than ovs-ctl's own restore takes. * docs: add a resiliency test to the Vultr guide and the nginx-alb workbook The Vultr guide gains step 8, which spreads three web servers across a three-node deployment, powers a node off and back on, and shows what was measured on VMs and bare metal. The workbook gains the same test as step 6. The guide's table of contents is repaired, and its limits now cover losing two nodes and recovery inside a spread placement group. * fix: never terminate an instance that has run because a relaunch failed The pending watchdog terminated any instance pending past five minutes, and terminating deletes DeleteOnTermination volumes. A start of a stopped instance, a recovery from a failed node and a daemon relaunch all pass through pending, so a slow one destroyed an existing instance and its root disk. On Vultr a recovered instance was terminated seconds after QEMU started and its root volume deleted. An instance is now marked FirstLaunch from RunInstances until it first reaches running, cleared before the running state is written. The watchdog acts only on first launches, as AWS terminates only a launch that never ran. MarkFailed refuses to terminate any other instance and marks it recovery-failed instead, which keeps its volumes, ENI and addresses and shows it as stopped. A claimed relaunch is never a first launch. Invariant tests pin ADR-0003:S7. The instancerecovery e2e suite gains a test that ages its guest past the watchdog before recovering it, then stops and starts it, and requires the instance, its root volume and the data on it to survive both. * feat: run the Terraform workbooks against a Vultr deployment on demand, and show the Vultr portal in the guide e2e-cloudvendor-vultr.yml is dispatch-only. It builds the ref, deploys it with deploy-vultr.sh on one or three VMs or bare-metal servers, runs the published workbooks from node1 with the same driver as the hypervisor nightly and OCI, and destroys the deployment, failing if anything is left billing. It needs one secret, VULTR_API_KEY, and generates its SSH key per run. The guide gains screenshots of the API key's access control list and of the nodes in the Vultr portal, and its portal navigation now matches. * fix: install setup.sh on Vultr nodes when the tarball does not carry it build-install-artifacts.sh writes setup.sh beside the tarball, so a node installed from its tarball had no /usr/local/share/spinifex/setup.sh and arming the firewall failed. * fix: pass the remote command after the destination in the Vultr e2e driver * fix: run each workbook in the cluster's own region The workbook driver passed no region, so every workbook used its default, ap-southeast-2, and on a cluster in any other region every request failed SignatureDoesNotMatch. It now passes the region of the node's spinifex profile. * feat: count a node's vCPUs as online CPUs and reserve by host size (ADR-0015) A node counted physical cores, collapsing SMT threads, and held back a fixed 2 vCPU, so a 16-thread host offered 6 vCPU to instances. A vCPU is now one online CPU, as AWS counts it: a thread with SMT on, a core with SMT off or on ARM. The OS and Spinifex keep 1 CPU up to 8, 2 up to 16, 4 up to 63 and 8 from 64, and the rest is allocated one to one. The existing overrides stay operator-only. Invariant tests pin every tier boundary. * docs: state the Vultr guide's sizing under the online-CPU count * docs: drop an unmeasured EKS sizing claim from the Vultr README * fix: let a guest reach an Elastic IP hosted on its own node A guest's packet to an EIP on its own node left its VPC from the transit range, was masqueraded to the transit host address, and went back into OVN, which exempts that range from EIP DNAT, so it was never answered. On Vultr an EKS worker could not reach its control plane's endpoint, so it never joined. AWS lets an instance reach a public address in its own VPC. Hairpinned transit traffic now jumps to SPX-EIP-HAIRPIN, above the egress MASQUERADE, where each EIP's rule sends it from the uplink address, as the host's own traffic to the EIP already is. The rule follows the EIP's ingress plumbing in and out, and anything else returns to the MASQUERADE. * fix: resolve Spinifex's LB and EKS names on Vultr nodes Vultr's Ubuntu image writes a static resolv.conf naming Vultr's resolvers, so lookups bypassed the split DNS Spinifex configures in systemd-resolved and an EKS endpoint name did not resolve on the node. deploy-vultr.sh points resolv.conf at the resolved stub, as stock Ubuntu does. * test: prove a guest reaches an EIP on its own node in the natuplink e2e The nat-single cells run it with a public pool, so the hairpin path that left EKS workers unable to reach their control plane on Vultr is now covered nightly. * docs: record the natuplink hairpin check in the e2e coverage index * fix: keep each Vultr node's logs when an e2e run fails The driver destroyed a node whose install failed together with the log that said why. It now copies /root/*.log from every node into the run's artifacts before the teardown, and the workflow uploads them. ---------