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 "crypto/ed25519"
5 "encoding/json"
6 "errors"
7 "os"
8 "os/exec"
9 "path/filepath"
10 "strings"
11 "testing"
12)
13
14type retirementCrashStore struct {
15 *healthyRetryStore
16 boundary string
17}
18
19func (s *retirementCrashStore) Write(name string, raw []byte) error {
20 if !strings.HasSuffix(name, ".retired") {
21 return s.healthyRetryStore.Write(name, raw)
22 }
23 if s.boundary == "before" {
24 os.Exit(75)
25 }
26 if s.boundary == "visible" {
27 s.fail = 1
28 }
29 err := s.healthyRetryStore.Write(name, raw)
30 if err != nil && s.boundary != "visible" {
31 return err
32 }
33 os.Exit(75)
34 return nil
35}
36func TestPlan14HistoryRetirementProcessCrash(t *testing.T) {
37 if boundary := os.Getenv("PLAN14_RETIRE_CRASH"); boundary != "" {
38 dir := os.Getenv("PLAN14_RETIRE_DIR")
39 wire, err := os.ReadFile(filepath.Join(dir, "signed"))
40 if err != nil {
41 t.Fatal(err)
42 }
43 pub, err := os.ReadFile(filepath.Join(dir, "public"))
44 if err != nil {
45 t.Fatal(err)
46 }
47 var g HistoryRecoveryRetirement
48 if err := VerifyRootEnvelope(ed25519.PublicKey(pub), wire, &g); err != nil {
49 t.Fatal(err)
50 }
51 h := &RootHistory{Store: &retirementCrashStore{healthyRetryStore: &healthyRetryStore{dir: dir, fault: os.ErrPermission}, boundary: boundary}}
52 if err := h.RetireRecovery(pub, wire, g.Binding, g.IssuedAt, func(HistoryBinding) (string, error) { return "canonical", nil }); err != nil {
53 t.Fatal(err)
54 }
55 t.Fatal("child missed crash boundary")
56 }
57 for _, boundary := range []string{"before", "durable", "visible"} {
58 t.Run(boundary, func(t *testing.T) {
59 _, s, old, _, g, pub, key := retirementFixture(t)
60 wire, err := SignRootEnvelope(RootSigner(key), g)
61 if err != nil {
62 t.Fatal(err)
63 }
64 if err := os.WriteFile(filepath.Join(s.dir, "signed"), wire, 0600); err != nil {
65 t.Fatal(err)
66 }
67 if err := os.WriteFile(filepath.Join(s.dir, "public"), pub, 0600); err != nil {
68 t.Fatal(err)
69 }
70 cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^TestPlan14HistoryRetirementProcessCrash$", "-test.count=1")
71 cmd.Dir = t.TempDir()
72 cmd.Env = append(os.Environ(), "PLAN14_RETIRE_CRASH="+boundary, "PLAN14_RETIRE_DIR="+s.dir)
73 out, err := cmd.CombinedOutput()
74 var exit *exec.ExitError
75 if !errors.As(err, &exit) || exit.ExitCode() != 75 {
76 t.Fatalf("child: %v\n%s", err, out)
77 }
78 fault := &retirementSyncStore{Store: s, fail: boundary == "visible"}
79 h := &RootHistory{Store: fault}
80 view, statusErr := h.MigrationStatus(old.Binding, func(HistoryBinding) (string, error) { return "canonical", nil })
81 if boundary == "visible" {
82 if statusErr == nil {
83 t.Fatal("unsynced retirement completion after crash")
84 }
85 } else {
86 expected := ""
87 if boundary == "durable" {
88 accepted, _ := json.Marshal(g)
89 expected = reconstructionDigest(accepted)
90 }
91 if statusErr != nil || view.Hosts[0].RetirementDigests[old.Attempt.ID] != expected {
92 t.Fatalf("crash completion: %+v %v", view, statusErr)
93 }
94 }
95 refs, err := h.Refusals(old.Binding)
96 if boundary == "visible" {
97 if err == nil {
98 t.Fatal("unsynced retirement granted after crash")
99 }
100 fault.fail = false
101 refs, err = h.Refusals(old.Binding)
102 }
103 view, statusErr = h.MigrationStatus(old.Binding, func(HistoryBinding) (string, error) { return "canonical", nil })
104 expected := ""
105 if boundary != "before" {
106 accepted, _ := json.Marshal(g)
107 expected = reconstructionDigest(accepted)
108 }
109 if statusErr != nil || view.Hosts[0].RetirementDigests[old.Attempt.ID] != expected {
110 t.Fatalf("repaired completion: %+v %v", view, statusErr)
111 }
112 want := 0
113 if boundary == "before" {
114 want = 1
115 }
116 if err != nil || len(refs) != want {
117 t.Fatalf("reopened retirement: %+v %v", refs, err)
118 }
119 raw, err := s.Read(historyRecoveryKey(old))
120 if err != nil {
121 t.Fatal(err)
122 }
123 var retained historyRecoveryRecord
124 if err := json.Unmarshal(raw, &retained); err != nil {
125 t.Fatal(err)
126 }
127 if !retained.Failed || retained.Attempt.SHA256 != old.Attempt.SHA256 {
128 t.Fatal("retirement erased evidence")
129 }
130 })
131 }
132}
133