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 "os"
7 "reflect"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/hostbox"
13 "github.com/carlosframework/platform/internal/hostclaim"
14 "github.com/carlosframework/platform/internal/hostguard"
15 "github.com/carlosframework/platform/internal/provision"
16 "github.com/carlosframework/platform/internal/release"
17 "github.com/carlosframework/platform/internal/rollout"
18 "github.com/carlosframework/platform/internal/rootparticipation"
19)
20
21type r11RecordingStore struct {
22 release.Store
23 reads []string
24}
25
26func (s *r11RecordingStore) Get(ctx context.Context, key string) ([]byte, string, error) {
27 s.reads = append(s.reads, "get:"+key)
28 return s.Store.Get(ctx, key)
29}
30func (s *r11RecordingStore) List(ctx context.Context, prefix string) ([]string, error) {
31 s.reads = append(s.reads, "list:"+prefix)
32 return s.Store.List(ctx, prefix)
33}
34func TestPlan14NoCensusProofObservation(t *testing.T) {
35 for _, flag := range []string{"", "1"} {
36 t.Run(flag, func(t *testing.T) {
37 t.Setenv("CARLOS_HOST_CLAIM", flag)
38 _, public, err := release.GenerateKey()
39 if err != nil {
40 t.Fatal(err)
41 }
42 t.Setenv("CARLOS_RELEASE_PUBKEY", public)
43 e, rt, rec, source := plan14ProofFixture(t)
44 store := &r11RecordingStore{Store: e.store}
45 production := productionProofBindingSource(store, source.sys, source.optDir)
46 if production.signedRecovery {
47 t.Fatal("no census enabled signed recovery")
48 }
49 source.store = store
50 source.signedRecovery = production.signedRecovery
51 if production.claim != nil {
52 rec.CreatedAt = time.Now().UTC()
53 plan14PutRecord(t, e.store, rec)
54 body, err := json.Marshal(rec)
55 if err != nil {
56 t.Fatal(err)
57 }
58 o, err := hostclaim.Read(t.Context(), e.store, rec.Host)
59 if err != nil {
60 t.Fatal(err)
61 }
62 o, err = hostclaim.Update(t.Context(), e.store, o, hostclaim.BeginCreate(hostclaim.NewHolder{Family: "instance", Key: provision.RecordKey(rec.Account, rec.App, rec.Host), Host: rec.Host, Account: rec.Account, App: rec.App, Region: "ie", CreatedAt: rec.CreatedAt}, body, "create", "console", rec.CreatedAt))
63 if err != nil {
64 t.Fatal(err)
65 }
66 o, err = hostclaim.Update(t.Context(), e.store, o, hostclaim.CompleteCreate("create", "box"))
67 if err != nil {
68 t.Fatal(err)
69 }
70 if _, err = hostclaim.Update(t.Context(), e.store, o, hostclaim.AdmitWriter(hostclaim.Admission{Box: "box", Region: "ie", Mode: "cold", NewGeneration: "first"})); err != nil {
71 t.Fatal(err)
72 }
73 source.claim = production.claim
74 source.box = func(context.Context) (string, error) { return "box", nil }
75 }
76 source.healthy = func(string, string, string, string) *release.HealthyBuild { return nil }
77 live, err := e.root.obs.observe(rt, 0)
78 if err != nil {
79 t.Fatal(err)
80 }
81 if live.binding.Historical || live.binding.Signed {
82 t.Fatal("fabricated recovery authority")
83 }
84 baseline := append([]string(nil), store.reads...)
85 store.reads = nil
86 if _, _, err = provision.FindRecordByHost(t.Context(), store, rec.Host); err != nil {
87 t.Fatal(err)
88 }
89 if source.claim != nil {
90 if _, err = source.claim(t.Context(), rec.Host); err != nil {
91 t.Fatal(err)
92 }
93 }
94 // Observation binds the unit and repeats that binding after probing it.
95 want := append(append([]string(nil), store.reads...), store.reads...)
96 if !reflect.DeepEqual(baseline, want) {
97 t.Fatalf("legacy observation read beyond its two canonical record/claim lookups: got %v, want %v", baseline, want)
98 }
99 store.reads = nil
100 source.signedRecovery = true
101 if _, err = e.root.obs.observe(rt, 0); err != nil {
102 t.Fatal(err)
103 }
104 if reflect.DeepEqual(baseline, store.reads) {
105 t.Fatal("negative control did not read authorization")
106 }
107 for _, key := range baseline {
108 if strings.Contains(key, "manifest") || strings.Contains(key, "promotion") {
109 t.Fatal("legacy authorization read", key)
110 }
111 }
112 })
113 }
114}
115
116func TestPlan14PrelaunchCancellation(t *testing.T) {
117 for _, mode := range []string{"reserved", "opener-failure", "open-writers", "wrong-controller", "kernel", "lost-close", "lost-retirement"} {
118 t.Run(mode, func(t *testing.T) {
119 f, disk, _ := generationLaunchFixture(t)
120 if mode == "opener-failure" {
121 f.root.generations.beginUpdate = func(context.Context, *rootGenerationSource, release.GenerationBinding) (func(string, error, func() error) error, func() error, error) {
122 return nil, nil, os.ErrPermission
123 }
124 }
125 permit, err := f.root.generations.reserve(t.Context(), f.root, f.peer, f.req)
126 if mode == "opener-failure" {
127 if err == nil {
128 t.Fatal("opener succeeded")
129 }
130 } else {
131 if err != nil {
132 t.Fatal(err)
133 }
134 defer permit.ledger.Store.Close()
135 }
136 r := readGenerationLaunch(t, disk)
137 launches, err := f.journal.GenerationLaunches(t.Context())
138 if err != nil {
139 t.Fatal(err)
140 }
141 for _, l := range launches {
142 if len(l.Reservations) != 0 {
143 t.Fatal("unit intention preceded cancellation test")
144 }
145 }
146 if mode != "open-writers" {
147 if err = f.journal.CloseWriters(t.Context(), f.target); err != nil {
148 t.Fatal(err)
149 }
150 }
151 if mode == "wrong-controller" {
152 r.Binding.ControllerPID++
153 }
154 checks := 0
155 v := generationRetirement{guard: f.root.Guard, journal: f.journal, kernel: func(context.Context, release.GenerationReservation) (string, error) {
156 checks++
157 if mode == "kernel" {
158 return "", os.ErrPermission
159 }
160 if mode == "lost-retirement" {
161 disk.fail = 1
162 }
163 return strings.Repeat("a", 64), nil
164 }}
165 if mode == "lost-close" {
166 disk.fail = 1
167 }
168 ledger := &release.RootGenerations{Store: disk}
169 err = v.retire(t.Context(), ledger, r)
170 got := readGenerationLaunch(t, disk)
171 switch mode {
172 case "open-writers":
173 if err != nil || got != r || checks != 0 {
174 t.Fatal("open reservation changed", err)
175 }
176 return
177 case "wrong-controller", "kernel":
178 if err == nil || got.Retirement != "" {
179 t.Fatal("uncertain cancellation accepted", err)
180 }
181 return
182 case "lost-close", "lost-retirement":
183 if err == nil {
184 t.Fatal("durability failure ignored")
185 }
186 v.kernel = func(context.Context, release.GenerationReservation) (string, error) {
187 return strings.Repeat("a", 64), nil
188 }
189 if err = v.retire(t.Context(), ledger, r); err != nil {
190 t.Fatal("recovery", err)
191 }
192 got = readGenerationLaunch(t, disk)
193 default:
194 if err != nil {
195 t.Fatal(err)
196 }
197 }
198 if !got.Closed || got.Retirement == "" || got.Process != (release.GenerationProcess{}) {
199 t.Fatal("reservation not cancelled", got)
200 }
201 if err = f.journal.IntendUnit(t.Context(), *f.req.Reservation); err == nil {
202 t.Fatal("closed controller replayed intention")
203 }
204 if _, err = ledger.Reserve(r.Binding); err == nil {
205 t.Fatal("cancelled reservation replayed")
206 }
207 views, err := f.root.Guard.Root.GenerationInstallations(t.Context())
208 if err != nil {
209 t.Fatal(err)
210 }
211 launches, err = f.journal.GenerationLaunches(t.Context())
212 if err != nil {
213 t.Fatal(err)
214 }
215 var exact []hostbox.LaunchInventory
216 for _, l := range launches {
217 if generationOwner(l.Target) == r.Binding.Target {
218 exact = append(exact, l)
219 }
220 }
221 snapshot := generationInventory{Installations: views, Launches: exact, Payloads: generationPayloadFixture(views)}
222 snapshot.Census.Schema = 1
223 snapshot.Census.Complete = true
224 snapshot.Census.Box = views[0].Installation.Box
225 snapshot.Census.Enrollment = views[0].Installation.Enrollment
226 snapshot.Census.Migration = 1
227 snapshot.Census.Revision = 1
228 snapshot.Census.LaunchRevision = 1
229 verifier := generationReconciler{read: func(context.Context) (generationInventory, error) { return snapshot, nil }, kernel: func(context.Context) (string, string, error) { return "boot", strings.Repeat("a", 64), nil }}
230 if _, err = verifier.reconcile(t.Context(), []release.GenerationReservation{got}); err != nil {
231 t.Fatal("cancelled pin failed census", err)
232 }
233 })
234 }
235}
236
237func TestPlan14RetiredRecordDeletion(t *testing.T) {
238 f, _, rec := generationLaunchFixture(t)
239 rec.Schema = 1
240 plan14PutRecord(t, f.root.generations.store, rec)
241 views, err := f.root.Guard.Root.GenerationInstallations(t.Context())
242 if err != nil {
243 t.Fatal(err)
244 }
245 in := views[0].Installation
246 views[0].Retired = []hostguard.Captured{{Incarnation: in.Incarnation, Stamp: in.Stamp}}
247 views[0].Installation.Empty = true
248 snapshot := generationInventory{Installations: views, Payloads: generationPayloadFixture(views)[:1]}
249 if err = f.root.generations.store.Delete(t.Context(), in.Record); err != nil {
250 t.Fatal(err)
251 }
252 if _, err = generationRequiredInventory(t.Context(), f.root.generations.store, snapshot, nil); err != nil {
253 t.Fatal("retired record required", err)
254 }
255 views[0].Retired = nil
256 if _, err = generationRequiredInventory(t.Context(), f.root.generations.store, snapshot, nil); err == nil {
257 t.Fatal("missing active record accepted")
258 }
259}
260
261func TestPlan14SettingsSuccessorPreservesDenials(t *testing.T) {
262 ctx := t.Context()
263 store := release.NewDirStore(t.TempDir())
264 rec := provision.Record{Schema: 1, Host: "settings.example", Account: "bab", App: "shop", Channel: "stable", CreatedAt: time.Now().UTC()}
265 if err := provision.CreateRecord(ctx, store, rec); err != nil {
266 t.Fatal(err)
267 }
268 inc, err := hostclaim.Incarnation(rec.Account, rec.App, rec.Host, rec.CreatedAt)
269 if err != nil {
270 t.Fatal(err)
271 }
272 state := &rollbackMemory{files: map[string][]byte{}}
273 book, err := rollout.InitializeRootFailures(state, "box/en/1", nil)
274 if err != nil {
275 t.Fatal(err)
276 }
277 p := rootUpdateSource{enforceFailures: true, state: state, store: store, book: book, binding: &proofBindingSource{}, data: t.TempDir()}
278 p.source.census = func() (rootparticipation.Census, error) {
279 return rootparticipation.Census{Schema: 1, Complete: true, Box: "box", Enrollment: "en", Migration: 1}, nil
280 }
281 in := hostguard.RootInstallation{Host: rec.Host, Incarnation: inc, Stamp: 1, Account: rec.Account, App: rec.App, Record: provision.RecordKey(rec.Account, rec.App, rec.Host), Box: "box", Enrollment: "en", Migration: 1, InventoryRevision: 1}
282 for _, stamp := range []uint64{1, 2} {
283 authority := &rollbackMemory{files: map[string][]byte{}}
284 in.Stamp = stamp
285 if err = hostguard.NewRootAuthority(authority).RetainMigrationDeny(ctx, in, stamp); err != nil {
286 t.Fatal(err)
287 }
288 p.source.state = authority
289 row, err := p.resolve(ctx, rec.Host)
290 if err != nil {
291 t.Fatal("successor settings", stamp, err)
292 }
293 if stamp == 1 {
294 if err = row.Save(release.Consumed{Completed: 17}); err != nil {
295 t.Fatal(err)
296 }
297 finish, err := row.Begin("v1", strings.Repeat("a", 64), "first")
298 if err != nil {
299 t.Fatal(err)
300 }
301 if err = finish("", os.ErrPermission, nil); err != nil {
302 t.Fatal(err)
303 }
304 key := rootSettingsKey(rec.Host, rootHistoryInstallation(in))
305 raw, err := state.Read(key)
306 if err != nil {
307 t.Fatal(err)
308 }
309 if err = state.Write("settings-"+rec.Host+".json", raw); err != nil {
310 t.Fatal(err)
311 }
312 if err = state.Remove(key); err != nil {
313 t.Fatal(err)
314 }
315 recovered, err := p.resolve(ctx, rec.Host)
316 if err != nil || recovered.Consumed == nil || recovered.Consumed.Completed != 17 {
317 t.Fatal("same-installation legacy settings lost", err)
318 }
319 } else {
320 if row.Consumed != nil || row.Build.State != nil {
321 t.Fatal("foreign completion reused")
322 }
323 if !row.Failed("v1") {
324 t.Fatal("successor lost required denial")
325 }
326 if row.Route.Socket != provision.NestedSocket(rec.Host) || row.Route.DBPath != provision.NestedDBPath(p.data, rec.Host) {
327 t.Fatal("claim readiness used legacy paths", row.Route)
328 }
329 }
330 }
331}
332