carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package rollout
2
3import (
4 "context"
5 "testing"
6 "time"
7
8 "github.com/carlosframework/platform/internal/activator"
9)
10
11// Final review wave 2, N3: an older wake W1's healthy event passes the token
12// check (it is still the host's newest wake), then pauses in served's
13// registry write. Meanwhile a newer wake W2 is planned, with config c2
14// delivered, and its healthy event activates c2. W1 resumes. Its c1 must
15// not be activated: active stays c2, with nothing left pending. Checked
16// before the pause and acted on after it, W1 put c1 back, and the host was
17// shown with config pending for a revision whose attempt had already
18// cleared.
19func TestAStaleWakeCannotActivateItsConfigAfterANewerWake(t *testing.T) {
20 ctx := context.Background()
21 h := newHarness(t, "a")
22 h.stage("v2")
23 h.pass()
24 // The queue stays out of it: no config attempt runs beside the wakes,
25 // so only their events activate anything.
26 s := h.m.schedulerOf("a")
27 s.mu.Lock()
28 s.q.deferHost("a", t0.Add(time.Hour))
29 s.mu.Unlock()
30
31 // W1: an ordinary wake of the good v1, planned with c1 delivered.
32 h.wakeHost("a")
33 h.m.ConfigDelivered("a", "sha256:c1")
34 w1 := h.m.Plan(ctx, h.rows.get("a"))
35 if w1.FirstWake || w1.Boot.Version != "v1" || w1.ConfigRev != "sha256:c1" {
36 t.Fatalf("W1 = %+v: want an ordinary wake of v1 carrying c1", w1)
37 }
38 h.act.serve("a", "v1")
39 g1, _ := h.act.Current("a")
40
41 paused, resume := make(chan struct{}), make(chan struct{})
42 h.rows.mu.Lock()
43 h.rows.setServingHook = func(string) {
44 h.rows.mu.Lock()
45 h.rows.setServingHook = nil
46 h.rows.mu.Unlock()
47 close(paused)
48 <-resume
49 }
50 h.rows.mu.Unlock()
51 w1Done := make(chan struct{})
52 go func() {
53 defer close(w1Done)
54 h.m.OnGen(activator.GenEvent{Kind: activator.GenHealthy, Route: h.rows.get("a"), Gen: g1,
55 Token: w1.Token, ConfigRev: w1.ConfigRev})
56 }()
57 <-paused // W1 is past its token check, inside served
58
59 // W2: v2 installed, c2 delivered, a first wake planned and healthy.
60 h.install("v2", "a")
61 h.m.ConfigDelivered("a", "sha256:c2")
62 w2 := h.m.Plan(ctx, h.rows.get("a"))
63 if !w2.FirstWake || w2.Boot.Version != "v2" || w2.ConfigRev != "sha256:c2" {
64 t.Fatalf("W2 = %+v: want a first wake of v2 carrying c2", w2)
65 }
66 h.m.OnGen(activator.GenEvent{Kind: activator.GenHealthy, Route: h.rows.get("a"), Gen: activator.Gen{N: 99, Build: "v2"},
67 FirstWake: true, Token: w2.Token, ConfigRev: w2.ConfigRev})
68 if got := h.activeConfig("a"); got != "sha256:c2" {
69 t.Fatalf("active = %q after W2's healthy event, want c2", got)
70 }
71
72 close(resume)
73 <-w1Done
74 if got := h.activeConfig("a"); got != "sha256:c2" {
75 t.Fatalf("active = %q: the older wake activated its revision after the newer wake's", got)
76 }
77 if got := h.m.Status().Hosts["a"]; got.ConfigPending {
78 t.Fatalf("a = %+v: config pending after the newer wake activated the delivered revision", got)
79 }
80}
81
82// activeConfig is host's active config revision.
83func (h *harness) activeConfig(host string) string {
84 h.m.mu.Lock()
85 defer h.m.mu.Unlock()
86 return h.m.track(host).active
87}
88