Keep an original by its path and its content, so a second original at one path is not discarded (hq ADR 0100)

This commit is contained in:
2026-09-22 19:53:15 +02:00
parent 232ed74940
commit eb2f4fcf53
2 changed files with 75 additions and 7 deletions
+11 -5
View File
@@ -22,13 +22,19 @@ import (
// first copy is the one that was there before the mesh. // first copy is the one that was there before the mesh.
type Keep func(path string, content []byte, mode os.FileMode) (string, error) type Keep func(path string, content []byte, mode os.FileMode) (string, error)
// KeepIn keeps originals under dir/kept, each named for the path it came from, readable by root // KeepIn keeps originals under dir/kept, each named for the path it came from AND for what was in
// alone — a predecessor's configuration may carry its credentials. // it, readable by root alone — a predecessor's configuration may carry its credentials.
//
// **By content as well as path, because a path has more than one original.** A file held, let go
// when its module was unassigned, rewritten by the predecessor and found again is a second
// original; named by path alone the second copy was silently discarded while the report said it
// was kept (novox/hq ADR 0100). The same content at the same path is kept once.
func KeepIn(dir string) Keep { func KeepIn(dir string) Keep {
return func(path string, content []byte, _ os.FileMode) (string, error) { return func(path string, content []byte, _ os.FileMode) (string, error) {
sum := sha256.Sum256([]byte(path)) where := sha256.Sum256([]byte(path))
kept := filepath.Join(dir, "kept", what := sha256.Sum256(content)
hex.EncodeToString(sum[:])[:16]+"-"+filepath.Base(path)) kept := filepath.Join(dir, "kept", hex.EncodeToString(where[:])[:12]+"-"+
hex.EncodeToString(what[:])[:12]+"-"+filepath.Base(path))
if _, err := os.Lstat(kept); err == nil { if _, err := os.Lstat(kept); err == nil {
return kept, nil return kept, nil
} }
+64 -2
View File
@@ -382,8 +382,13 @@ func TestAHoldNoLongerDeclaredIsLetGoAndFoundAgainIfItsModuleReturns(t *testing.
t.Fatalf("a returning module's found file was not held again: %+v", o) t.Fatalf("a returning module's found file was not held again: %+v", o)
} }
again, _ := state.HeldAt("hello-web.page") again, _ := state.HeldAt("hello-web.page")
if kept, _ := os.ReadFile(again.Kept); again.Kept != first.Kept || string(kept) != "the predecessor's page\n" { // The predecessor rewrote it while nothing held it, so that is a second original, kept beside
t.Errorf("the first kept original was lost: %s %q", again.Kept, kept) // the first rather than in place of it (novox/hq ADR 0100).
if kept, _ := os.ReadFile(again.Kept); string(kept) != "the predecessor wrote again\n" {
t.Errorf("what is named as kept is %q", kept)
}
if kept, _ := os.ReadFile(first.Kept); string(kept) != "the predecessor's page\n" {
t.Errorf("the first kept original was lost: %q", kept)
} }
if got, _ := os.ReadFile(page); string(got) != "the predecessor wrote again\n" { if got, _ := os.ReadFile(page); string(got) != "the predecessor wrote again\n" {
t.Errorf("the found file was touched: %q", got) t.Errorf("the found file was touched: %q", got)
@@ -958,3 +963,60 @@ func TestAPackagedUnitTheMachineUsesIsStillHeld(t *testing.T) {
t.Errorf("the predecessor's service was restarted: %v", m.asked) t.Errorf("the predecessor's service was restarted: %v", m.asked)
} }
} }
func TestASecondOriginalAtTheSamePathIsKeptToo(t *testing.T) {
// Held, let go when the module was unassigned, rewritten by the predecessor, found again:
// both originals are kept, and the report names the one it kept (novox/hq ADR 0100).
dir := t.TempDir()
page := filepath.Join(dir, "index.html")
keep := KeepIn(dir)
first, err := keep(page, []byte("the predecessor's page\n"), 0o640)
if err != nil {
t.Fatal(err)
}
same, err := keep(page, []byte("the predecessor's page\n"), 0o640)
if err != nil || same != first {
t.Errorf("the same original was kept twice: %s %s %v", first, same, err)
}
second, err := keep(page, []byte("the predecessor wrote again\n"), 0o640)
if err != nil {
t.Fatal(err)
}
if second == first {
t.Fatal("a second original was kept under the first's name")
}
if got, _ := os.ReadFile(first); string(got) != "the predecessor's page\n" {
t.Errorf("the first original is %q", got)
}
if got, _ := os.ReadFile(second); string(got) != "the predecessor wrote again\n" {
t.Errorf("the second original is %q", got)
}
}
func TestAHoldLetGoAndFoundAgainKeepsBothOriginals(t *testing.T) {
dir, page, m := predecessor(t)
_, state := applyAdopted(t, adopted(t, untaken("hello-web.page", "hello-web.server"), webResources(page)),
store.State{}, m, dir)
first, _ := state.HeldAt("hello-web.page")
// Unassigned, then the predecessor writes again, then assigned once more.
_, state = applyAdopted(t, adopted(t, `{"taken":[]}`, withConf(dir)), state, m, dir)
if err := os.WriteFile(page, []byte("the predecessor wrote again\n"), 0o640); err != nil {
t.Fatal(err)
}
report, state := applyAdopted(t, adopted(t, untaken("hello-web.page", "hello-web.server"), webResources(page)),
state, m, dir)
again, _ := state.HeldAt("hello-web.page")
if again.Kept == first.Kept {
t.Fatalf("the second original was kept under the first's name: %s", again.Kept)
}
if got, _ := os.ReadFile(again.Kept); string(got) != "the predecessor wrote again\n" {
t.Errorf("what the report names as kept is %q", got)
}
if got, _ := os.ReadFile(first.Kept); string(got) != "the predecessor's page\n" {
t.Errorf("the first original was lost: %q", got)
}
if o := outcomeOf(report, "hello-web.page"); o.Action != "held" {
t.Errorf("the file found again was not held: %+v", o)
}
}