carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "encoding/json"
5 "errors"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "testing"
10)
11
12type restartCrashStore struct {
13 *healthyRetryStore
14 boundary string
15}
16
17func (s *restartCrashStore) Write(name string, raw []byte) error {
18 var r rootRestartRecord
19 if err := json.Unmarshal(raw, &r); err != nil {
20 return err
21 }
22 stage := "baseline"
23 if r.Pending != nil {
24 stage = "pending"
25 if r.Pending.Targets[0].Before != "" {
26 stage = "intent"
27 }
28 if r.Pending.Targets[0].After != "" {
29 stage = "completed"
30 }
31 } else if r.ScopeID == "request-1" {
32 stage = "consumed"
33 }
34 crash := s.boundary == stage || (s.boundary == "intent-visible" && stage == "intent")
35 if crash && s.boundary == "intent-visible" {
36 s.fail = 1
37 }
38 err := s.healthyRetryStore.Write(name, raw)
39 if crash {
40 if err != nil && !errors.Is(err, s.fault) {
41 return err
42 }
43 os.Exit(74)
44 }
45 return err
46}
47
48func TestPlan14RootRestartProcessCrash(t *testing.T) {
49 if boundary := os.Getenv("PLAN14_RESTART_CRASH"); boundary != "" {
50 _, _, b, in := rootRestartFixture(t)
51 dir := os.Getenv("PLAN14_RESTART_DIR")
52 disk := &healthyRetryStore{dir: dir, fault: errors.New("injected directory sync failure")}
53 h := &RootRestarts{Store: &restartCrashStore{healthyRetryStore: disk, boundary: boundary}}
54 if boundary == "baseline" {
55 h.Observe(b, in)
56 t.Fatal("baseline crash not reached")
57 }
58 in = nextRootRestart(in)
59 p, err := h.Observe(b, in)
60 if err != nil {
61 t.Fatal(err)
62 }
63 target := p.Targets[0].Target
64 if err := h.Begin(b, p.ID, target, "boot-a/old"); err != nil {
65 t.Fatal(err)
66 }
67 // This is the fake unit's effect. The process exits without running defers.
68 if err := os.WriteFile(filepath.Join(dir, "invocation"), []byte("boot-a/new"), 0600); err != nil {
69 t.Fatal(err)
70 }
71 if boundary == "effect" {
72 os.Exit(74)
73 }
74 if err := h.Complete(b, p.ID, target, "boot-a/new"); err != nil {
75 t.Fatal(err)
76 }
77 if err := h.Finish(b, p.ID); err != nil {
78 t.Fatal(err)
79 }
80 t.Fatal("crash boundary not reached")
81 }
82 for _, boundary := range []string{"baseline", "pending", "intent", "intent-visible", "effect", "completed", "consumed"} {
83 t.Run(boundary, func(t *testing.T) {
84 h, s, b, in := rootRestartFixture(t)
85 if boundary != "baseline" {
86 if _, err := h.Observe(b, in); err != nil {
87 t.Fatal(err)
88 }
89 }
90 cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^TestPlan14RootRestartProcessCrash$", "-test.count=1")
91 cmd.Dir = s.dir
92 cmd.Env = append(os.Environ(), "TMPDIR="+s.dir, "PLAN14_RESTART_CRASH="+boundary, "PLAN14_RESTART_DIR="+s.dir)
93 out, err := cmd.CombinedOutput()
94 var exit *exec.ExitError
95 if !errors.As(err, &exit) || exit.ExitCode() != 74 {
96 t.Fatalf("child: %v\n%s", err, out)
97 }
98 h = &RootRestarts{Store: &healthyRetryStore{dir: s.dir}}
99 if boundary == "baseline" {
100 if p, err := h.Observe(b, in); err != nil || p != nil {
101 t.Fatalf("baseline replay: %+v %v", p, err)
102 }
103 return
104 }
105 in = nextRootRestart(in)
106 p, err := h.Observe(b, in)
107 if err != nil {
108 t.Fatal(err)
109 }
110 if boundary == "consumed" {
111 if p != nil {
112 t.Fatal("consumed request replayed")
113 }
114 return
115 }
116 if p == nil {
117 t.Fatal("lost pending request")
118 }
119 target := p.Targets[0].Target
120 if boundary == "pending" {
121 if err := h.Begin(b, p.ID, target, "boot-a/old"); err != nil {
122 t.Fatal(err)
123 }
124 return
125 }
126 if err := h.Begin(b, p.ID, target, "boot-a/old"); err == nil {
127 t.Fatal("crashed attempt dispatched twice")
128 }
129 invocation, err := os.ReadFile(filepath.Join(s.dir, "invocation"))
130 if boundary == "intent" || boundary == "intent-visible" {
131 if !errors.Is(err, os.ErrNotExist) {
132 t.Fatalf("pre-effect crash: %v", err)
133 }
134 if err := h.Complete(b, p.ID, target, "boot-a/old"); err == nil {
135 t.Fatal("ambiguous attempt consumed")
136 }
137 if err := h.Finish(b, p.ID); err == nil {
138 t.Fatal("unfinished attempt consumed")
139 }
140 return
141 }
142 if err != nil {
143 t.Fatal(err)
144 }
145 if err := h.Complete(b, p.ID, target, string(invocation)); err != nil {
146 t.Fatal(err)
147 }
148 if err := h.Finish(b, p.ID); err != nil {
149 t.Fatal(err)
150 }
151 if p, err := h.Observe(b, in); err != nil || p != nil {
152 t.Fatalf("recovered request replayed: %+v %v", p, err)
153 }
154 })
155 }
156}
157