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 linux && rootrunner
2
3package host
4
5import (
6 "encoding/json"
7 "errors"
8 "os"
9 "os/exec"
10 "strings"
11 "testing"
12
13 "github.com/carlosframework/platform/internal/controlsock"
14 "github.com/carlosframework/platform/internal/hostguard"
15 "github.com/carlosframework/platform/internal/rootstate"
16)
17
18const lifecycleCrashPath = "/var/lib/carlos-claims/lifecycle-crash"
19
20func lifecycleCrash(t *testing.T, point string) {
21 t.Helper()
22 if os.Getenv("PLAN14_LIFECYCLE_CRASH") != point {
23 return
24 }
25 if _, err := os.Stat(lifecycleCrashPath); err == nil {
26 return
27 } else if !errors.Is(err, os.ErrNotExist) {
28 t.Fatal(err)
29 }
30 state, err := rootstate.Open("/var/lib/carlos-claims", false, false)
31 lifecycleCheck(t, "crash checkpoint store", err)
32 lifecycleCheck(t, "durable crash checkpoint", state.Write("lifecycle-crash", []byte(point)))
33 os.Exit(86)
34}
35
36type lifecycleCrashStore struct {
37 rootstate.Store
38 t *testing.T
39}
40
41func (s lifecycleCrashStore) Write(name string, body []byte) error {
42 if err := s.Store.Write(name, body); err != nil {
43 return err
44 }
45 if name != lifecycleHost+".json" {
46 return nil
47 }
48 var r struct {
49 Installation hostguard.RootInstallation
50 Retired []hostguard.Captured
51 Barrier *hostguard.FenceTarget
52 State struct {
53 ActiveStamp uint64 `json:"active_stamp"`
54 }
55 }
56 if err := json.Unmarshal(body, &r); err != nil {
57 return err
58 }
59 if r.Barrier != nil {
60 lifecycleCrash(s.t, "handover")
61 }
62 if r.State.ActiveStamp == 0 && r.Installation.Stamp != 0 && len(r.Retired) != 0 {
63 lifecycleCrash(s.t, "retirement")
64 }
65 return nil
66}
67
68func lifecycleControlFaults(t *testing.T) {
69 original := openClaimRootAuthority
70 openClaimRootAuthority = func() (rootstate.Store, error) {
71 s, err := original()
72 if err != nil {
73 return nil, err
74 }
75 return lifecycleCrashStore{s, t}, nil
76 }
77 t.Cleanup(func() { openClaimRootAuthority = original })
78}
79
80func lifecycleStartControl(t *testing.T, endpoint string) func() {
81 t.Helper()
82 raw, err := unitsFS.ReadFile("units/carlos-instance-control.service")
83 lifecycleCheck(t, "controller template", err)
84 var lines []string
85 for _, line := range strings.Split(string(raw), "\n") {
86 key, _, _ := strings.Cut(line, "=")
87 if key == "ExecStart" || key == "Requires" || key == "After" {
88 continue
89 }
90 lines = append(lines, line)
91 }
92 lines = append(lines, "[Service]", "ExecStart=/opt/carlos/carlos -test.run=^TestPlan14LifecycleControlProcessPayload$ -test.v -test.timeout=12m", "Environment=PLAN14_LIFECYCLE_CONTROL=1", "Environment=PLAN14_LIFECYCLE_CRASH="+os.Getenv("PLAN14_LIFECYCLE_CRASH"), "Environment=CARLOS_S3_ENDPOINT="+endpoint, "ReadWritePaths=/data /opt/carlos /var/lib/carlos-claims /var/lib/carlos-release /sys/fs/cgroup "+lifecycleHome, "RestrictAddressFamilies=", "RestrictAddressFamilies=AF_UNIX AF_INET", "MemoryMax=512M", "StandardOutput=append:"+lifecycleHome+"/controller.log", "StandardError=append:"+lifecycleHome+"/controller.log")
93 path := "/run/systemd/system/carlos-instance-control.service"
94 f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
95 lifecycleCheck(t, "exclusive controller unit", err)
96 _, err = f.WriteString(strings.Join(lines, "\n"))
97 f.Close()
98 lifecycleCheck(t, "controller unit", err)
99 out, err := exec.Command("systemctl", "daemon-reload").CombinedOutput()
100 lifecycleCheck(t, string(out), err)
101 start := func() {
102 exec.Command("systemctl", "stop", "carlos-instance-control.service").Run()
103 os.Remove("/run/carlos-control/lifecycle-ready")
104 os.Remove(controlsock.Path)
105 out, err := exec.Command("systemctl", "start", "carlos-instance-control.service").CombinedOutput()
106 lifecycleCheck(t, string(out), err)
107 }
108 t.Cleanup(func() {
109 exec.Command("systemctl", "stop", "carlos-instance-control.service").Run()
110 os.Remove(path)
111 exec.Command("systemctl", "daemon-reload").Run()
112 body, _ := os.ReadFile(lifecycleHome + "/controller.log")
113 t.Logf("controller log: %s", body)
114 })
115 start()
116 return start
117}
118