feat(influxd): Migrate influxd binary to cobra Command package
This commit consists of several improvements or changes: * migrate the influxd binary to cobra.Command * introduce a default run sub-command to start the server * register the run sub-command flags with viper to maintain compatibility with the existing behavior of automatic binding of flags to environment variables. Closes #12602pull/12615/head
parent
f5c54a00b0
commit
e8045ae187
|
@ -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)
|
||||
|
|
|
@ -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 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)
|
||||
}()
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
// Attempt clean shutdown.
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
m.Shutdown(ctx)
|
||||
wg.Wait()
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "influxd",
|
||||
Short: "Influx Server",
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd := find(os.Args[1:])
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
3
go.sum
3
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=
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue