Review of L2, round 3: a have written with no composition keeps what the module's last have said runs (fingerprint, digest, sealed values), for every writer; a record module run nothing of since the start judges its move against its last have and waits for a yes (issue 1126, ADR 0547 §3)
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 23d66187a881 on ff9386dcccc2 | novox/mesh-host#149 in group feat/1126-nodes-compose
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 23d66187a881 on ff9386dcccc2 | novox/mesh-host#149 in group feat/1126-nodes-compose
This commit is contained in:
@@ -380,7 +380,20 @@ func (a *Agent) NotComposed(ctx context.Context, why string) {
|
||||
// writeHave writes a module's have; on a node composing its own, with the sealed values of the composition it names
|
||||
// when the caller gave none (Have.Sealed): those of the composition the agent applied or holds at that digest, else
|
||||
// those the module's last have said at that digest.
|
||||
//
|
||||
// A writer that names no composition (one written from the agent's running entry before this agent ran anything of the
|
||||
// module since its start: held by a failed backup, waiting for a yes, not composed) keeps what the module's last have
|
||||
// said runs, its fingerprint, digest and sealed values, rather than a have naming nothing: what runs is not lost, and
|
||||
// the boxes the composer reuses after a restart stay (review of L2, rounds 2 and 3).
|
||||
func (a *Agent) writeHave(ctx context.Context, h Have) {
|
||||
if h.Composed == "" && h.Module != NodeModule {
|
||||
if last, ok := a.env.Haves.Read(ctx, h.Module); ok && last.Composed != "" {
|
||||
h.FP, h.Composed = last.FP, last.Composed
|
||||
if h.Sealed == nil {
|
||||
h.Sealed = last.Sealed
|
||||
}
|
||||
}
|
||||
}
|
||||
if a.env.SelfComposed && h.Sealed == nil && h.Module != NodeModule && h.Composed != "" {
|
||||
h.Sealed = a.sealedOf(ctx, h.Module, h.FP, h.Composed)
|
||||
}
|
||||
@@ -1100,6 +1113,17 @@ func (a *Agent) waitsForYes(ctx context.Context, module string, w wantNow, comp
|
||||
from, known := a.runningComposition(module)
|
||||
prior := ran || a.prior[module]
|
||||
a.mu.Unlock()
|
||||
if !ran && !known {
|
||||
// Nothing of it run since this agent's start, and no kept declaration: its last have says what runs (review of L2,
|
||||
// round 3). The same composition is no move; another one is a move whose source cannot be read, so it waits for
|
||||
// a yes rather than being taken as the module's first composition.
|
||||
if h, ok := a.env.Haves.Read(ctx, module); ok && h.Composed != "" {
|
||||
if h.Composed == comp.Dig {
|
||||
return false
|
||||
}
|
||||
prior = true
|
||||
}
|
||||
}
|
||||
var j delivery.Judged
|
||||
switch {
|
||||
case known:
|
||||
|
||||
@@ -965,7 +965,7 @@ func TestTheNodesOwnCompositionIsAppliedWhenItDiffersAndARecordMoveWaitsForAYes(
|
||||
b := newBed(t)
|
||||
b.env.SelfComposed, b.env.Trusted = true, nil
|
||||
b.env.Policy = func(m string) string {
|
||||
if m == "db" {
|
||||
if m == "db" || m == "rec" {
|
||||
return catalogue.PolicyRecord
|
||||
}
|
||||
return catalogue.PolicyRoll
|
||||
@@ -1039,7 +1039,8 @@ func TestTheNodesOwnCompositionIsAppliedWhenItDiffersAndARecordMoveWaitsForAYes(
|
||||
|
||||
// A record module: its first composition is no move; a new image recreates its container and waits for a yes.
|
||||
container := func(image string) json.RawMessage {
|
||||
raw, _ := json.Marshal(map[string]any{"id": "db.server", "type": "container", "module": "db", "name": "db", "image": image})
|
||||
module := strings.SplitN(image, "@", 2)[0]
|
||||
raw, _ := json.Marshal(map[string]any{"id": module + ".server", "type": "container", "module": module, "name": module, "image": image})
|
||||
return raw
|
||||
}
|
||||
dk, draw, _ := unsigned("db", fp, container("db@sha256:"+strings.Repeat("1", 64)))
|
||||
@@ -1048,18 +1049,40 @@ func TestTheNodesOwnCompositionIsAppliedWhenItDiffersAndARecordMoveWaitsForAYes(
|
||||
eventually(t, "the record module's first composition was not applied", func() bool { return len(b.appliedList()) == 3 })
|
||||
fpB := fpOf("b")
|
||||
dk2, draw2, ddig2 := unsigned("db", fpB, container("db@sha256:"+strings.Repeat("2", 64)))
|
||||
// rec is a record module this agent has run nothing of since its start (the switch and every delivery of the
|
||||
// node-engine restart it): its have alone says what runs. Its move waits for a yes as db's does, and its have keeps
|
||||
// what runs (fingerprint, digest, sealed values) rather than naming nothing (review of L2, round 3).
|
||||
recFP, recDig := fpOf("d"), "sha256:"+strings.Repeat("8", 64)
|
||||
b.haves.Write(context.Background(), Have{Module: "rec", FP: recFP, Composed: recDig, Phase: string(delivery.PhaseRunning),
|
||||
Sealed: []string{`{"box":"rec"}`}})
|
||||
rk, rraw, rdig := unsigned("rec", fpB, container("rec@sha256:"+strings.Repeat("3", 64)))
|
||||
b.comps.put(dk2, draw2)
|
||||
b.comps.put(rk, rraw)
|
||||
want("db", fpB, "")
|
||||
eventually(t, "the move did not wait for a yes", func() bool {
|
||||
h := b.haves.last("db")
|
||||
return h.Phase == string(delivery.PhaseWaitingYes) && h.Judgement == string(delivery.JudgedRecreates) && h.Waits == ddig2
|
||||
})
|
||||
if got := b.appliedList(); len(got) != 3 {
|
||||
want("rec", fpB, "")
|
||||
applied := len(b.appliedList())
|
||||
for _, row := range []struct {
|
||||
module, fp, composed, waits string
|
||||
sealed []string
|
||||
}{{"db", fp, "", ddig2, nil}, {"rec", recFP, recDig, rdig, []string{`{"box":"rec"}`}}} {
|
||||
eventually(t, row.module+"'s move did not wait for a yes", func() bool {
|
||||
h := b.haves.last(row.module)
|
||||
return h.Phase == string(delivery.PhaseWaitingYes) && h.Waits == row.waits
|
||||
})
|
||||
h := b.haves.last(row.module)
|
||||
if h.FP != row.fp || (row.composed != "" && h.Composed != row.composed) || h.Composed == "" ||
|
||||
(row.sealed != nil && !slices.Equal(h.Sealed, row.sealed)) {
|
||||
t.Errorf("%s's have waiting for a yes does not keep what runs: %+v", row.module, h)
|
||||
}
|
||||
}
|
||||
if got := b.appliedList(); len(got) != applied {
|
||||
t.Fatalf("a move waiting for a yes was applied: %v", got)
|
||||
}
|
||||
want("db", fpB, ddig2)
|
||||
want("rec", fpB, rdig)
|
||||
eventually(t, "the yes did not apply the move", func() bool {
|
||||
h := b.haves.last("db")
|
||||
return len(b.appliedList()) == 4 && h.Phase == string(delivery.PhaseRunning) && h.FP == fpB
|
||||
h, r := b.haves.last("db"), b.haves.last("rec")
|
||||
return len(b.appliedList()) == applied+2 && h.Phase == string(delivery.PhaseRunning) && h.FP == fpB &&
|
||||
r.Phase == string(delivery.PhaseRunning) && r.FP == fpB && r.Composed == rdig
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user