carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "bytes"
5 "context"
6 "fmt"
7 "net/http"
8 "net/http/httptest"
9 "strings"
10 "testing"
11)
12
13// TestPrintStatusInstancesLabelsPlatformAndStatesItsLimits is the CLI half of
14// what the console door promises. Two things have to survive rendering.
15//
16// A row that matched no route is the box's own overhead — the console, the
17// agent, a system unit — and it prints under its unit name and the word
18// (platform) rather than as two blank cells a reader would take for missing
19// data.
20//
21// And the table is followed, every time, by the two ways it misleads: a
22// hibernating instance has no process and so no reading, and the readings are
23// fleet-wide while the account each is attributed to comes from the console's
24// own box's registry. Neither caveat is conditional, because both are ways a
25// SHORT list reads as a complete one, and short is exactly when nobody thinks
26// to ask.
27func TestPrintStatusInstancesLabelsPlatformAndStatesItsLimits(t *testing.T) {
28 rows := []statusInstanceRow{
29 {Box: "box-a", Host: "app.example.com", Account: "cdef", App: "widgets",
30 CPU: f64(0.25), Mem: i64(1 << 20)},
31 {Box: "box-a", Unit: "console.service", Account: "bab", Platform: true},
32 }
33 var buf bytes.Buffer
34 printStatusInstances(&buf, rows, "")
35 out := buf.String()
36
37 for _, want := range []string{
38 "app.example.com", "cdef", "widgets", "0.250", "1MB",
39 "console.service", "(platform)",
40 "awake instances only",
41 "console's own box's route registry",
42 } {
43 if !strings.Contains(out, want) {
44 t.Errorf("missing %q in:\n%s", want, out)
45 }
46 }
47}
48
49// TestPrintStatusLayoutRendersSortedAndMarksStale pins the CLI table for
50// `carlos status --layout` (R21): the approved heading, byte for byte; rows
51// sorted by box then host regardless of the order the door answered in; and
52// a stale box's name suffixed with the approved stale marker.
53func TestPrintStatusLayoutRendersSortedAndMarksStale(t *testing.T) {
54 boxes := []statusLayoutBox{
55 {Box: "zeta", Mode: "flat", Stale: false, Rows: []statusLayoutRow{
56 {Host: "z.example.com", Layout: "flat"},
57 }},
58 {Box: "alpha", Mode: "nested", Stale: true, Rows: []statusLayoutRow{
59 {Host: "b.example.com", Layout: "foreign", Reason: "db and socket are neither default"},
60 {Host: "a.example.com", Layout: "nested"},
61 }},
62 }
63 var buf bytes.Buffer
64 printStatusLayout(&buf, boxes)
65 out := buf.String()
66
67 if !strings.Contains(out, "BOX HOST LAYOUT REASON") {
68 t.Fatalf("missing the approved heading:\n%s", out)
69 }
70 if !strings.Contains(out, "alpha (stale)") {
71 t.Errorf("stale box not marked:\n%s", out)
72 }
73 if strings.Contains(out, "zeta (stale)") {
74 t.Errorf("fresh box marked stale:\n%s", out)
75 }
76
77 // Sorted by box then host: alpha's two rows (host-sorted) come before
78 // zeta's, regardless of the door's own order.
79 aIdx := strings.Index(out, "a.example.com")
80 bIdx := strings.Index(out, "b.example.com")
81 zIdx := strings.Index(out, "z.example.com")
82 if aIdx < 0 || bIdx < 0 || zIdx < 0 || !(aIdx < bIdx && bIdx < zIdx) {
83 t.Fatalf("rows not sorted box-then-host:\n%s", out)
84 }
85 if !strings.Contains(out, "db and socket are neither default") {
86 t.Errorf("missing the foreign row's reason:\n%s", out)
87 }
88 if strings.Contains(out, "WARNING") {
89 t.Errorf("no box is mixed, yet a warning printed:\n%s", out)
90 }
91}
92
93// A box the door marks mixed (nested rows beside flat ones) gets a warning
94// after the table naming it: on that box the nested rows are not isolated
95// from the flat tenant that still shares the agent's uid.
96func TestPrintStatusLayoutWarnsOnAMixedBox(t *testing.T) {
97 boxes := []statusLayoutBox{
98 {Box: "kass-1", Mode: "nested", Mixed: true, Rows: []statusLayoutRow{
99 {Host: "a.example.com", Layout: "nested"},
100 {Host: "b.example.com", Layout: "flat", Reason: "awake (stopped for the move in 40m0s unless it sleeps first)"},
101 }},
102 {Box: "kass-2", Mode: "nested", Rows: []statusLayoutRow{{Host: "c.example.com", Layout: "nested"}}},
103 }
104 var buf bytes.Buffer
105 printStatusLayout(&buf, boxes)
106 out := buf.String()
107 if !strings.Contains(out, "WARNING: kass-1 runs nested instances beside flat, foreign or external ones") {
108 t.Fatalf("mixed box not warned about:\n%s", out)
109 }
110 if strings.Contains(out, "WARNING: kass-2") {
111 t.Errorf("an all-nested box warned about:\n%s", out)
112 }
113}
114
115// TestStatusLayoutListTranslatesNotFound pins the approved 404 message
116// (cli.status.layout.notfound), byte for byte — the same "not found" this
117// console gives a non-operator or predates the door with, which is why the
118// message covers both readings.
119func TestStatusLayoutListTranslatesNotFound(t *testing.T) {
120 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
121 http.NotFound(w, r)
122 }))
123 defer ts.Close()
124
125 _, err := newTestSession(ts.URL).statusLayoutList(context.Background())
126 if err == nil {
127 t.Fatal("want an error on 404")
128 }
129 want := "not found. this needs the fleet-operator bit on your account, or this console predates `carlos status --layout` (deploy a console built from current main)"
130 if err.Error() != want {
131 t.Fatalf("error = %q, want %q", err.Error(), want)
132 }
133}
134
135// TestStatusLayoutListDecodesBoxes pins the fetch half: the door's
136// {"boxes":[...]} shape decodes into statusLayoutBox/statusLayoutRow.
137func TestStatusLayoutListDecodesBoxes(t *testing.T) {
138 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
139 if r.URL.Path != "/api/cli/status/layout" {
140 t.Errorf("path = %s", r.URL.Path)
141 }
142 w.Header().Set("Content-Type", "application/json")
143 fmt.Fprint(w, `{"boxes":[{"box":"ie","mode":"nested","stale":false,"rows":[{"host":"h.example.com","layout":"nested"}]}]}`)
144 }))
145 defer ts.Close()
146
147 boxes, err := newTestSession(ts.URL).statusLayoutList(context.Background())
148 if err != nil {
149 t.Fatal(err)
150 }
151 if len(boxes) != 1 || boxes[0].Box != "ie" || boxes[0].Mode != "nested" || boxes[0].Stale ||
152 len(boxes[0].Rows) != 1 || boxes[0].Rows[0].Host != "h.example.com" || boxes[0].Rows[0].Layout != "nested" {
153 t.Fatalf("boxes = %+v", boxes)
154 }
155}
156
157// Boxes that disagree about their mode get one warning naming both
158// sides; a stale box is left out of it, and agreement gets none.
159func TestPrintStatusLayoutWarnsWhenBoxesDisagreeAboutTheMode(t *testing.T) {
160 boxes := []statusLayoutBox{
161 {Box: "kass-1", Mode: "nested", Rows: []statusLayoutRow{{Host: "a.example.com", Layout: "nested"}}},
162 {Box: "kass-2", Mode: "none", Rows: []statusLayoutRow{{Host: "b.example.com", Layout: "flat"}}},
163 {Box: "kass-3", Mode: "flat", Rows: []statusLayoutRow{{Host: "c.example.com", Layout: "flat"}}},
164 {Box: "kass-4", Mode: "none", Stale: true, Rows: []statusLayoutRow{{Host: "d.example.com", Layout: "flat"}}},
165 }
166 var buf bytes.Buffer
167 printStatusLayout(&buf, boxes)
168 out := buf.String()
169 want := "WARNING: kass-1 is nested while kass-2 and kass-3 are not: an instance placed or moved onto a box that is not nested starts flat there."
170 if !strings.Contains(out, want) {
171 t.Fatalf("disagreeing boxes not warned about:\n%s", out)
172 }
173 if strings.Contains(out, "kass-4") && strings.Contains(out[strings.Index(out, "WARNING"):], "kass-4") {
174 t.Errorf("a stale box was counted:\n%s", out)
175 }
176 buf.Reset()
177 printStatusLayout(&buf, boxes[:1])
178 if strings.Contains(buf.String(), "WARNING") {
179 t.Errorf("an agreeing fleet warned:\n%s", buf.String())
180 }
181}
182
183// A box nested by default is marked on its name; a box the default has not
184// reached says why under the table; a stale box's reason is not repeated.
185func TestPrintStatusLayoutSaysNestedByDefaultAndWhyNot(t *testing.T) {
186 boxes := []statusLayoutBox{
187 {Box: "kass-1", Mode: "nested", Default: true, Rows: []statusLayoutRow{{Host: "a.example.com", Layout: "nested"}}},
188 {Box: "kass-2", Mode: "none", DefaultBlocked: "isolation not ready: probe: pid1 fail", Rows: []statusLayoutRow{{Host: "b.example.com", Layout: "flat"}}},
189 {Box: "kass-3", Mode: "none", Stale: true, DefaultBlocked: "no activator is armed on this box, so nothing could wake nested", Rows: []statusLayoutRow{{Host: "c.example.com", Layout: "flat"}}},
190 }
191 var buf bytes.Buffer
192 printStatusLayout(&buf, boxes)
193 out := buf.String()
194 if !strings.Contains(out, "kass-1 (nested by default) a.example.com nested") {
195 t.Errorf("nested by default not marked on the box:\n%s", out)
196 }
197 if !strings.Contains(out, "kass-2 is not nested by default: isolation not ready: probe: pid1 fail") {
198 t.Errorf("blocked reason missing:\n%s", out)
199 }
200 if strings.Contains(out, "kass-3 is not nested by default") {
201 t.Errorf("a stale box's reason was repeated:\n%s", out)
202 }
203}
204