From e3701077c3e487e40a27028ea4636d4046c38084 Mon Sep 17 00:00:00 2001 From: Goutham Veeramachaneni Date: Wed, 21 Jun 2017 17:02:04 +0530 Subject: [PATCH] Move promtool to kingpin Signed-off-by: Goutham Veeramachaneni --- cmd/promtool/main.go | 190 ++++++++++++++++++------------------------- util/cli/cli.go | 176 --------------------------------------- 2 files changed, 79 insertions(+), 287 deletions(-) delete mode 100644 util/cli/cli.go diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go index 2af10249cc..325f62162e 100644 --- a/cmd/promtool/main.go +++ b/cmd/promtool/main.go @@ -20,6 +20,7 @@ import ( "path/filepath" "strings" + kingpin "gopkg.in/alecthomas/kingpin.v2" yaml "gopkg.in/yaml.v2" "github.com/prometheus/common/model" @@ -27,36 +28,73 @@ import ( "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/pkg/rulefmt" "github.com/prometheus/prometheus/promql" - "github.com/prometheus/prometheus/util/cli" "github.com/prometheus/prometheus/util/promlint" ) -// CheckConfigCmd validates configuration files. -func CheckConfigCmd(t cli.Term, args ...string) int { - if len(args) == 0 { - t.Infof("usage: promtool check-config ") - return 2 +func main() { + app := kingpin.New(filepath.Base(os.Args[0]), "Tooling for the Prometheus monitoring system.") + app.Version(version.Print("promtool")) + app.HelpFlag.Short('h') + + checkCmd := app.Command("check", "Check the resources for validity.") + + checkConfigCmd := checkCmd.Command("config", "Check if the config files are valid or not.") + configFiles := checkConfigCmd.Arg( + "config-files", + "The config files to check.", + ).Required().ExistingFiles() + + checkRulesCmd := checkCmd.Command("rules", "Check if the rule files are valid or not.") + ruleFiles := checkRulesCmd.Arg( + "rule-files", + "The rule files to check.", + ).Required().ExistingFiles() + + checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage) + + updateCmd := app.Command("update", "Update the resources to newer formats.") + updateRulesCmd := updateCmd.Command("rules", "Update rules from the 1.x to 2.x format.") + ruleFilesUp := updateRulesCmd.Arg("rule-files", "The rule files to update.").Required().ExistingFiles() + + switch kingpin.MustParse(app.Parse(os.Args[1:])) { + case checkConfigCmd.FullCommand(): + os.Exit(CheckConfig(*configFiles...)) + + case checkRulesCmd.FullCommand(): + os.Exit(CheckRules(*ruleFiles...)) + + case checkMetricsCmd.FullCommand(): + os.Exit(CheckMetrics()) + + case updateRulesCmd.FullCommand(): + os.Exit(UpdateRules(*ruleFilesUp...)) + } + +} + +// CheckConfig validates configuration files. +func CheckConfig(files ...string) int { failed := false - for _, arg := range args { - ruleFiles, err := checkConfig(t, arg) + for _, f := range files { + ruleFiles, err := checkConfig(f) if err != nil { - t.Errorf(" FAILED: %s", err) + fmt.Fprintln(os.Stderr, " FAILED:", err) failed = true } else { - t.Infof(" SUCCESS: %d rule files found", len(ruleFiles)) + fmt.Printf(" SUCCESS: %d rule files found\n", len(ruleFiles)) } - t.Infof("") + fmt.Println() for _, rf := range ruleFiles { - if n, err := checkRules(t, rf); err != nil { - t.Errorf(" FAILED: %s", err) + if n, err := checkRules(rf); err != nil { + fmt.Fprintln(os.Stderr, " FAILED:", err) failed = true } else { - t.Infof(" SUCCESS: %d rules found", n) + fmt.Printf(" SUCCESS: %d rules found\n", n) } - t.Infof("") + fmt.Println() } } if failed { @@ -74,14 +112,8 @@ func checkFileExists(fn string) error { return err } -func checkConfig(t cli.Term, filename string) ([]string, error) { - t.Infof("Checking %s", filename) - - if stat, err := os.Stat(filename); err != nil { - return nil, fmt.Errorf("cannot get file info") - } else if stat.IsDir() { - return nil, fmt.Errorf("is a directory") - } +func checkConfig(filename string) ([]string, error) { + fmt.Println("Checking", filename) cfg, err := config.LoadFile(filename) if err != nil { @@ -143,25 +175,21 @@ func checkTLSConfig(tlsConfig config.TLSConfig) error { return nil } -// CheckRulesCmd validates rule files. -func CheckRulesCmd(t cli.Term, args ...string) int { - if len(args) == 0 { - t.Infof("usage: promtool check-rules ") - return 2 - } +// CheckRules validates rule files. +func CheckRules(files ...string) int { failed := false - for _, arg := range args { - if n, errs := checkRules(t, arg); errs != nil { - t.Errorf(" FAILED:") + for _, f := range files { + if n, errs := checkRules(f); errs != nil { + fmt.Fprintln(os.Stderr, " FAILED:") for _, e := range errs { - t.Errorf(e.Error()) + fmt.Fprintln(os.Stderr, e.Error()) } failed = true } else { - t.Infof(" SUCCESS: %d rules found", n) + fmt.Printf(" SUCCESS: %d rules found\n", n) } - t.Infof("") + fmt.Println() } if failed { return 1 @@ -169,14 +197,8 @@ func CheckRulesCmd(t cli.Term, args ...string) int { return 0 } -func checkRules(t cli.Term, filename string) (int, []error) { - t.Infof("Checking %s", filename) - - if stat, err := os.Stat(filename); err != nil { - return 0, []error{fmt.Errorf("cannot get file info")} - } else if stat.IsDir() { - return 0, []error{fmt.Errorf("is a directory")} - } +func checkRules(filename string) (int, []error) { + fmt.Println("Checking", filename) rgs, errs := rulefmt.ParseFile(filename) if errs != nil { @@ -191,17 +213,13 @@ func checkRules(t cli.Term, filename string) (int, []error) { return numRules, nil } -// UpdateRulesCmd updates the rule files. -func UpdateRulesCmd(t cli.Term, args ...string) int { - if len(args) == 0 { - t.Infof("usage: promtool update-rules ") - return 2 - } +// UpdateRules updates the rule files. +func UpdateRules(files ...string) int { failed := false - for _, arg := range args { - if err := updateRules(t, arg); err != nil { - t.Errorf(" FAILED: %s", err) + for _, f := range files { + if err := updateRules(f); err != nil { + fmt.Fprintln(os.Stderr, " FAILED:", err) failed = true } } @@ -212,14 +230,8 @@ func UpdateRulesCmd(t cli.Term, args ...string) int { return 0 } -func updateRules(t cli.Term, filename string) error { - t.Infof("Updating %s", filename) - - if stat, err := os.Stat(filename); err != nil { - return fmt.Errorf("cannot get file info") - } else if stat.IsDir() { - return fmt.Errorf("is a directory") - } +func updateRules(filename string) error { + fmt.Println("Updating", filename) content, err := ioutil.ReadFile(filename) if err != nil { @@ -270,32 +282,26 @@ func updateRules(t cli.Term, filename string) error { } var checkMetricsUsage = strings.TrimSpace(` -usage: promtool check-metrics - Pass Prometheus metrics over stdin to lint them for consistency and correctness. examples: -$ cat metrics.prom | promtool check-metrics -$ curl -s http://localhost:9090/metrics | promtool check-metrics +$ cat metrics.prom | promtool check metrics + +$ curl -s http://localhost:9090/metrics | promtool check metrics `) -// CheckMetricsCmd performs a linting pass on input metrics. -func CheckMetricsCmd(t cli.Term, args ...string) int { - if len(args) != 0 { - t.Infof(checkMetricsUsage) - return 2 - } - +// CheckMetrics performs a linting pass on input metrics. +func CheckMetrics() int { l := promlint.New(os.Stdin) problems, err := l.Lint() if err != nil { - t.Errorf("error while linting: %v", err) + fmt.Fprintln(os.Stderr, "error while linting:", err) return 1 } for _, p := range problems { - t.Errorf("%s: %s", p.Metric, p.Text) + fmt.Fprintln(os.Stderr, p.Metric, p.Text) } if len(problems) > 0 { @@ -304,41 +310,3 @@ func CheckMetricsCmd(t cli.Term, args ...string) int { return 0 } - -// VersionCmd prints the binaries version information. -func VersionCmd(t cli.Term, _ ...string) int { - fmt.Fprintln(os.Stdout, version.Print("promtool")) - return 0 -} - -func main() { - app := cli.NewApp("promtool") - - app.Register("check-config", &cli.Command{ - Desc: "validate configuration files for correctness", - Run: CheckConfigCmd, - }) - - app.Register("check-rules", &cli.Command{ - Desc: "validate rule files for correctness", - Run: CheckRulesCmd, - }) - - app.Register("check-metrics", &cli.Command{ - Desc: "validate metrics for correctness", - Run: CheckMetricsCmd, - }) - - app.Register("update-rules", &cli.Command{ - Desc: "update rules to the new YAML format", - Run: UpdateRulesCmd, - }) - - app.Register("version", &cli.Command{ - Desc: "print the version of this binary", - Run: VersionCmd, - }) - - t := cli.BasicTerm(os.Stdout, os.Stderr) - os.Exit(app.Run(t, os.Args[1:]...)) -} diff --git a/util/cli/cli.go b/util/cli/cli.go deleted file mode 100644 index 5df6dce637..0000000000 --- a/util/cli/cli.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2015 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cli - -import ( - "bytes" - "fmt" - "io" - "sort" - "strings" - "text/template" -) - -// Command represents a single command within an application. -type Command struct { - Desc string - Run func(t Term, args ...string) int -} - -// Term handles an application's output. -type Term interface { - Infof(format string, v ...interface{}) - Errorf(format string, v ...interface{}) - Out() io.Writer - Err() io.Writer -} - -type basicTerm struct { - out, err io.Writer -} - -// Infof implements Term. -func (t *basicTerm) Infof(format string, v ...interface{}) { - fmt.Fprintf(t.err, format, v...) - fmt.Fprint(t.err, "\n") -} - -// Errorf implements Term. -func (t *basicTerm) Errorf(format string, v ...interface{}) { - fmt.Fprintf(t.err, format, v...) - fmt.Fprint(t.err, "\n") -} - -// Out implements Term. -func (t *basicTerm) Out() io.Writer { - return t.out -} - -// Err implements Term. -func (t *basicTerm) Err() io.Writer { - return t.err -} - -// BasicTerm returns a Term writing Infof and Errorf to err and Out to out. -func BasicTerm(out, err io.Writer) Term { - return &basicTerm{out: out, err: err} -} - -// App represents an application that may consist of multiple commands. -type App struct { - Name string - Help func() string - - commands map[string]*Command -} - -// NewApp creates a new application with a pre-registered help command. -func NewApp(name string) *App { - app := &App{ - Name: name, - commands: map[string]*Command{}, - } - app.Register("help", &Command{ - Desc: "prints this help text", - Run: func(t Term, _ ...string) int { - help := app.Help - if help == nil { - help = BasicHelp(app, tmpl) - } - t.Infof(help() + "\n") - return 0 - }, - }) - return app -} - -// Register adds a new command to the application. -func (app *App) Register(name string, cmd *Command) { - name = strings.TrimSpace(name) - if name == "" { - panic("command name must not be empty") - } - if _, ok := app.commands[name]; ok { - panic("command cannot be registered twice") - } - app.commands[name] = cmd -} - -// Run the application with the given arguments. Output is sent to t. -func (app *App) Run(t Term, args ...string) int { - help := app.commands["help"] - - if len(args) == 0 || strings.HasPrefix(args[0], "-") { - help.Run(t) - return 2 - } - cmd, ok := app.commands[args[0]] - if !ok { - help.Run(t) - return 2 - } - - return cmd.Run(t, args[1:]...) -} - -var tmpl = ` -usage: {{ .Name }} [] - -Available commands: - {{ range .Commands }}{{ .Name }} {{ .Desc }} - {{ end }} -` - -// BasicHelp returns a function that creates a basic help text for the application -// with its commands. -func BasicHelp(app *App, ts string) func() string { - t := template.Must(template.New("help").Parse(ts)) - - return func() string { - type command struct { - Name, Desc string - } - cmds := []command{} - - var maxLen int - names := []string{} - for name := range app.commands { - names = append(names, name) - if len(name) > maxLen { - maxLen = len(name) - } - } - sort.Strings(names) - - for _, name := range names { - cmds = append(cmds, command{ - Name: name + strings.Repeat(" ", maxLen-len(name)), - Desc: app.commands[name].Desc, - }) - } - - var buf bytes.Buffer - err := t.Execute(&buf, struct { - Name string - Commands []command - }{ - Name: app.Name, - Commands: cmds, - }) - if err != nil { - panic(fmt.Errorf("error executing help template: %s", err)) - } - return strings.TrimSpace(buf.String()) - } -}