linkerd2/cli/cmd/check.go

122 lines
3.4 KiB
Go

package cmd
import (
"errors"
"fmt"
"io"
"os"
"github.com/runconduit/conduit/controller/api/public"
healthcheckPb "github.com/runconduit/conduit/controller/gen/common/healthcheck"
pb "github.com/runconduit/conduit/controller/gen/public"
"github.com/runconduit/conduit/pkg/healthcheck"
"github.com/runconduit/conduit/pkg/k8s"
"github.com/runconduit/conduit/pkg/shell"
"github.com/spf13/cobra"
)
const lineWidth = 80
var checkCmd = &cobra.Command{
Use: "check",
Short: "Check your Conduit installation for potential problems.",
Long: `Check your Conduit installation for potential problems. The check command will perform various checks of your
local system, the Conduit control plane, and connectivity between those. The process will exit with non-zero check if
problems were found.`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
kubectl, err := k8s.NewKubectl(shell.NewUnixShell())
if err != nil {
fmt.Fprintf(os.Stderr, "Error with kubectl: %s\n", err.Error())
statusCheckResultWasError(os.Stdout)
os.Exit(2)
}
kubeApi, err := k8s.NewK8sAPI(shell.NewUnixShell().HomeDir(), kubeconfigPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error with Kubernetes API: %s\n", err.Error())
statusCheckResultWasError(os.Stdout)
os.Exit(2)
}
var conduitApi pb.ApiClient
if apiAddr != "" {
conduitApi, err = public.NewInternalClient(apiAddr)
} else {
conduitApi, err = public.NewExternalClient(controlPlaneNamespace, kubeApi)
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error with Conduit API: %s\n", err.Error())
statusCheckResultWasError(os.Stdout)
os.Exit(2)
}
err = checkStatus(os.Stdout, kubectl, kubeApi, healthcheck.NewGrpcStatusChecker(public.ConduitApiSubsystemName, conduitApi))
if err != nil {
os.Exit(2)
}
},
}
func checkStatus(w io.Writer, checkers ...healthcheck.StatusChecker) error {
prettyPrintResults := func(result *healthcheckPb.CheckResult) {
checkLabel := fmt.Sprintf("%s: %s", result.SubsystemName, result.CheckDescription)
filler := ""
for i := 0; i < lineWidth-len(checkLabel); i++ {
filler = filler + "."
}
switch result.Status {
case healthcheckPb.CheckStatus_OK:
fmt.Fprintf(w, "%s%s[ok]\n", checkLabel, filler)
case healthcheckPb.CheckStatus_FAIL:
fmt.Fprintf(w, "%s%s[FAIL] -- %s\n", checkLabel, filler, result.FriendlyMessageToUser)
case healthcheckPb.CheckStatus_ERROR:
fmt.Fprintf(w, "%s%s[ERROR] -- %s\n", checkLabel, filler, result.FriendlyMessageToUser)
}
}
checker := healthcheck.MakeHealthChecker()
for _, c := range checkers {
checker.Add(c)
}
checkStatus := checker.PerformCheck(prettyPrintResults)
fmt.Fprintln(w, "")
var err error
switch checkStatus {
case healthcheckPb.CheckStatus_OK:
err = statusCheckResultWasOk(w)
case healthcheckPb.CheckStatus_FAIL:
err = statusCheckResultWasFail(w)
case healthcheckPb.CheckStatus_ERROR:
err = statusCheckResultWasError(w)
}
return err
}
func statusCheckResultWasOk(w io.Writer) error {
fmt.Fprintln(w, "Status check results are [ok]")
return nil
}
func statusCheckResultWasFail(w io.Writer) error {
fmt.Fprintln(w, "Status check results are [FAIL]")
return errors.New("failed status check")
}
func statusCheckResultWasError(w io.Writer) error {
fmt.Fprintln(w, "Status check results are [ERROR]")
return errors.New("error during status check")
}
func init() {
RootCmd.AddCommand(checkCmd)
addControlPlaneNetworkingArgs(checkCmd)
}