A delivered host reads its version from where it sits #57
+41
-4
@@ -51,6 +51,43 @@ var builtFor = ""
|
||||
|
||||
var version = "development build"
|
||||
|
||||
// runningVersion is this host's version: the directory it was delivered into, or the link-time stamp
|
||||
// for one placed by hand.
|
||||
//
|
||||
// **From where it sits, not from its linker** (novox/hq ADR 0142): "It is unpacked into a directory
|
||||
// named for its version, so it can read its own version from its path. The stamp goes, and with it the
|
||||
// need for a build to know what it will be called."
|
||||
//
|
||||
// The mesh's toolchain does not stamp a version, on purpose — a build does not know what it will be
|
||||
// called — so a delivered host read as "development build" and the mesh could not tell which host any
|
||||
// machine ran (novox/hq 04-ISSUES/161, and 087 for why that matters). The path knows: a delivered host
|
||||
// lives at `<libexec>/versions/<version>/<binary>`.
|
||||
//
|
||||
// A host placed by hand keeps its stamp, which is the honest answer for one the mesh did not deliver.
|
||||
func runningVersion() string {
|
||||
self, err := os.Executable()
|
||||
if err != nil {
|
||||
return version
|
||||
}
|
||||
return versionAt(self, version)
|
||||
}
|
||||
|
||||
// versionAt is runningVersion's decision, with the executable's path and the link-time stamp given —
|
||||
// so a test can ask it about a path without being that binary.
|
||||
func versionAt(self, stamped string) string {
|
||||
// .../versions/<version>/<binary> — the parent is the version, and its parent is the versions
|
||||
// directory. Checked rather than assumed, so a binary somewhere else does not read a directory
|
||||
// name as a version.
|
||||
dir := filepath.Dir(self)
|
||||
if filepath.Base(filepath.Dir(dir)) != upgrade.VersionsDirName {
|
||||
return stamped
|
||||
}
|
||||
if name := filepath.Base(dir); name != "" && name != "." && name != string(filepath.Separator) {
|
||||
return name
|
||||
}
|
||||
return stamped
|
||||
}
|
||||
|
||||
const usage = `mesh-host — the node host
|
||||
|
||||
profile what this machine can be asked to do
|
||||
@@ -254,7 +291,7 @@ func run(ctx context.Context, command string, opts options) error {
|
||||
return runLink(ctx, opts)
|
||||
|
||||
case "version":
|
||||
fmt.Println(version)
|
||||
fmt.Println(runningVersion())
|
||||
return nil
|
||||
|
||||
case "", "help", "-h", "--help":
|
||||
@@ -594,8 +631,8 @@ func runApply(ctx context.Context, opts options, d *declaration.Declaration, raw
|
||||
//
|
||||
// A failure to record is reported and does not fail the apply. The apply worked; what is
|
||||
// lost is a rollback's ability to come back here, which is worse to hide than to say.
|
||||
if version != "" {
|
||||
if err := upgrade.RecordKnownGood(upgrade.KnownGoodPath(opts.state), version); err != nil {
|
||||
if v := runningVersion(); v != "" {
|
||||
if err := upgrade.RecordKnownGood(upgrade.KnownGoodPath(opts.state), v); err != nil {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"mesh-host: applied, but could not record %s as known-good: %v\n"+
|
||||
" a rollback would have nothing to return to.\n", version, err)
|
||||
@@ -1369,7 +1406,7 @@ func applyAndKeep(ctx context.Context, opts options, raw []byte, signed *store.D
|
||||
sched.Sync(declared, held)
|
||||
}
|
||||
|
||||
report := link.Report{Carried: carriedPorts(updated), Declared: digestOf(raw), Host: version}
|
||||
report := link.Report{Carried: carriedPorts(updated), Declared: digestOf(raw), Host: runningVersion()}
|
||||
// Which of this machine's links face outside, for the filter the mesh writes around them
|
||||
// (novox/hq ADR 0140). Reported whatever the node's mode: a converged node's filter needs it,
|
||||
// and an adopted one becomes converged without a further round trip. A machine that cannot read
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A component's version comes from where it sits, not from its linker (novox/hq ADR 0142). The mesh's
|
||||
// toolchain stamps no version — a build does not know what it will be called — so a delivered host
|
||||
// read as "development build" and the mesh could not tell which host a machine ran (04-ISSUES/161).
|
||||
|
||||
func TestADeliveredHostReadsItsVersionFromItsPath(t *testing.T) {
|
||||
// A delivered host lives at <libexec>/versions/<version>/<binary>.
|
||||
dir := t.TempDir()
|
||||
versioned := filepath.Join(dir, "versions", "637f65559d16")
|
||||
if err := os.MkdirAll(versioned, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
self := filepath.Join(versioned, "nox-mesh-host")
|
||||
if err := os.WriteFile(self, []byte("#!/bin/sh\n"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := versionAt(self, "development build"); got != "637f65559d16" {
|
||||
t.Fatalf("a delivered host read its version as %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAHostPlacedByHandKeepsItsStamp(t *testing.T) {
|
||||
// The honest answer for one the mesh did not deliver — and every machine is in that state until
|
||||
// a delivery reaches it.
|
||||
if got := versionAt("/usr/bin/nox-mesh-host", "04a27ca"); got != "04a27ca" {
|
||||
t.Fatalf("a hand-placed host read its version as %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestADirectoryThatIsNotAVersionIsNotReadAsOne(t *testing.T) {
|
||||
// A binary sitting anywhere else must not have its parent directory's name read as a version.
|
||||
for _, path := range []string{
|
||||
"/opt/somewhere/nox-mesh-host",
|
||||
"/usr/lib/nox-mesh-host/launch",
|
||||
"/home/someone/build/nox-mesh-host",
|
||||
} {
|
||||
if got := versionAt(path, "the stamp"); got != "the stamp" {
|
||||
t.Fatalf("%s read its version as %q", path, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user