carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostclaim
2
3import (
4 "errors"
5 "testing"
6)
7
8// plan11Relocating is a relocating claim at phase whose target, uk-box-2,
9// executes from installing on. The source, uk-box, executes before pinning.
10func plan11Relocating(phase string) *Claim {
11 exec := "uk-box-2"
12 switch phase {
13 case "starting", "releasing":
14 exec = "uk-box"
15 case "pinning":
16 exec = "none"
17 }
18 return &Claim{V: schemaVersion, Host: "a.go.example.com", Gen: 20,
19 Holder: &Holder{Family: "instance", Incarnation: "inc", Location: "instances/a.go.example.com", Generation: "g1", Stamp: 19, Session: 18, WriterBox: "uk-box",
20 Admissions: []AdmissionRecord{{Session: 18, Box: "uk-box", Generation: "g1", Mode: "cold", Region: "uk", PredecessorRegion: "uk"}}},
21 Op: Op{Kind: KindRelocating, ID: "r1", Phase: phase, Exec: exec, Participants: []string{"uk-box", "uk-box-2"},
22 Intent: map[string]string{"region": "uk", "source_box": "uk-box", "target_box": "uk-box-2"}}}
23}
24
25func plan11Probe(c *Claim) Admission {
26 return Admission{ObservedOpID: c.Op.ID, ObservedPhase: c.Op.Phase, ObservedSession: c.Holder.Session, ObservedGeneration: c.Holder.Generation,
27 Box: c.Op.Exec, Mode: "cold", NewGeneration: "p1", Region: "uk", PredecessorRegion: "uk"}
28}
29
30// The relocating session row is probing then serving, adjacent and forward
31// only (claim §4 phase table: probing admits cold probes; serving is the
32// successful target serving on the admitted session).
33func TestPlan11RelocatingSessionStepsAreAdjacentAndForward(t *testing.T) {
34 for _, tc := range []struct {
35 from, to string
36 want bool
37 }{
38 {"probing", "probing", true},
39 {"probing", "serving", true},
40 {"serving", "serving", true},
41 {"serving", "probing", false},
42 {"probing", "probe-failed", false},
43 {"installed", "probing", false},
44 {"installing", "probing", false},
45 {"releasing", "pinning", false},
46 {"starting", "releasing", false},
47 } {
48 if got := SessionContinues(KindRelocating, tc.from, tc.to); got != tc.want {
49 t.Errorf("SessionContinues(relocating, %q, %q) = %v, want %v", tc.from, tc.to, got, tc.want)
50 }
51 }
52 for phase, want := range map[string]bool{"probing": true, "serving": true, "starting": false, "releasing": false, "pinning": false, "installing": false, "installed": false, "probe-failed": false} {
53 if got := SessionPhase(KindRelocating, phase); got != want {
54 t.Errorf("SessionPhase(relocating, %q) = %v, want %v", phase, got, want)
55 }
56 }
57 if !SessionObserves(KindRelocating, "probing", "serving") || SessionObserves(KindRelocating, "serving", "probing") {
58 t.Fatal("observation follows the row forward only")
59 }
60 // The landing row is unchanged.
61 if !SessionContinues(KindLanding, "probed", "committed") || SessionContinues(KindLanding, "probing", "committed") {
62 t.Fatal("landing row changed")
63 }
64}
65
66func TestPlan11KeepWriterSessionCarriesTheProbeIntoServing(t *testing.T) {
67 toServing := func(c *Claim) error { c.Op.Phase = "serving"; return nil }
68 c := plan11Relocating("probing")
69 if err := KeepWriterSession("r1", toServing)(c); err != nil {
70 t.Fatalf("probing to serving keeps the probe's session: %v", err)
71 }
72 c = plan11Relocating("probing")
73 failed := func(c *Claim) error { c.Op.Phase = "probe-failed"; return nil }
74 if err := KeepWriterSession("r1", failed)(c); !errors.Is(err, ErrWrongState) {
75 t.Fatalf("probe-failed ends the session: %v", err)
76 }
77 c = plan11Relocating("probing")
78 newSession := func(c *Claim) error { c.Op.Phase = "serving"; c.Holder.Session++; return nil }
79 if err := KeepWriterSession("r1", newSession)(c); !errors.Is(err, ErrWrongState) {
80 t.Fatalf("a step that moves the session is refused: %v", err)
81 }
82}
83
84// AdmitWriter under relocating admits only op.exec's cold probe at probing.
85// The source's ordinary admission in starting or releasing stays refused
86// until its admission-fence authority exists (claim Plan 11, R1 as accepted).
87func TestPlan11AdmitWriterUnderRelocatingAdmitsOnlyTheExecutorsColdProbe(t *testing.T) {
88 c := plan11Relocating("probing")
89 in := plan11Probe(c)
90 if err := AdmitWriter(in)(c); err != nil {
91 t.Fatalf("op.exec's cold probe at probing: %v", err)
92 }
93 if c.Holder.WriterBox != "uk-box-2" || c.Holder.Generation != "p1" || c.Holder.Session != 21 {
94 t.Fatalf("admission not recorded: %+v", c.Holder)
95 }
96 refused := map[string]func() (*Claim, Admission){
97 "not the executor": func() (*Claim, Admission) {
98 c := plan11Relocating("probing")
99 in := plan11Probe(c)
100 c.Op.Exec, in.Box = "uk-box-2", "uk-box"
101 return c, in
102 },
103 "warm": func() (*Claim, Admission) {
104 c := plan11Relocating("probing")
105 in := plan11Probe(c)
106 in.Mode, in.NewGeneration, in.PredecessorCertified = "warm", "", true
107 return c, in
108 },
109 "released coverage": func() (*Claim, Admission) {
110 c := plan11Relocating("probing")
111 in := plan11Probe(c)
112 in.Coverage = "released"
113 return c, in
114 },
115 "stuck": func() (*Claim, Admission) {
116 c := plan11Relocating("probing")
117 c.Op.Journal = map[string]string{"stuck": "operator"}
118 return c, plan11Probe(c)
119 },
120 }
121 for _, phase := range []string{"starting", "releasing", "pinning", "installing", "installed", "probe-failed", "serving"} {
122 phase := phase
123 refused["phase "+phase] = func() (*Claim, Admission) {
124 c := plan11Relocating(phase)
125 in := plan11Probe(c)
126 if c.Op.Exec == "none" {
127 c.Op.Exec = "uk-box-2"
128 in.Box = "uk-box-2"
129 }
130 return c, in
131 }
132 }
133 for name, build := range refused {
134 c, in := build()
135 before := *c.Holder
136 if err := AdmitWriter(in)(c); !errors.Is(err, ErrWrongState) {
137 t.Errorf("%s: %v", name, err)
138 }
139 if c.Holder.Session != before.Session || c.Holder.Generation != before.Generation {
140 t.Errorf("%s: refused admission changed the holder", name)
141 }
142 }
143}
144
145func TestPlan11PublishGenerationUnderRelocatingNeedsAWriterPhase(t *testing.T) {
146 for phase, want := range map[string]bool{"probing": true, "serving": true, "installing": false, "installed": false, "probe-failed": false, "releasing": false} {
147 c := plan11Relocating(phase)
148 err := PublishGeneration("r1", phase, c.Holder.Session, "g1", "uk", func() string { return "chore" })(c)
149 if (err == nil) != want {
150 t.Errorf("relocating/%s publish: %v, want allowed=%v", phase, err, want)
151 }
152 }
153}
154