diff --git a/src/pixie_cli/pkg/cmd/deploy.go b/src/pixie_cli/pkg/cmd/deploy.go index 2515b37e41e..ee244b2dad2 100644 --- a/src/pixie_cli/pkg/cmd/deploy.go +++ b/src/pixie_cli/pkg/cmd/deploy.go @@ -620,7 +620,7 @@ func waitForHealthCheckTaskGenerator(cloudAddr string, clusterID uuid.UUID) func } // The health check warning error indicates the cluster successfully deployed, but there are some warnings. // Return the error to end the polling and show the warnings. - if _, ok := err.(*vizier.HealthCheckWarning); ok { + if _, ok := err.(*components.UIWarning); ok { return err } time.Sleep(5 * time.Second) @@ -642,15 +642,13 @@ func waitForHealthCheck(cloudAddr string, clusterID uuid.UUID, clientset *kubern hc := utils.NewSerialTaskRunner(healthCheckJobs) err := hc.RunAndMonitor() if err != nil { - if _, ok := err.(*vizier.HealthCheckWarning); ok { - utils.WithError(err).Error("Pixie healthcheck detected the following warnings:") - } else { - _ = pxanalytics.Client().Enqueue(&analytics.Track{ - UserId: pxconfig.Cfg().UniqueClientID, - Event: "Deploy Healthcheck Failed", - Properties: analytics.NewProperties(). - Set("err", err.Error()), - }) + _ = pxanalytics.Client().Enqueue(&analytics.Track{ + UserId: pxconfig.Cfg().UniqueClientID, + Event: "Deploy Healthcheck Failed", + Properties: analytics.NewProperties(). + Set("err", err.Error()), + }) + if _, ok := err.(*components.UIWarning); !ok { utils.WithError(err).Fatal("Failed Pixie healthcheck") } } diff --git a/src/pixie_cli/pkg/components/spinner.go b/src/pixie_cli/pkg/components/spinner.go index 2c5efde3ffd..a82d814ef8c 100644 --- a/src/pixie_cli/pkg/components/spinner.go +++ b/src/pixie_cli/pkg/components/spinner.go @@ -108,6 +108,9 @@ func (d *statusDecorator) Decor(stat *decor.Statistics) string { return "" } if d.err != nil { + if _, ok := d.err.(*UIWarning); ok { + return StatusWarn(d.width) + } return StatusErr(d.width) } return StatusOK(d.width) @@ -136,6 +139,9 @@ func (d *errorViewDecorator) Decor(stat *decor.Statistics) string { if d.err == nil { return "" } + if _, ok := d.err.(*UIWarning); ok { + return color.YellowString(fmt.Sprintf(" WARN: %s", d.err.Error())) + } return color.RedString(fmt.Sprintf(" ERR: %s", d.err.Error())) } diff --git a/src/pixie_cli/pkg/components/status.go b/src/pixie_cli/pkg/components/status.go index ad08c1e3dab..82b86ab9416 100644 --- a/src/pixie_cli/pkg/components/status.go +++ b/src/pixie_cli/pkg/components/status.go @@ -25,10 +25,26 @@ import ( ) var ( - statusOK = "\u2714" - statusErr = "\u2715" + statusOK = "\u2714" + statusErr = "\u2715" + statusWarn = "\u26a0" ) +// UIWarning is a wrapper type that marks errors as warnings for UI display. +// Errors wrapped in UIWarning will be displayed with yellow warning colors +// instead of red error colors in the spinner table. +type UIWarning struct { + Err error +} + +func (w UIWarning) Error() string { + return w.Err.Error() +} + +func (w UIWarning) Unwrap() error { + return w.Err +} + func computePadding(s string, pad int) (int, int) { var padS, padE int pad -= len([]rune(s)) @@ -55,3 +71,9 @@ func StatusErr(pad int) string { padS, padE := computePadding(statusErr, pad) return strings.Repeat(" ", padS) + color.RedString(statusErr) + strings.Repeat(" ", padE) } + +// StatusWarn prints out the default Warn symbol, center padded to the size specified. +func StatusWarn(pad int) string { + padS, padE := computePadding(statusWarn, pad) + return strings.Repeat(" ", padS) + color.YellowString(statusWarn) + strings.Repeat(" ", padE) +} diff --git a/src/pixie_cli/pkg/vizier/logs.go b/src/pixie_cli/pkg/vizier/logs.go index d5691be25ed..789e1030e5c 100644 --- a/src/pixie_cli/pkg/vizier/logs.go +++ b/src/pixie_cli/pkg/vizier/logs.go @@ -30,6 +30,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "px.dev/pixie/src/pixie_cli/pkg/components" "px.dev/pixie/src/utils/script" "px.dev/pixie/src/utils/shared/k8s" ) @@ -132,7 +133,7 @@ func (c *LogCollector) CollectPixieLogs(fName string) error { outputCh, err := RunSimpleHealthCheckScript(c.br, c.cloudAddr, clusterID) if err != nil { entry := log.WithError(err) - if _, ok := err.(*HealthCheckWarning); ok { + if _, ok := err.(*components.UIWarning); ok { entry.Warn("healthcheck script detected the following warnings:") } else { entry.Warn("failed to run healthcheck script") diff --git a/src/pixie_cli/pkg/vizier/script.go b/src/pixie_cli/pkg/vizier/script.go index cab9fcd3988..382ba4617ab 100644 --- a/src/pixie_cli/pkg/vizier/script.go +++ b/src/pixie_cli/pkg/vizier/script.go @@ -44,8 +44,10 @@ import ( ) const ( - equalityThreshold = 0.01 - headersInstalledPercColumn = "headers_installed_percent" // must match the column in px/agent_status_diagnostics + equalityThreshold = 0.01 + headersInstalledPercColumn = "headers_installed_percent" // must match the column in px/agent_status_diagnostics + missingKernelHeadersMsg = "Detected missing kernel headers on your cluster's nodes. This may cause issues with the Pixie agent. Please see https://px.dev/kernel-headers for more details." + missingKernelHeadersSuspectedMsg = "Detected missing kernel headers on your cluster's nodes. This may cause issues with the Pixie agent. Please see https://px.dev/kernel-headers for more details. If you are using a distro kernel, this is expected and can be ignored." ) type taskWrapper struct { @@ -266,18 +268,6 @@ func RunScript(ctx context.Context, conns []*Connector, execScript *script.Execu return mergedResponses, nil } -type HealthCheckWarning struct { - message string -} - -func (h *HealthCheckWarning) Error() string { - return h.message -} - -func newHealthCheckWarning(message string) error { - return &HealthCheckWarning{message} -} - func evaluateHealthCheckResult(output string) error { jsonData := make(map[string]interface{}) @@ -289,12 +279,11 @@ func evaluateHealthCheckResult(output string) error { switch t := v.(type) { case float64: if math.Abs(1.0-t) > equalityThreshold { - msg := "Detected missing kernel headers on your cluster's nodes. This may cause issues with the Pixie agent. Please install kernel headers on all nodes." - return newHealthCheckWarning(msg) + return &components.UIWarning{Err: fmt.Errorf("%s", missingKernelHeadersMsg)} } } } else { - return newHealthCheckWarning("Unable to detect if the cluster's nodes have the distro kernel headers installed (vizier too old to perform this check). Please ensure that the kernel headers are installed on all nodes.") + return &components.UIWarning{Err: fmt.Errorf("%s", missingKernelHeadersSuspectedMsg)} } return nil }