carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "os"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/hostclaim"
11 execlease "github.com/carlosframework/platform/internal/hostexec"
12 "github.com/carlosframework/platform/internal/hostguard"
13 "github.com/carlosframework/platform/internal/rootparticipation"
14)
15
16func TestPlan14ExecutionMigrationIsIndependent(t *testing.T) {
17 t.Parallel()
18 for _, attack := range []string{"none", "missing", "legacy marker", "incomplete", "pending", "never enrolled", "enrollment", "box", "generation"} {
19 t.Run(attack, func(t *testing.T) {
20 _, root, iq := plan14InstallRegistration(t, hostclaim.KindLanding, "checking")
21 if _, err := root.commitRootInventory(t.Context(), iq); err != nil {
22 t.Fatal(err)
23 }
24 disk := &rollbackDisk{dir: t.TempDir()}
25 census := rootparticipation.Census{Schema: 1, Box: "box-1", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}
26 switch attack {
27 case "incomplete":
28 census.Complete = false
29 case "pending":
30 census.Pending = "acquisition"
31 case "never enrolled":
32 census.NeverEnrolled = true
33 case "enrollment":
34 census.Enrollment = "other"
35 case "box":
36 census.Box = "other"
37 case "generation":
38 census.Migration++
39 }
40 if attack != "missing" && attack != "legacy marker" {
41 raw, _ := json.Marshal(census)
42 if err := disk.Write("census.json", raw); err != nil {
43 t.Fatal(err)
44 }
45 }
46 if attack == "legacy marker" {
47 if err := disk.Write("migration.json", []byte(`{"complete":true}`)); err != nil {
48 t.Fatal(err)
49 }
50 }
51 q := execlease.ExecutionRequest{Schema: 1, ID: "begin", Operation: "executionBegin", Host: iq.Target.Host, Incarnation: iq.Target.Incarnation, Stamp: iq.Target.Stamp, Session: iq.Target.Session, Op: iq.Target.Op}
52 a, err := root.executionAuthorityWithMigration(t.Context(), disk, q, execlease.ControllerProcess{})
53 if attack == "none" {
54 if err != nil || a.Migration != 1 {
55 t.Fatalf("valid migration: %+v %v", a, err)
56 }
57 } else if err == nil {
58 t.Fatal("invalid migration admitted", attack)
59 }
60 })
61 }
62}
63
64func TestPlan14ExecutionPreinstallationBindsMigration(t *testing.T) {
65 _, root, iq := plan14InstallRegistration(t, hostclaim.KindLanding, "checking")
66 disk := &rollbackDisk{dir: t.TempDir()}
67 raw, _ := json.Marshal(rootparticipation.Census{Schema: 1, Box: "box-1", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true})
68 if err := disk.Write("census.json", raw); err != nil {
69 t.Fatal(err)
70 }
71 q := execlease.ExecutionRequest{Schema: 1, ID: "begin", Operation: "executionBegin", Host: iq.Target.Host, Incarnation: iq.Target.Incarnation, Stamp: iq.Target.Stamp, Session: iq.Target.Session, Op: iq.Target.Op}
72 a, err := root.executionAuthorityWithMigration(context.Background(), disk, q, execlease.ControllerProcess{})
73 if err != nil || !a.ParticipationOnly || a.Migration != 1 || a.Installation != "" {
74 t.Fatalf("participation before registration: %+v %v", a, err)
75 }
76 if _, err := disk.Read(q.Host + ".json"); !os.IsNotExist(err) {
77 t.Fatal("authority read created participation", err)
78 }
79}
80
81func TestPlan14ExecutionServingBeforeInstallation(t *testing.T) {
82 _, root, iq := plan14InstallRegistration(t, hostclaim.KindServing, "")
83 disk := &rollbackDisk{dir: t.TempDir()}
84 raw, _ := json.Marshal(rootparticipation.Census{Schema: 1, Box: "box-1", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true})
85 if err := disk.Write("census.json", raw); err != nil {
86 t.Fatal(err)
87 }
88 boot, err := execlease.BootID()
89 if err != nil {
90 t.Fatal(err)
91 }
92 q := execlease.ExecutionRequest{Schema: 1, ID: "first-serving", Operation: "executionView", Host: iq.Target.Host, Incarnation: iq.Target.Incarnation, Stamp: iq.Target.Stamp, Session: iq.Target.Session}
93 peer := execlease.ControllerProcess{KernelBootID: boot, PID: os.Getpid(), Start: "fixture"}
94 broker, err := execlease.NewRootBroker(execlease.RootBrokerConfig{
95 State: disk, Remote: root.Stop.Coordinator.Evidence, Clock: execlease.SystemClock{}, BootID: boot, Identity: "broker",
96 Authority: func(ctx context.Context, q execlease.ExecutionRequest, p execlease.ControllerProcess) (execlease.ExecutionAuthority, error) {
97 return root.executionAuthorityWithMigration(ctx, disk, q, p)
98 },
99 Alive: func(context.Context, execlease.ControllerProcess) error { return nil },
100 Cleanup: func(context.Context, execlease.ExecutionRecord) error { t.Error("serving cleanup"); return nil },
101 Settle: func(context.Context, execlease.ExecutionRecord) error { t.Error("serving settlement"); return nil },
102 Exclude: func(context.Context, func() error) error {
103 t.Error("serving acquired lease")
104 return execlease.ErrState
105 },
106 })
107 if err != nil {
108 t.Fatal(err)
109 }
110 t.Cleanup(func() { _ = broker.Close(context.Background()) })
111 out, err := broker.Execute(t.Context(), peer, q)
112 if err != nil || out.Status != "serving" || out.Token != "" {
113 t.Fatalf("first serving participation: %+v %v", out, err)
114 }
115 if _, err := disk.Read(q.Host + ".json"); !os.IsNotExist(err) {
116 t.Fatal("serving created lease", err)
117 }
118}
119
120func TestPlan14ExecutionRetainedSourceParticipation(t *testing.T) {
121 t.Parallel()
122 for _, attack := range []string{"none", "source", "enrollment", "stamp"} {
123 t.Run(attack, func(t *testing.T) {
124 f, root, iq := plan14InstallRegistration(t, hostclaim.KindServing, "")
125 if _, err := root.commitRootInventory(t.Context(), iq); err != nil {
126 t.Fatal(err)
127 }
128 prior := f.read().Claim.Holder.Stamp
129 _, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error {
130 c.Holder.Stamp++
131 c.Op = hostclaim.Op{ID: "move", Kind: hostclaim.KindLanding, Phase: "checking", Exec: "box-2", Participants: []string{"box-1", "box-2"}, Intent: map[string]string{"source": "box-1", "source_enrollment": "en", "target": "box-2", "to": "ie"}, Journal: map[string]string{hostclaim.JournalSourceStamp: fmt.Sprint(prior)}}
132 switch attack {
133 case "source":
134 c.Op.Intent["source"] = "box-3"
135 case "enrollment":
136 c.Op.Intent["source_enrollment"] = "other"
137 case "stamp":
138 c.Op.Journal[hostclaim.JournalSourceStamp] = fmt.Sprint(prior + 1)
139 }
140 return nil
141 })
142 if err != nil {
143 t.Fatal(err)
144 }
145 q := executionExpectation(f.read().Claim)
146 q.ID, q.Operation = "source", "executionBegin"
147 a, err := root.executionAuthority(t.Context(), q, execlease.ControllerProcess{})
148 if attack != "none" {
149 if err == nil {
150 t.Fatal("foreign retained source admitted")
151 }
152 return
153 }
154 if err != nil || !a.ParticipationOnly || a.Installation == "" {
155 t.Fatalf("retained source: %+v %v", a, err)
156 }
157 })
158 }
159}
160
161func TestPlan14ExecutionInstalledWithoutLaunchCleanup(t *testing.T) {
162 t.Parallel()
163 for _, positive := range []bool{false, true} {
164 t.Run(fmt.Sprint(positive), func(t *testing.T) {
165 f, root, iq := plan14InstallRegistration(t, hostclaim.KindLanding, "checking")
166 if _, err := root.commitRootInventory(t.Context(), iq); err != nil {
167 t.Fatal(err)
168 }
169 q := executionExpectation(f.read().Claim)
170 a, err := root.executionAuthority(t.Context(), q, execlease.ControllerProcess{})
171 if err != nil {
172 t.Fatal(err)
173 }
174 calls := 0
175 root.Stop.Coordinator.Kernel = executionNoLaunchKernel{stopKernelFunc(func(context.Context, hostguard.FenceTarget, StopInventory, DrainAcknowledgement) (StopKernelEvidence, error) {
176 return StopKernelEvidence{}, ErrClaimStopInventory
177 }), func(_ context.Context, target hostguard.FenceTarget, inv StopInventory) (StopKernelEvidence, error) {
178 calls++
179 if target.Host != q.Host || target.Stamp != q.Stamp || target.Session != 0 || inv.InstallationStamp != q.Stamp {
180 t.Fatal("wrong empty installation")
181 }
182 if !positive {
183 return StopKernelEvidence{}, ErrClaimStopInventory
184 }
185 return StopKernelEvidence{}, nil
186 }}
187 err = root.cleanupExecution(t.Context(), execlease.ExecutionRecord{Request: q, Authority: a})
188 if calls != 1 || (err == nil) != positive {
189 t.Fatalf("kernel completeness calls=%d err=%v", calls, err)
190 }
191 })
192 }
193}
194
195type executionNoLaunchKernel struct {
196 stopKernelFunc
197 verify func(context.Context, hostguard.FenceTarget, StopInventory) (StopKernelEvidence, error)
198}
199
200func (k executionNoLaunchKernel) VerifyNoProcess(ctx context.Context, target hostguard.FenceTarget, inv StopInventory) (StopKernelEvidence, error) {
201 return k.verify(ctx, target, inv)
202}
203
204func TestPlan14ExecutionRecoveryStartsCanonicalFence(t *testing.T) {
205 for _, changed := range []bool{false, true} {
206 t.Run(fmt.Sprint(changed), func(t *testing.T) {
207 coordinator, req, _ := claimStopFixture(t)
208 root := ClaimRootHTTP{Stop: ClaimStopHTTP{Coordinator: coordinator}}
209 target := hostguard.FenceTarget{Host: req.Host, Incarnation: req.Incarnation, Stamp: req.InstallationStamp, Session: req.WriterSession, Box: req.Box, Enrollment: req.Enrollment, BootID: req.BootID, ControllerID: req.ControllerID, DirectoryRevision: req.ExpectedDirectoryRevision, ObligationID: req.ObligationID}
210 if changed {
211 target.Session++
212 }
213 got, err := root.beginExecutionRecoveryFence(t.Context(), target)
214 if changed {
215 if err == nil {
216 t.Fatal("fenced a different writer session")
217 }
218 return
219 }
220 if err != nil {
221 t.Fatal(err)
222 }
223 if got.FenceID == "" || got.ClaimETag != req.ExpectedClaimETag || got.Op != req.OperationID || got.Phase != req.Phase {
224 t.Fatalf("unbound fence: %+v", got)
225 }
226 if err := coordinator.Guard.ResumeFence(t.Context(), got); err != nil {
227 t.Fatal(err)
228 }
229 })
230 }
231}
232
233func TestPlan14ExecutionFirstSessionTransition(t *testing.T) {
234 t.Parallel()
235 f, root, iq := plan14InstallRegistration(t, hostclaim.KindLanding, "checking")
236 q := executionExpectation(f.read().Claim)
237 before, err := root.executionAuthority(t.Context(), q, execlease.ControllerProcess{})
238 if err != nil {
239 t.Fatal(err)
240 }
241 if _, err := root.commitRootInventory(t.Context(), iq); err != nil {
242 t.Fatal(err)
243 }
244 if _, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error { c.Holder.Session++; return nil }); err != nil {
245 t.Fatal(err)
246 }
247 nextQ := executionExpectation(f.read().Claim)
248 after, err := root.executionAuthority(t.Context(), nextQ, execlease.ControllerProcess{})
249 if err != nil {
250 t.Fatal(err)
251 }
252 before.Migration = after.Migration
253 old := execlease.ExecutionRecord{Request: q, Authority: before}
254 if err := root.transitionExecution(t.Context(), old, nextQ, after); err != nil {
255 t.Fatal("first root-authorized session refused", err)
256 }
257 nextQ.Op = "other"
258 if err := root.transitionExecution(t.Context(), old, nextQ, after); err == nil {
259 t.Fatal("foreign operation admitted")
260 }
261}
262
263func TestPlan14ExecutionEstablishedSessionTransitionNeedsCleanup(t *testing.T) {
264 t.Parallel()
265 for _, clean := range []bool{false, true} {
266 t.Run(fmt.Sprint(clean), func(t *testing.T) {
267 f, root, iq := plan14InstallRegistration(t, hostclaim.KindLanding, "checking")
268 if _, err := root.commitRootInventory(t.Context(), iq); err != nil {
269 t.Fatal(err)
270 }
271 q := executionExpectation(f.read().Claim)
272 before, err := root.executionAuthority(t.Context(), q, execlease.ControllerProcess{})
273 if err != nil {
274 t.Fatal(err)
275 }
276 if _, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error { c.Holder.Session++; return nil }); err != nil {
277 t.Fatal(err)
278 }
279 nextQ := executionExpectation(f.read().Claim)
280 after, err := root.executionAuthority(t.Context(), nextQ, execlease.ControllerProcess{})
281 if err != nil {
282 t.Fatal(err)
283 }
284 calls := 0
285 root.Stop.Coordinator.Kernel = executionNoLaunchKernel{verify: func(context.Context, hostguard.FenceTarget, StopInventory) (StopKernelEvidence, error) {
286 calls++
287 if !clean {
288 return StopKernelEvidence{}, ErrClaimStopInventory
289 }
290 return StopKernelEvidence{}, nil
291 }}
292 err = root.transitionExecution(t.Context(), execlease.ExecutionRecord{Request: q, Authority: before}, nextQ, after)
293 if (err == nil) != clean || calls != 1 {
294 t.Fatalf("cleanup=%t calls=%d err=%v", clean, calls, err)
295 }
296 })
297 }
298}
299