carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux && rootrunner
2
3package host
4
5import (
6 "errors"
7 "fmt"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/moves"
11 "github.com/carlosframework/platform/internal/rootstate"
12)
13
14func TestPlan14RootFloorSyscallCrashOrdering(t *testing.T) {
15 testRootProtocolFloorCrashes(t, rootFloorDir)
16}
17func TestPlan14RootSeedsFloorSyscallCrashOrdering(t *testing.T) {
18 testRootProtocolFloorCrashes(t, rootSeedsFloorDir)
19}
20func testRootProtocolFloorCrashes(t *testing.T, floorDir func(string) string) {
21 for _, stage := range []string{"write", "chmod", "file-sync", "close", "rename", "parent-sync"} {
22 for _, after := range []bool{false, true} {
23 for cut := 1; cut <= 5; cut++ {
24 t.Run(fmt.Sprintf("%s/after=%t/write=%d", stage, after, cut), func(t *testing.T) {
25 opt := generationRootRunnerDir(t)
26 id := rootFloorIdentity{Deployment: "deploy", Box: "box", Enrollment: "en"}
27 f, err := openRootProtocolFloors(floorDir(opt), id, true)
28 if err != nil {
29 t.Fatal(err)
30 }
31 a := moves.Floor{Min: 1, MinRelease: "v1.0.0"}
32 b := moves.Floor{Min: 2, MinRelease: "v2.0.0"}
33 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &a, Source: "history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
34 t.Fatal(err)
35 }
36 n := 0
37 injected := errors.New("interrupted syscall")
38 fault := func(_ string, point string, op func() error) error {
39 if point != stage {
40 return op()
41 }
42 n++
43 if n != cut {
44 return op()
45 }
46 if after {
47 if err := op(); err != nil {
48 return err
49 }
50 }
51 return injected
52 }
53 broken := f
54 broken.private = rootstate.WithWriteFault(f.private, fault)
55 broken.public = rootstate.WithWriteFault(f.public, fault)
56 if err := broken.refresh(t.Context(), &b, nil, nil); !errors.Is(err, injected) {
57 t.Fatalf("cut not reached: %v", err)
58 }
59 f.close()
60 f, err = openRootProtocolFloors(floorDir(opt), id, false)
61 if err != nil {
62 t.Fatal(err)
63 }
64 defer f.close()
65 // Before the second write's rename B is not visible. Thereafter the
66 // selector itself retains B even before accepted.json is replaced.
67 bound, err := f.bound()
68 if err != nil {
69 t.Fatal(err)
70 }
71 bVisible := cut > 2 || cut == 2 && (stage == "parent-sync" || stage == "rename" && after)
72 if bVisible && (bound == nil || *bound != b) {
73 t.Fatalf("lost visible deny: %+v", bound)
74 }
75 if p, e := readRootProtocolProjection(floorDir(opt)); e == nil && p.Floor != nil && *p.Floor == b {
76 t.Fatal("failed syscall acknowledged B")
77 }
78 if err := f.refresh(t.Context(), &b, nil, nil); err != nil {
79 t.Fatal("retry", err)
80 }
81 p, err := readRootProtocolProjection(floorDir(opt))
82 if err != nil || p.Floor == nil || *p.Floor != b {
83 t.Fatal("retry did not durably accept B", p, err)
84 }
85 if err := f.refresh(t.Context(), &a, nil, nil); err == nil {
86 t.Fatal("regressed after recovery")
87 }
88 bound, err = f.bound()
89 if err != nil || bound == nil || *bound != b {
90 t.Fatal("weakened deny", bound, err)
91 }
92 })
93 }
94 }
95 }
96}
97