diff --git a/cmd/profilecli/main.go b/cmd/profilecli/main.go index 25a72e20c9..f88f69424c 100644 --- a/cmd/profilecli/main.go +++ b/cmd/profilecli/main.go @@ -89,6 +89,9 @@ func main() { bucketWebCmd := bucketCmd.Command("web", "Run the web tool for visualizing blocks in object-store buckets.") bucketWebParams := addBucketWebToolParams(bucketWebCmd) + readyCmd := app.Command("ready", "Check Pyroscope health.") + readyParams := addReadyParams(readyCmd) + // parse command line arguments parsedCmd := kingpin.MustParse(app.Parse(os.Args[1:])) @@ -151,17 +154,26 @@ func main() { if err := blocksCompact(ctx, cfg.blocks.compact.src, cfg.blocks.compact.dst, cfg.blocks.compact.shards); err != nil { os.Exit(checkError(err)) } + case readyCmd.FullCommand(): + if err := ready(ctx, readyParams); err != nil { + os.Exit(checkError(err)) + } default: level.Error(logger).Log("msg", "unknown command", "cmd", parsedCmd) } } func checkError(err error) int { - if err != nil { + switch err { + case nil: + return 0 + case notReadyErr: + // The reason for the failed ready is already logged, so just exit with + // an error code. + default: fmt.Fprintf(os.Stderr, "error: %v\n", err) - return 1 } - return 0 + return 1 } type contextKey uint8 diff --git a/cmd/profilecli/ready.go b/cmd/profilecli/ready.go new file mode 100644 index 0000000000..d4df70d788 --- /dev/null +++ b/cmd/profilecli/ready.go @@ -0,0 +1,55 @@ +package main + +import ( + "context" + "fmt" + "io" + "net/http" + + "github.com/go-kit/log/level" + "github.com/pkg/errors" +) + +var ( + // Occurs when Pyroscope is not ready. + notReadyErr = errors.New("not ready") +) + +type readyParams struct { + *phlareClient +} + +func addReadyParams(cmd commander) *readyParams { + params := &readyParams{} + params.phlareClient = addPhlareClient(cmd) + + return params +} + +func ready(ctx context.Context, params *readyParams) error { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/ready", params.URL), nil) + if err != nil { + return err + } + req = req.WithContext(ctx) + + client := params.phlareClient.httpClient() + res, err := client.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + bytes, err := io.ReadAll(res.Body) + if err != nil { + return err + } + + if res.StatusCode != http.StatusOK { + level.Error(logger).Log("msg", "not ready", "status", res.Status, "body", string(bytes)) + return notReadyErr + } + + level.Info(logger).Log("msg", "ready") + return nil +}