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 "context"
5 "encoding/json"
6 "os"
7 "os/exec"
8 "path/filepath"
9 "strings"
10 "testing"
11)
12
13type generationRetirementCrashStore struct {
14 generationListingStore
15 phase string
16}
17
18func (s generationRetirementCrashStore) Write(name string, body []byte) error {
19 if err := s.generationListingStore.Write(name, body); err != nil {
20 return err
21 }
22 var r GenerationReservation
23 if json.Unmarshal(body, &r) != nil {
24 return os.ErrPermission
25 }
26 if s.phase == "closed" && r.Closed && r.Retirement == "" {
27 os.Exit(74)
28 }
29 if s.phase == "retired" && r.Retirement != "" {
30 os.Exit(74)
31 }
32 return nil
33}
34
35func TestPlan14GenerationRetirementProcessCrash(t *testing.T) {
36 if dir := os.Getenv("PLAN14_GENERATION_RETIRE_CHILD"); dir != "" {
37 raw, err := os.ReadFile(filepath.Join(dir, "expected"))
38 if err != nil {
39 t.Fatal(err)
40 }
41 var expected GenerationReservation
42 if json.Unmarshal(raw, &expected) != nil {
43 t.Fatal("bad expected record")
44 }
45 store := generationRetirementCrashStore{generationListingStore: generationListingStore{&healthyRetryStore{dir: filepath.Join(dir, "state")}}, phase: os.Getenv("PLAN14_GENERATION_RETIRE_PHASE")}
46 g := &RootGenerations{Store: store}
47 err = g.Retire(t.Context(), expected, func(context.Context, GenerationReservation) error { return nil }, func(context.Context, GenerationReservation) (string, error) {
48 if store.phase == "verify" {
49 os.Exit(74)
50 }
51 return strings.Repeat("d", 64), nil
52 })
53 t.Fatalf("crash hook not reached: %v", err)
54 }
55 for _, phase := range []string{"closed", "verify", "retired"} {
56 t.Run(phase, func(t *testing.T) {
57 g, store, b := generationFixture(t)
58 r, err := g.Reserve(b)
59 if err != nil {
60 t.Fatal(err)
61 }
62 dir := t.TempDir()
63 if err := os.Rename(store.dir, filepath.Join(dir, "state")); err != nil {
64 t.Fatal(err)
65 }
66 store.dir = filepath.Join(dir, "state")
67 raw, _ := json.Marshal(r)
68 if err := os.WriteFile(filepath.Join(dir, "expected"), raw, 0600); err != nil {
69 t.Fatal(err)
70 }
71 cmd := exec.Command(os.Args[0], "-test.run=^TestPlan14GenerationRetirementProcessCrash$", "-test.count=1")
72 cmd.Dir = dir
73 cmd.Env = append(os.Environ(), "PLAN14_GENERATION_RETIRE_CHILD="+dir, "PLAN14_GENERATION_RETIRE_PHASE="+phase)
74 if output, err := cmd.CombinedOutput(); err == nil {
75 t.Fatalf("no crash: %s", output)
76 } else if code, ok := err.(*exec.ExitError); !ok || code.ExitCode() != 74 {
77 t.Fatalf("wrong crash: %v %s", err, output)
78 }
79 got, err := g.Read(b)
80 if err != nil {
81 t.Fatal(err)
82 }
83 if !got.Closed || (got.Retirement != "") != (phase == "retired") {
84 t.Fatalf("crash lost restriction: %+v", got)
85 }
86 if _, err := g.Reserve(b); err == nil {
87 t.Fatal("crashed retirement revived launch")
88 }
89 if phase != "retired" {
90 if err := g.Retire(t.Context(), r, func(context.Context, GenerationReservation) error { t.Fatal("closed authority reopened"); return nil }, func(context.Context, GenerationReservation) (string, error) { return "", os.ErrPermission }); err == nil {
91 t.Fatal("recovery released pin without evidence")
92 }
93 }
94 if err := g.Retire(t.Context(), r, func(context.Context, GenerationReservation) error { return nil }, func(context.Context, GenerationReservation) (string, error) { return strings.Repeat("d", 64), nil }); err != nil {
95 t.Fatal(err)
96 }
97 })
98 }
99}
100