1 package cmd
2
3 import (
4 "fmt"
5 "io"
6 "os"
7 "time"
8
9 pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
10 "github.com/linkerd/linkerd2/pkg/healthcheck"
11 vizHealthCheck "github.com/linkerd/linkerd2/viz/pkg/healthcheck"
12 "github.com/spf13/cobra"
13 )
14
15 type checkOptions struct {
16 proxy bool
17 wait time.Duration
18 namespace string
19 output string
20 }
21
22 func newCheckOptions() *checkOptions {
23 return &checkOptions{
24 wait: 300 * time.Second,
25 output: healthcheck.TableOutput,
26 }
27 }
28
29 func (options *checkOptions) validate() error {
30 if options.output != healthcheck.TableOutput && options.output != healthcheck.JSONOutput && options.output != healthcheck.ShortOutput {
31 return fmt.Errorf("Invalid output type '%s'. Supported output types are: %s, %s, %s", options.output, healthcheck.JSONOutput, healthcheck.TableOutput, healthcheck.ShortOutput)
32 }
33 return nil
34 }
35
36
37 func NewCmdCheck() *cobra.Command {
38 options := newCheckOptions()
39 cmd := &cobra.Command{
40 Use: "check [flags]",
41 Args: cobra.NoArgs,
42 Short: "Check the Linkerd Viz extension for potential problems",
43 Long: `Check the Linkerd Viz extension for potential problems.
44
45 The check command will perform a series of checks to validate that the Linkerd Viz
46 extension is configured correctly. If the command encounters a failure it will
47 print additional information about the failure and exit with a non-zero exit
48 code.`,
49 Example: ` # Check that the viz extension is up and running
50 linkerd viz check`,
51 RunE: func(cmd *cobra.Command, args []string) error {
52
53 return configureAndRunChecks(stdout, stderr, options)
54 },
55 }
56
57 cmd.Flags().StringVarP(&options.output, "output", "o", options.output, "Output format. One of: table, json, short")
58 cmd.Flags().BoolVar(&options.proxy, "proxy", options.proxy, "Also run data-plane checks, to determine if the data plane is healthy")
59 cmd.Flags().DurationVar(&options.wait, "wait", options.wait, "Maximum allowed time for all tests to pass")
60 cmd.Flags().StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace to use for --proxy checks (default: all namespaces)")
61
62 pkgcmd.ConfigureNamespaceFlagCompletion(
63 cmd, []string{"namespace"},
64 kubeconfigPath, impersonate, impersonateGroup, kubeContext)
65 return cmd
66 }
67
68 func configureAndRunChecks(wout io.Writer, werr io.Writer, options *checkOptions) error {
69 err := options.validate()
70 if err != nil {
71 return fmt.Errorf("validation error when executing check command: %w", err)
72 }
73
74 hc := vizHealthCheck.NewHealthChecker([]healthcheck.CategoryID{}, &vizHealthCheck.VizOptions{
75 Options: &healthcheck.Options{
76 ControlPlaneNamespace: controlPlaneNamespace,
77 KubeConfig: kubeconfigPath,
78 KubeContext: kubeContext,
79 Impersonate: impersonate,
80 ImpersonateGroup: impersonateGroup,
81 APIAddr: apiAddr,
82 RetryDeadline: time.Now().Add(options.wait),
83 DataPlaneNamespace: options.namespace,
84 },
85 VizNamespaceOverride: vizNamespace,
86 })
87 err = hc.InitializeKubeAPIClient()
88 if err != nil {
89 fmt.Fprintf(werr, "Error initializing k8s API client: %s\n", err)
90 os.Exit(1)
91 }
92
93 hc.AppendCategories(hc.VizCategory(true))
94 if options.proxy {
95 hc.AppendCategories(hc.VizDataPlaneCategory())
96 }
97 success, warning := healthcheck.RunChecks(wout, werr, hc, options.output)
98 healthcheck.PrintChecksResult(wout, options.output, success, warning)
99
100 if !success {
101 os.Exit(1)
102 }
103
104 return nil
105 }
106
View as plain text