Let go of a hold whose resource is no longer declared, touching nothing on disk (hq ADR 0100)
This commit is contained in:
@@ -202,6 +202,24 @@ func ApplyKeeping(
|
||||
}
|
||||
}
|
||||
|
||||
// **A hold whose resource is no longer declared is let go, and nothing on disk is touched.**
|
||||
// What was found stays as it was; only the host's note that it holds it for a module goes, so
|
||||
// the node stops reporting a hold for a module no longer assigned. Should the module come back,
|
||||
// what is there is present with no record and is found, and held, again — its first kept
|
||||
// original is never overwritten (novox/hq ADR 0100). Only a declaration from the mesh says
|
||||
// what is assigned: a carried bundle's silence is not an unassignment.
|
||||
if origin == store.OriginDeclared {
|
||||
for _, h := range append([]store.Held{}, known.Held...) {
|
||||
if declared[h.ID] {
|
||||
continue
|
||||
}
|
||||
known.Release(h.ID)
|
||||
report.Outcomes = append(report.Outcomes, Outcome{ID: h.ID, Type: h.Kind, Target: h.Target,
|
||||
Action: "forgotten", Detail: "no longer declared; left as found"})
|
||||
log(fmt.Sprintf(" forgotten %s (%s): no longer declared; left as found", h.ID, h.Target))
|
||||
}
|
||||
}
|
||||
|
||||
// What moved in this apply, so a service that must reflect a file can be told the file
|
||||
// moved. Only within one apply: a change from an earlier one has already been reflected, and
|
||||
// restarting for it every time would make a steady machine restart its services for ever.
|
||||
|
||||
+135
-2
@@ -332,8 +332,141 @@ func TestAHeldFileIsNeverRemovedWhenItsModuleIsUnassigned(t *testing.T) {
|
||||
if m.removed("hello-web") {
|
||||
t.Fatal("a held container was removed when its module left")
|
||||
}
|
||||
if _, still := state.HeldAt("hello-web.page"); !still {
|
||||
t.Error("the hold was forgotten, so a return of the module would read the file as the mesh's")
|
||||
if _, still := state.HeldAt("hello-web.page"); still {
|
||||
t.Error("a hold outlived its resource leaving the declaration, so the node reports it for ever")
|
||||
}
|
||||
if _, recorded := state.Find("hello-web.page"); recorded {
|
||||
t.Error("a file let go was recorded as the mesh's")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAHoldNoLongerDeclaredIsLetGoAndFoundAgainIfItsModuleReturns(t *testing.T) {
|
||||
dir, page, m := predecessor(t)
|
||||
_, state := applyAdopted(t, adopted(t, untakenWeb, webResources(page)), store.State{}, m, dir)
|
||||
first, _ := state.HeldAt("hello-web.page")
|
||||
|
||||
report, state := applyAdopted(t, adopted(t, `{"taken":[]}`, withConf(dir)), state, m, dir)
|
||||
if o := outcomeOf(report, "hello-web.page"); o.Action != "forgotten" || o.Detail != "no longer declared; left as found" {
|
||||
t.Errorf("letting a hold go was not reported: %+v", o)
|
||||
}
|
||||
if len(state.Held) != 0 {
|
||||
t.Errorf("holds outlived their resources: %+v", state.Held)
|
||||
}
|
||||
|
||||
// The module comes back: what is there is found again, and the original first kept stays.
|
||||
if err := os.WriteFile(page, []byte("the predecessor wrote again\n"), 0o640); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
report, state = applyAdopted(t, adopted(t, untakenWeb, webResources(page)), state, m, dir)
|
||||
if o := outcomeOf(report, "hello-web.page"); o.Action != "held" {
|
||||
t.Fatalf("a returning module's found file was not held again: %+v", o)
|
||||
}
|
||||
again, _ := state.HeldAt("hello-web.page")
|
||||
if kept, _ := os.ReadFile(again.Kept); again.Kept != first.Kept || string(kept) != "the predecessor's page\n" {
|
||||
t.Errorf("the first kept original was lost: %s %q", again.Kept, kept)
|
||||
}
|
||||
if got, _ := os.ReadFile(page); string(got) != "the predecessor wrote again\n" {
|
||||
t.Errorf("the found file was touched: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestACarriedApplyLetsNoHoldGo(t *testing.T) {
|
||||
dir, page, m := predecessor(t)
|
||||
_, state := applyAdopted(t, adopted(t, untakenWeb, webResources(page)), store.State{}, m, dir)
|
||||
carried := parse(t, `{"declaration":1,"resources":[`+withConf(dir)+`]}`)
|
||||
_, state, err := ApplyKeeping(context.Background(), archHost(t), carried, state, store.OriginCarried,
|
||||
m.run, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(state.Held) != 2 {
|
||||
t.Errorf("a carried apply let holds go: %+v", state.Held)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAHeldFileRewrittenIsReportedAndNotReverted(t *testing.T) {
|
||||
dir, page, m := predecessor(t)
|
||||
d := adopted(t, untakenWeb, webResources(page))
|
||||
_, state := applyAdopted(t, d, store.State{}, m, dir)
|
||||
|
||||
if err := os.WriteFile(page, []byte("the predecessor wrote again\n"), 0o640); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
report, state := applyAdopted(t, d, state, m, dir)
|
||||
if got, _ := os.ReadFile(page); string(got) != "the predecessor wrote again\n" {
|
||||
t.Fatalf("a held file was reverted: %q", got)
|
||||
}
|
||||
if h, _ := state.HeldAt("hello-web.page"); h.Changed != "rewritten" || h.ChangedAt.IsZero() {
|
||||
t.Errorf("a rewrite was not recorded: %+v", h)
|
||||
}
|
||||
if o := outcomeOf(report, "hello-web.page"); !strings.Contains(o.Detail, "rewritten") {
|
||||
t.Errorf("a rewrite was not reported: %+v", o)
|
||||
}
|
||||
h, _ := state.HeldAt("hello-web.page")
|
||||
if kept, _ := os.ReadFile(h.Kept); string(kept) != "the predecessor's page\n" {
|
||||
t.Errorf("the kept original was overwritten by a later write: %q", kept)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAHeldContainerStoppedOrReplacedIsReportedAndNotRestarted(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
change func(*machine)
|
||||
want string
|
||||
}{
|
||||
{func(m *machine) { m.containers["hello-web"].running = false }, "stopped"},
|
||||
{func(m *machine) { m.containers["hello-web"].id = "another" }, "replaced"},
|
||||
{func(m *machine) { delete(m.containers, "hello-web") }, "gone"},
|
||||
} {
|
||||
dir, page, m := predecessor(t)
|
||||
d := adopted(t, untakenWeb, webResources(page))
|
||||
_, state := applyAdopted(t, d, store.State{}, m, dir)
|
||||
c.change(m)
|
||||
m.asked = nil
|
||||
_, state = applyAdopted(t, d, state, m, dir)
|
||||
if h, _ := state.HeldAt("hello-web.server"); h.Changed != c.want {
|
||||
t.Errorf("%s: recorded as %q", c.want, h.Changed)
|
||||
}
|
||||
for _, a := range m.asked {
|
||||
if strings.HasPrefix(a, "docker run") || strings.HasPrefix(a, "docker rm") ||
|
||||
strings.HasPrefix(a, "docker start") {
|
||||
t.Errorf("%s: the held container was acted on: %s", c.want, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAHeldFileThatVanishesIsNotCreated(t *testing.T) {
|
||||
dir, page, m := predecessor(t)
|
||||
d := adopted(t, untakenWeb, webResources(page))
|
||||
_, state := applyAdopted(t, d, store.State{}, m, dir)
|
||||
if err := os.Remove(page); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, state = applyAdopted(t, d, state, m, dir)
|
||||
if _, err := os.Stat(page); !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatal("a held file that vanished was created before its module was taken")
|
||||
}
|
||||
if h, _ := state.HeldAt("hello-web.page"); h.Changed != "gone" {
|
||||
t.Errorf("a vanished held file was not reported gone: %+v", h)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAConvergedNodeStillReplacesWhatItFinds(t *testing.T) {
|
||||
// No adoption, no holds: byte for byte what a converged node did before ADR 0100.
|
||||
dir, page, m := predecessor(t)
|
||||
d := parse(t, `{"declaration":1,"resources":[`+webResources(page)+`]}`)
|
||||
report, state := applyAdopted(t, d, store.State{}, m, dir)
|
||||
if got, _ := os.ReadFile(page); string(got) != "the mesh's page\n" {
|
||||
t.Errorf("a converged node kept a found file: %q", got)
|
||||
}
|
||||
if !m.removed("hello-web") {
|
||||
t.Error("a converged node kept a found container")
|
||||
}
|
||||
if len(state.Held) != 0 || outcomeOf(report, "hello-web.page").Action == "held" {
|
||||
t.Errorf("a converged node held something: %+v", state.Held)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "kept")); !errors.Is(err, os.ErrNotExist) {
|
||||
t.Error("a converged node kept originals")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user