Assigner: a node running an old-form build gets its first want at F once the canary runs F healthy (issue 1945)
An old-form build (not src2:) has no composition, so no want can ever name what such a node runs. Design limit, decided by the lead (lead-47): its first want names the newest F, by every rule as usual - the canary (rule 3: this node is the canary, or the canary runs F healthy), everything F needs (built, not held, granted, composed) and the providers (rule 5). Otherwise nothing is written and the neither condition stays. The canary stays in force, and old-form modules (clipmenu and screen-lock on g14) leave neither list by themselves, without a hand-written want. The code already behaved so (an old-form have falls through to Holds); the case is now named in Wants, and proven: a unit test for both sides of the canary, the providers and the grant, and the integration test gives g14 F and clears its neither condition once the canary ace runs F healthy.
This commit is contained in:
@@ -285,7 +285,8 @@ func (b *bed) get(t *testing.T, bucket, key string) []byte {
|
|||||||
// Started whole, the assigner wants F on the canary and writes no difference at all: there is no send to compare with (ADR 0539
|
// Started whole, the assigner wants F on the canary and writes no difference at all: there is no send to compare with (ADR 0539
|
||||||
// §8). 1945/AC19, AC21: shanks, which runs the older fingerprint (composed and granted) with no want, gets its first want at that
|
// §8). 1945/AC19, AC21: shanks, which runs the older fingerprint (composed and granted) with no want, gets its first want at that
|
||||||
// fingerprint, never at F; g14, which runs a build of the old form, gets none, so web is on neither list there and the assigner
|
// fingerprint, never at F; g14, which runs a build of the old form, gets none, so web is on neither list there and the assigner
|
||||||
// raises the condition naming g14, web and the canary rule.
|
// raises the condition naming g14, web and the canary rule; once ace runs F healthy, g14's first want names F and the condition is
|
||||||
|
// cleared.
|
||||||
func TestWantsAreWrittenAndNoDifferenceIs(t *testing.T) {
|
func TestWantsAreWrittenAndNoDifferenceIs(t *testing.T) {
|
||||||
b := startBed(t, "")
|
b := startBed(t, "")
|
||||||
fNew, fOld := delivery.SourcePrefix2+strings.Repeat("b", 64), delivery.SourcePrefix2+strings.Repeat("a", 64)
|
fNew, fOld := delivery.SourcePrefix2+strings.Repeat("b", 64), delivery.SourcePrefix2+strings.Repeat("a", 64)
|
||||||
@@ -340,6 +341,22 @@ func TestWantsAreWrittenAndNoDifferenceIs(t *testing.T) {
|
|||||||
if e, err := b.buckets[delivery.AssignerWantsBucket].Get(b.ctx, "want.g14.web"); err == nil {
|
if e, err := b.buckets[delivery.AssignerWantsBucket].Get(b.ctx, "want.g14.web"); err == nil {
|
||||||
t.Errorf("g14, which runs an old-form build and is held back by the canary, was given a want: %s", e.Value())
|
t.Errorf("g14, which runs an old-form build and is held back by the canary, was given a want: %s", e.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1945/AC21: once the canary ace runs F healthy, g14's first want names F by the rules as usual, and web leaves neither list
|
||||||
|
// there by itself: the condition is cleared.
|
||||||
|
healthyF, _ := delivery.EncodeHave(delivery.Have{FP: fNew, Phase: delivery.PhaseRunning, Health: delivery.HealthOK})
|
||||||
|
b.putJSON(t, delivery.HaveBucket, "ace.web", healthyF)
|
||||||
|
if w := b.wantNow(t, "g14", "web", "g14's first want at F once the canary runs it healthy", func(w delivery.Want) bool { return w.FP != "" }); w.FP != fNew || w.Composed != "sha256:"+strings.Repeat("c", 64) || w.Newest != "" {
|
||||||
|
t.Errorf("g14's want = %+v, want F with its composition", w)
|
||||||
|
}
|
||||||
|
cleared := false
|
||||||
|
for deadline := time.Now().Add(10 * time.Second); time.Now().Before(deadline) && !cleared; time.Sleep(100 * time.Millisecond) {
|
||||||
|
c, ok := b.conditionsSaid(t)[neither.Key]
|
||||||
|
cleared = ok && c.State == asks.StateCleared
|
||||||
|
}
|
||||||
|
if !cleared {
|
||||||
|
t.Errorf("the condition of web on neither list on g14 was not cleared once g14 got its want")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1297/B9 (the assigner's half, ADR 0528 clause 6): started whole, the assigner holds a fingerprint whose health check
|
// 1297/B9 (the assigner's half, ADR 0528 clause 6): started whole, the assigner holds a fingerprint whose health check
|
||||||
|
|||||||
@@ -239,7 +239,12 @@ func Wants(m Module, n Nodes, wants map[string]delivery.Want, lastHealthy map[st
|
|||||||
case !ran:
|
case !ran:
|
||||||
case have.FP == m.FP:
|
case have.FP == m.FP:
|
||||||
ok, rule, words = holds(mm, node, n, false)
|
ok, rule, words = holds(mm, node, n, false)
|
||||||
case strings.HasPrefix(have.FP, delivery.SourcePrefix2) && composed && m.GrantedAt != nil && m.GrantedAt(have.FP):
|
case !strings.HasPrefix(have.FP, delivery.SourcePrefix2):
|
||||||
|
// An old-form build (not src2:) has no composition, so no want can ever name it: its first want names F, and only by every
|
||||||
|
// rule as usual, the canary (3) and the providers (5) included, which here hold it back. Nothing is written and the module
|
||||||
|
// stays on neither list until the canary runs F healthy; then Holds passes and the want names F (issue 1945, a design
|
||||||
|
// limit set by the lead: the canary stays in force and old-form modules leave neither list by themselves).
|
||||||
|
case composed && m.GrantedAt != nil && m.GrantedAt(have.FP):
|
||||||
ok, target = true, have.FP
|
ok, target = true, have.FP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -690,3 +690,69 @@ func TestANewAssignmentWaitingIsNotOnNeitherList(t *testing.T) {
|
|||||||
t.Errorf("a module of the node's own composition named on neither list: %+v", out.Neither)
|
t.Errorf("a module of the node's own composition named on neither list: %+v", out.Neither)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1945/AC21: a node whose have names an old-form build (no fingerprint of the new form, so no composition of it) can never be
|
||||||
|
// named in a want at what it runs; its first want names F, under the canary rule as usual and with everything F needs and the
|
||||||
|
// providers rule. Given the canary runs F healthy, shanks's first want names F; given it does not yet, shanks gets no want and
|
||||||
|
// stays on neither list. The old-form node that is itself the canary gets F once its providers run healthy.
|
||||||
|
func TestAnOldFormNodeGetsItsFirstWantAtFOnlyUnderTheCanary(t *testing.T) {
|
||||||
|
oldForm := func(n Nodes, node string) {
|
||||||
|
n.Haves[node]["screen-lock"] = delivery.Have{FP: "build-1792260876956849831", Phase: delivery.PhaseRunning, Health: delivery.HealthUnknown}
|
||||||
|
}
|
||||||
|
providersHealthy := func(n Nodes) {
|
||||||
|
for _, node := range []string{"g14", "shanks"} {
|
||||||
|
n.Haves[node]["xorg"] = delivery.Have{FP: fpOf('x'), Phase: delivery.PhaseRunning, Health: delivery.HealthOK}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the canary g14 runs F healthy: shanks's first want names F
|
||||||
|
m, n := x11()
|
||||||
|
providersHealthy(n)
|
||||||
|
oldForm(n, "shanks")
|
||||||
|
n.Haves["g14"]["screen-lock"] = delivery.Have{FP: fNew, Phase: delivery.PhaseRunning, Health: delivery.HealthOK}
|
||||||
|
out := Wants(m, n, nil, nil)
|
||||||
|
if w := out.Wants["shanks"]; w.FP != fNew || w.Composed != dig(fNew) || w.Newest != "" || w.Rule != 0 {
|
||||||
|
t.Errorf("canary healthy on F: shanks's want = %+v, want F", w)
|
||||||
|
}
|
||||||
|
if _, ok := out.Neither["shanks"]; ok {
|
||||||
|
t.Errorf("canary healthy on F: shanks still on neither list: %+v", out.Neither)
|
||||||
|
}
|
||||||
|
// the canary does not run F healthy yet: no want, and shanks stays on neither list by rule 3
|
||||||
|
m, n = x11()
|
||||||
|
providersHealthy(n)
|
||||||
|
oldForm(n, "shanks")
|
||||||
|
n.Haves["g14"]["screen-lock"] = delivery.Have{FP: fNew, Phase: delivery.PhaseRunning, Health: delivery.HealthUnknown}
|
||||||
|
out = Wants(m, n, nil, nil)
|
||||||
|
if w, ok := out.Wants["shanks"]; ok {
|
||||||
|
t.Errorf("canary not healthy on F: shanks got a want: %+v", w)
|
||||||
|
}
|
||||||
|
if h, ok := out.Neither["shanks"]; !ok || h.Rule != delivery.RuleCanary {
|
||||||
|
t.Errorf("canary not healthy on F: shanks on neither list = %+v (named %v), want rule 3", h, ok)
|
||||||
|
}
|
||||||
|
// the canary healthy on F but the providers not healthy on shanks: rule 5 still holds it back
|
||||||
|
m, n = x11()
|
||||||
|
oldForm(n, "shanks")
|
||||||
|
n.Haves["g14"]["screen-lock"] = delivery.Have{FP: fNew, Phase: delivery.PhaseRunning, Health: delivery.HealthOK}
|
||||||
|
out = Wants(m, n, nil, nil)
|
||||||
|
if w, ok := out.Wants["shanks"]; ok {
|
||||||
|
t.Errorf("providers not healthy: shanks got a want: %+v", w)
|
||||||
|
}
|
||||||
|
if h := out.Neither["shanks"]; h.Rule != delivery.RuleProviders {
|
||||||
|
t.Errorf("providers not healthy: shanks on neither list = %+v, want rule 5", h)
|
||||||
|
}
|
||||||
|
// F not granted: nothing, even with the canary healthy
|
||||||
|
m, n = x11()
|
||||||
|
providersHealthy(n)
|
||||||
|
oldForm(n, "shanks")
|
||||||
|
n.Haves["g14"]["screen-lock"] = delivery.Have{FP: fNew, Phase: delivery.PhaseRunning, Health: delivery.HealthOK}
|
||||||
|
m.Granted = false
|
||||||
|
if w, ok := Wants(m, n, nil, nil).Wants["shanks"]; ok {
|
||||||
|
t.Errorf("F not granted: shanks got a want: %+v", w)
|
||||||
|
}
|
||||||
|
// the old-form node is the canary itself: F once its providers run healthy
|
||||||
|
m, n = x11()
|
||||||
|
providersHealthy(n)
|
||||||
|
oldForm(n, "g14")
|
||||||
|
if w := Wants(m, n, nil, nil).Wants["g14"]; w.FP != fNew || w.Composed != dig(fNew) {
|
||||||
|
t.Errorf("old-form canary: g14's want = %+v, want F", w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user