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
2
3package hostexec
4
5import (
6 "context"
7 "encoding/json"
8 "fmt"
9 "os"
10 "path/filepath"
11 "testing"
12
13 "golang.org/x/sys/unix"
14)
15
16func (d *brokerDisk) HoldLive(name string) (func(), error) {
17 f, err := os.OpenFile(filepath.Join(d.dir, name), os.O_CREATE|os.O_RDWR, 0600)
18 if err != nil {
19 return nil, err
20 }
21 lock := unix.Flock_t{Type: unix.F_WRLCK}
22 if err := unix.FcntlFlock(f.Fd(), unix.F_OFD_SETLK, &lock); err != nil {
23 f.Close()
24 return nil, err
25 }
26 return func() { f.Close() }, nil
27}
28func (d *brokerDisk) LiveHeld(name string) (bool, error) {
29 f, err := os.OpenFile(filepath.Join(d.dir, name), os.O_RDWR, 0)
30 if err != nil {
31 return false, err
32 }
33 defer f.Close()
34 lock := unix.Flock_t{Type: unix.F_RDLCK}
35 err = unix.FcntlFlock(f.Fd(), unix.F_OFD_GETLK, &lock)
36 return lock.Type == unix.F_WRLCK && lock.Pid == -1, err
37}
38
39type pausedProbe struct {
40 *brokerDisk
41 entered, resume chan struct{}
42 mutate bool
43}
44
45func (d *pausedProbe) LiveHeld(name string) (bool, error) {
46 if d.mutate {
47 unlock, err := d.Lock(context.Background(), name)
48 if err != nil {
49 return false, err
50 }
51 close(d.entered)
52 <-d.resume
53 unlock()
54 return false, nil
55 }
56 held, err := d.brokerDisk.LiveHeld(name)
57 close(d.entered)
58 <-d.resume
59 return held, err
60}
61
62type falseBrokerProbe struct{ *brokerDisk }
63
64func (d falseBrokerProbe) LiveHeld(name string) (bool, error) {
65 f, err := os.OpenFile(filepath.Join(d.dir, name), os.O_RDWR, 0)
66 if err != nil {
67 return false, err
68 }
69 defer f.Close()
70 err = unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB)
71 if err == unix.EWOULDBLOCK {
72 return true, nil
73 }
74 return false, err
75}
76
77func TestPlan14ConcurrentReadersNeverGrantLiveness(t *testing.T) {
78 for _, mutate := range []bool{false, true} {
79 t.Run(fmt.Sprint(mutate), func(t *testing.T) {
80 b, d, _, clock, q, oldPeer := brokerFixture(t)
81 peer, err := currentControllerProcess()
82 if err != nil {
83 t.Fatal(err)
84 }
85 b.cfg.BootID = peer.KernelBootID
86 original := b.cfg.Authority
87 b.cfg.Authority = func(ctx context.Context, q ExecutionRequest, _ ControllerProcess) (ExecutionAuthority, error) {
88 return original(ctx, q, oldPeer)
89 }
90 if _, err = b.Execute(t.Context(), peer, q); err != nil {
91 t.Fatal(err)
92 }
93 census, _ := json.Marshal(map[string]any{"Schema": 1, "Box": "box", "Enrollment": "en", "Migration": 1, "Revision": 1, "LaunchRevision": 1, "Complete": true})
94 if err = d.Write("census.json", census); err != nil {
95 t.Fatal(err)
96 }
97 if _, err = ReadRoot(t.Context(), d, clock, q, false); err != nil {
98 t.Fatal(err)
99 }
100 raw, err := d.Read(q.Host + ".json")
101 if err != nil {
102 t.Fatal(err)
103 }
104 r, err := b.read(q.Host)
105 if err != nil {
106 t.Fatal(err)
107 }
108 d.failAt = d.writes + 1
109 if err = b.deny(t.Context(), r, ErrLost); err == nil {
110 t.Fatal("denial succeeded")
111 }
112 if err = os.WriteFile(filepath.Join(d.dir, q.Host+".json"), raw, 0600); err != nil {
113 t.Fatal(err)
114 }
115 a := &pausedProbe{brokerDisk: d, entered: make(chan struct{}), resume: make(chan struct{}), mutate: mutate}
116 done := make(chan error, 1)
117 go func() { _, err := ReadRoot(t.Context(), a, clock, q, false); done <- err }()
118 <-a.entered
119 var readErr error
120 if mutate {
121 _, readErr = ReadRoot(t.Context(), falseBrokerProbe{d}, clock, q, false)
122 } else {
123 _, readErr = ReadRoot(t.Context(), d, clock, q, false)
124 }
125 close(a.resume)
126 if err := <-done; err == nil {
127 t.Fatal("reader A admitted")
128 }
129 if (readErr == nil) != mutate {
130 t.Fatalf("mutation=%v B error=%v", mutate, readErr)
131 }
132 })
133 }
134}
135