carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "os"
8 "os/exec"
9 "path/filepath"
10 "testing"
11
12 "github.com/carlosframework/platform/internal/hostbox"
13 "github.com/carlosframework/platform/internal/hostguard"
14 "github.com/carlosframework/platform/internal/release"
15)
16
17func TestPlan14PrelaunchCancellationProcessDeath(t *testing.T) {
18 if dir := os.Getenv("PLAN14_R11_RESERVE_CRASH"); dir != "" {
19 f, disk, _ := generationLaunchFixture(t)
20 if err := os.Mkdir(filepath.Join(dir, "pins"), 0700); err != nil {
21 t.Fatal(err)
22 }
23 if err := os.Mkdir(filepath.Join(dir, "fences"), 0700); err != nil {
24 t.Fatal(err)
25 }
26 views, err := f.root.Guard.Root.GenerationInstallations(t.Context())
27 if err != nil || len(views) != 1 {
28 t.Fatal(err)
29 }
30 in := views[0].Installation
31 raw, _ := json.Marshal(map[string]any{"Schema": 1, "Installation": in, "State": map[string]any{"v": 1, "host": in.Host, "incarnation": in.Incarnation, "active_stamp": in.Stamp}})
32 if err = os.WriteFile(filepath.Join(dir, "fences", in.Host+".json"), raw, 0600); err != nil {
33 t.Fatal(err)
34 }
35 launch, err := f.journal.Read(t.Context(), f.target)
36 if err != nil {
37 t.Fatal(err)
38 }
39 journal, err := hostbox.OpenLaunchJournal(filepath.Join(dir, "launch.db"))
40 if err != nil {
41 t.Fatal(err)
42 }
43 if err = journal.Prelaunch(t.Context(), launch); err != nil {
44 t.Fatal(err)
45 }
46 if err = journal.Close(); err != nil {
47 t.Fatal(err)
48 }
49 disk.dir = filepath.Join(dir, "pins")
50 disk.afterWrite = func(_ string, raw []byte) {
51 var r release.GenerationReservation
52 if json.Unmarshal(raw, &r) == nil && r.Revision == 1 {
53 os.Exit(73)
54 }
55 }
56 _, err = f.root.generations.reserve(t.Context(), f.root, f.peer, f.req)
57 t.Fatal("reservation crash not reached", err)
58 }
59 dir := t.TempDir()
60 cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^TestPlan14PrelaunchCancellationProcessDeath$", "-test.count=1")
61 cmd.Dir = dir
62 cmd.Env = append(os.Environ(), "PLAN14_R11_RESERVE_CRASH="+dir, "TMPDIR="+dir)
63 out, err := cmd.CombinedOutput()
64 var status *exec.ExitError
65 if !errors.As(err, &status) || status.ExitCode() != 73 {
66 t.Fatal("crash child", err, string(out))
67 }
68 disk := &generationDisk{rollbackDisk: &rollbackDisk{dir: filepath.Join(dir, "pins")}}
69 r := readGenerationLaunch(t, disk)
70 journal, err := hostbox.OpenLaunchJournal(filepath.Join(dir, "launch.db"))
71 if err != nil {
72 t.Fatal(err)
73 }
74 defer journal.Close()
75 launches, err := journal.GenerationLaunches(t.Context())
76 if err != nil || len(launches) != 1 || len(launches[0].Reservations) != 0 {
77 t.Fatal("pre-intention boundary", err, launches)
78 }
79 verifier := generationRetirement{guard: hostguard.Guard{DataDir: dir, LockDir: t.TempDir(), Root: hostguard.NewRootAuthority(&rollbackDisk{dir: filepath.Join(dir, "fences")})}, journal: journal, kernel: func(context.Context, release.GenerationReservation) (string, error) { return r.Binding.Digest, nil }}
80 ledger := &release.RootGenerations{Store: disk}
81 if err = verifier.retire(t.Context(), ledger, r); err != nil {
82 t.Fatal(err)
83 }
84 if got := readGenerationLaunch(t, disk); got != r {
85 t.Fatal("death substituted for writer closure")
86 }
87 if err = journal.CloseWriters(t.Context(), launches[0].Target); err != nil {
88 t.Fatal(err)
89 }
90 if err = verifier.retire(t.Context(), ledger, r); err != nil {
91 t.Fatal("cancellation recovery", err)
92 }
93 if got := readGenerationLaunch(t, disk); !got.Closed || got.Retirement == "" {
94 t.Fatal("recovered reservation pinned")
95 }
96}
97