1 package testutil
2
3 import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "io"
8 "strings"
9 "time"
10
11 "github.com/linkerd/linkerd2/pkg/healthcheck"
12 vizHealthcheck "github.com/linkerd/linkerd2/viz/pkg/healthcheck"
13 )
14
15 var preCategories = []healthcheck.CategoryID{
16 healthcheck.KubernetesAPIChecks,
17 healthcheck.KubernetesVersionChecks,
18 healthcheck.LinkerdPreInstallChecks,
19 healthcheck.LinkerdVersionChecks,
20 }
21
22 var coreCategories = []healthcheck.CategoryID{
23 healthcheck.KubernetesAPIChecks,
24 healthcheck.KubernetesVersionChecks,
25 healthcheck.LinkerdControlPlaneExistenceChecks,
26 healthcheck.LinkerdConfigChecks,
27 healthcheck.LinkerdIdentity,
28 healthcheck.LinkerdWebhooksAndAPISvcTLS,
29 healthcheck.LinkerdVersionChecks,
30 healthcheck.LinkerdControlPlaneProxyChecks,
31 }
32
33 var dataPlaneCategories = []healthcheck.CategoryID{
34 healthcheck.LinkerdIdentityDataPlane,
35 healthcheck.LinkerdControlPlaneProxyChecks,
36 healthcheck.LinkerdDataPlaneChecks,
37 }
38
39
40 func (h *TestHelper) TestCheckPre() error {
41 cmd := []string{"check", "--pre", "--output", "json", "--wait", "5m"}
42 return h.testCheck(cmd, preCategories)
43 }
44
45
46 func (h *TestHelper) TestCheck(extraArgs ...string) error {
47 return h.TestCheckWith([]healthcheck.CategoryID{healthcheck.LinkerdControlPlaneVersionChecks, vizHealthcheck.LinkerdVizExtensionCheck}, extraArgs...)
48 }
49
50
51
52 func (h *TestHelper) TestCheckWith(additional []healthcheck.CategoryID, extraArgs ...string) error {
53 cmd := []string{"check", "--output", "json", "--wait", "5m"}
54 cmd = append(cmd, extraArgs...)
55 categories := append(coreCategories, additional...)
56 return h.testCheck(cmd, categories)
57 }
58
59
60 func (h *TestHelper) TestCheckProxy(expectedVersion, namespace string) error {
61 cmd := []string{"check", "--proxy", "--expected-version", expectedVersion,
62 "--namespace", namespace, "--output", "json", "--wait", "5m"}
63 categories := append(coreCategories, vizHealthcheck.LinkerdVizExtensionCheck,
64 vizHealthcheck.LinkerdVizExtensionDataPlaneCheck)
65 categories = append(categories, dataPlaneCategories...)
66 return h.testCheck(cmd, categories)
67 }
68
69 func (h *TestHelper) testCheck(cmd []string, categories []healthcheck.CategoryID) error {
70 timeout := time.Minute * 10
71 return RetryFor(timeout, func() error {
72 res, err := h.LinkerdRun(cmd...)
73 if err != nil {
74 return fmt.Errorf("'linkerd check' command failed\n%w\n%s", err, res)
75 }
76
77 returnedCats := map[healthcheck.CategoryID]struct{}{}
78
79
80 d := json.NewDecoder(strings.NewReader(res))
81 for {
82 var out healthcheck.CheckOutput
83 err := d.Decode(&out)
84 if err != nil {
85
86 if !errors.Is(err, io.EOF) {
87 return fmt.Errorf("error processing 'linkerd check' output: %w", err)
88 }
89 break
90 }
91
92 errs := []string{}
93 for _, cat := range out.Categories {
94 for _, check := range cat.Checks {
95 returnedCats[cat.Name] = struct{}{}
96 if check.Result == healthcheck.CheckErr {
97 errs = append(errs, fmt.Sprintf("%s: %s", cat.Name, check.Error))
98 }
99 }
100 }
101 if len(errs) > 0 {
102 return errors.New(strings.Join(errs, "\n"))
103 }
104 }
105
106 errs := []string{}
107 for _, cat := range categories {
108 if _, ok := returnedCats[cat]; !ok {
109 errs = append(errs, fmt.Sprintf("missing category '%s'", cat))
110 }
111 }
112 if len(errs) > 0 {
113 return errors.New(strings.Join(errs, "\n"))
114 }
115
116 return nil
117 })
118 }
119
View as plain text