Skip to content

v0.12.0

Choose a tag to compare

@snonux snonux released this 17 Sep 05:42
· 502 commits to main since this release

Release v0.12.0

Release Notes

Overview

This release introduces a Cluster abstraction layered beneath the existing Fleet concept, giving you a cleaner, more expressive way to organize your host inventory. Where Fleet previously described a flat list of hosts, it is now a list of clusters — named groups of hosts that can be composed into larger fleets. This makes it straightforward to model real-world topologies (e.g., "edge nodes" and "core nodes" as separate clusters, both belonging to a single "homelab" fleet) while pushing to the union of all hosts.

Breaking Changes

Fleet semantics changed — this is the most significant change in the release.

  • Fleet(name, hosts...) → Fleet(name, clusters...): Fleet now accepts ClusterRef handles, not HostRef handles. A fleet is a named set of clusters, and hosts may overlap across member clusters (they are deduplicated automatically on push).
  • Cluster is the new unit of host grouping: The former Fleet role (a named set of hosts with a parallelism setting) is now Cluster(name, hosts...). All the host-level options (WithSSHUser, WithSSHPort, WithPrivilege, etc.) and the Parallel(n) concurrency control now live on Cluster.
  • Renamed task-scope helpers:
    • FleetHosts() → ClusterHosts()
    • WithTaskFleet(name) → WithTaskCluster(name)
    • WithFleet(name) (on RegisterMethods) → WithCluster(name)
    • LookupFleet / MustFleet now resolve to FleetRef (a list of clusters); use LookupCluster / MustCluster for the host-grouping unit.
    • Fleets() listing now reports Clusters []string and a flattened Hosts []string.

Migration steps:

  1. Replace every Fleet("name", hostA, hostB, ...) call with Cluster("name", hostA, hostB, ...).
  2. If you need a group-of-groups, introduce a Fleet("name", cluster1, cluster2, ...) that references your new clusters.
  3. Rename task-scope calls: FleetHosts() → ClusterHosts(), WithTaskFleet → WithTaskCluster, WithFleet → WithCluster.
  4. Replace PushFleet("name", tasks...) with PushCluster("name", tasks...) when pushing to a single host group; use PushFleet when pushing across multiple clusters.

If you were using Fleet purely as "a set of hosts" with no composition, the rename to Cluster is the only change you need — drop the wrapper Fleet entirely and push to the cluster directly.

Features

  • Cluster inventory type — A first-class, named group of hosts with its own parallelism knob (Cluster("edge", h1, h2, h3).Parallel(4)). This is where SSH credentials, privilege mode, and per-host values belong. It gives you a natural place to draw the line between "how to talk to these machines" (cluster) and "which machines are in scope for this recipe" (fleet).
  • Fleet as a list of clusters — A fleet now composes clusters, enabling overlapping host membership. For example, an edge cluster and a core cluster can both include a shared bastion host; PushFleet pushes to each unique host exactly once. This mirrors how infrastructure is actually organized (role-based groups that share some machines).
  • FleetRef.ClusterNames() — Inspect which clusters make up a fleet, useful for debugging and reporting.

Improvements

  • Cleaner conceptual separation — Previously "fleet" conflated two ideas: how to connect (SSH user, port, privilege) and which hosts to target. Splitting these into Cluster (connection + host set) and Fleet (composition of clusters) makes recipes easier to read and reuse across environments.
  • Deduplication on fleet push — Overlapping hosts across clusters are transparently deduplicated, so a shared bastion in two clusters receives exactly one push rather than duplicate or conflicting ones.
  • Documentation updated — helpers.md and plan.md now reference the new Cluster / ClusterHosts / WithCluster names, and the gap-analysis doc is corrected to reflect PushCluster.

Bug Fixes

  • Test suite corrected for the new API — All fleet tests were renamed and updated (TestFleetOfClusters, TestClusterDuplicateHostNames, TestPushClusterParallel, etc.) to validate the new cluster/fleet semantics, including a new test confirming that overlapping hosts across clusters are deduplicated correctly.

Summary

If you manage more than a handful of hosts, this release gives you the vocabulary to describe your infrastructure the way you actually think about it: groups of similar machines (clusters) composed into broader scopes (fleets). The cost is a one-time rename, but the payoff is recipes that scale beyond a single flat host list.