1 package cmd
2
3 import (
4 "bytes"
5 "errors"
6 "path/filepath"
7 "reflect"
8 "testing"
9
10 "k8s.io/utils/exec"
11 fakeexec "k8s.io/utils/exec/testing"
12 )
13
14 func TestFindExtensions(t *testing.T) {
15 fakeGlob := func(path string) ([]string, error) {
16 dir, _ := filepath.Split(path)
17 return []string{
18 filepath.Join(dir, "linkerd-bar"),
19 filepath.Join(dir, "linkerd-baz"),
20 filepath.Join(dir, "linkerd-foo"),
21 }, nil
22 }
23
24 fcmd := fakeexec.FakeCmd{
25 RunScript: []fakeexec.FakeAction{
26 func() ([]byte, []byte, error) {
27 return []byte(`{"name":"linkerd-baz","checks":"always"}`), nil, nil
28 },
29 func() ([]byte, []byte, error) {
30 return []byte(`{"name":"linkerd-foo-no-match","checks":"always"}`), nil, nil
31 },
32 func() ([]byte, []byte, error) { return []byte(`{"name":"linkerd-bar","checks":"always"}`), nil, nil },
33 },
34 }
35
36 lookPathSuccess := false
37
38 fexec := &fakeexec.FakeExec{
39 CommandScript: []fakeexec.FakeCommandAction{
40 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
41 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
42 func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
43 },
44 LookPathFunc: func(cmd string) (string, error) {
45 if lookPathSuccess {
46 return cmd, nil
47 }
48 lookPathSuccess = true
49 return "", errors.New("fake-error")
50 },
51 }
52
53 extensions, missing := findExtensions("/path1:/this/is/a/fake/path2", fakeGlob, fexec, []string{"foo", "missing-cli"})
54
55 expExtensions := []extension{
56 {path: "/this/is/a/fake/path2/linkerd-bar"},
57 {path: "/path1/linkerd-baz"},
58 {path: "/path1/linkerd-foo"},
59 }
60 expMissing := []string{"linkerd-missing-cli"}
61
62 if !reflect.DeepEqual(expExtensions, extensions) {
63 t.Errorf("Expected [%+v] Got [%+v]", expExtensions, extensions)
64 }
65 if !reflect.DeepEqual(expMissing, missing) {
66 t.Errorf("Expected [%+v] Got [%+v]", expMissing, missing)
67 }
68 }
69
70 func TestRunExtensionsChecks(t *testing.T) {
71 successJSON := `
72 {
73 "success": true,
74 "categories": [
75 {
76 "categoryName": "success check name",
77 "checks": [
78 {
79 "description": "success check desc",
80 "result": "success"
81 }
82 ]
83 }
84 ]
85 }
86 `
87
88 warningJSON := `
89 {
90 "success": true,
91 "categories": [
92 {
93 "categoryName": "warning check name",
94 "checks": [
95 {
96 "description": "warning check desc",
97 "hint": "https://example.com/warning",
98 "error": "this is the warning message",
99 "result": "warning"
100 }
101 ]
102 }
103 ]
104 }
105 `
106
107 errorJSON := `
108 {
109 "success": false,
110 "categories": [
111 {
112 "categoryName": "error check name",
113 "checks": [
114 {
115 "description": "error check desc",
116 "hint": "https://example.com/error",
117 "error": "this is the error message",
118 "result": "error"
119 }
120 ]
121 }
122 ]
123 }
124 `
125
126 multiJSON := `
127 {
128 "success": true,
129 "categories": [
130 {
131 "categoryName": "multi check name",
132 "checks": [
133 {
134 "description": "multi check desc success",
135 "result": "success"
136 },
137 {
138 "description": "multi check desc warning",
139 "hint": "https://example.com/multi",
140 "error": "this is the multi warning message",
141 "result": "warning"
142 }
143 ]
144 }
145 ]
146 }
147 `
148
149 testCases := []struct {
150 name string
151 extensions []extension
152 missing []string
153 fakeActions []fakeexec.FakeAction
154 expSuccess bool
155 expWarning bool
156 expOutput string
157 }{
158 {
159 "no checks",
160 nil,
161 nil,
162 []fakeexec.FakeAction{
163 func() ([]byte, []byte, error) {
164 return nil, nil, nil
165 },
166 },
167 true,
168 false,
169 "",
170 },
171 {
172 "invalid JSON",
173 []extension{{path: "/path/linkerd-invalid"}},
174 nil,
175 []fakeexec.FakeAction{
176 func() ([]byte, []byte, error) {
177 return []byte("bad json"), nil, nil
178 },
179 },
180 false,
181 false,
182 `linkerd-invalid
183 ---------------
184 × Running: /path/linkerd-invalid check
185 invalid extension check output from "/path/linkerd-invalid check" (JSON object expected):
186 bad json
187 [invalid character 'b' looking for beginning of value]
188 see https://linkerd.io/2/checks/#extensions for hints
189
190 `,
191 },
192 {
193 "one successful check",
194 []extension{{path: "/path/linkerd-success"}},
195 nil,
196 []fakeexec.FakeAction{
197 func() ([]byte, []byte, error) {
198 return []byte(successJSON), nil, nil
199 },
200 },
201 true,
202 false,
203 `success check name
204 ------------------
205 √ success check desc
206
207 `,
208 },
209 {
210 "one warning check",
211 []extension{{path: "/path/linkerd-warning"}},
212 nil,
213 []fakeexec.FakeAction{
214 func() ([]byte, []byte, error) {
215 return []byte(warningJSON), nil, nil
216 },
217 },
218 true,
219 true,
220 `warning check name
221 ------------------
222 ‼ warning check desc
223 this is the warning message
224 see https://example.com/warning for hints
225
226 `,
227 },
228 {
229 "one error check",
230 []extension{{path: "/path/linkerd-error"}},
231 nil,
232 []fakeexec.FakeAction{
233 func() ([]byte, []byte, error) {
234 return []byte(errorJSON), nil, nil
235 },
236 },
237 false,
238 false,
239 `error check name
240 ----------------
241 × error check desc
242 this is the error message
243 see https://example.com/error for hints
244
245 `,
246 },
247 {
248 "one missing check",
249 nil,
250 []string{"missing"},
251 nil,
252 true,
253 true,
254 `missing
255 -------
256 ‼ Linkerd extension command missing exists
257 exec: "missing": executable file not found in $PATH
258 see https://linkerd.io/2/checks/#extensions for hints
259
260 `,
261 },
262 {
263 "multiple checks with success, warnings, errors, and missing",
264 []extension{{path: "/path/linkerd-success"}, {path: "/path/linkerd-warning"}, {path: "/path/linkerd-error"}, {path: "/path/linkerd-multi"}},
265 []string{"missing1", "missing2"},
266 []fakeexec.FakeAction{
267 func() ([]byte, []byte, error) {
268 return []byte(successJSON), nil, nil
269 },
270 func() ([]byte, []byte, error) {
271 return []byte(warningJSON), nil, nil
272 },
273 func() ([]byte, []byte, error) {
274 return []byte(errorJSON), nil, nil
275 },
276 func() ([]byte, []byte, error) {
277 return []byte(multiJSON), nil, nil
278 },
279 },
280 false,
281 true,
282 `success check name
283 ------------------
284 √ success check desc
285
286 warning check name
287 ------------------
288 ‼ warning check desc
289 this is the warning message
290 see https://example.com/warning for hints
291
292 error check name
293 ----------------
294 × error check desc
295 this is the error message
296 see https://example.com/error for hints
297
298 multi check name
299 ----------------
300 √ multi check desc success
301 ‼ multi check desc warning
302 this is the multi warning message
303 see https://example.com/multi for hints
304
305 missing1
306 --------
307 ‼ Linkerd extension command missing1 exists
308 exec: "missing1": executable file not found in $PATH
309 see https://linkerd.io/2/checks/#extensions for hints
310
311 missing2
312 --------
313 ‼ Linkerd extension command missing2 exists
314 exec: "missing2": executable file not found in $PATH
315 see https://linkerd.io/2/checks/#extensions for hints
316
317 `,
318 },
319 }
320
321 for _, tc := range testCases {
322 tc := tc
323 t.Run(tc.name, func(t *testing.T) {
324 fcmd := fakeexec.FakeCmd{
325 RunScript: tc.fakeActions,
326 }
327
328 fakeCommandActions := make([]fakeexec.FakeCommandAction, len(tc.fakeActions))
329 for i := 0; i < len(tc.fakeActions); i++ {
330 fakeCommandActions[i] = func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }
331 }
332 fexec := &fakeexec.FakeExec{
333 CommandScript: fakeCommandActions,
334 }
335
336 var stdout, stderr bytes.Buffer
337 success, warning := runExtensionsChecks(&stdout, &stderr, tc.extensions, tc.missing, fexec, nil, "")
338 if tc.expSuccess != success {
339 t.Errorf("Expected success to be %t, got %t", tc.expSuccess, success)
340 }
341 if tc.expWarning != warning {
342 t.Errorf("Expected warning to be %t, got %t", tc.expWarning, warning)
343 }
344 output := stdout.String()
345 if tc.expOutput != output {
346 t.Errorf("Expected output to be:\n%s\nGot:\n%s", tc.expOutput, output)
347 }
348 })
349 }
350 }
351
352 func TestSuffix(t *testing.T) {
353 testCases := []*struct {
354 testName string
355 input string
356 exp string
357 }{
358 {
359 "empty",
360 "",
361 "",
362 },
363 {
364 "no path",
365 "linkerd-foo",
366 "foo",
367 },
368 {
369 "extra dash",
370 "linkerd-foo-bar",
371 "foo-bar",
372 },
373 {
374 "with path",
375 "/tmp/linkerd-foo",
376 "foo",
377 },
378 }
379 for _, tc := range testCases {
380 tc := tc
381 t.Run(tc.testName, func(t *testing.T) {
382 result := suffix(tc.input)
383 if !reflect.DeepEqual(tc.exp, result) {
384 t.Fatalf("Expected [%s] Got [%s]", tc.exp, result)
385 }
386 })
387 }
388 }
389
View as plain text