Documentation
¶
Overview ¶
Package seam holds the registration mechanism the open-core seams share.
Per D6 the private companion repository replaces a seam's implementation by blank-importing a package whose init() registers its own. Go initialises an imported package before its importer, so a default registered in the seam package's own init() is always in place before a companion's init() runs, and the companion always wins. No build tags, no stubbed files in the public tree. It is the mechanism swarmcli-be already uses.
That argument holds only while the default lives in the package the companion imports. A default in a package of its own — swarms/local, which is there so that the seam does not drag the Docker applier in behind it — is an unrelated sibling of the companion's, and gc initialises siblings in import-path order, which nothing about the two implementations controls. Such a default must register only if nothing has, and swarms/local says why at its own init(). The rule for anything added here: a default that is not in the seam package does not overwrite.
Two shapes cover every seam. Slot holds one implementation and registering replaces it: there is exactly one answer to "which swarm registry is in force". List holds every implementation and returns all of them: a companion adding a Slack notifier must not remove the log notifier or the API's event stream.
When registration is over ¶
The two shapes settle at different times, and consumers may rely on it.
A Slot is settled by the end of init(). Every replacement comes from a blank import, so by the time main runs there is nothing left to register, and a consumer may read Get once and keep the result — which api.New, reconcile.New and prune.New all do.
A List is not. The API server registers itself as a notifier during wiring, long after every init() has run, so notify.Dispatch re-reads All on every event. A consumer that snapshotted a List at construction would silently drop whatever registered after it, and for notify that is the UI's live updates.
Entitlement gating is not a re-registration ¶
The consequence, for the licence package docs/extensibility.md sketches: an implementation whose entitlement lapses must start refusing, from inside itself. It must not be swapped out of a Slot at runtime. Half the consumers would never see the swap — they hold the old value — and the half that did would see it mid-flight: replacing a swarms.Registry under a running reconcile hands a half-finished sync a different daemon. The mutexes here make registration safe against a concurrent read; they do not make a live replacement meaningful.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type List ¶
type List[T any] struct { // contains filtered or unexported fields }
List holds every registered implementation of T, in registration order. The zero List is ready to use.
func (*List[T]) All ¶
func (l *List[T]) All() []T
All returns every registered implementation. The result is a copy, so a caller ranging over it cannot race a late registration.
type Slot ¶
type Slot[T any] struct { // contains filtered or unexported fields }
Slot holds exactly one implementation of T. The zero Slot is ready to use and returns the zero T until something registers.