diff --git a/go/delivery/assigner.go b/go/delivery/assigner.go index 1c9cdf0..3fbe5be 100644 --- a/go/delivery/assigner.go +++ b/go/delivery/assigner.go @@ -193,6 +193,10 @@ type Want struct { // of policy record that the node agent judged to recreate a container or move its data is applied only when its // want names the composition so allowed. Empty when none was asked. Yes string `json:"yes,omitempty"` + // Release names the release whose Approve answered the record ask of this want's build (novox/hq ADR 0601 §1): the + // group of pull requests mesh-delivery's gate merged on that Approve. Set with the yes it gave; empty when a person's + // answer to a record ask gave it, or none was needed. + Release string `json:"release,omitempty"` // Self says the node composes this module itself (ADR 0547 §2): the want names the build alone, and no composition. // A want that is not Self names the one composition it allows (ADR 0539 clause 1), as before. Self bool `json:"self,omitempty"` @@ -200,7 +204,7 @@ type Want struct { func (w Want) check() error { if w.Removed { - if w.FP != "" || w.Composed != "" || w.Newest != "" || w.Rule != 0 || w.Backup != "" || w.Put || w.Hold || w.Self || w.Yes != "" { + if w.FP != "" || w.Composed != "" || w.Newest != "" || w.Rule != 0 || w.Backup != "" || w.Put || w.Hold || w.Self || w.Yes != "" || w.Release != "" { return fmt.Errorf("delivery: a removed want names no fingerprint, composition, rule, backup answer or put") } return nil diff --git a/go/delivery/delivery.go b/go/delivery/delivery.go index 3b72bb7..10d23a9 100644 --- a/go/delivery/delivery.go +++ b/go/delivery/delivery.go @@ -351,6 +351,11 @@ type Have struct { // composition that waits for a person's yes (ADR 0547 §3); empty otherwise. Judgement Judgement `json:"judgement,omitempty"` Waits string `json:"waits,omitempty"` + // BuildAlone says, in phase waiting-yes, that the move changes the module's build alone (novox/hq ADR 0601 §1): the + // node composed the build it runs again with today's settings and secrets and got the composition it runs, so the + // release that carries the new build answers the yes. False when a setting or a secret changed too, or the node could + // not tell: the move is then asked of a person as before. + BuildAlone bool `json:"build-alone,omitempty"` // Sealed is, for a module the node composes itself, each value its running composition carries sealed to the node's // key (a secret, a credential, a vault box), as applied (ADR 0547 §3, issue 1126): what the node's composer reuses // after a restart while it opens to the vault's value, so a value sealed again moves no composition. Openable by the @@ -476,8 +481,8 @@ func decode(data []byte, into any) error { func (h Have) check() error { switch h.Phase { case PhaseBackingUp, PhaseApplying, PhaseChecking, PhaseRunning, PhaseFailed, PhaseRemoved, PhaseHeld: - if h.Judgement != "" || h.Waits != "" { - return fmt.Errorf("delivery: only a have waiting for a yes names a judgement and the composition waiting") + if h.Judgement != "" || h.Waits != "" || h.BuildAlone { + return fmt.Errorf("delivery: only a have waiting for a yes names a judgement, the composition waiting and whether it changes the build alone") } case PhaseWaitingYes: if h.Judgement != JudgedRecreates && h.Judgement != JudgedMovesData && h.Judgement != JudgedCannot { diff --git a/go/delivery/delivery_test.go b/go/delivery/delivery_test.go index 328fad7..1d341a6 100644 --- a/go/delivery/delivery_test.go +++ b/go/delivery/delivery_test.go @@ -380,10 +380,19 @@ func testPhaseHealthRefused(t *testing.T) { } else if back, err := DecodeHave(raw); err != nil || back.Judgement != JudgedRecreates || back.Waits != waits.Waits { t.Errorf("a have waiting for a yes came back %+v, %v", back, err) } + // ADR 0601 §1: and whether the move changes the build alone, read back as said. + alone := waits + alone.BuildAlone = true + if raw, err := EncodeHave(alone); err != nil { + t.Errorf("a have waiting for a yes to its build alone: %v", err) + } else if back, err := DecodeHave(raw); err != nil || !back.BuildAlone { + t.Errorf("a have waiting for a yes to its build alone came back %+v, %v", back, err) + } for name, h := range map[string]Have{ - "neither judged": {Phase: PhaseWaitingYes, Health: HealthOK, Judgement: JudgedNeither, Waits: waits.Waits}, - "no waiting digest": {Phase: PhaseWaitingYes, Health: HealthOK, Judgement: JudgedMovesData}, - "running judged": {Phase: PhaseRunning, Health: HealthOK, Judgement: JudgedRecreates}, + "running build alone": {Phase: PhaseRunning, Health: HealthOK, BuildAlone: true}, + "neither judged": {Phase: PhaseWaitingYes, Health: HealthOK, Judgement: JudgedNeither, Waits: waits.Waits}, + "no waiting digest": {Phase: PhaseWaitingYes, Health: HealthOK, Judgement: JudgedMovesData}, + "running judged": {Phase: PhaseRunning, Health: HealthOK, Judgement: JudgedRecreates}, } { if _, err := EncodeHave(h); err == nil { t.Errorf("%s: encoded", name) diff --git a/go/delivery/want_composed_test.go b/go/delivery/want_composed_test.go index f206367..eec127f 100644 --- a/go/delivery/want_composed_test.go +++ b/go/delivery/want_composed_test.go @@ -24,6 +24,8 @@ func TestAWantNamesItsComposition(t *testing.T) { // ADR 0547 §2, §3 (issue 1126): from the second milestone a want names the build alone, and the yes a person gave. {FP: fp2of('a'), Self: true}, {FP: fp2of('a'), Self: true, Yes: hashOf('3')}, + // ADR 0601 §1: the yes the release's Approve gave names the release. + {FP: fp2of('a'), Self: true, Yes: hashOf('3'), Release: "feat/l3b-bus"}, } { raw, err := EncodeWant(w) if err != nil { @@ -43,6 +45,7 @@ func TestAWantNamesItsComposition(t *testing.T) { {Want{FP: fp2of('a'), Self: true, Composed: hashOf('1')}, "names a composition"}, {Want{FP: fp2of('a'), Self: true, Yes: "sha256:12"}, "a want's yes allows"}, {Want{Removed: true, Self: true}, "removed want names no"}, + {Want{Removed: true, Release: "feat/l3b-bus"}, "removed want names no"}, {Want{FP: fp2of('a'), Composed: "sha256:12"}, "not sha256"}, {Want{Removed: true, Composed: hashOf('1')}, "removed want names no"}, {Want{FP: fp2of('a'), Composed: hashOf('1'), Backup: "later"}, `backup answer "later"`},