...

Source file src/github.com/linkerd/linkerd2/jaeger/cmd/check.go

Documentation: github.com/linkerd/linkerd2/jaeger/cmd

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"time"
     9  
    10  	pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
    11  	"github.com/linkerd/linkerd2/pkg/healthcheck"
    12  	"github.com/linkerd/linkerd2/pkg/k8s"
    13  	"github.com/linkerd/linkerd2/pkg/version"
    14  	"github.com/spf13/cobra"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  )
    17  
    18  const (
    19  
    20  	// JaegerExtensionName is the name of jaeger extension
    21  	JaegerExtensionName = "jaeger"
    22  
    23  	// JaegerLegacyExtension is the name of the jaeger extension prior to
    24  	// stable-2.10.0 when the linkerd prefix was removed.
    25  	JaegerLegacyExtension = "linkerd-jaeger"
    26  
    27  	// linkerdJaegerExtensionCheck adds checks related to the jaeger extension
    28  	linkerdJaegerExtensionCheck healthcheck.CategoryID = "linkerd-jaeger"
    29  )
    30  
    31  var (
    32  	jaegerNamespace string
    33  )
    34  
    35  type checkOptions struct {
    36  	wait      time.Duration
    37  	output    string
    38  	proxy     bool
    39  	namespace string
    40  }
    41  
    42  func jaegerCategory(hc *healthcheck.HealthChecker) *healthcheck.Category {
    43  
    44  	checkers := []healthcheck.Checker{}
    45  
    46  	checkers = append(checkers,
    47  		*healthcheck.NewChecker("linkerd-jaeger extension Namespace exists").
    48  			WithHintAnchor("l5d-jaeger-ns-exists").
    49  			Fatal().
    50  			WithCheck(func(ctx context.Context) error {
    51  				// Get  jaeger Extension Namespace
    52  				ns, err := hc.KubeAPIClient().GetNamespaceWithExtensionLabel(ctx, JaegerExtensionName)
    53  				if err != nil {
    54  					return err
    55  				}
    56  				jaegerNamespace = ns.Name
    57  				return nil
    58  			}))
    59  
    60  	checkers = append(checkers,
    61  		*healthcheck.NewChecker("jaeger extension pods are injected").
    62  			WithHintAnchor("l5d-jaeger-pods-injection").
    63  			Warning().
    64  			WithCheck(func(ctx context.Context) error {
    65  				// Check if Jaeger Extension pods have been injected
    66  				pods, err := hc.KubeAPIClient().GetPodsByNamespace(ctx, jaegerNamespace)
    67  				if err != nil {
    68  					return err
    69  				}
    70  				return healthcheck.CheckIfDataPlanePodsExist(pods)
    71  			}))
    72  
    73  	checkers = append(checkers,
    74  		*healthcheck.NewChecker("jaeger injector pods are running").
    75  			WithHintAnchor("l5d-jaeger-pods-running").
    76  			Warning().
    77  			WithRetryDeadline(hc.RetryDeadline).
    78  			SurfaceErrorOnRetry().
    79  			WithCheck(func(ctx context.Context) error {
    80  				podList, err := hc.KubeAPIClient().CoreV1().Pods(jaegerNamespace).List(ctx, metav1.ListOptions{
    81  					LabelSelector: fmt.Sprintf("%s=%s", k8s.LinkerdExtensionLabel, JaegerExtensionName),
    82  				})
    83  				if err != nil {
    84  					return err
    85  				}
    86  
    87  				// Check for relevant pods to be present
    88  				err = healthcheck.CheckForPods(podList.Items, []string{"jaeger-injector"})
    89  				if err != nil {
    90  					return err
    91  				}
    92  
    93  				return healthcheck.CheckPodsRunning(podList.Items, jaegerNamespace)
    94  			}))
    95  
    96  	checkers = append(checkers,
    97  		*healthcheck.NewChecker("jaeger extension proxies are healthy").
    98  			WithHintAnchor("l5d-jaeger-proxy-healthy").
    99  			Warning().
   100  			WithRetryDeadline(hc.RetryDeadline).
   101  			SurfaceErrorOnRetry().
   102  			WithCheck(func(ctx context.Context) error {
   103  				return hc.CheckProxyHealth(ctx, hc.ControlPlaneNamespace, jaegerNamespace)
   104  			}))
   105  
   106  	checkers = append(checkers,
   107  		*healthcheck.NewChecker("jaeger extension proxies are up-to-date").
   108  			WithHintAnchor("l5d-jaeger-proxy-cp-version").
   109  			Warning().
   110  			WithCheck(func(ctx context.Context) error {
   111  				var err error
   112  				if hc.VersionOverride != "" {
   113  					hc.LatestVersions, err = version.NewChannels(hc.VersionOverride)
   114  				} else {
   115  					uuid := "unknown"
   116  					if hc.UUID() != "" {
   117  						uuid = hc.UUID()
   118  					}
   119  					hc.LatestVersions, err = version.GetLatestVersions(ctx, uuid, "cli")
   120  				}
   121  				if err != nil {
   122  					return err
   123  				}
   124  
   125  				pods, err := hc.KubeAPIClient().GetPodsByNamespace(ctx, jaegerNamespace)
   126  				if err != nil {
   127  					return err
   128  				}
   129  
   130  				return hc.CheckProxyVersionsUpToDate(pods)
   131  			}))
   132  
   133  	checkers = append(checkers,
   134  		*healthcheck.NewChecker("jaeger extension proxies and cli versions match").
   135  			WithHintAnchor("l5d-jaeger-proxy-cli-version").
   136  			Warning().
   137  			WithCheck(func(ctx context.Context) error {
   138  				pods, err := hc.KubeAPIClient().GetPodsByNamespace(ctx, jaegerNamespace)
   139  				if err != nil {
   140  					return err
   141  				}
   142  
   143  				return healthcheck.CheckIfProxyVersionsMatchWithCLI(pods)
   144  			}))
   145  
   146  	return healthcheck.NewCategory(linkerdJaegerExtensionCheck, checkers, true)
   147  }
   148  
   149  func newCheckOptions() *checkOptions {
   150  	return &checkOptions{
   151  		wait:   300 * time.Second,
   152  		output: healthcheck.TableOutput,
   153  	}
   154  }
   155  
   156  func (options *checkOptions) validate() error {
   157  	if options.output != healthcheck.TableOutput && options.output != healthcheck.JSONOutput && options.output != healthcheck.ShortOutput {
   158  		return fmt.Errorf("Invalid output type '%s'. Supported output types are: %s, %s, %s", options.output, healthcheck.JSONOutput, healthcheck.TableOutput, healthcheck.ShortOutput)
   159  	}
   160  	return nil
   161  }
   162  
   163  // NewCmdCheck generates a new cobra command for the jaeger extension.
   164  func NewCmdCheck() *cobra.Command {
   165  	options := newCheckOptions()
   166  	cmd := &cobra.Command{
   167  		Use:   "check [flags]",
   168  		Args:  cobra.NoArgs,
   169  		Short: "Check the Jaeger extension for potential problems",
   170  		Long: `Check the Jaeger extension for potential problems.
   171  
   172  The check command will perform a series of checks to validate that the Jaeger
   173  extension is configured correctly. If the command encounters a failure it will
   174  print additional information about the failure and exit with a non-zero exit
   175  code.`,
   176  		Example: `  # Check that the Jaeger extension is up and running
   177    linkerd jaeger check`,
   178  		RunE: func(cmd *cobra.Command, args []string) error {
   179  			return configureAndRunChecks(stdout, stderr, options)
   180  		},
   181  	}
   182  
   183  	cmd.Flags().StringVarP(&options.output, "output", "o", options.output, "Output format. One of: table, json, short")
   184  	cmd.Flags().DurationVar(&options.wait, "wait", options.wait, "Maximum allowed time for all tests to pass")
   185  	cmd.Flags().BoolVar(&options.proxy, "proxy", options.proxy, "Also run data-plane checks, to determine if the data plane is healthy")
   186  	cmd.Flags().StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace to use for --proxy checks (default: all namespaces)")
   187  
   188  	pkgcmd.ConfigureNamespaceFlagCompletion(
   189  		cmd, []string{"namespace"},
   190  		kubeconfigPath, impersonate, impersonateGroup, kubeContext)
   191  	pkgcmd.ConfigureOutputFlagCompletion(cmd)
   192  
   193  	return cmd
   194  }
   195  
   196  func configureAndRunChecks(wout io.Writer, werr io.Writer, options *checkOptions) error {
   197  	err := options.validate()
   198  	if err != nil {
   199  		return fmt.Errorf("Validation error when executing check command: %w", err)
   200  	}
   201  
   202  	hc := healthcheck.NewHealthChecker([]healthcheck.CategoryID{}, &healthcheck.Options{
   203  		ControlPlaneNamespace: controlPlaneNamespace,
   204  		KubeConfig:            kubeconfigPath,
   205  		KubeContext:           kubeContext,
   206  		Impersonate:           impersonate,
   207  		ImpersonateGroup:      impersonateGroup,
   208  		APIAddr:               apiAddr,
   209  		RetryDeadline:         time.Now().Add(options.wait),
   210  		DataPlaneNamespace:    options.namespace,
   211  	})
   212  
   213  	err = hc.InitializeKubeAPIClient()
   214  	if err != nil {
   215  		fmt.Fprintf(werr, "Error initializing k8s API client: %s\n", err)
   216  		os.Exit(1)
   217  	}
   218  
   219  	hc.AppendCategories(jaegerCategory(hc))
   220  
   221  	success, warning := healthcheck.RunChecks(wout, werr, hc, options.output)
   222  	healthcheck.PrintChecksResult(wout, options.output, success, warning)
   223  
   224  	if !success {
   225  		os.Exit(1)
   226  	}
   227  
   228  	return nil
   229  }
   230  

View as plain text