Rename mesh-control -> mesh-controller, substrate -> foundation
One name per thing, per the HQ glossary: the module/container/image/binary/repo becomes mesh-controller, the seat the-controller, and the store+broker pair the foundation (embedded base bundles, default template and example lock renamed with their go:embed directives). No behaviour change — a pure vocabulary rename. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/identity"
|
||||
"github.com/novox/mesh-controller/internal/inventory"
|
||||
"github.com/novox/mesh-controller/internal/licences"
|
||||
"github.com/novox/mesh-controller/internal/store"
|
||||
)
|
||||
|
||||
// reaching each context's store, which no other context may touch.
|
||||
//
|
||||
// Split out of main.go, which had reached 2,769 lines because appending was always the
|
||||
// cheapest next step. That is how novox/hq ADR 0001 records `hal/sdk` reaching 34,636:
|
||||
// nothing in it was wrong, and no one edit was the one that should have been a new file.
|
||||
|
||||
// migrate brings every held context's schema up to date.
|
||||
//
|
||||
// Reported per context and per migration, because this runs during a bootstrap on a machine with
|
||||
// nothing else on it — the output is the only account of what happened, and "migrated" is not one.
|
||||
func migrate(ctx context.Context) error {
|
||||
for _, c := range held {
|
||||
migrations, err := c.migrations()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s, err := store.Open(ctx, c.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
// The bootstrap raises PostgreSQL moments before this runs, and a container that is
|
||||
// running is not a database that will answer — a distinction this project has already
|
||||
// paid for once, when a crash-looping database reported itself as up between restarts.
|
||||
if err := s.Ready(ctx, 60*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done, err := s.Migrate(ctx, migrations)
|
||||
for _, m := range done {
|
||||
fmt.Printf("%s: applied %04d-%s\n", c.name, m.Number, m.Name)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(done) == 0 {
|
||||
applied, err := s.AppliedMigrations(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s: already up to date — %d migration(s)\n", c.name, len(applied))
|
||||
}
|
||||
}
|
||||
|
||||
// The modules the control plane ships with itself. Recorded here rather than by hand, because
|
||||
// a mesh whose own private network is missing from the catalogue would have nothing to assign
|
||||
// and no way to say why.
|
||||
open, err := openStores(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer open.Close()
|
||||
inv := open.inventory
|
||||
for _, m := range provided {
|
||||
if err := inv.Provide(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("provided %s\n", m.Module)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// openInventory connects and waits, the way every command that touches it needs to.
|
||||
func openInventory(ctx context.Context) (*inventory.Inventory, error) {
|
||||
inv, err := inventory.Open(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := inv.Ready(ctx, 30*time.Second); err != nil {
|
||||
inv.Close()
|
||||
return nil, err
|
||||
}
|
||||
return inv, nil
|
||||
}
|
||||
|
||||
func openIdentity(ctx context.Context) (*identity.Identity, error) {
|
||||
ident, err := identity.Open(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ident.Ready(ctx, 30*time.Second); err != nil {
|
||||
ident.Close()
|
||||
return nil, err
|
||||
}
|
||||
return ident, nil
|
||||
}
|
||||
|
||||
// openLicences connects to the context that holds which model access exists and who may use it.
|
||||
func openLicences(ctx context.Context) (*licences.Licences, error) {
|
||||
held, err := licences.Open(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := held.Ready(ctx, 30*time.Second); err != nil {
|
||||
held.Close()
|
||||
return nil, err
|
||||
}
|
||||
return held, nil
|
||||
}
|
||||
|
||||
// stores is what one command has open.
|
||||
//
|
||||
// **Opened once, not once per machine.** Working out what a machine should be reaches the identity
|
||||
// context for its certificate and the licence context for its model access, and both were opened —
|
||||
// and waited on — inside functions called for every node in a push. Two machines hid it; fifty
|
||||
// would be fifty connect-and-wait cycles for data that does not change while the push runs.
|
||||
//
|
||||
// Each is opened on first use rather than up front, because most commands need one context and
|
||||
// paying to reach three would be the same waste from the other side.
|
||||
type stores struct {
|
||||
inventory *inventory.Inventory
|
||||
identity *identity.Identity
|
||||
licences *licences.Licences
|
||||
}
|
||||
|
||||
// open connects to the inventory, which every command that touches the mesh needs.
|
||||
func openStores(ctx context.Context) (*stores, error) {
|
||||
inv, err := openInventory(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &stores{inventory: inv}, nil
|
||||
}
|
||||
|
||||
// Identity is this control plane's own identity context, opened if it has not been.
|
||||
func (h *stores) Identity(ctx context.Context) (*identity.Identity, error) {
|
||||
if h.identity != nil {
|
||||
return h.identity, nil
|
||||
}
|
||||
opened, err := openIdentity(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.identity = opened
|
||||
return opened, nil
|
||||
}
|
||||
|
||||
// Licences is the context holding model access, opened if it has not been.
|
||||
func (h *stores) Licences(ctx context.Context) (*licences.Licences, error) {
|
||||
if h.licences != nil {
|
||||
return h.licences, nil
|
||||
}
|
||||
opened, err := openLicences(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.licences = opened
|
||||
return opened, nil
|
||||
}
|
||||
|
||||
// Close lets go of everything that was opened, in any order: they are separate connections to
|
||||
// separate databases and none of them knows about the others.
|
||||
func (h *stores) Close() {
|
||||
if h.licences != nil {
|
||||
h.licences.Close()
|
||||
}
|
||||
if h.identity != nil {
|
||||
h.identity.Close()
|
||||
}
|
||||
if h.inventory != nil {
|
||||
h.inventory.Close()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user