diff --git a/cmd/influxd/launcher/launcher.go b/cmd/influxd/launcher/launcher.go index dab7bb6af12..9a486af3185 100644 --- a/cmd/influxd/launcher/launcher.go +++ b/cmd/influxd/launcher/launcher.go @@ -14,8 +14,11 @@ import ( "github.com/influxdata/flux/control" "github.com/influxdata/flux/execute" + "github.com/influxdata/influxdb/kit/signals" + "github.com/influxdata/influxdb/telemetry" "github.com/opentracing/opentracing-go" "github.com/prometheus/client_golang/prometheus" + "github.com/spf13/cobra" jaegerconfig "github.com/uber/jaeger-client-go/config" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -65,6 +68,128 @@ const ( JaegerTracing = "jaeger" ) +func NewCommand() *cobra.Command { + l := NewLauncher() + cmd := &cobra.Command{ + Use: "run", + Short: "Start the influxd server (default)", + Run: func(cmd *cobra.Command, args []string) { + // exit with SIGINT and SIGTERM + ctx := context.Background() + ctx = signals.WithStandardSignals(ctx) + + // m.SetBuild(version, commit, date) + if err := l.run(ctx); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } else if !l.Running() { + os.Exit(1) + } + + var wg sync.WaitGroup + if !l.ReportingDisabled() { + reporter := telemetry.NewReporter(l.Registry()) + reporter.Interval = 8 * time.Hour + reporter.Logger = l.Logger() + wg.Add(1) + go func() { + defer wg.Done() + reporter.Report(ctx) + }() + } + + <-ctx.Done() + + // Attempt clean shutdown. + ctx, cancel := context.WithTimeout(ctx, 2*time.Second) + defer cancel() + l.Shutdown(ctx) + wg.Wait() + }, + } + + buildLauncherCommand(l, cmd) + + return cmd +} + +func buildLauncherCommand(l *Launcher, cmd *cobra.Command) { + dir, err := fs.InfluxDir() + if err != nil { + panic(fmt.Errorf("failed to determine influx directory: %v", err)) + } + + opts := []cli.Opt{ + { + DestP: &l.logLevel, + Flag: "log-level", + Default: "info", + Desc: "supported log levels are debug, info, and error", + }, + { + DestP: &l.tracingType, + Flag: "tracing-type", + Default: "", + Desc: fmt.Sprintf("supported tracing types are %s, %s", LogTracing, JaegerTracing), + }, + { + DestP: &l.httpBindAddress, + Flag: "http-bind-address", + Default: ":9999", + Desc: "bind address for the REST HTTP API", + }, + { + DestP: &l.boltPath, + Flag: "bolt-path", + Default: filepath.Join(dir, "influxd.bolt"), + Desc: "path to boltdb database", + }, + { + DestP: &l.assetsPath, + Flag: "assets-path", + Desc: "override default assets by serving from a specific directory (developer mode)", + }, + { + DestP: &l.storeType, + Flag: "store", + Default: "bolt", + Desc: "backing store for REST resources (bolt or memory)", + }, + { + DestP: &l.testing, + Flag: "e2e-testing", + Default: false, + Desc: "add /debug/flush endpoint to clear stores; used for end-to-end tests", + }, + { + DestP: &l.enginePath, + Flag: "engine-path", + Default: filepath.Join(dir, "engine"), + Desc: "path to persistent engine files", + }, + { + DestP: &l.secretStore, + Flag: "secret-store", + Default: "bolt", + Desc: "data store for secrets (bolt or vault)", + }, + { + DestP: &l.protosPath, + Flag: "protos-path", + Default: filepath.Join(dir, "protos"), + Desc: "path to protos on the filesystem", + }, + { + DestP: &l.reportingDisabled, + Flag: "reporting-disabled", + Default: false, + Desc: "disable sending telemetry data to https://telemetry.influxdata.com every 8 hours", + }, + } + + cli.BindOptions(cmd, opts) +} + // Launcher represents the main program execution. type Launcher struct { wg sync.WaitGroup @@ -103,9 +228,6 @@ type Launcher struct { logger *zap.Logger reg *prom.Registry - // BuildInfo contains commit, version and such of influxdb. - BuildInfo platform.BuildInfo - Stdin io.Reader Stdout io.Writer Stderr io.Writer @@ -141,13 +263,6 @@ func (m *Launcher) Logger() *zap.Logger { return m.logger } -// SetBuild adds version, commit, and date to prometheus metrics. -func (m *Launcher) SetBuild(version, commit, date string) { - m.BuildInfo.Version = version - m.BuildInfo.Commit = commit - m.BuildInfo.Date = date -} - // URL returns the URL to connect to the HTTP server. func (m *Launcher) URL() string { return fmt.Sprintf("http://127.0.0.1:%d", m.httpPort) @@ -200,84 +315,16 @@ func (m *Launcher) Cancel() { m.cancel() } // Run executes the program with the given CLI arguments. func (m *Launcher) Run(ctx context.Context, args ...string) error { - dir, err := fs.InfluxDir() - if err != nil { - return fmt.Errorf("failed to determine influx directory: %v", err) - } - - prog := &cli.Program{ - Name: "influxd", - Run: func() error { return m.run(ctx) }, - Opts: []cli.Opt{ - { - DestP: &m.logLevel, - Flag: "log-level", - Default: "info", - Desc: "supported log levels are debug, info, and error", - }, - { - DestP: &m.tracingType, - Flag: "tracing-type", - Default: "", - Desc: fmt.Sprintf("supported tracing types are %s, %s", LogTracing, JaegerTracing), - }, - { - DestP: &m.httpBindAddress, - Flag: "http-bind-address", - Default: ":9999", - Desc: "bind address for the REST HTTP API", - }, - { - DestP: &m.boltPath, - Flag: "bolt-path", - Default: filepath.Join(dir, "influxd.bolt"), - Desc: "path to boltdb database", - }, - { - DestP: &m.assetsPath, - Flag: "assets-path", - Desc: "override default assets by serving from a specific directory (developer mode)", - }, - { - DestP: &m.storeType, - Flag: "store", - Default: "bolt", - Desc: "backing store for REST resources (bolt or memory)", - }, - { - DestP: &m.testing, - Flag: "e2e-testing", - Default: false, - Desc: "add /debug/flush endpoint to clear stores; used for end-to-end tests", - }, - { - DestP: &m.enginePath, - Flag: "engine-path", - Default: filepath.Join(dir, "engine"), - Desc: "path to persistent engine files", - }, - { - DestP: &m.secretStore, - Flag: "secret-store", - Default: "bolt", - Desc: "data store for secrets (bolt or vault)", - }, - { - DestP: &m.protosPath, - Flag: "protos-path", - Default: filepath.Join(dir, "protos"), - Desc: "path to protos on the filesystem", - }, - { - DestP: &m.reportingDisabled, - Flag: "reporting-disabled", - Default: false, - Desc: "disable sending telemetry data to https://telemetry.influxdata.com every 8 hours", - }, + cmd := &cobra.Command{ + Use: "run", + Short: "Start the influxd server (default)", + RunE: func(cmd *cobra.Command, args []string) error { + return m.run(ctx) }, } - cmd := cli.NewCommand(prog) + buildLauncherCommand(m, cmd) + cmd.SetArgs(args) return cmd.Execute() } @@ -304,10 +351,11 @@ func (m *Launcher) run(ctx context.Context) (err error) { return err } + info := platform.GetBuildInfo() m.logger.Info("Welcome to InfluxDB", - zap.String("version", m.BuildInfo.Version), - zap.String("commit", m.BuildInfo.Commit), - zap.String("build_date", m.BuildInfo.Date), + zap.String("version", info.Version), + zap.String("commit", info.Commit), + zap.String("build_date", info.Date), ) switch m.tracingType { @@ -373,7 +421,7 @@ func (m *Launcher) run(ctx context.Context) (err error) { m.reg = prom.NewRegistry() m.reg.MustRegister( prometheus.NewGoCollector(), - infprom.NewInfluxCollector(m.boltClient, m.BuildInfo), + infprom.NewInfluxCollector(m.boltClient, info), ) m.reg.WithLogger(m.logger) m.reg.MustRegister(m.boltClient) diff --git a/cmd/influxd/main.go b/cmd/influxd/main.go index da8fb687b5a..f058e0838d8 100644 --- a/cmd/influxd/main.go +++ b/cmd/influxd/main.go @@ -1,19 +1,17 @@ package main import ( - "context" - "fmt" _ "net/http/pprof" "os" - "sync" - "time" + "strings" + "github.com/influxdata/influxdb" "github.com/influxdata/influxdb/cmd/influxd/launcher" - "github.com/influxdata/influxdb/kit/signals" _ "github.com/influxdata/influxdb/query/builtin" - "github.com/influxdata/influxdb/telemetry" _ "github.com/influxdata/influxdb/tsdb/tsi1" _ "github.com/influxdata/influxdb/tsdb/tsm1" + "github.com/spf13/cobra" + "github.com/spf13/viper" ) var ( @@ -22,37 +20,36 @@ var ( date = "unknown" ) -func main() { - // exit with SIGINT and SIGTERM - ctx := context.Background() - ctx = signals.WithStandardSignals(ctx) - - m := launcher.NewLauncher() - m.SetBuild(version, commit, date) - if err := m.Run(ctx, os.Args[1:]...); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } else if !m.Running() { - os.Exit(1) - } +var rootCmd = &cobra.Command{ + Use: "influxd", + Short: "Influx Server", +} - var wg sync.WaitGroup - if !m.ReportingDisabled() { - reporter := telemetry.NewReporter(m.Registry()) - reporter.Interval = 8 * time.Hour - reporter.Logger = m.Logger() - wg.Add(1) - go func() { - defer wg.Done() - reporter.Report(ctx) - }() +func init() { + influxdb.SetBuildInfo(version, commit, date) + viper.SetEnvPrefix("INFLUXD") + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + rootCmd.InitDefaultHelpCmd() + rootCmd.AddCommand(launcher.NewCommand()) +} + +// find determines the default behavior when running influxd. +// Specifically, find will return the influxd run command if no sub-command +// was specified. +func find(args []string) *cobra.Command { + cmd, _, err := rootCmd.Find(args) + if err == nil && cmd == rootCmd { + // Execute the run command if no sub-command is specified + return launcher.NewCommand() } - <-ctx.Done() + return rootCmd +} - // Attempt clean shutdown. - ctx, cancel := context.WithTimeout(ctx, 2*time.Second) - defer cancel() - m.Shutdown(ctx) - wg.Wait() +func main() { + cmd := find(os.Args[1:]) + if err := cmd.Execute(); err != nil { + os.Exit(1) + } } diff --git a/go.sum b/go.sum index e57640ed5af..ac7bd6c8339 100644 --- a/go.sum +++ b/go.sum @@ -157,9 +157,6 @@ github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/goreleaser/goreleaser v0.94.0/go.mod h1:OjbYR2NhOI6AEUWCowMSBzo9nP1aRif3sYtx+rhp+Zo= -github.com/goreleaser/goreleaser v0.97.0 h1:2/GZKg0cLk5HgIiiSaW/lbDfj0T/GMgF6qEFexy0Vfs= -github.com/goreleaser/goreleaser v0.97.0/go.mod h1:MnjA0e0Uq6ISqjG1WxxMAl+3VS1QYjILSWVnMYDxasE= github.com/goreleaser/nfpm v0.9.7 h1:h8RQMDztu6cW7b0/s4PGbdeMYykAbJG0UMXaWG5uBMI= github.com/goreleaser/nfpm v0.9.7/go.mod h1:F2yzin6cBAL9gb+mSiReuXdsfTrOQwDMsuSpULof+y4= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= diff --git a/kit/cli/viper.go b/kit/cli/viper.go index 4314ae6efdc..00c39c144ea 100644 --- a/kit/cli/viper.go +++ b/kit/cli/viper.go @@ -3,10 +3,10 @@ package cli import ( "fmt" "strings" + "time" "github.com/spf13/cobra" "github.com/spf13/viper" - "time" ) // Opt is a single command-line option @@ -57,49 +57,66 @@ func NewCommand(p *Program) *cobra.Command { // This normalizes "-" to an underscore in env names. viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - for _, o := range p.Opts { - switch o.DestP.(type) { + BindOptions(cmd, p.Opts) + + return cmd +} + +// BindOptions adds opts to the specified command and automatically +// registers those options with viper. +func BindOptions(cmd *cobra.Command, opts []Opt) { + for _, o := range opts { + switch destP := o.DestP.(type) { case *string: - if o.Default == nil { - o.Default = "" + var d string + if o.Default != nil { + d = o.Default.(string) } - cmd.Flags().StringVar(o.DestP.(*string), o.Flag, o.Default.(string), o.Desc) - viper.BindPFlag(o.Flag, cmd.Flags().Lookup(o.Flag)) - *o.DestP.(*string) = viper.GetString(o.Flag) + cmd.Flags().StringVar(destP, o.Flag, d, o.Desc) + mustBindPFlag(o.Flag, cmd) + *destP = viper.GetString(o.Flag) case *int: - if o.Default == nil { - o.Default = 0 + var d int + if o.Default != nil { + d = o.Default.(int) } - cmd.Flags().IntVar(o.DestP.(*int), o.Flag, o.Default.(int), o.Desc) - viper.BindPFlag(o.Flag, cmd.Flags().Lookup(o.Flag)) - *o.DestP.(*int) = viper.GetInt(o.Flag) + cmd.Flags().IntVar(destP, o.Flag, d, o.Desc) + mustBindPFlag(o.Flag, cmd) + *destP = viper.GetInt(o.Flag) case *bool: - if o.Default == nil { - o.Default = false + var d bool + if o.Default != nil { + d = o.Default.(bool) } - cmd.Flags().BoolVar(o.DestP.(*bool), o.Flag, o.Default.(bool), o.Desc) - viper.BindPFlag(o.Flag, cmd.Flags().Lookup(o.Flag)) - *o.DestP.(*bool) = viper.GetBool(o.Flag) + cmd.Flags().BoolVar(destP, o.Flag, d, o.Desc) + mustBindPFlag(o.Flag, cmd) + *destP = viper.GetBool(o.Flag) case *time.Duration: - if o.Default == nil { - o.Default = time.Duration(0) + var d time.Duration + if o.Default != nil { + d = o.Default.(time.Duration) } - cmd.Flags().DurationVar(o.DestP.(*time.Duration), o.Flag, o.Default.(time.Duration), o.Desc) - viper.BindPFlag(o.Flag, cmd.Flags().Lookup(o.Flag)) - *o.DestP.(*time.Duration) = viper.GetDuration(o.Flag) + cmd.Flags().DurationVar(destP, o.Flag, d, o.Desc) + mustBindPFlag(o.Flag, cmd) + *destP = viper.GetDuration(o.Flag) case *[]string: - if o.Default == nil { - o.Default = []string{} + var d []string + if o.Default != nil { + d = o.Default.([]string) } - cmd.Flags().StringSliceVar(o.DestP.(*[]string), o.Flag, o.Default.([]string), o.Desc) - viper.BindPFlag(o.Flag, cmd.Flags().Lookup(o.Flag)) - *o.DestP.(*[]string) = viper.GetStringSlice(o.Flag) + cmd.Flags().StringSliceVar(destP, o.Flag, d, o.Desc) + mustBindPFlag(o.Flag, cmd) + *destP = viper.GetStringSlice(o.Flag) default: // if you get a panic here, sorry about that! // anyway, go ahead and make a PR and add another type. panic(fmt.Errorf("unknown destination type %t", o.DestP)) } } +} - return cmd +func mustBindPFlag(key string, cmd *cobra.Command) { + if err := viper.BindPFlag(key, cmd.Flags().Lookup(key)); err != nil { + panic(err) + } }