Skip to content

Commit

Permalink
feat: Migrate to urfave/cli (#11700)
Browse files Browse the repository at this point in the history
  • Loading branch information
sspaink authored Aug 25, 2022
1 parent 7feb272 commit a57434e
Show file tree
Hide file tree
Showing 21 changed files with 1,182 additions and 631 deletions.
3 changes: 1 addition & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ linters:
# - telegraflinter
- bodyclose
- dogsled
- errcheck
- goprintffuncname
- gosimple
- govet
Expand Down Expand Up @@ -71,7 +70,7 @@ linters-settings:
- name: unconditional-recursion
- name: unexported-naming
- name: unhandled-error
arguments: ["fmt.Printf", "fmt.Println", "fmt.Print"]
arguments: ["outputBuffer.Write", "fmt.Printf", "fmt.Println", "fmt.Print"]
- name: unnecessary-stmt
- name: unreachable-code
# - name: unused-parameter
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ GOOS ?= $(shell go env GOOS)
GOARCH ?= $(shell go env GOARCH)
HOSTGO := env -u GOOS -u GOARCH -u GOARM -- go
INTERNAL_PKG=github.com/influxdata/telegraf/internal
LDFLAGS := $(LDFLAGS) -X $(INTERNAL_PKG).commit=$(commit) -X $(INTERNAL_PKG).branch=$(branch)
LDFLAGS := $(LDFLAGS) -X $(INTERNAL_PKG).Commit=$(commit) -X $(INTERNAL_PKG).Branch=$(branch)
ifneq ($(tag),)
LDFLAGS += -X $(INTERNAL_PKG).version=$(version)
LDFLAGS += -X $(INTERNAL_PKG).Version=$(version)
else
LDFLAGS += -X $(INTERNAL_PKG).version=$(version)-$(commit)
LDFLAGS += -X $(INTERNAL_PKG).Version=$(version)-$(commit)
endif

# Go built-in race detector works only for 64 bits architectures.
Expand Down
File renamed without changes.
355 changes: 355 additions & 0 deletions cmd/telegraf/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,355 @@
package main

import (
"fmt"
"io"
"log" //nolint:revive
"os"
"sort"
"strings"

"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/internal/goplugin"
"github.com/influxdata/telegraf/logger"
_ "github.com/influxdata/telegraf/plugins/aggregators/all"
"github.com/influxdata/telegraf/plugins/inputs"
_ "github.com/influxdata/telegraf/plugins/inputs/all"
"github.com/influxdata/telegraf/plugins/outputs"
_ "github.com/influxdata/telegraf/plugins/outputs/all"
_ "github.com/influxdata/telegraf/plugins/parsers/all"
_ "github.com/influxdata/telegraf/plugins/processors/all"
"github.com/urfave/cli/v2"
)

type TelegrafConfig interface {
CollectDeprecationInfos([]string, []string, []string, []string) map[string][]config.PluginDeprecationInfo
PrintDeprecationList([]config.PluginDeprecationInfo)
}

type Filters struct {
section []string
input []string
output []string
aggregator []string
processor []string
}

func processFilterFlags(section, input, output, aggregator, processor string) Filters {
sectionFilters := deleteEmpty(strings.Split(section, ":"))
inputFilters := deleteEmpty(strings.Split(input, ":"))
outputFilters := deleteEmpty(strings.Split(output, ":"))
aggregatorFilters := deleteEmpty(strings.Split(aggregator, ":"))
processorFilters := deleteEmpty(strings.Split(processor, ":"))
return Filters{sectionFilters, inputFilters, outputFilters, aggregatorFilters, processorFilters}
}

func deleteEmpty(s []string) []string {
var r []string
for _, str := range s {
if str != "" {
r = append(r, str)
}
}
return r
}

// runApp defines all the subcommands and flags for Telegraf
// this abstraction is used for testing, so outputBuffer and args can be changed
func runApp(args []string, outputBuffer io.Writer, pprof Server, c TelegrafConfig, m App) error {
pluginFilterFlags := []cli.Flag{
&cli.StringFlag{
Name: "section-filter",
Usage: "filter the sections to print, separator is ':'. Valid values are 'agent', 'global_tags', 'outputs', 'processors', 'aggregators' and 'inputs'",
},
&cli.StringFlag{
Name: "input-filter",
Usage: "filter the inputs to enable, separator is ':'",
},
&cli.StringFlag{
Name: "output-filter",
Usage: "filter the outputs to enable, separator is ':'",
},
&cli.StringFlag{
Name: "aggregator-filter",
Usage: "filter the aggregators to enable, separator is ':'",
},
&cli.StringFlag{
Name: "processor-filter",
Usage: "filter the processors to enable, separator is ':'",
},
}

extraFlags := append(pluginFilterFlags, cliFlags()...)

// This function is used when Telegraf is run with only flags
action := func(cCtx *cli.Context) error {
logger.SetupLogging(logger.LogConfig{})

// Deprecated: Use execd instead
// Load external plugins, if requested.
if cCtx.String("plugin-directory") != "" {
log.Printf("I! Loading external plugins from: %s", cCtx.String("plugin-directory"))
if err := goplugin.LoadExternalPlugins(cCtx.String("plugin-directory")); err != nil {
return fmt.Errorf("E! %w", err)
}
}

// switch for flags which just do something and exit immediately
switch {
// print available input plugins
case cCtx.Bool("deprecation-list"):
filters := processFilterFlags(
cCtx.String("section-filter"),
cCtx.String("input-filter"),
cCtx.String("output-filter"),
cCtx.String("aggregator-filter"),
cCtx.String("processor-filter"),
)
infos := c.CollectDeprecationInfos(
filters.input, filters.output, filters.aggregator, filters.processor,
)
outputBuffer.Write([]byte("Deprecated Input Plugins:\n"))
c.PrintDeprecationList(infos["inputs"])
outputBuffer.Write([]byte("Deprecated Output Plugins:\n"))
c.PrintDeprecationList(infos["outputs"])
outputBuffer.Write([]byte("Deprecated Processor Plugins:\n"))
c.PrintDeprecationList(infos["processors"])
outputBuffer.Write([]byte("Deprecated Aggregator Plugins:\n"))
c.PrintDeprecationList(infos["aggregators"])
return nil
// print available output plugins
case cCtx.Bool("output-list"):
outputBuffer.Write([]byte("Available Output Plugins:\n"))
names := make([]string, 0, len(outputs.Outputs))
for k := range outputs.Outputs {
names = append(names, k)
}
sort.Strings(names)
for _, k := range names {
outputBuffer.Write([]byte(fmt.Sprintf(" %s\n", k)))
}
return nil
// print available input plugins
case cCtx.Bool("input-list"):
outputBuffer.Write([]byte("Available Input Plugins:\n"))
names := make([]string, 0, len(inputs.Inputs))
for k := range inputs.Inputs {
names = append(names, k)
}
sort.Strings(names)
for _, k := range names {
outputBuffer.Write([]byte(fmt.Sprintf(" %s\n", k)))
}
return nil
// print usage for a plugin, ie, 'telegraf --usage mysql'
case cCtx.String("usage") != "":
err := PrintInputConfig(cCtx.String("usage"), outputBuffer)
err2 := PrintOutputConfig(cCtx.String("usage"), outputBuffer)
if err != nil && err2 != nil {
return fmt.Errorf("E! %s and %s", err, err2)
}
return nil
// DEPRECATED
case cCtx.Bool("version"):
outputBuffer.Write([]byte(fmt.Sprintf("%s\n", internal.FormatFullVersion())))
return nil
// DEPRECATED
case cCtx.Bool("sample-config"):
filters := processFilterFlags(
cCtx.String("section-filter"),
cCtx.String("input-filter"),
cCtx.String("output-filter"),
cCtx.String("aggregator-filter"),
cCtx.String("processor-filter"),
)

printSampleConfig(
outputBuffer,
filters.section,
filters.input,
filters.output,
filters.aggregator,
filters.processor,
)
return nil
}

if cCtx.String("pprof-addr") != "" {
pprof.Start(cCtx.String("pprof-addr"))
}

filters := processFilterFlags(
cCtx.String("section-filter"),
cCtx.String("input-filter"),
cCtx.String("output-filter"),
cCtx.String("aggregator-filter"),
cCtx.String("processor-filter"),
)

g := GlobalFlags{
config: cCtx.StringSlice("config"),
configDir: cCtx.StringSlice("config-directory"),
testWait: cCtx.Int("test-wait"),
watchConfig: cCtx.String("watch-config"),
pidFile: cCtx.String("pidfile"),
plugindDir: cCtx.String("plugin-directory"),
test: cCtx.Bool("test"),
debug: cCtx.Bool("debug"),
once: cCtx.Bool("once"),
quiet: cCtx.Bool("quiet"),
}

w := WindowFlags{
service: cCtx.String("service"),
serviceName: cCtx.String("service-name"),
serviceDisplayName: cCtx.String("service-display-name"),
serviceRestartDelay: cCtx.String("service-restart-delay"),
serviceAutoRestart: cCtx.Bool("service-auto-restart"),
console: cCtx.Bool("console"),
}

m.Init(pprof.ErrChan(), filters, g, w)
return m.Run()
}

app := &cli.App{
Name: "Telegraf",
Usage: "The plugin-driven server agent for collecting & reporting metrics.",
Writer: outputBuffer,
Flags: append(
[]cli.Flag{
// String slice flags
&cli.StringSliceFlag{
Name: "config",
Usage: "configuration file to load",
},
&cli.StringSliceFlag{
Name: "config-directory",
Usage: "directory containing additional *.conf files",
},
// Int flags
&cli.IntFlag{
Name: "test-wait",
Usage: "wait up to this many seconds for service inputs to complete in test mode",
},
//
// String flags
&cli.StringFlag{
Name: "usage",
Usage: "print usage for a plugin, ie, 'telegraf --usage mysql'",
},
&cli.StringFlag{
Name: "pprof-addr",
Usage: "pprof host/IP and port to listen on (e.g. 'localhost:6060')",
},
&cli.StringFlag{
Name: "watch-config",
Usage: "monitoring config changes [notify, poll]",
},
&cli.StringFlag{
Name: "pidfile",
Usage: "file to write our pid to",
},
//
// Bool flags
&cli.BoolFlag{
Name: "once",
Usage: "run one gather and exit",
},
&cli.BoolFlag{
Name: "debug",
Usage: "turn on debug logging",
},
&cli.BoolFlag{
Name: "quiet",
Usage: "run in quiet mode",
},
&cli.BoolFlag{
Name: "test",
Usage: "enable test mode: gather metrics, print them out, and exit. Note: Test mode only runs inputs, not processors, aggregators, or outputs",
},
// TODO: Change "deprecation-list, input-list, output-list" flags to become a subcommand "list" that takes
// "input,output,aggregator,processor, deprecated" as parameters
&cli.BoolFlag{
Name: "deprecation-list",
Usage: "print all deprecated plugins or plugin options",
},
&cli.BoolFlag{
Name: "input-list",
Usage: "print available input plugins",
},
&cli.BoolFlag{
Name: "output-list",
Usage: "print available output plugins",
},
//
// !!! The following flags are DEPRECATED !!!
// Already covered with the subcommand `./telegraf version`
&cli.BoolFlag{
Name: "version",
Usage: "DEPRECATED: display the version and exit",
},
// Already covered with the subcommand `./telegraf config`
&cli.BoolFlag{
Name: "sample-config",
Usage: "DEPRECATED: print out full sample configuration",
},
// Using execd plugin to add external plugins is preffered (less size impact, easier for end user)
&cli.StringFlag{
Name: "plugin-directory",
Usage: "DEPRECATED: path to directory containing external plugins",
},
// !!!
}, extraFlags...),
Action: action,
Commands: []*cli.Command{
{
Name: "config",
Usage: "print out full sample configuration to stdout",
Flags: pluginFilterFlags,
Action: func(cCtx *cli.Context) error {
// The sub_Filters are populated when the filter flags are set after the subcommand config
// e.g. telegraf config --section-filter inputs
filters := processFilterFlags(
cCtx.String("section-filter"),
cCtx.String("input-filter"),
cCtx.String("output-filter"),
cCtx.String("aggregator-filter"),
cCtx.String("processor-filter"),
)

printSampleConfig(
outputBuffer,
filters.section,
filters.input,
filters.output,
filters.aggregator,
filters.processor,
)
return nil
},
},
{
Name: "version",
Usage: "print current version to stdout",
Action: func(cCtx *cli.Context) error {
outputBuffer.Write([]byte(fmt.Sprintf("%s\n", internal.FormatFullVersion())))
return nil
},
},
},
}

return app.Run(args)
}

func main() {
agent := Telegraf{}
pprof := NewPprofServer()
c := config.NewConfig()
err := runApp(os.Args, os.Stdout, pprof, c, &agent)
if err != nil {
log.Fatalf("E! %s", err)
}
}
Loading

0 comments on commit a57434e

Please sign in to comment.