A delivered host reads its version from where it sits
novox/hq ADR 0142, which decided this and was not implemented: "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 stamps no version, on purpose, so a delivered host called itself "development build" and the mesh could not tell which host any machine ran — which is the whole of what 087 added. A delivered host lives at <libexec>/versions/<version>/<binary>, and that directory is the answer. A host placed by hand keeps its stamp, which is the honest answer for one the mesh did not deliver, and is every machine until a delivery reaches it. A binary sitting anywhere else is not read as a version at all. The decision is split from the reading so a test can ask about a path without being that binary.
This commit is contained in:
+41
-4
@@ -51,6 +51,43 @@ var builtFor = ""
|
|||||||
|
|
||||||
var version = "development build"
|
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
|
const usage = `mesh-host — the node host
|
||||||
|
|
||||||
profile what this machine can be asked to do
|
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)
|
return runLink(ctx, opts)
|
||||||
|
|
||||||
case "version":
|
case "version":
|
||||||
fmt.Println(version)
|
fmt.Println(runningVersion())
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case "", "help", "-h", "--help":
|
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
|
// 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.
|
// lost is a rollback's ability to come back here, which is worse to hide than to say.
|
||||||
if version != "" {
|
if v := runningVersion(); v != "" {
|
||||||
if err := upgrade.RecordKnownGood(upgrade.KnownGoodPath(opts.state), version); err != nil {
|
if err := upgrade.RecordKnownGood(upgrade.KnownGoodPath(opts.state), v); err != nil {
|
||||||
fmt.Fprintf(os.Stderr,
|
fmt.Fprintf(os.Stderr,
|
||||||
"mesh-host: applied, but could not record %s as known-good: %v\n"+
|
"mesh-host: applied, but could not record %s as known-good: %v\n"+
|
||||||
" a rollback would have nothing to return to.\n", version, err)
|
" 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)
|
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
|
// 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,
|
// (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
|
// 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