From 34ab7a885a6d4ec877a0f74281fcf413660d360c Mon Sep 17 00:00:00 2001 From: Fabian Reinartz Date: Tue, 20 Jun 2017 17:38:01 +0200 Subject: [PATCH] cmd/prometheus: switch to kingpin --- cmd/prometheus/config.go | 90 ++-------------- cmd/prometheus/main.go | 219 ++++++++++++++++----------------------- 2 files changed, 97 insertions(+), 212 deletions(-) diff --git a/cmd/prometheus/config.go b/cmd/prometheus/config.go index 72914334c..f81e8c2bd 100644 --- a/cmd/prometheus/config.go +++ b/cmd/prometheus/config.go @@ -14,34 +14,28 @@ package main import ( - "flag" "fmt" "net" "net/url" "os" "sort" "strings" - "text/template" "time" - "unicode" "github.com/asaskevich/govalidator" + "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/notifier" "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/storage/tsdb" "github.com/prometheus/prometheus/web" - "github.com/spf13/pflag" ) // cfg contains immutable configuration parameters for a running Prometheus // server. It is populated by its flag set. var cfg = struct { - fs *pflag.FlagSet - printVersion bool configFile string @@ -78,20 +72,9 @@ var cfg = struct { }, } -func parse(args []string) error { - err := cfg.fs.Parse(args) - if err != nil || len(cfg.fs.Args()) != 0 { - if err != flag.ErrHelp { - log.Errorf("Invalid command line arguments. Help: %s -h", os.Args[0]) - } - if err == nil { - err = fmt.Errorf("Non-flag argument on command line: %q", cfg.fs.Args()[0]) - } - return err - } - +func validate() error { if err := parsePrometheusURL(); err != nil { - return err + return errors.Wrapf(err, "parse external URL %q", cfg.prometheusURL) } cfg.web.ReadTimeout = time.Duration(cfg.webTimeout) @@ -112,9 +95,7 @@ func parse(args []string) error { cfg.tsdb.MaxBlockDuration = cfg.tsdb.Retention / 10 } - if cfg.lookbackDelta > 0 { - promql.LookbackDelta = time.Duration(cfg.lookbackDelta) - } + promql.LookbackDelta = time.Duration(cfg.lookbackDelta) cfg.queryEngine.Timeout = time.Duration(cfg.queryTimeout) @@ -122,11 +103,13 @@ func parse(args []string) error { } func parsePrometheusURL() error { + fmt.Println("promurl", cfg.prometheusURL) if cfg.prometheusURL == "" { hostname, err := os.Hostname() if err != nil { return err } + fmt.Println("listenaddr", cfg.web.ListenAddress) _, port, err := net.SplitHostPort(cfg.web.ListenAddress) if err != nil { return err @@ -206,67 +189,6 @@ func parseAlertmanagerURLToConfig(us string) (*config.AlertmanagerConfig, error) return acfg, nil } -var helpTmpl = ` -usage: prometheus [] -{{ range $cat, $flags := . }}{{ if ne $cat "." }} == {{ $cat | upper }} =={{ end }} - {{ range $flags }} - -{{ .Name }} {{ .DefValue | quote }} - {{ .Usage | wrap 80 6 }} - {{ end }} -{{ end }} -` - -func usage() { - helpTmpl = strings.TrimSpace(helpTmpl) - t := template.New("usage") - t = t.Funcs(template.FuncMap{ - "wrap": func(width, indent int, s string) (ns string) { - width = width - indent - length := indent - for _, w := range strings.SplitAfter(s, " ") { - if length+len(w) > width { - ns += "\n" + strings.Repeat(" ", indent) - length = 0 - } - ns += w - length += len(w) - } - return strings.TrimSpace(ns) - }, - "quote": func(s string) string { - if len(s) == 0 || s == "false" || s == "true" || unicode.IsDigit(rune(s[0])) { - return s - } - return fmt.Sprintf("%q", s) - }, - "upper": strings.ToUpper, - }) - t = template.Must(t.Parse(helpTmpl)) - - groups := make(map[string][]*pflag.Flag) - - // Bucket flags into groups based on the first of their dot-separated levels. - cfg.fs.VisitAll(func(fl *pflag.Flag) { - parts := strings.SplitN(fl.Name, ".", 2) - if len(parts) == 1 { - groups["."] = append(groups["."], fl) - } else { - name := parts[0] - groups[name] = append(groups[name], fl) - } - }) - for cat, fl := range groups { - if len(fl) < 2 && cat != "." { - groups["."] = append(groups["."], fl...) - delete(groups, cat) - } - } - - if err := t.Execute(os.Stdout, groups); err != nil { - panic(fmt.Errorf("error executing usage template: %s", err)) - } -} - type stringset map[string]struct{} func (ss stringset) Set(s string) error { diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index 81071b1df..7df4f7297 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -19,15 +19,15 @@ import ( _ "net/http/pprof" // Comment this line to disable pprof endpoint. "os" "os/signal" + "path/filepath" "syscall" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/prometheus/common/version" - "github.com/spf13/cobra" - "github.com/spf13/pflag" "golang.org/x/net/context" + "gopkg.in/alecthomas/kingpin.v2" "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/notifier" @@ -39,7 +39,92 @@ import ( ) func main() { - newRootCmd().Execute() + a := kingpin.New(filepath.Base(os.Args[0]), "The Prometheus monitoring server") + + a.Version(version.Print("prometheus")) + + a.HelpFlag.Short('h') + + a.Flag("log.level", + "Only log messages with the given severity or above. One of: [debug, info, warn, error, fatal]"). + Default("info").StringVar(&cfg.logLevel) + + a.Flag("log.format", + `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`). + Default("logger:stderr").StringVar(&cfg.logFormat) + + a.Flag("config.file", "Prometheus configuration file path."). + Default("prometheus.yml").StringVar(&cfg.configFile) + + a.Flag("web.listen-address", "Address to listen on for UI, API, and telemtry."). + Default("0.0.0.0:9090").StringVar(&cfg.web.ListenAddress) + + a.Flag("web.read-timeout", + "Maximum duration before timing out read of the request, and closing idle connections."). + Default("5m").SetValue(&cfg.webTimeout) + + a.Flag("web.max-connections", "Maximum number of simultaneous connections."). + Default("512").IntVar(&cfg.web.MaxConnections) + + a.Flag("web.external-url", + "The URL under which Prometheus is externally reachable (for example, if Prometheus is served via a reverse proxy). Used for generating relative and absolute links back to Prometheus itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Prometheus. If omitted, relevant URL components will be derived automatically."). + PlaceHolder("").StringVar(&cfg.prometheusURL) + + a.Flag("web.route-prefix", + "Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url."). + PlaceHolder("").StringVar(&cfg.web.RoutePrefix) + + a.Flag("web.user-assets", "Path to static asset directory, available at /user."). + PlaceHolder("").StringVar(&cfg.web.UserAssetsPath) + + a.Flag("web.enable-remote-shutdown", "Enable shutdown via HTTP request."). + Default("false").BoolVar(&cfg.web.EnableQuit) + + a.Flag("web.console.templates", "Path to the console template directory, available at /consoles."). + Default("consoles").StringVar(&cfg.web.ConsoleTemplatesPath) + + a.Flag("web.console.libraries", "Path to the console library directory."). + Default("console_libraries").StringVar(&cfg.web.ConsoleLibrariesPath) + + a.Flag("storage.tsdb.path", "Base path for metrics storage."). + Default("data/").StringVar(&cfg.localStoragePath) + + a.Flag("storage.tsdb.min-block-duration", "Minimum duration of a data block before being persisted."). + Default("2h").SetValue(&cfg.tsdb.MinBlockDuration) + + a.Flag("storage.tsdb.max-block-duration", + "Maximum duration compacted blocks may span. (Defaults to 10% of the retention period)"). + PlaceHolder("").SetValue(&cfg.tsdb.MaxBlockDuration) + + a.Flag("storage.tsdb.retention", "How long to retain samples in the storage."). + Default("15d").SetValue(&cfg.tsdb.Retention) + + a.Flag("alertmanager.notification-queue-capacity", "The capacity of the queue for pending alert manager notifications."). + Default("10000").IntVar(&cfg.notifier.QueueCapacity) + + a.Flag("alertmanager.timeout", "Timeout for sending alerts to Alertmanager"). + SetValue(&cfg.notifierTimeout) + + a.Flag("query.lookback-delta", "The delta difference allowed for retrieving metrics during expression evaluations."). + Default("5m").SetValue(&cfg.lookbackDelta) + + a.Flag("query.timeout", "Maximum time a query may take before being aborted."). + SetValue(&cfg.queryTimeout) + + a.Flag("query.max-concurrency", "Maximum number of queries executed concurrently."). + Default("20").IntVar(&cfg.queryEngine.MaxConcurrentQueries) + + if _, err := a.Parse(os.Args[1:]); err != nil { + a.Usage(os.Args[1:]) + os.Exit(2) + } + fmt.Printf("a %+v\n", cfg) + if err := validate(); err != nil { + fmt.Fprintf(os.Stderr, "invalid argument: %s\n", err) + os.Exit(2) + } + + os.Exit(Main(a)) } var ( @@ -59,130 +144,8 @@ func init() { prometheus.MustRegister(version.NewCollector("prometheus")) } -func newRootCmd() *cobra.Command { - rootCmd := &cobra.Command{ - Use: "prometheus", - Short: "prometheus --config.file=prometheus.yaml", - Run: func(cmd *cobra.Command, args []string) { - os.Exit(Main()) - }, - } - - rootCmd.PersistentFlags().BoolVar( - &cfg.printVersion, "version", false, - "Print version information.", - ) - rootCmd.PersistentFlags().StringVar( - &cfg.configFile, "config.file", "prometheus.yml", - "Prometheus configuration file name.", - ) - - // Web. - rootCmd.PersistentFlags().StringVar( - &cfg.web.ListenAddress, "web.listen-address", ":9090", - "Address to listen on for the web interface, API, and telemetry.", - ) - - rootCmd.PersistentFlags().Var( - &cfg.webTimeout, "web.read-timeout", - "Maximum duration before timing out read of the request, and closing idle connections.", - ) - rootCmd.PersistentFlags().IntVar( - &cfg.web.MaxConnections, "web.max-connections", 512, - "Maximum number of simultaneous connections.", - ) - rootCmd.PersistentFlags().StringVar( - &cfg.prometheusURL, "web.external-url", "", - "The URL under which Prometheus is externally reachable (for example, if Prometheus is served via a reverse proxy). Used for generating relative and absolute links back to Prometheus itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Prometheus. If omitted, relevant URL components will be derived automatically.", - ) - rootCmd.PersistentFlags().StringVar( - &cfg.web.RoutePrefix, "web.route-prefix", "", - "Prefix for the internal routes of web endpoints. Defaults to path of -web.external-url.", - ) - rootCmd.PersistentFlags().StringVar( - &cfg.web.UserAssetsPath, "web.user-assets", "", - "Path to static asset directory, available at /user.", - ) - rootCmd.PersistentFlags().BoolVar( - &cfg.web.EnableQuit, "web.enable-remote-shutdown", false, - "Enable remote service shutdown.", - ) - rootCmd.PersistentFlags().StringVar( - &cfg.web.ConsoleTemplatesPath, "web.console.templates", "consoles", - "Path to the console template directory, available at /consoles.", - ) - rootCmd.PersistentFlags().StringVar( - &cfg.web.ConsoleLibrariesPath, "web.console.libraries", "console_libraries", - "Path to the console library directory.", - ) - - // Storage. - rootCmd.PersistentFlags().StringVar( - &cfg.localStoragePath, "storage.tsdb.path", "data", - "Base path for metrics storage.", - ) - rootCmd.PersistentFlags().BoolVar( - &cfg.tsdb.NoLockfile, "storage.tsdb.no-lockfile", false, - "Disable lock file usage.", - ) - rootCmd.PersistentFlags().Var( - &cfg.tsdb.MinBlockDuration, "storage.tsdb.min-block-duration", - "Minimum duration of a data block before being persisted.", - ) - rootCmd.PersistentFlags().Var( - &cfg.tsdb.MaxBlockDuration, "storage.tsdb.max-block-duration", - "Maximum duration compacted blocks may span. (Defaults to 10% of the retention period)", - ) - rootCmd.PersistentFlags().Var( - &cfg.tsdb.Retention, "storage.tsdb.retention", - "How long to retain samples in the storage.", - ) - - // Alertmanager. - rootCmd.PersistentFlags().IntVar( - &cfg.notifier.QueueCapacity, "alertmanager.notification-queue-capacity", 10000, - "The capacity of the queue for pending alert manager notifications.", - ) - rootCmd.PersistentFlags().Var( - &cfg.notifierTimeout, "alertmanager.timeout", - "Alert manager HTTP API timeout.", - ) - - // Query engine. - rootCmd.PersistentFlags().Var( - &cfg.lookbackDelta, "query.lookback-delta", - "The delta difference allowed for retrieving metrics during expression evaluations.", - ) - rootCmd.PersistentFlags().Var( - &cfg.queryTimeout, "query.timeout", - "Maximum time a query may take before being aborted.", - ) - rootCmd.PersistentFlags().IntVar( - &cfg.queryEngine.MaxConcurrentQueries, "query.max-concurrency", 20, - "Maximum number of queries executed concurrently.", - ) - - // Logging. - rootCmd.PersistentFlags().StringVar( - &cfg.logLevel, "log.level", "info", - "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]", - ) - rootCmd.PersistentFlags().StringVar( - &cfg.logFormat, "log.format", "logger:stderr", - `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`, - ) - - cfg.fs = rootCmd.PersistentFlags() - return rootCmd -} - // Main manages the stup and shutdown lifecycle of the entire Prometheus server. -func Main() int { - if err := parse(os.Args[1:]); err != nil { - log.Error(err) - return 2 - } - +func Main(a *kingpin.Application) int { logger := log.NewLogger(os.Stdout) logger.SetLevel(cfg.logLevel) logger.SetFormat(cfg.logFormat) @@ -252,9 +215,9 @@ func Main() int { } cfg.web.Flags = map[string]string{} - cfg.fs.VisitAll(func(f *pflag.Flag) { + for _, f := range a.Model().Flags { cfg.web.Flags[f.Name] = f.Value.String() - }) + } webHandler := web.New(&cfg.web)