Files
mesh-host/internal/bundle/bundle.go
T
mesh-tools 52600ec7bd
mesh/merge-gate head 52600ec7bd61 merged into trunk cdcba9ca7375 — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
mesh/repo-check head 52600ec7bd61 merged into trunk cdcba9ca7375 — pass: its merge-check.sh passed
mesh/delivery delivered
Take the node-engine's parser from mesh-sdk: remaining imports (issue 1392)
2026-10-20 11:17:14 +00:00

83 lines
3.0 KiB
Go

// Package bundle is the declaration the host carries.
//
// novox/hq ADR 0004: the host has one behaviour and two sources of declaration — the control
// plane when a mesh is reachable, and this when none is. The first node is not a different
// kind of node; it is a node whose mesh is not up yet, and this is what it applies until it is.
//
// Carried inside the binary rather than beside it, because "copy it onto a machine and run it
// is the whole installation" (ADR 0005) stops being true the moment a second file has to
// arrive with it.
package bundle
import (
_ "embed"
"errors"
"fmt"
"strings"
"git.novox.be/novox/mesh-sdk/go/declaration"
)
// One bundle per operating system, because its CONTENTS are per system even though its
// mechanism is not: package names, unit names and service names all differ
// (novox/hq ADR 0005). All three are embedded and the host applies the one it was built for —
// an arch host never reads the alpine bundle.
//
// A host whose bundle is only comments carries nothing, and says so rather than applying
// nothing and reporting success — a host that silently did nothing on a first node would look
// exactly like one that worked.
//
//go:embed foundation-arch.lock
var archLock []byte
//go:embed foundation-alpine.lock
var alpineLock []byte
//go:embed foundation-android.lock
var androidLock []byte
var locks = map[string][]byte{
"arch": archLock,
"alpine": alpineLock,
"android": androidLock,
}
// ErrEmpty means this host carries no bundle.
var ErrEmpty = errors.New(
"this host carries no bundle. A host without one cannot raise a first node, and applying " +
"nothing would look exactly like applying something")
// Raw returns the carried bytes, for inspection.
func Raw(system string) []byte { return locks[system] }
// IsEmpty reports whether anything was built in. A bundle of only comments and whitespace is
// empty for this purpose: a placeholder is a comment, and treating it as content would mean a
// host claims to carry a foundation it does not.
func IsEmpty(system string) bool {
for _, line := range strings.Split(string(locks[system]), "\n") {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "//") {
return false
}
}
return true
}
// Load parses the carried bundle.
//
// The same parser the link will use. A bundle that reaches a machine and is then refused by the
// host that carries it would be a build-time mistake discovered at the worst possible moment,
// which is why `mesh-host bundle` exists to ask before it matters.
func Load(system string) (*declaration.Declaration, error) {
if _, known := locks[system]; !known {
return nil, fmt.Errorf("no bundle is built for %q", system)
}
if IsEmpty(system) {
return nil, ErrEmpty
}
// ParseTrusted: the bundle arrives with the binary, so it may carry actions the link may
// not (novox/hq ADR 0005). The bootstrap needs them — creating the control plane's database
// happens before there is any mesh to ask for one.
return declaration.ParseFileTrusted(locks[system])
}