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 unix
2
3package hostguard
4
5import (
6 "context"
7 "encoding/json"
8 "errors"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "testing"
13
14 "github.com/carlosframework/platform/internal/hostclaim"
15)
16
17type artifactsCrashStore struct {
18 *rootTestStore
19 phase string
20}
21
22func (s *artifactsCrashStore) Write(name string, raw []byte) error {
23 if err := s.rootTestStore.Write(name, raw); err != nil {
24 return err
25 }
26 var r rootRecord
27 if json.Unmarshal(raw, &r) != nil {
28 return ErrMalformedRecord
29 }
30 if s.phase == "intent" && r.PendingInstallation != nil {
31 os.Exit(77)
32 }
33 if s.phase == "sync" && r.PendingInstallation == nil {
34 return os.ErrPermission
35 }
36 return nil
37}
38
39func TestPlan14InventoryArtifactsCrash(t *testing.T) {
40 if dir := os.Getenv("PLAN14_E37_ARTIFACT_CHILD"); dir != "" {
41 f, target, _ := fenceFixture(t)
42 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "app"
43 store := &artifactsCrashStore{rootTestStore: &rootTestStore{dir: dir}, phase: os.Getenv("PLAN14_E37_PHASE")}
44 f.g.Root = NewRootAuthority(store)
45 r, err := f.g.Root.read(target.Host)
46 if err != nil {
47 t.Fatal(err)
48 }
49 in := r.installation
50 in.InventoryRevision++
51 err = f.g.CommitRootInventoryArtifacts(t.Context(), in, []byte("request"), allow, nil, func(RootInstallation) error {
52 if err := os.WriteFile(filepath.Join(dir, "effect.lock"), []byte("committed"), 0600); err != nil {
53 return err
54 }
55 if store.phase == "commit" {
56 os.Exit(77)
57 }
58 return nil
59 }, func(prior RootInstallation) (InstallationArtifacts, error) {
60 a := prior.Artifacts
61 a.Timers = "backup"
62 return a, nil
63 })
64 if store.phase == "sync" && errors.Is(err, os.ErrPermission) {
65 os.Exit(77)
66 }
67 t.Fatal("crash boundary not reached", err)
68 }
69 for _, phase := range []string{"intent", "commit", "sync"} {
70 t.Run(phase, func(t *testing.T) {
71 f, target, captured := fenceFixture(t)
72 dir := t.TempDir()
73 store := &rootTestStore{dir: dir}
74 f.g.Root = NewRootAuthority(store)
75 f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "app"
76 old := RootInstallation{Artifacts: InstallationArtifacts{Schema: 1, StateDirectory: true}, Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "app", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, InventoryRevision: 1}
77 if err := f.g.EstablishRoot(t.Context(), old, allow); err != nil {
78 t.Fatal(err)
79 }
80 binary, err := os.Executable()
81 if err != nil {
82 t.Fatal(err)
83 }
84 cmd := exec.CommandContext(t.Context(), binary, "-test.run=^TestPlan14InventoryArtifactsCrash$", "-test.count=1")
85 cmd.Dir = dir
86 cmd.Env = append(os.Environ(), "PLAN14_E37_ARTIFACT_CHILD="+dir, "PLAN14_E37_PHASE="+phase)
87 output, err := cmd.CombinedOutput()
88 var exit *exec.ExitError
89 if !errors.As(err, &exit) || exit.ExitCode() != 77 {
90 t.Fatalf("child: %v %s", err, output)
91 }
92 f.g.Root = NewRootAuthority(store)
93 r, err := f.g.Root.read(target.Host)
94 if err != nil {
95 t.Fatal(err)
96 }
97 want := InstallationArtifacts{Schema: 1, StateDirectory: true, Timers: "backup"}
98 if phase != "sync" {
99 if r.pendingInstallation == nil || r.pendingInstallation.Artifacts != want {
100 t.Fatal("intent lost requirements", r, err)
101 }
102 if _, err := f.g.Root.GenerationInstallations(t.Context()); err == nil {
103 t.Fatal("pending inventory certified")
104 }
105 } else {
106 if r.installation.Artifacts != want {
107 t.Fatal("visible commit lost requirements")
108 }
109 failing := NewRootAuthority(&artifactsCrashStore{rootTestStore: store, phase: "sync"})
110 for i := 0; i < 2; i++ {
111 if _, err := failing.GenerationInstallations(t.Context()); !errors.Is(err, os.ErrPermission) {
112 t.Fatal("unsynced requirements certified", err)
113 }
114 }
115 }
116 next := old
117 next.InventoryRevision++
118 if err := f.g.CommitRootInventoryArtifacts(t.Context(), next, []byte("request"), allow, nil, func(RootInstallation) error { return nil }, func(RootInstallation) (InstallationArtifacts, error) { return want, nil }); err != nil {
119 t.Fatal("resume", err)
120 }
121 views, err := f.g.Root.GenerationInstallations(t.Context())
122 if err != nil || len(views) != 1 || views[0].Installation.Artifacts != want {
123 t.Fatal("reopen lost requirements", views, err)
124 }
125 f.obs.Claim.Holder.Stamp++
126 f.obs.Claim.Holder.Session++
127 next.Stamp++
128 next.InventoryRevision = 1
129 next.Artifacts = InstallationArtifacts{}
130 if err := f.g.RebindRootInventory(t.Context(), captured, 2, next, []byte("rebind"), func(*hostclaim.Claim) error { return nil }, func() error { return nil }); err != nil {
131 t.Fatal(err)
132 }
133 views, err = f.g.Root.GenerationInstallations(context.Background())
134 if err != nil || len(views) != 1 || views[0].Installation.Artifacts != want || len(views[0].Retired) != 1 {
135 t.Fatal("rebind erased requirements", views, err)
136 }
137 })
138 }
139}
140