The file on PATH was the engine a person copied at installation, and every delivered engine ran from the versions directory: the operator typed hand-over and a build from before it answered "unexpected argument". A proved engine now copies itself over that file when the bytes differ (never a link). And the hand-over of a directory used as found (issue 339) gets its channel: the controller's terminal asks this node's engine on mesh.node.<node>.ask.hand-over; the engine judges every value — the ask's size and shape, who asked, the path as it states it, a directory and no link, through no link — and records it or refuses and records nothing. Every text the engine addresses to the operator names the nox line (ADR 0272); the engine's own subcommands are install-time and rescue, and its usage says so.
155 lines
5.2 KiB
Go
155 lines
5.2 KiB
Go
package upgrade
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The engine placed by hand becomes a copy of the proved one when the two differ (novox/hq issue 356): the
|
|
// command on PATH is the build that runs the machine.
|
|
func TestTheFallbackBecomesACopyOfTheProvedEngine(t *testing.T) {
|
|
dir := t.TempDir()
|
|
self := filepath.Join(dir, VersionsDirName, "v2", BinaryName)
|
|
if err := os.MkdirAll(filepath.Dir(self), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(self, []byte("the proved engine"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fallback := filepath.Join(dir, "bin", BinaryName)
|
|
if err := os.MkdirAll(filepath.Dir(fallback), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(fallback, []byte("the stale engine placed by hand"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
renewed, err := RenewFallback(self, fallback)
|
|
if err != nil || !renewed {
|
|
t.Fatalf("renewed %v, %v", renewed, err)
|
|
}
|
|
got, err := os.ReadFile(fallback)
|
|
if err != nil || string(got) != "the proved engine" {
|
|
t.Fatalf("the fallback holds %q, %v", got, err)
|
|
}
|
|
info, err := os.Stat(fallback)
|
|
if err != nil || info.Mode().Perm() != 0o755 {
|
|
t.Fatalf("the fallback's mode is %v, %v; want 0755 so it can be run", info.Mode(), err)
|
|
}
|
|
if _, err := os.Stat(self); err != nil {
|
|
t.Fatalf("the running engine was touched: %v", err)
|
|
}
|
|
|
|
// Already a copy: left alone, so a proof every start does not rewrite it every start.
|
|
before, _ := os.Stat(fallback)
|
|
renewed, err = RenewFallback(self, fallback)
|
|
if err != nil || renewed {
|
|
t.Fatalf("a fallback that is already a copy was renewed again: %v, %v", renewed, err)
|
|
}
|
|
after, _ := os.Stat(fallback)
|
|
if !os.SameFile(before, after) {
|
|
t.Fatal("a fallback that is already a copy was replaced")
|
|
}
|
|
if left, _ := filepath.Glob(filepath.Join(filepath.Dir(fallback), "."+BinaryName+"-*")); len(left) > 0 {
|
|
t.Fatalf("temporary files left beside the fallback: %v", left)
|
|
}
|
|
}
|
|
|
|
// An engine running from the fallback itself — nothing delivered yet — renews nothing: it would copy a file
|
|
// over itself.
|
|
func TestAnEngineRunningFromTheFallbackRenewsNothing(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fallback := filepath.Join(dir, BinaryName)
|
|
if err := os.WriteFile(fallback, []byte("placed by hand and running"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
renewed, err := RenewFallback(fallback, fallback)
|
|
if err != nil || renewed {
|
|
t.Fatalf("renewed %v, %v", renewed, err)
|
|
}
|
|
}
|
|
|
|
// Nothing placed by hand: the proved engine is placed, so the launcher has a fallback and the machine a command.
|
|
func TestAMissingFallbackIsPlaced(t *testing.T) {
|
|
dir := t.TempDir()
|
|
self := filepath.Join(dir, "v1", BinaryName)
|
|
if err := os.MkdirAll(filepath.Dir(self), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(self, []byte("proved"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fallback := filepath.Join(dir, BinaryName)
|
|
renewed, err := RenewFallback(self, fallback)
|
|
if err != nil || !renewed {
|
|
t.Fatalf("renewed %v, %v", renewed, err)
|
|
}
|
|
if got, _ := os.ReadFile(fallback); string(got) != "proved" {
|
|
t.Fatalf("placed %q", got)
|
|
}
|
|
}
|
|
|
|
// A link at the fallback's path is refused and left as it is: the mesh creates no link and writes through none
|
|
// (novox/hq ADR 0012). The link's target is untouched too.
|
|
func TestALinkAtTheFallbackIsRefusedAndLeft(t *testing.T) {
|
|
dir := t.TempDir()
|
|
self := filepath.Join(dir, "v1", BinaryName)
|
|
if err := os.MkdirAll(filepath.Dir(self), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(self, []byte("proved"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
target := filepath.Join(dir, "somebody-elses")
|
|
if err := os.WriteFile(target, []byte("theirs"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fallback := filepath.Join(dir, BinaryName)
|
|
if err := os.Symlink(target, fallback); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
renewed, err := RenewFallback(self, fallback)
|
|
if err == nil || renewed || !strings.Contains(err.Error(), "link") {
|
|
t.Fatalf("a link was taken: %v, %v", renewed, err)
|
|
}
|
|
if got, _ := os.ReadFile(target); string(got) != "theirs" {
|
|
t.Fatalf("the link's target was written: %q", got)
|
|
}
|
|
if info, err := os.Lstat(fallback); err != nil || info.Mode()&os.ModeSymlink == 0 {
|
|
t.Fatalf("the link was replaced: %v %v", info, err)
|
|
}
|
|
}
|
|
|
|
// A running engine that cannot be read is said, not copied as nothing.
|
|
func TestAnUnreadableRunningEngineIsSaid(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fallback := filepath.Join(dir, BinaryName)
|
|
if err := os.WriteFile(fallback, []byte("placed"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
renewed, err := RenewFallback(filepath.Join(dir, "gone"), fallback)
|
|
if err == nil || renewed {
|
|
t.Fatalf("renewed %v, %v", renewed, err)
|
|
}
|
|
if got, _ := os.ReadFile(fallback); string(got) != "placed" {
|
|
t.Fatalf("the fallback was written from nothing: %q", got)
|
|
}
|
|
if _, err := RenewFallback("", fallback); err == nil {
|
|
t.Fatal("an empty path was taken")
|
|
}
|
|
}
|
|
|
|
// The fallback's path is the launcher's: its environment variable, or the default.
|
|
func TestTheFallbackPathIsTheLaunchers(t *testing.T) {
|
|
t.Setenv(FallbackVar, "")
|
|
if FallbackPath() != DefaultFallback {
|
|
t.Fatalf("the default is %q", FallbackPath())
|
|
}
|
|
t.Setenv(FallbackVar, "/elsewhere/nox-mesh-host")
|
|
if FallbackPath() != "/elsewhere/nox-mesh-host" {
|
|
t.Fatalf("the override is %q", FallbackPath())
|
|
}
|
|
}
|