Found raising two machines: `apply <file>` refused the bundle example in this repository with `invalid character '/'`. The bundle strips whole-line comments; apply handed the raw bytes to the parser. So a file this repo ships could be built into a binary and not applied from disk. This is the third instance of one fault. There is already a test here named "what validates is what is applied", written when `mesh-host bundle` said yes and `reconcile` said no about the same artefact — two paths to one thing, disagreeing. Fixing that instance left the shape intact, so it came back somewhere else. So the fix is structural rather than local: `declaration.ParseFileTrusted` is the one way to read a declaration from disk, and the bundle and apply both use it. Comment handling and its test now live in one place, since having them in two is how it came to be done in two. The wire format is untouched — over the link it stays exactly JSON, because a format with a second thing to strip is a format with a second thing to disagree about. Asserted, and confirmed to fail if the link starts stripping.
83 lines
3.0 KiB
Go
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"
|
|
|
|
"github.com/novox/mesh-host/internal/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 substrate-arch.lock
|
|
var archLock []byte
|
|
|
|
//go:embed substrate-alpine.lock
|
|
var alpineLock []byte
|
|
|
|
//go:embed substrate-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 substrate 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])
|
|
}
|