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 "errors"
5 "fmt"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10)
11
12// systemctlExit is the shape of every answer the two probes get on a real
13// box: systemd prints the state and exits non-zero to say which state it is
14// — 3 for is-active's "inactive", 1 for is-enabled's "disabled". A fake that
15// returned a nil error here would test a systemctl that does not exist.
16func systemctlExit(status int) error {
17 return fmt.Errorf("exit status %d", status)
18}
19
20func TestEnsureSystemUnitsInstallsAndArmsWhatIsMissing(t *testing.T) {
21 etc := t.TempDir()
22 os.MkdirAll(filepath.Join(etc, "systemd", "system"), 0o755)
23 lockPath := filepath.Join(t.TempDir(), "lock")
24 var calls []string
25 systemctl := func(args ...string) (string, error) {
26 calls = append(calls, strings.Join(args, " "))
27 return "", nil
28 }
29 probe := func(args ...string) (string, error) {
30 calls = append(calls, strings.Join(args, " "))
31 switch args[0] {
32 case "is-active":
33 return "inactive\n", systemctlExit(3)
34 case "is-enabled":
35 return "disabled\n", systemctlExit(1)
36 }
37 return "", nil
38 }
39 ensureSystemUnits(etc, lockPath, systemctl, probe, t.Logf)
40 for _, u := range []string{"carlos-system-update.path", "carlos-system-update.service", "carlos-system-recover.service"} {
41 if _, err := os.Stat(filepath.Join(etc, "systemd", "system", u)); err != nil {
42 t.Errorf("%s not installed", u)
43 }
44 }
45 joined := strings.Join(calls, "\n")
46 for _, want := range []string{"daemon-reload", "enable carlos-system-update.path carlos-system-recover.service", "start carlos-system-update.path"} {
47 if !strings.Contains(joined, want) {
48 t.Errorf("calls lack %q:\n%s", want, joined)
49 }
50 }
51 if strings.Contains(joined, "start carlos-system-recover.service") || strings.Contains(joined, "start carlos-system-update.service") {
52 t.Fatal("a service was started")
53 }
54 // All present and armed: no writes, no systemctl beyond the five checks
55 // (is-active x3: the self-update path unit, the control socket and the
56 // db-read socket; is-enabled x2: the recover service and the db-read
57 // socket). The db-read socket is wanted because host.env arms db reads.
58 dbReadsHostEnv(t, etc)
59 calls = nil
60 probe2 := func(args ...string) (string, error) {
61 calls = append(calls, strings.Join(args, " "))
62 switch args[0] {
63 case "is-active":
64 return "active\n", nil
65 case "is-enabled":
66 return "enabled\n", nil
67 }
68 return "", nil
69 }
70 ensureSystemUnits(etc, lockPath, systemctl, probe2, t.Logf)
71 if len(calls) != 5 {
72 t.Fatalf("an armed box got %v", calls)
73 }
74}
75
76// The unit files are not on disk at all, which is what a box the roll script
77// forgot looks like: is-enabled prints NOTHING and exits 1 for a unit file
78// systemd cannot find. That silence is "not enabled", not "no systemctl".
79func TestEnsureSystemUnitsArmsAUnitSystemdHasNeverHeardOf(t *testing.T) {
80 etc := t.TempDir()
81 lockPath := filepath.Join(t.TempDir(), "lock")
82 var calls []string
83 systemctl := func(args ...string) (string, error) {
84 calls = append(calls, strings.Join(args, " "))
85 return "", nil
86 }
87 probe := func(args ...string) (string, error) {
88 switch args[0] {
89 case "is-active":
90 return "inactive\n", systemctlExit(3)
91 case "is-enabled":
92 return "", systemctlExit(1)
93 }
94 return "", nil
95 }
96 ensureSystemUnits(etc, lockPath, systemctl, probe, t.Logf)
97 for _, u := range []string{"carlos-system-update.path", "carlos-system-update.service", "carlos-system-recover.service"} {
98 if _, err := os.Stat(filepath.Join(etc, "systemd", "system", u)); err != nil {
99 t.Errorf("%s not installed", u)
100 }
101 }
102 if !strings.Contains(strings.Join(calls, "\n"), "start carlos-system-update.path") {
103 t.Errorf("the path unit was never started: %v", calls)
104 }
105}
106
107// An enabled recover unit whose path unit is enabled but never started —
108// the flagship's own failure — is armed, not left alone.
109func TestEnsureSystemUnitsStartsAnEnabledButUnstartedPathUnit(t *testing.T) {
110 etc := t.TempDir()
111 unitDir := filepath.Join(etc, "systemd", "system")
112 os.MkdirAll(unitDir, 0o755)
113 for _, u := range systemUnitFiles {
114 os.WriteFile(filepath.Join(unitDir, u), []byte("[Unit]\n"), 0o644)
115 }
116 lockPath := filepath.Join(t.TempDir(), "lock")
117 var calls []string
118 systemctl := func(args ...string) (string, error) {
119 calls = append(calls, strings.Join(args, " "))
120 return "", nil
121 }
122 probe := func(args ...string) (string, error) {
123 switch args[0] {
124 case "is-active":
125 return "inactive\n", systemctlExit(3)
126 case "is-enabled":
127 return "enabled\n", nil
128 }
129 return "", nil
130 }
131 ensureSystemUnits(etc, lockPath, systemctl, probe, t.Logf)
132 if !strings.Contains(strings.Join(calls, "\n"), "start carlos-system-update.path") {
133 t.Errorf("an enabled-but-inactive path unit was left unstarted: %v", calls)
134 }
135}
136
137func TestEnsureSystemUnitsLeavesAConcurrentApplyAlone(t *testing.T) {
138 etc := t.TempDir()
139 os.MkdirAll(filepath.Join(etc, "systemd", "system"), 0o755)
140 lockPath := filepath.Join(t.TempDir(), "lock")
141 // Hold the lock as `carlos system apply` would.
142 held, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0o600)
143 if err != nil {
144 t.Fatal(err)
145 }
146 defer held.Close()
147 if err := flockExclusiveNonblock(held); err != nil {
148 t.Skipf("flock unsupported on this platform: %v", err)
149 }
150 var calls []string
151 record := func(args ...string) (string, error) {
152 calls = append(calls, strings.Join(args, " "))
153 return "", nil
154 }
155 ensureSystemUnits(etc, lockPath, record, record, t.Logf)
156 if len(calls) != 0 {
157 t.Fatalf("a locked-out pass called systemctl: %v", calls)
158 }
159 for _, u := range []string{"carlos-system-update.path", "carlos-system-update.service", "carlos-system-recover.service"} {
160 if _, err := os.Stat(filepath.Join(etc, "systemd", "system", u)); err == nil {
161 t.Errorf("%s was installed by a locked-out pass", u)
162 }
163 }
164}
165
166// No systemctl at all: the probe prints no word, and a box we cannot ask is
167// a box we do not touch.
168func TestEnsureSystemUnitsTrustsNoBrokenSystemctl(t *testing.T) {
169 etc := t.TempDir()
170 os.MkdirAll(filepath.Join(etc, "systemd", "system"), 0o755)
171 lockPath := filepath.Join(t.TempDir(), "lock")
172 systemctl := func(args ...string) (string, error) {
173 return "", errors.New("systemctl: command not found")
174 }
175 ensureSystemUnits(etc, lockPath, systemctl, systemctl, t.Logf)
176 for _, u := range []string{"carlos-system-update.path", "carlos-system-update.service", "carlos-system-recover.service"} {
177 if _, err := os.Stat(filepath.Join(etc, "systemd", "system", u)); err == nil {
178 t.Errorf("%s was installed despite systemctl being unavailable", u)
179 }
180 }
181}
182
183func TestParseIsEnabled(t *testing.T) {
184 yes, no := true, false
185 for _, c := range []struct {
186 out string
187 want *bool
188 }{
189 {"enabled\n", &yes},
190 {"enabled-runtime\n", &yes},
191 {"static\n", &yes},
192 {"disabled\n", &no},
193 {"masked\n", &no},
194 {"", nil},
195 {"something-systemd-invents\n", nil},
196 } {
197 got := parseIsEnabled(c.out)
198 switch {
199 case got == nil && c.want != nil:
200 t.Errorf("parseIsEnabled(%q) = nil, want %v", c.out, *c.want)
201 case got != nil && c.want == nil:
202 t.Errorf("parseIsEnabled(%q) = %v, want nil", c.out, *got)
203 case got != nil && *got != *c.want:
204 t.Errorf("parseIsEnabled(%q) = %v, want %v", c.out, *got, *c.want)
205 }
206 }
207}
208