1 package cmd
2
3 import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "io"
8 "log"
9 "os"
10 "path/filepath"
11 "testing"
12
13 "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
14 "github.com/linkerd/linkerd2/pkg/cmd"
15 "github.com/linkerd/linkerd2/pkg/k8s"
16 "github.com/linkerd/linkerd2/testutil"
17 )
18
19 type testCase struct {
20 inputFileName string
21 goldenFileName string
22 reportFileName string
23 injectProxy bool
24 testInjectConfig *linkerd2.Values
25 enableDebugSidecarFlag bool
26 }
27
28 func mkFilename(filename string, verbose bool) string {
29 if verbose {
30 return fmt.Sprintf("%s.verbose", filename)
31 }
32 return filename
33 }
34
35 func testUninjectAndInject(t *testing.T, tc testCase) {
36 t.Helper()
37
38 file, err := os.Open("testdata/" + tc.inputFileName)
39 if err != nil {
40 t.Errorf("error opening test input file: %v\n", err)
41 }
42
43 read := bufio.NewReader(file)
44
45 output := new(bytes.Buffer)
46 report := new(bytes.Buffer)
47 transformer := &resourceTransformerInject{
48 injectProxy: tc.injectProxy,
49 values: tc.testInjectConfig,
50 overrideAnnotations: getOverrideAnnotations(tc.testInjectConfig, defaultConfig()),
51 enableDebugSidecar: tc.enableDebugSidecarFlag,
52 allowNsInject: true,
53 }
54
55 if exitCode := uninjectAndInject([]io.Reader{read}, report, output, transformer, "yaml"); exitCode != 0 {
56 t.Errorf("Unexpected error injecting YAML: %v", report)
57 }
58 if err := testDataDiffer.DiffTestYAML(tc.goldenFileName, output.String()); err != nil {
59 t.Error(err)
60 }
61
62 reportFileName := mkFilename(tc.reportFileName, verbose)
63 testDataDiffer.DiffTestdata(t, reportFileName, report.String())
64 }
65
66 func defaultConfig() *linkerd2.Values {
67 defaultConfig, err := testInstallValues()
68 if err != nil {
69 log.Fatalf("Unexpected error: %v", err)
70 }
71 defaultConfig.LinkerdVersion = "test-inject-control-plane-version"
72 defaultConfig.Proxy.Image.Version = "test-inject-proxy-version"
73 defaultConfig.DebugContainer.Image.Version = "test-inject-debug-version"
74
75 return defaultConfig
76 }
77
78 func TestUninjectAndInject(t *testing.T) {
79 defaultValues := defaultConfig()
80
81 overrideConfig := defaultConfig()
82 overrideConfig.Proxy.Image.Version = "override"
83
84 proxyResourceConfig := defaultConfig()
85 proxyResourceConfig.Proxy.Resources = &linkerd2.Resources{
86 CPU: linkerd2.Constraints{
87 Request: "110m",
88 Limit: "160m",
89 },
90 Memory: linkerd2.Constraints{
91 Request: "100Mi",
92 Limit: "150Mi",
93 },
94 }
95
96 cniEnabledConfig := defaultConfig()
97 cniEnabledConfig.CNIEnabled = true
98
99 opaquePortsConfig := defaultConfig()
100 opaquePortsConfig.Proxy.OpaquePorts = "3000,5000-6000,mysql"
101
102 ingressConfig := defaultConfig()
103 ingressConfig.Proxy.IsIngress = true
104
105 proxyIgnorePortsConfig := defaultConfig()
106 proxyIgnorePortsConfig.ProxyInit.IgnoreInboundPorts = "22,8100-8102"
107 proxyIgnorePortsConfig.ProxyInit.IgnoreOutboundPorts = "5432"
108
109 testCases := []testCase{
110 {
111 inputFileName: "inject_emojivoto_deployment.input.yml",
112 goldenFileName: "inject_emojivoto_deployment.golden.yml",
113 reportFileName: "inject_emojivoto_deployment.report",
114 injectProxy: true,
115 testInjectConfig: defaultValues,
116 },
117 {
118 inputFileName: "inject_emojivoto_deployment.input.yml",
119 goldenFileName: "inject_emojivoto_deployment_overridden_noinject.golden.yml",
120 reportFileName: "inject_emojivoto_deployment.report",
121 injectProxy: false,
122 testInjectConfig: func() *linkerd2.Values {
123 values := defaultConfig()
124 values.Proxy.Ports.Admin = 1234
125 return values
126 }(),
127 },
128 {
129 inputFileName: "inject_emojivoto_deployment.input.yml",
130 goldenFileName: "inject_emojivoto_deployment_overridden.golden.yml",
131 reportFileName: "inject_emojivoto_deployment.report",
132 injectProxy: true,
133 testInjectConfig: func() *linkerd2.Values {
134 values := defaultConfig()
135 values.Proxy.Ports.Admin = 1234
136 return values
137 }(),
138 },
139 {
140 inputFileName: "inject_emojivoto_deployment.input.yml",
141 goldenFileName: "inject_emojivoto_deployment_access_log.golden.yml",
142 reportFileName: "inject_emojivoto_deployment.report",
143 injectProxy: true,
144 testInjectConfig: func() *linkerd2.Values {
145 values := defaultConfig()
146 values.Proxy.AccessLog = "apache"
147 return values
148 }(),
149 },
150 {
151 inputFileName: "inject_emojivoto_list.input.yml",
152 goldenFileName: "inject_emojivoto_list.golden.yml",
153 reportFileName: "inject_emojivoto_list.report",
154 injectProxy: true,
155 testInjectConfig: defaultValues,
156 },
157 {
158 inputFileName: "inject_emojivoto_deployment_hostNetwork_false.input.yml",
159 goldenFileName: "inject_emojivoto_deployment_hostNetwork_false.golden.yml",
160 reportFileName: "inject_emojivoto_deployment_hostNetwork_false.report",
161 injectProxy: true,
162 testInjectConfig: defaultValues,
163 },
164 {
165 inputFileName: "inject_emojivoto_deployment_capabilities.input.yml",
166 goldenFileName: "inject_emojivoto_deployment_capabilities.golden.yml",
167 reportFileName: "inject_emojivoto_deployment.report",
168 injectProxy: true,
169 testInjectConfig: defaultValues,
170 },
171 {
172 inputFileName: "inject_emojivoto_deployment_injectDisabled.input.yml",
173 goldenFileName: "inject_emojivoto_deployment_injectDisabled.input.yml",
174 reportFileName: "inject_emojivoto_deployment_injectDisabled.report",
175 injectProxy: true,
176 testInjectConfig: defaultValues,
177 },
178 {
179 inputFileName: "inject_emojivoto_deployment_controller_name.input.yml",
180 goldenFileName: "inject_emojivoto_deployment_controller_name.golden.yml",
181 reportFileName: "inject_emojivoto_deployment_controller_name.report",
182 injectProxy: true,
183 testInjectConfig: defaultValues,
184 },
185 {
186 inputFileName: "inject_emojivoto_statefulset.input.yml",
187 goldenFileName: "inject_emojivoto_statefulset.golden.yml",
188 reportFileName: "inject_emojivoto_statefulset.report",
189 injectProxy: true,
190 testInjectConfig: defaultValues,
191 },
192 {
193 inputFileName: "inject_emojivoto_cronjob.input.yml",
194 goldenFileName: "inject_emojivoto_cronjob.golden.yml",
195 reportFileName: "inject_emojivoto_cronjob.report",
196 injectProxy: false,
197 testInjectConfig: defaultValues,
198 },
199 {
200 inputFileName: "inject_emojivoto_cronjob_nometa.input.yml",
201 goldenFileName: "inject_emojivoto_cronjob_nometa.golden.yml",
202 reportFileName: "inject_emojivoto_cronjob.report",
203 injectProxy: false,
204 testInjectConfig: defaultValues,
205 },
206 {
207 inputFileName: "inject_emojivoto_pod.input.yml",
208 goldenFileName: "inject_emojivoto_pod.golden.yml",
209 reportFileName: "inject_emojivoto_pod.report",
210 injectProxy: true,
211 testInjectConfig: defaultValues,
212 },
213 {
214 inputFileName: "inject_emojivoto_pod_with_requests.input.yml",
215 goldenFileName: "inject_emojivoto_pod_with_requests.golden.yml",
216 reportFileName: "inject_emojivoto_pod_with_requests.report",
217 injectProxy: true,
218 testInjectConfig: proxyResourceConfig,
219 },
220 {
221 inputFileName: "inject_emojivoto_deployment_udp.input.yml",
222 goldenFileName: "inject_emojivoto_deployment_udp.golden.yml",
223 reportFileName: "inject_emojivoto_deployment_udp.report",
224 injectProxy: true,
225 testInjectConfig: defaultValues,
226 },
227 {
228 inputFileName: "inject_emojivoto_already_injected.input.yml",
229 goldenFileName: "inject_emojivoto_already_injected.golden.yml",
230 reportFileName: "inject_emojivoto_already_injected.report",
231 injectProxy: true,
232 testInjectConfig: defaultValues,
233 },
234 {
235 inputFileName: "inject_contour.input.yml",
236 goldenFileName: "inject_contour.golden.yml",
237 reportFileName: "inject_contour.report",
238 injectProxy: true,
239 testInjectConfig: defaultValues,
240 },
241 {
242 inputFileName: "inject_emojivoto_deployment_empty_resources.input.yml",
243 goldenFileName: "inject_emojivoto_deployment_empty_resources.golden.yml",
244 reportFileName: "inject_emojivoto_deployment_empty_resources.report",
245 injectProxy: true,
246 testInjectConfig: defaultValues,
247 },
248 {
249 inputFileName: "inject_emojivoto_list_empty_resources.input.yml",
250 goldenFileName: "inject_emojivoto_list_empty_resources.golden.yml",
251 reportFileName: "inject_emojivoto_list_empty_resources.report",
252 injectProxy: true,
253 testInjectConfig: defaultValues,
254 },
255 {
256 inputFileName: "inject_emojivoto_deployment.input.yml",
257 goldenFileName: "inject_emojivoto_deployment_no_init_container.golden.yml",
258 reportFileName: "inject_emojivoto_deployment.report",
259 injectProxy: true,
260 testInjectConfig: cniEnabledConfig,
261 },
262 {
263 inputFileName: "inject_emojivoto_deployment_config_overrides.input.yml",
264 goldenFileName: "inject_emojivoto_deployment_config_overrides.golden.yml",
265 reportFileName: "inject_emojivoto_deployment.report",
266 injectProxy: true,
267 testInjectConfig: overrideConfig,
268 },
269 {
270 inputFileName: "inject_emojivoto_deployment.input.yml",
271 goldenFileName: "inject_emojivoto_deployment_debug.golden.yml",
272 reportFileName: "inject_emojivoto_deployment.report",
273 injectProxy: true,
274 testInjectConfig: defaultValues,
275 enableDebugSidecarFlag: true,
276 },
277 {
278 inputFileName: "inject_tap_deployment.input.yml",
279 goldenFileName: "inject_tap_deployment_debug.golden.yml",
280 reportFileName: "inject_tap_deployment_debug.report",
281 injectProxy: true,
282 testInjectConfig: defaultValues,
283 enableDebugSidecarFlag: true,
284 },
285 {
286 inputFileName: "inject_emojivoto_namespace_good.input.yml",
287 goldenFileName: "inject_emojivoto_namespace_good.golden.yml",
288 reportFileName: "inject_emojivoto_namespace_good.golden.report",
289 injectProxy: false,
290 testInjectConfig: defaultConfig(),
291 },
292 {
293 inputFileName: "inject_emojivoto_namespace_good.input.yml",
294 goldenFileName: "inject_emojivoto_namespace_overidden_good.golden.yml",
295 reportFileName: "inject_emojivoto_namespace_good.golden.report",
296 injectProxy: false,
297 testInjectConfig: defaultConfig(),
298 },
299 {
300 inputFileName: "inject_emojivoto_deployment.input.yml",
301 goldenFileName: "inject_emojivoto_deployment_proxyignores.golden.yml",
302 reportFileName: "inject_emojivoto_deployment.report",
303 injectProxy: true,
304 testInjectConfig: proxyIgnorePortsConfig,
305 },
306 {
307 inputFileName: "inject_emojivoto_pod.input.yml",
308 goldenFileName: "inject_emojivoto_pod_proxyignores.golden.yml",
309 reportFileName: "inject_emojivoto_pod.report",
310 injectProxy: true,
311 testInjectConfig: proxyIgnorePortsConfig,
312 },
313 {
314 inputFileName: "inject_emojivoto_deployment.input.yml",
315 goldenFileName: "inject_emojivoto_deployment_opaque_ports.golden.yml",
316 reportFileName: "inject_emojivoto_deployment_opaque_ports.report",
317 injectProxy: true,
318 testInjectConfig: opaquePortsConfig,
319 },
320 {
321 inputFileName: "inject_emojivoto_pod.input.yml",
322 goldenFileName: "inject_emojivoto_pod_ingress.golden.yml",
323 reportFileName: "inject_emojivoto_pod_ingress.report",
324 injectProxy: true,
325 testInjectConfig: ingressConfig,
326 },
327 {
328 inputFileName: "inject_emojivoto_deployment.input.yml",
329 goldenFileName: "inject_emojivoto_deployment_default_inbound_policy.golden.yml",
330 reportFileName: "inject_emojivoto_deployment_default_inbound_policy.golden.report",
331 injectProxy: false,
332 testInjectConfig: func() *linkerd2.Values {
333 values := defaultConfig()
334 values.Proxy.DefaultInboundPolicy = k8s.AllAuthenticated
335 return values
336 }(),
337 },
338 {
339 inputFileName: "inject_emojivoto_pod.input.yml",
340 goldenFileName: "inject_emojivoto_pod_default_inbound_policy.golden.yml",
341 reportFileName: "inject_emojivoto_pod_default_inbound_policy.golden.report",
342 injectProxy: false,
343 testInjectConfig: func() *linkerd2.Values {
344 values := defaultConfig()
345 values.Proxy.DefaultInboundPolicy = k8s.AllAuthenticated
346 return values
347 }(),
348 },
349 {
350 inputFileName: "inject_emojivoto_deployment.input.yml",
351 goldenFileName: "inject_emojivoto_deployment_native_sidecar.golden.yml",
352 reportFileName: "inject_emojivoto_deployment.report",
353 injectProxy: true,
354 testInjectConfig: func() *linkerd2.Values {
355 values := defaultConfig()
356 values.Proxy.NativeSidecar = true
357 return values
358 }(),
359 },
360 {
361 inputFileName: "inject_emojivoto_deployment.input.yml",
362 goldenFileName: "inject_emojivoto_deployment_params.golden.yml",
363 reportFileName: "inject_emojivoto_deployment.report",
364 injectProxy: true,
365 testInjectConfig: func() *linkerd2.Values {
366 values := defaultConfig()
367 values.Proxy.Inbound = linkerd2.ProxyParams{
368 "scope": linkerd2.ProxyScopeParams{
369 "proto": linkerd2.ProxyProtoParams{
370 "appleSauce": "valueA",
371 "blueberry": 3.14,
372 },
373 },
374 }
375 values.Proxy.Outbound = linkerd2.ProxyParams{
376 "scope": linkerd2.ProxyScopeParams{
377 "proto": linkerd2.ProxyProtoParams{
378 "applesauce": "valueA",
379 "blueBerry": true,
380 },
381 },
382 }
383 return values
384 }(),
385 },
386 }
387
388 for i, tc := range testCases {
389 tc := tc
390 verbose = true
391 t.Run(fmt.Sprintf("%d: %s --verbose", i, tc.inputFileName), func(t *testing.T) {
392 testUninjectAndInject(t, tc)
393 })
394 verbose = false
395 t.Run(fmt.Sprintf("%d: %s", i, tc.inputFileName), func(t *testing.T) {
396 testUninjectAndInject(t, tc)
397 })
398 }
399 }
400
401 type injectCmd struct {
402 inputFileName string
403 stdErrGoldenFileName string
404 stdOutGoldenFileName string
405 exitCode int
406 injectProxy bool
407 values *linkerd2.Values
408 }
409
410 func testInjectCmd(t *testing.T, tc injectCmd) {
411 t.Helper()
412
413 testConfig := tc.values
414 if testConfig == nil {
415 var err error
416 testConfig, err = testInstallValues()
417 if err != nil {
418 t.Fatalf("Unexpected error: %v", err)
419 }
420 }
421 testConfig.Proxy.Image.Version = "testinjectversion"
422
423 errBuffer := &bytes.Buffer{}
424 outBuffer := &bytes.Buffer{}
425
426 in, err := os.Open(fmt.Sprintf("testdata/%s", tc.inputFileName))
427 if err != nil {
428 t.Fatalf("Unexpected error: %v", err)
429 }
430
431 transformer := &resourceTransformerInject{
432 injectProxy: tc.injectProxy,
433 values: testConfig,
434 }
435 exitCode := runInjectCmd([]io.Reader{in}, errBuffer, outBuffer, transformer, "yaml")
436 if exitCode != tc.exitCode {
437 t.Fatalf("Expected exit code to be %d but got: %d", tc.exitCode, exitCode)
438 }
439 if tc.stdOutGoldenFileName != "" {
440 testDataDiffer.DiffTestdata(t, tc.stdOutGoldenFileName, outBuffer.String())
441 } else if outBuffer.Len() != 0 {
442 t.Fatalf("Expected no standard output, but got: %s", outBuffer)
443 }
444
445 stdErrGoldenFileName := mkFilename(tc.stdErrGoldenFileName, verbose)
446 testDataDiffer.DiffTestdata(t, stdErrGoldenFileName, errBuffer.String())
447 }
448
449 func TestRunInjectCmd(t *testing.T) {
450 testCases := []injectCmd{
451 {
452 inputFileName: "inject_gettest_deployment.bad.input.yml",
453 stdErrGoldenFileName: "inject_gettest_deployment.bad.golden",
454 exitCode: 1,
455 injectProxy: true,
456 },
457 {
458 inputFileName: "inject_tap_deployment.input.yml",
459 stdErrGoldenFileName: "inject_tap_deployment.bad.golden",
460 exitCode: 1,
461 injectProxy: false,
462 },
463 {
464 inputFileName: "inject_gettest_deployment.good.input.yml",
465 stdOutGoldenFileName: "inject_gettest_deployment.good.golden.yml",
466 stdErrGoldenFileName: "inject_gettest_deployment.good.golden.stderr",
467 exitCode: 0,
468 injectProxy: true,
469 },
470 {
471 inputFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false.input.yml",
472 stdOutGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false.golden.yml",
473 stdErrGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false.golden.stderr",
474 exitCode: 0,
475 injectProxy: true,
476 },
477 {
478 inputFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false.input.yml",
479 stdOutGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false_volumeProjection_disabled.golden.yml",
480 stdErrGoldenFileName: "inject_emojivoto_deployment_automountServiceAccountToken_false_volumeProjection_disabled.golden.stderr",
481 exitCode: 1,
482 injectProxy: false,
483 values: func() *linkerd2.Values {
484 values, _ := testInstallValues()
485 values.Identity.ServiceAccountTokenProjection = false
486 return values
487 }(),
488 },
489 {
490 inputFileName: "inject_emojivoto_istio.input.yml",
491 stdOutGoldenFileName: "inject_emojivoto_istio.golden.yml",
492 stdErrGoldenFileName: "inject_emojivoto_istio.golden.stderr",
493 exitCode: 1,
494 injectProxy: true,
495 },
496 {
497 inputFileName: "inject_emojivoto_deployment_hostNetwork_true.input.yml",
498 stdOutGoldenFileName: "inject_emojivoto_deployment_hostNetwork_true.golden.yml",
499 stdErrGoldenFileName: "inject_emojivoto_deployment_hostNetwork_true.golden.stderr",
500 exitCode: 1,
501 injectProxy: true,
502 },
503 }
504
505 for i, tc := range testCases {
506 tc := tc
507 verbose = true
508 t.Run(fmt.Sprintf("%d: %s --verbose", i, tc.inputFileName), func(t *testing.T) {
509 testInjectCmd(t, tc)
510 })
511 verbose = false
512 t.Run(fmt.Sprintf("%d: %s", i, tc.inputFileName), func(t *testing.T) {
513 testInjectCmd(t, tc)
514 })
515 }
516 }
517
518 type injectFilePath struct {
519 resource string
520 resourceFile string
521 expectedFile string
522 stdErrFile string
523 }
524
525 func testInjectFilePath(t *testing.T, tc injectFilePath) {
526 in, err := read("testdata/" + tc.resourceFile)
527 if err != nil {
528 t.Fatal("Unexpected error: ", err)
529 }
530
531 errBuf := &bytes.Buffer{}
532 actual := &bytes.Buffer{}
533 values, err := testInstallValues()
534 if err != nil {
535 t.Fatalf("Unexpected error: %v", err)
536 }
537 transformer := &resourceTransformerInject{
538 injectProxy: true,
539 values: values,
540 }
541 if exitCode := runInjectCmd(in, errBuf, actual, transformer, "yaml"); exitCode != 0 {
542 t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
543 }
544 if err := testDataDiffer.DiffTestYAML(tc.expectedFile, actual.String()); err != nil {
545 t.Error(err)
546 }
547
548 stdErrFile := mkFilename(tc.stdErrFile, verbose)
549 testDataDiffer.DiffTestdata(t, stdErrFile, errBuf.String())
550 }
551
552 func testReadFromFolder(t *testing.T, resourceFolder string, expectedFolder string) {
553 in, err := read("testdata/" + resourceFolder)
554 if err != nil {
555 t.Fatal("Unexpected error: ", err)
556 }
557
558 values, err := testInstallValues()
559 if err != nil {
560 t.Fatalf("Unexpected error: %v", err)
561 }
562 errBuf := &bytes.Buffer{}
563 actual := &bytes.Buffer{}
564 transformer := &resourceTransformerInject{
565 injectProxy: true,
566 values: values,
567 }
568 if exitCode := runInjectCmd(in, errBuf, actual, transformer, "yaml"); exitCode != 0 {
569 t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
570 }
571
572 expectedFile := filepath.Join(expectedFolder, "injected_nginx_redis.yaml")
573 if err := testDataDiffer.DiffTestYAML(expectedFile, actual.String()); err != nil {
574 t.Error(err)
575 }
576
577 stdErrFileName := mkFilename(filepath.Join(expectedFolder, "injected_nginx_redis.stderr"), verbose)
578 testDataDiffer.DiffTestdata(t, stdErrFileName, errBuf.String())
579 }
580
581 func TestInjectFilePath(t *testing.T) {
582 var (
583 resourceFolder = filepath.Join("inject-filepath", "resources")
584 expectedFolder = filepath.Join("inject-filepath", "expected")
585 )
586
587 t.Run("read from files", func(t *testing.T) {
588 testCases := []injectFilePath{
589 {
590 resource: "nginx",
591 resourceFile: filepath.Join(resourceFolder, "nginx.yaml"),
592 expectedFile: filepath.Join(expectedFolder, "injected_nginx.yaml"),
593 stdErrFile: filepath.Join(expectedFolder, "injected_nginx.stderr"),
594 },
595 {
596 resource: "redis",
597 resourceFile: filepath.Join(resourceFolder, "db/redis.yaml"),
598 expectedFile: filepath.Join(expectedFolder, "injected_redis.yaml"),
599 stdErrFile: filepath.Join(expectedFolder, "injected_redis.stderr"),
600 },
601 }
602
603 for i, testCase := range testCases {
604 testCase := testCase
605 verbose = true
606 t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
607 testInjectFilePath(t, testCase)
608 })
609 verbose = false
610 t.Run(fmt.Sprintf("%d %s", i, testCase.resource), func(t *testing.T) {
611 testInjectFilePath(t, testCase)
612 })
613 }
614 })
615
616 verbose = true
617 t.Run("read from folder --verbose", func(t *testing.T) {
618 testReadFromFolder(t, resourceFolder, expectedFolder)
619 })
620 verbose = false
621 t.Run("read from folder --verbose", func(t *testing.T) {
622 testReadFromFolder(t, resourceFolder, expectedFolder)
623 })
624 }
625
626 func TestToURL(t *testing.T) {
627
628
629
630 tests := map[string]bool{
631 "http://www.linkerd.io": true,
632 "https://www.linkerd.io": true,
633 "www.linkerd.io/": false,
634 "~/foo/bar.yaml": false,
635 "./foo/bar.yaml": false,
636 "/foo/bar/baz.yml": false,
637 "../foo/bar/baz.yaml": false,
638 "https//": false,
639 }
640
641 for url, expectedValue := range tests {
642 _, ok := toURL(url)
643 if ok != expectedValue {
644 t.Errorf("Result mismatch for %s. expected %v, but got %v", url, expectedValue, ok)
645 }
646 }
647
648 }
649
650 func TestWalk(t *testing.T) {
651
652
653 var (
654 tmpFolderRoot = "linkerd-testdata"
655 tmpFolderData = filepath.Join(tmpFolderRoot, "data")
656 )
657
658 if err := os.MkdirAll(tmpFolderData, os.ModeDir|os.ModePerm); err != nil {
659 t.Fatal("Unexpected error: ", err)
660 }
661 defer func() {
662 err := os.RemoveAll(tmpFolderRoot)
663 if err != nil {
664 t.Errorf("failed to remove temp dir %q: %v", tmpFolderRoot, err)
665 }
666 }()
667
668 var (
669 data = []byte(testutil.ReadTestdata("inject_gettest_deployment.bad.input.yml"))
670 file1 = filepath.Join(tmpFolderRoot, "root.txt")
671 file2 = filepath.Join(tmpFolderData, "data.txt")
672 )
673 if err := os.WriteFile(file1, data, 0600); err != nil {
674 t.Fatal("Unexpected error: ", err)
675 }
676 if err := os.WriteFile(file2, data, 0600); err != nil {
677 t.Fatal("Unexpected error: ", err)
678 }
679
680 actual, err := walk(tmpFolderRoot)
681 if err != nil {
682 t.Fatal("Unexpected error: ", err)
683 }
684
685 for _, r := range actual {
686 b := make([]byte, len(data))
687 r.Read(b)
688
689 if string(b) != string(data) {
690 t.Errorf("Content mismatch. Expected %q, but got %q", data, b)
691 }
692 }
693 }
694
695 func TestProxyConfigurationAnnotations(t *testing.T) {
696 baseValues, err := linkerd2.NewValues()
697 if err != nil {
698 t.Fatal(err)
699 }
700 values, err := baseValues.DeepCopy()
701 if err != nil {
702 t.Fatal(err)
703 }
704 values.ProxyInit.IgnoreInboundPorts = "8500-8505"
705 values.ProxyInit.IgnoreOutboundPorts = "3306"
706 values.Proxy.Ports.Admin = 1234
707 values.Proxy.Ports.Control = 4191
708 values.Proxy.Ports.Inbound = 4144
709 values.Proxy.Ports.Outbound = 4141
710 values.Proxy.UID = 999
711 values.Proxy.GID = 999
712 values.Proxy.LogLevel = "debug"
713 values.Proxy.LogFormat = "cool"
714 values.Proxy.EnableExternalProfiles = true
715 values.Proxy.Resources.CPU.Request = "10m"
716 values.Proxy.Resources.CPU.Limit = "100m"
717 values.Proxy.Resources.Memory.Request = "10Mi"
718 values.Proxy.Resources.Memory.Limit = "50Mi"
719 values.Proxy.WaitBeforeExitSeconds = 10
720 values.Proxy.Await = false
721 values.Proxy.AccessLog = "apache"
722 values.Proxy.ShutdownGracePeriod = "60s"
723 values.Proxy.NativeSidecar = true
724
725 expectedOverrides := map[string]string{
726 k8s.ProxyIgnoreInboundPortsAnnotation: "8500-8505",
727 k8s.ProxyIgnoreOutboundPortsAnnotation: "3306",
728 k8s.ProxyAdminPortAnnotation: "1234",
729 k8s.ProxyControlPortAnnotation: "4191",
730 k8s.ProxyInboundPortAnnotation: "4144",
731 k8s.ProxyOutboundPortAnnotation: "4141",
732 k8s.ProxyUIDAnnotation: "999",
733 k8s.ProxyGIDAnnotation: "999",
734 k8s.ProxyLogLevelAnnotation: "debug",
735 k8s.ProxyLogFormatAnnotation: "cool",
736
737 k8s.ProxyEnableExternalProfilesAnnotation: "true",
738 k8s.ProxyCPURequestAnnotation: "10m",
739 k8s.ProxyCPULimitAnnotation: "100m",
740 k8s.ProxyMemoryRequestAnnotation: "10Mi",
741 k8s.ProxyMemoryLimitAnnotation: "50Mi",
742 k8s.ProxyWaitBeforeExitSecondsAnnotation: "10",
743 k8s.ProxyAwait: "disabled",
744 k8s.ProxyAccessLogAnnotation: "apache",
745 k8s.ProxyShutdownGracePeriodAnnotation: "60s",
746 k8s.ProxyEnableNativeSidecarAnnotation: "true",
747 }
748
749 overrides := getOverrideAnnotations(values, baseValues)
750
751 diffOverrides(t, expectedOverrides, overrides)
752 }
753
754 func TestProxyImageAnnotations(t *testing.T) {
755 baseValues, err := linkerd2.NewValues()
756 if err != nil {
757 t.Fatal(err)
758 }
759 values, err := baseValues.DeepCopy()
760 if err != nil {
761 t.Fatal(err)
762 }
763 values.Proxy.Image = &linkerd2.Image{
764 Name: "my.registry/linkerd/proxy",
765 Version: "test-proxy-version",
766 PullPolicy: "Always",
767 }
768
769 expectedOverrides := map[string]string{
770 k8s.ProxyImageAnnotation: "my.registry/linkerd/proxy",
771 k8s.ProxyVersionOverrideAnnotation: "test-proxy-version",
772 k8s.ProxyImagePullPolicyAnnotation: "Always",
773 }
774
775 overrides := getOverrideAnnotations(values, baseValues)
776
777 diffOverrides(t, expectedOverrides, overrides)
778 }
779
780 func TestProxyInitImageAnnotations(t *testing.T) {
781 baseValues, err := linkerd2.NewValues()
782 if err != nil {
783 t.Fatal(err)
784 }
785 values, err := baseValues.DeepCopy()
786 if err != nil {
787 t.Fatal(err)
788 }
789 values.ProxyInit.Image = &linkerd2.Image{
790 Name: "my.registry/linkerd/proxy-init",
791 Version: "test-proxy-init-version",
792 }
793
794 expectedOverrides := map[string]string{
795 k8s.ProxyInitImageAnnotation: "my.registry/linkerd/proxy-init",
796 k8s.ProxyInitImageVersionAnnotation: "test-proxy-init-version",
797 }
798
799 overrides := getOverrideAnnotations(values, baseValues)
800
801 diffOverrides(t, expectedOverrides, overrides)
802 }
803
804 func TestNoAnnotations(t *testing.T) {
805 baseValues, err := linkerd2.NewValues()
806 if err != nil {
807 t.Fatal(err)
808 }
809 values, err := baseValues.DeepCopy()
810 if err != nil {
811 t.Fatal(err)
812 }
813
814 expectedOverrides := map[string]string{}
815
816 overrides := getOverrideAnnotations(values, baseValues)
817
818 diffOverrides(t, expectedOverrides, overrides)
819 }
820
821 func TestOverwriteRegistry(t *testing.T) {
822 testCases := []struct {
823 image string
824 registry string
825 expected string
826 }{
827 {
828 image: "cr.l5d.io/linkerd/image",
829 registry: "my.custom.registry",
830 expected: "my.custom.registry/image",
831 },
832 {
833 image: "cr.l5d.io/linkerd/image",
834 registry: "my.custom.registry/",
835 expected: "my.custom.registry/image",
836 },
837 {
838 image: "my.custom.registry/image",
839 registry: "my.custom.registry",
840 expected: "my.custom.registry/image",
841 },
842 {
843 image: "my.custom.registry/image",
844 registry: "cr.l5d.io/linkerd",
845 expected: "cr.l5d.io/linkerd/image",
846 },
847 {
848 image: "",
849 registry: "my.custom.registry",
850 expected: "",
851 },
852 {
853 image: "cr.l5d.io/linkerd/image",
854 registry: "",
855 expected: "image",
856 },
857 {
858 image: "image",
859 registry: "cr.l5d.io/linkerd",
860 expected: "cr.l5d.io/linkerd/image",
861 },
862 }
863 for i, tc := range testCases {
864 tc := tc
865 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
866 actual := cmd.RegistryOverride(tc.image, tc.registry)
867 if actual != tc.expected {
868 t.Fatalf("expected %q, but got %q", tc.expected, actual)
869 }
870 })
871 }
872 }
873
874 func diffOverrides(t *testing.T, expectedOverrides map[string]string, actualOverrides map[string]string) {
875 if len(expectedOverrides) != len(actualOverrides) {
876 t.Fatalf("expected annotations:\n%s\nbut received:\n%s", expectedOverrides, actualOverrides)
877 }
878 for key, expected := range expectedOverrides {
879 actual := actualOverrides[key]
880 if actual != expected {
881 t.Fatalf("expected annotation %q with %q, but got %q", key, expected, actual)
882 }
883 }
884 }
885
View as plain text