diff --git a/.markdownlint.yml b/.markdownlint.yml index 1344b312f825e..893179487d310 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -1,3 +1,6 @@ { - "MD013": false + "MD013": false, + "MD033": { + "allowed_elements": ["br"] + } } diff --git a/Makefile b/Makefile index 290008d8c34b0..0e4b21bcb4b3d 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,8 @@ HOSTGO := env -u GOOS -u GOARCH -u GOARM -- go LDFLAGS := $(LDFLAGS) -X main.commit=$(commit) -X main.branch=$(branch) -X main.goos=$(GOOS) -X main.goarch=$(GOARCH) ifneq ($(tag),) LDFLAGS += -X main.version=$(version) +else + LDFLAGS += -X main.version=$(version)-$(commit) endif # Go built-in race detector works only for 64 bits architectures. @@ -148,19 +150,18 @@ lint-install: .PHONY: lint lint: - @which golangci-lint >/dev/null 2>&1 || { \ - echo "golangci-lint not found, please run: make lint-install"; \ - exit 1; \ - } - - golangci-lint run - - @which markdownlint >/dev/null 2>&1 || { \ - echo "markdownlint not found, please run: make lint-install"; \ - exit 1; \ - } - - markdownlint . + ifeq (, $(shell which golangci-lint)) + $(info golangci-lint can't be found, please run: make lint-install) + exit 1 + endif + + golangci-lint run + + ifeq (, $(shell which markdownlint-cli)) + $(info markdownlint-cli can't be found, please run: make lint-install) + exit 1 + endif + markdownlint-cli .PHONY: lint-branch lint-branch: diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index 688c1e5bdd6c5..66cdd35315301 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -60,6 +60,8 @@ var fVersion = flag.Bool("version", false, "display the version and exit") var fSampleConfig = flag.Bool("sample-config", false, "print out full sample configuration") var fPidfile = flag.String("pidfile", "", "file to write our pid to") +var fDeprecationList = flag.Bool("deprecation-list", false, + "print all deprecated plugins or plugin options.") var fSectionFilters = flag.String("section-filter", "", "filter the sections to print, separator is ':'. Valid values are 'agent', 'global_tags', 'outputs', 'processors', 'aggregators' and 'inputs'") var fInputFilters = flag.String("input-filter", "", @@ -270,6 +272,19 @@ func runAgent(ctx context.Context, log.Printf("I! Loaded outputs: %s", strings.Join(c.OutputNames(), " ")) log.Printf("I! Tags enabled: %s", c.ListTags()) + if count, found := c.Deprecations["inputs"]; found && (count[0] > 0 || count[1] > 0) { + log.Printf("W! Deprecated inputs: %d and %d options", count[0], count[1]) + } + if count, found := c.Deprecations["aggregators"]; found && (count[0] > 0 || count[1] > 0) { + log.Printf("W! Deprecated aggregators: %d and %d options", count[0], count[1]) + } + if count, found := c.Deprecations["processors"]; found && (count[0] > 0 || count[1] > 0) { + log.Printf("W! Deprecated processors: %d and %d options", count[0], count[1]) + } + if count, found := c.Deprecations["outputs"]; found && (count[0] > 0 || count[1] > 0) { + log.Printf("W! Deprecated outputs: %d and %d options", count[0], count[1]) + } + if *fPidfile != "" { f, err := os.OpenFile(*fPidfile, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { @@ -348,6 +363,11 @@ func main() { logger.SetupLogging(logger.LogConfig{}) + // Configure version + if err := internal.SetVersion(version); err != nil { + log.Println("Telegraf version already configured to: " + internal.Version()) + } + // Load external plugins, if requested. if *fPlugins != "" { log.Printf("I! Loading external plugins from: %s", *fPlugins) @@ -392,6 +412,27 @@ func main() { // switch for flags which just do something and exit immediately switch { + case *fDeprecationList: + c := config.NewConfig() + infos := c.CollectDeprecationInfos( + inputFilters, + outputFilters, + aggregatorFilters, + processorFilters, + ) + //nolint:revive // We will notice if Println fails + fmt.Println("Deprecated Input Plugins: ") + c.PrintDeprecationList(infos["inputs"]) + //nolint:revive // We will notice if Println fails + fmt.Println("Deprecated Output Plugins: ") + c.PrintDeprecationList(infos["outputs"]) + //nolint:revive // We will notice if Println fails + fmt.Println("Deprecated Processor Plugins: ") + c.PrintDeprecationList(infos["processors"]) + //nolint:revive // We will notice if Println fails + fmt.Println("Deprecated Aggregator Plugins: ") + c.PrintDeprecationList(infos["aggregators"]) + return case *fOutputList: fmt.Println("Available Output Plugins: ") names := make([]string, 0, len(outputs.Outputs)) @@ -435,16 +476,6 @@ func main() { return } - shortVersion := version - if shortVersion == "" { - shortVersion = "unknown" - } - - // Configure version - if err := internal.SetVersion(shortVersion); err != nil { - log.Println("Telegraf version already configured to: " + internal.Version()) - } - run( inputFilters, outputFilters, diff --git a/config/config.go b/config/config.go index 97f9c35b3ab55..4121c71687e66 100644 --- a/config/config.go +++ b/config/config.go @@ -17,6 +17,8 @@ import ( "strings" "time" + "github.com/coreos/go-semver/semver" + "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/internal/choice" @@ -77,6 +79,9 @@ type Config struct { // Processors have a slice wrapper type because they need to be sorted Processors models.RunningProcessors AggProcessors models.RunningProcessors + + Deprecations map[string][]int64 + version *semver.Version } // NewConfig creates a new struct to hold the Telegraf config. @@ -102,7 +107,15 @@ func NewConfig() *Config { AggProcessors: make([]*models.RunningProcessor, 0), InputFilters: make([]string, 0), OutputFilters: make([]string, 0), + Deprecations: make(map[string][]int64), + } + + // Handle unknown version + version := internal.Version() + if version == "" || version == "unknown" { + version = "0.0.0-unknown" } + c.version = semver.New(version) tomlCfg := &toml.Config{ NormFieldName: toml.DefaultConfig.NormFieldName, @@ -1009,6 +1022,11 @@ func parseConfig(contents []byte) (*ast.Table, error) { func (c *Config) addAggregator(name string, table *ast.Table) error { creator, ok := aggregators.Aggregators[name] if !ok { + // Handle removed, deprecated plugins + if di, deprecated := aggregators.Deprecations[name]; deprecated { + printHistoricPluginDeprecationNotice("aggregators", name, di) + return fmt.Errorf("plugin deprecated") + } return fmt.Errorf("Undefined but requested aggregator: %s", name) } aggregator := creator() @@ -1022,6 +1040,10 @@ func (c *Config) addAggregator(name string, table *ast.Table) error { return err } + if err := c.printUserDeprecation("aggregators", name, aggregator); err != nil { + return err + } + c.Aggregators = append(c.Aggregators, models.NewRunningAggregator(aggregator, conf)) return nil } @@ -1029,6 +1051,11 @@ func (c *Config) addAggregator(name string, table *ast.Table) error { func (c *Config) addProcessor(name string, table *ast.Table) error { creator, ok := processors.Processors[name] if !ok { + // Handle removed, deprecated plugins + if di, deprecated := processors.Deprecations[name]; deprecated { + printHistoricPluginDeprecationNotice("processors", name, di) + return fmt.Errorf("plugin deprecated") + } return fmt.Errorf("Undefined but requested processor: %s", name) } @@ -1070,6 +1097,10 @@ func (c *Config) newRunningProcessor( } } + if err := c.printUserDeprecation("processors", processorConfig.Name, processor); err != nil { + return nil, err + } + rf := models.NewRunningProcessor(processor, processorConfig) return rf, nil } @@ -1080,6 +1111,11 @@ func (c *Config) addOutput(name string, table *ast.Table) error { } creator, ok := outputs.Outputs[name] if !ok { + // Handle removed, deprecated plugins + if di, deprecated := outputs.Deprecations[name]; deprecated { + printHistoricPluginDeprecationNotice("outputs", name, di) + return fmt.Errorf("plugin deprecated") + } return fmt.Errorf("Undefined but requested output: %s", name) } output := creator() @@ -1104,6 +1140,10 @@ func (c *Config) addOutput(name string, table *ast.Table) error { return err } + if err := c.printUserDeprecation("outputs", name, output); err != nil { + return err + } + ro := models.NewRunningOutput(output, outputConfig, c.Agent.MetricBatchSize, c.Agent.MetricBufferLimit) c.Outputs = append(c.Outputs, ro) return nil @@ -1113,13 +1153,23 @@ func (c *Config) addInput(name string, table *ast.Table) error { if len(c.InputFilters) > 0 && !sliceContains(name, c.InputFilters) { return nil } + // Legacy support renaming io input to diskio if name == "io" { + if err := c.printUserDeprecation("inputs", name, nil); err != nil { + return err + } name = "diskio" } creator, ok := inputs.Inputs[name] if !ok { + // Handle removed, deprecated plugins + if di, deprecated := inputs.Deprecations[name]; deprecated { + printHistoricPluginDeprecationNotice("inputs", name, di) + return fmt.Errorf("plugin deprecated") + } + return fmt.Errorf("Undefined but requested input: %s", name) } input := creator() @@ -1153,6 +1203,10 @@ func (c *Config) addInput(name string, table *ast.Table) error { return err } + if err := c.printUserDeprecation("inputs", name, input); err != nil { + return err + } + rp := models.NewRunningInput(input, pluginConfig) rp.SetDefaultTags(c.Tags) c.Inputs = append(c.Inputs, rp) diff --git a/config/deprecation.go b/config/deprecation.go new file mode 100644 index 0000000000000..ab5d2a0caba46 --- /dev/null +++ b/config/deprecation.go @@ -0,0 +1,378 @@ +package config + +import ( + "fmt" + "log" //nolint:revive // log is ok here as the logging facility is not set-up yet + "reflect" + "sort" + "strings" + + "github.com/coreos/go-semver/semver" + "github.com/fatih/color" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/aggregators" + "github.com/influxdata/telegraf/plugins/inputs" + "github.com/influxdata/telegraf/plugins/outputs" + "github.com/influxdata/telegraf/plugins/processors" +) + +// Escalation level for the plugin or option +type Escalation int + +func (e Escalation) String() string { + switch e { + case Warn: + return "WARN" + case Error: + return "ERROR" + } + return "NONE" +} + +const ( + // None means no deprecation + None Escalation = iota + // Warn means deprecated but still within the grace period + Warn + // Error means deprecated and beyond grace period + Error +) + +// deprecationInfo contains all important information to describe a deprecated entity +type deprecationInfo struct { + // Name of the plugin or plugin option + Name string + // LogLevel is the level of deprecation which currently corresponds to a log-level + LogLevel Escalation + info telegraf.DeprecationInfo +} + +func (di *deprecationInfo) determineEscalation(telegrafVersion *semver.Version) error { + di.LogLevel = None + if di.info.Since == "" { + return nil + } + + since, err := semver.NewVersion(di.info.Since) + if err != nil { + return fmt.Errorf("cannot parse 'since' version %q: %v", di.info.Since, err) + } + + var removal *semver.Version + if di.info.RemovalIn != "" { + removal, err = semver.NewVersion(di.info.RemovalIn) + if err != nil { + return fmt.Errorf("cannot parse 'removal' version %q: %v", di.info.RemovalIn, err) + } + } else { + removal = &semver.Version{Major: since.Major} + removal.BumpMajor() + di.info.RemovalIn = removal.String() + } + + // Drop potential pre-release tags + version := semver.Version{ + Major: telegrafVersion.Major, + Minor: telegrafVersion.Minor, + Patch: telegrafVersion.Patch, + } + if !version.LessThan(*removal) { + di.LogLevel = Error + } else if !version.LessThan(*since) { + di.LogLevel = Warn + } + return nil +} + +// pluginDeprecationInfo holds all information about a deprecated plugin or it's options +type pluginDeprecationInfo struct { + deprecationInfo + + // Options deprecated for this plugin + Options []deprecationInfo +} + +func (c *Config) incrementPluginDeprecations(category string) { + newcounts := []int64{1, 0} + if counts, found := c.Deprecations[category]; found { + newcounts = []int64{counts[0] + 1, counts[1]} + } + c.Deprecations[category] = newcounts +} + +func (c *Config) incrementPluginOptionDeprecations(category string) { + newcounts := []int64{0, 1} + if counts, found := c.Deprecations[category]; found { + newcounts = []int64{counts[0], counts[1] + 1} + } + c.Deprecations[category] = newcounts +} + +func (c *Config) collectDeprecationInfo(category, name string, plugin interface{}, all bool) pluginDeprecationInfo { + info := pluginDeprecationInfo{ + deprecationInfo: deprecationInfo{ + Name: category + "." + name, + LogLevel: None, + }, + } + + // First check if the whole plugin is deprecated + switch category { + case "aggregators": + if pi, deprecated := aggregators.Deprecations[name]; deprecated { + info.deprecationInfo.info = pi + } + case "inputs": + if pi, deprecated := inputs.Deprecations[name]; deprecated { + info.deprecationInfo.info = pi + } + case "outputs": + if pi, deprecated := outputs.Deprecations[name]; deprecated { + info.deprecationInfo.info = pi + } + case "processors": + if pi, deprecated := processors.Deprecations[name]; deprecated { + info.deprecationInfo.info = pi + } + } + if err := info.determineEscalation(c.version); err != nil { + panic(fmt.Errorf("plugin %q: %v", info.Name, err)) + } + if info.LogLevel != None { + c.incrementPluginDeprecations(category) + } + + // Allow checking for names only. + if plugin == nil { + return info + } + + // Check for deprecated options + walkPluginStruct(reflect.ValueOf(plugin), func(field reflect.StructField, value reflect.Value) { + // Try to report only those fields that are set + if !all && value.IsZero() { + return + } + + tags := strings.SplitN(field.Tag.Get("deprecated"), ";", 3) + if len(tags) < 1 || tags[0] == "" { + return + } + optionInfo := deprecationInfo{Name: field.Name} + optionInfo.info.Since = tags[0] + + if len(tags) > 1 { + optionInfo.info.Notice = tags[len(tags)-1] + } + if len(tags) > 2 { + optionInfo.info.RemovalIn = tags[1] + } + if err := optionInfo.determineEscalation(c.version); err != nil { + panic(fmt.Errorf("plugin %q option %q: %v", info.Name, field.Name, err)) + } + + if optionInfo.LogLevel != None { + c.incrementPluginOptionDeprecations(category) + } + + // Get the toml field name + option := field.Tag.Get("toml") + if option != "" { + optionInfo.Name = option + } + info.Options = append(info.Options, optionInfo) + }) + + return info +} + +func (c *Config) printUserDeprecation(category, name string, plugin interface{}) error { + info := c.collectDeprecationInfo(category, name, plugin, false) + + switch info.LogLevel { + case Warn: + prefix := "W! " + color.YellowString("DeprecationWarning") + printPluginDeprecationNotice(prefix, info.Name, info.info) + // We will not check for any deprecated options as the whole plugin is deprecated anyway. + return nil + case Error: + prefix := "E! " + color.RedString("DeprecationError") + printPluginDeprecationNotice(prefix, info.Name, info.info) + // We are past the grace period + return fmt.Errorf("plugin deprecated") + } + + // Print deprecated options + deprecatedOptions := make([]string, 0) + for _, option := range info.Options { + switch option.LogLevel { + case Warn: + prefix := "W! " + color.YellowString("DeprecationWarning") + printOptionDeprecationNotice(prefix, info.Name, option.Name, option.info) + case Error: + prefix := "E! " + color.RedString("DeprecationError") + printOptionDeprecationNotice(prefix, info.Name, option.Name, option.info) + deprecatedOptions = append(deprecatedOptions, option.Name) + } + } + + if len(deprecatedOptions) > 0 { + return fmt.Errorf("plugin options %q deprecated", strings.Join(deprecatedOptions, ",")) + } + + return nil +} + +func (c *Config) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFilter []string) map[string][]pluginDeprecationInfo { + infos := make(map[string][]pluginDeprecationInfo) + + infos["inputs"] = make([]pluginDeprecationInfo, 0) + for name, creator := range inputs.Inputs { + if len(inFilter) > 0 && !sliceContains(name, inFilter) { + continue + } + + plugin := creator() + info := c.collectDeprecationInfo("inputs", name, plugin, true) + + if info.LogLevel != None || len(info.Options) > 0 { + infos["inputs"] = append(infos["inputs"], info) + } + } + + infos["outputs"] = make([]pluginDeprecationInfo, 0) + for name, creator := range outputs.Outputs { + if len(outFilter) > 0 && !sliceContains(name, outFilter) { + continue + } + + plugin := creator() + info := c.collectDeprecationInfo("outputs", name, plugin, true) + + if info.LogLevel != None || len(info.Options) > 0 { + infos["outputs"] = append(infos["outputs"], info) + } + } + + infos["processors"] = make([]pluginDeprecationInfo, 0) + for name, creator := range processors.Processors { + if len(procFilter) > 0 && !sliceContains(name, procFilter) { + continue + } + + plugin := creator() + info := c.collectDeprecationInfo("processors", name, plugin, true) + + if info.LogLevel != None || len(info.Options) > 0 { + infos["processors"] = append(infos["processors"], info) + } + } + + infos["aggregators"] = make([]pluginDeprecationInfo, 0) + for name, creator := range aggregators.Aggregators { + if len(aggFilter) > 0 && !sliceContains(name, aggFilter) { + continue + } + + plugin := creator() + info := c.collectDeprecationInfo("aggregators", name, plugin, true) + + if info.LogLevel != None || len(info.Options) > 0 { + infos["aggregators"] = append(infos["aggregators"], info) + } + } + + return infos +} + +func (c *Config) PrintDeprecationList(plugins []pluginDeprecationInfo) { + sort.Slice(plugins, func(i, j int) bool { return plugins[i].Name < plugins[j].Name }) + + for _, plugin := range plugins { + switch plugin.LogLevel { + case Warn, Error: + _, _ = fmt.Printf( + " %-40s %-5s since %-5s removal in %-5s %s\n", + plugin.Name, plugin.LogLevel, plugin.info.Since, plugin.info.RemovalIn, plugin.info.Notice, + ) + } + + if len(plugin.Options) < 1 { + continue + } + sort.Slice(plugin.Options, func(i, j int) bool { return plugin.Options[i].Name < plugin.Options[j].Name }) + for _, option := range plugin.Options { + _, _ = fmt.Printf( + " %-40s %-5s since %-5s removal in %-5s %s\n", + plugin.Name+"/"+option.Name, option.LogLevel, option.info.Since, option.info.RemovalIn, option.info.Notice, + ) + } + } +} + +func printHistoricPluginDeprecationNotice(category, name string, info telegraf.DeprecationInfo) { + prefix := "E! " + color.RedString("DeprecationError") + log.Printf( + "%s: Plugin %q deprecated since version %s and removed: %s", + prefix, category+"."+name, info.Since, info.Notice, + ) +} + +func printPluginDeprecationNotice(prefix, name string, info telegraf.DeprecationInfo) { + log.Printf( + "%s: Plugin %q deprecated since version %s and will be removed in %s: %s", + prefix, name, info.Since, info.RemovalIn, info.Notice, + ) +} + +func printOptionDeprecationNotice(prefix, plugin, option string, info telegraf.DeprecationInfo) { + log.Printf( + "%s: Option %q of plugin %q deprecated since version %s and will be removed in %s: %s", + prefix, option, plugin, info.Since, info.RemovalIn, info.Notice, + ) +} + +// walkPluginStruct iterates over the fields of a structure in depth-first search (to cover nested structures) +// and calls the given function for every visited field. +func walkPluginStruct(value reflect.Value, fn func(f reflect.StructField, fv reflect.Value)) { + v := reflect.Indirect(value) + t := v.Type() + + // Only works on structs + if t.Kind() != reflect.Struct { + return + } + + // Walk over the struct fields and call the given function. If we encounter more complex embedded + // elements (stucts, slices/arrays, maps) we need to descend into those elements as they might + // contain structures nested in the current structure. + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + fieldValue := v.Field(i) + + if field.PkgPath != "" { + continue + } + switch field.Type.Kind() { + case reflect.Struct: + walkPluginStruct(fieldValue, fn) + case reflect.Array, reflect.Slice: + for j := 0; j < fieldValue.Len(); j++ { + element := fieldValue.Index(j) + // The array might contain structs + walkPluginStruct(element, fn) + fn(field, element) + } + case reflect.Map: + iter := fieldValue.MapRange() + for iter.Next() { + element := iter.Value() + // The map might contain structs + walkPluginStruct(element, fn) + fn(field, element) + } + } + fn(field, fieldValue) + } +} diff --git a/docs/COMMANDS_AND_FLAGS.md b/docs/COMMANDS_AND_FLAGS.md index babccb54ba152..9a57a0b832bd7 100644 --- a/docs/COMMANDS_AND_FLAGS.md +++ b/docs/COMMANDS_AND_FLAGS.md @@ -24,6 +24,7 @@ telegraf [flags] |`--watch-config` |Telegraf will restart on local config changes. Monitor changes using either fs notifications or polling. Valid values: `inotify` or `poll`. Monitoring is off by default.| |`--plugin-directory` |directory containing *.so files, this directory will be searched recursively. Any Plugin found will be loaded and namespaced.| |`--debug` |turn on debug logging| +|`--deprecation-list` |print all deprecated plugins or plugin options| |`--input-filter ` |filter the inputs to enable, separator is `:`| |`--input-list` |print available input plugins.| |`--output-filter ` |filter the outputs to enable, separator is `:`| diff --git a/docs/LICENSE_OF_DEPENDENCIES.md b/docs/LICENSE_OF_DEPENDENCIES.md index f9e46d94c04a4..68dac79be569b 100644 --- a/docs/LICENSE_OF_DEPENDENCIES.md +++ b/docs/LICENSE_OF_DEPENDENCIES.md @@ -64,6 +64,7 @@ following works: - github.com/cespare/xxhash [MIT License](https://github.com/cespare/xxhash/blob/master/LICENSE.txt) - github.com/cisco-ie/nx-telemetry-proto [Apache License 2.0](https://github.com/cisco-ie/nx-telemetry-proto/blob/master/LICENSE) - github.com/containerd/containerd [Apache License 2.0](https://github.com/containerd/containerd/blob/master/LICENSE) +- github.com/coreos/go-semver [Apache License 2.0](https://github.com/coreos/go-semver/blob/main/LICENSE) - github.com/couchbase/go-couchbase [MIT License](https://github.com/couchbase/go-couchbase/blob/master/LICENSE) - github.com/couchbase/gomemcached [MIT License](https://github.com/couchbase/gomemcached/blob/master/LICENSE) - github.com/couchbase/goutils [Apache License 2.0](https://github.com/couchbase/goutils/blob/master/LICENSE.md) diff --git a/go.mod b/go.mod index 1429f31fded52..c2ba30160ee8c 100644 --- a/go.mod +++ b/go.mod @@ -78,7 +78,7 @@ require ( github.com/cisco-ie/nx-telemetry-proto v0.0.0-20190531143454-82441e232cf6 github.com/containerd/cgroups v1.0.1 // indirect github.com/containerd/containerd v1.5.7 // indirect - github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-semver v0.3.0 github.com/couchbase/go-couchbase v0.1.0 github.com/couchbase/gomemcached v0.1.3 // indirect github.com/couchbase/goutils v0.1.0 // indirect @@ -98,7 +98,7 @@ require ( github.com/eapache/queue v1.1.0 // indirect github.com/echlebek/timeproxy v1.0.0 // indirect github.com/eclipse/paho.mqtt.golang v1.3.0 - github.com/fatih/color v1.10.0 // indirect + github.com/fatih/color v1.10.0 github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-logfmt/logfmt v0.5.0 diff --git a/internal/internal.go b/internal/internal.go index 4441e9acfbf03..49f92bfcd1265 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -42,6 +42,10 @@ func SetVersion(v string) error { return ErrorVersionAlreadySet } version = v + if version == "" { + version = "unknown" + } + return nil } diff --git a/internal/usage.go b/internal/usage.go index 916b5cb86e908..78e9a846b198b 100644 --- a/internal/usage.go +++ b/internal/usage.go @@ -17,13 +17,14 @@ The commands & flags are: --aggregator-filter filter the aggregators to enable, separator is : --config configuration file to load --config-directory directory containing additional *.conf files - --watch-config Telegraf will restart on local config changes. Monitor changes - using either fs notifications or polling. Valid values: 'inotify' or 'poll'. + --watch-config Telegraf will restart on local config changes. Monitor changes + using either fs notifications or polling. Valid values: 'inotify' or 'poll'. Monitoring is off by default. --plugin-directory directory containing *.so files, this directory will be searched recursively. Any Plugin found will be loaded and namespaced. --debug turn on debug logging + --deprecation-list print all deprecated plugins or plugin options. --input-filter filter the inputs to enable, separator is : --input-list print available input plugins. --output-filter filter the outputs to enable, separator is : diff --git a/plugin.go b/plugin.go index f9dcaeac0344c..3f4004d766457 100644 --- a/plugin.go +++ b/plugin.go @@ -2,6 +2,16 @@ package telegraf var Debug bool +// DeprecationInfo contains information for marking a plugin deprecated. +type DeprecationInfo struct { + // Since specifies the version since when the plugin is deprecated + Since string + // RemovalIn optionally specifies the version when the plugin is scheduled for removal + RemovalIn string + // Notice for the user on suggested replacements etc. + Notice string +} + // Initializer is an interface that all plugin types: Inputs, Outputs, // Processors, and Aggregators can optionally implement to initialize the // plugin. diff --git a/plugins/aggregators/deprecations.go b/plugins/aggregators/deprecations.go new file mode 100644 index 0000000000000..dd2302e0255c3 --- /dev/null +++ b/plugins/aggregators/deprecations.go @@ -0,0 +1,6 @@ +package aggregators + +import "github.com/influxdata/telegraf" + +// Deprecations lists the deprecated plugins +var Deprecations = map[string]telegraf.DeprecationInfo{} diff --git a/plugins/inputs/deprecations.go b/plugins/inputs/deprecations.go new file mode 100644 index 0000000000000..14a497baff30a --- /dev/null +++ b/plugins/inputs/deprecations.go @@ -0,0 +1,48 @@ +package inputs + +import "github.com/influxdata/telegraf" + +// Deprecations lists the deprecated plugins +var Deprecations = map[string]telegraf.DeprecationInfo{ + "cassandra": { + Since: "1.7.0", + Notice: "use 'inputs.jolokia2' with the 'cassandra.conf' example configuration instead", + }, + "io": { + Since: "0.10.0", + RemovalIn: "2.0.0", + Notice: "use 'inputs.diskio' instead", + }, + "http_listener_v2": { + Since: "1.9.0", + Notice: "has been renamed to 'influxdb_listener', use 'inputs.influxdb_listener' or 'inputs.influxdb_listener_v2' instead", + }, + "httpjson": { + Since: "1.6.0", + Notice: "use 'inputs.http' instead", + }, + "jolokia": { + Since: "1.5.0", + Notice: "use 'inputs.jolokia2' instead", + }, + "kafka_consumer_legacy": { + Since: "1.4.0", + Notice: "use 'inputs.kafka_consumer' instead, NOTE: 'kafka_consumer' only supports Kafka v0.8+", + }, + "logparser": { + Since: "1.15.0", + Notice: "use 'inputs.tail' with 'grok' data format instead", + }, + "snmp_legacy": { + Since: "1.0.0", + Notice: "use 'inputs.snmp' instead", + }, + "tcp_listener": { + Since: "1.3.0", + Notice: "use 'inputs.socket_listener' instead", + }, + "udp_listener": { + Since: "1.3.0", + Notice: "use 'inputs.socket_listener' instead", + }, +} diff --git a/plugins/inputs/http_response/http_response.go b/plugins/inputs/http_response/http_response.go index 799f664d1e7b0..f0da6294aa263 100644 --- a/plugins/inputs/http_response/http_response.go +++ b/plugins/inputs/http_response/http_response.go @@ -28,7 +28,7 @@ const ( // HTTPResponse struct type HTTPResponse struct { - Address string // deprecated in 1.12 + Address string `toml:"address" deprecated:"1.12.0;use 'urls' instead"` URLs []string `toml:"urls"` HTTPProxy string `toml:"http_proxy"` Body string diff --git a/plugins/outputs/deprecations.go b/plugins/outputs/deprecations.go new file mode 100644 index 0000000000000..6c318cdf6bc4b --- /dev/null +++ b/plugins/outputs/deprecations.go @@ -0,0 +1,11 @@ +package outputs + +import "github.com/influxdata/telegraf" + +// Deprecations lists the deprecated plugins +var Deprecations = map[string]telegraf.DeprecationInfo{ + "riemann_legacy": { + Since: "1.3.0", + Notice: "use 'outputs.riemann' instead (see https://github.com/influxdata/telegraf/issues/1878)", + }, +} diff --git a/plugins/processors/deprecations.go b/plugins/processors/deprecations.go new file mode 100644 index 0000000000000..fa344d8cb155e --- /dev/null +++ b/plugins/processors/deprecations.go @@ -0,0 +1,6 @@ +package processors + +import "github.com/influxdata/telegraf" + +// Deprecations lists the deprecated plugins +var Deprecations = map[string]telegraf.DeprecationInfo{}