From 5d6748fcb59fda6c0f412515338b8764fa32d5a7 Mon Sep 17 00:00:00 2001 From: Sebastian Spaink <3441183+sspaink@users.noreply.github.com> Date: Tue, 5 Apr 2022 17:11:09 -0500 Subject: [PATCH] feat: generate the plugins sample config (#10886) --- .circleci/config.yml | 3 +- Makefile | 17 ++- config/config.go | 7 +- config/config_test.go | 4 - docs/AGGREGATORS.md | 39 +++--- docs/INPUTS.md | 37 +++--- docs/OUTPUTS.md | 33 +++-- docs/PROCESSORS.md | 48 +++---- docs/developers/SAMPLE_CONFIG.md | 3 +- go.mod | 1 + go.sum | 1 + plugin.go | 5 +- plugins/common/shim/config_test.go | 10 -- plugins/common/shim/goshim_test.go | 4 - plugins/processors/streamingprocessor.go | 4 - tools/generate_plugindata/main.go | 157 +++++++++++++++++++++++ tools/generate_plugindata/main_test.go | 133 +++++++++++++++++++ 17 files changed, 389 insertions(+), 117 deletions(-) create mode 100644 tools/generate_plugindata/main.go create mode 100644 tools/generate_plugindata/main_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index a40187f80dc17..2adc3b57f2df2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -78,6 +78,7 @@ commands: key: windows-go-<< parameters.cache_version >>-{{ checksum "go.sum" }} - run: 'sh ./scripts/installgo_windows.sh' - run: choco install mingw + - run: 'make generate' - run: mkdir -p test-results - run: ./scripts/install_gotestsum.sh << parameters.os >> << parameters.gotestsum >> - unless: @@ -120,7 +121,7 @@ commands: paths: - 'C:\Go' - 'C:\Users\circleci\project\gotestsum.exe' - + - run: 'make generate-clean' package-build: parameters: type: diff --git a/Makefile b/Makefile index 6c2e02b02495b..a1d41be5e5e27 100644 --- a/Makefile +++ b/Makefile @@ -109,10 +109,21 @@ versioninfo: go run scripts/generate_versioninfo/main.go; \ go generate cmd/telegraf/telegraf_windows.go; \ -.PHONY: telegraf -telegraf: +.PHONY: generate +generate: + go generate -run="plugindata/main.go$$" ./plugins/inputs/... ./plugins/outputs/... ./plugins/processors/... ./plugins/aggregators/... + +.PHONY: generate-clean +generate-clean: + go generate -run="plugindata/main.go --clean" ./plugins/inputs/... ./plugins/outputs/... ./plugins/processors/... ./plugins/aggregators/... + +.PHONY: build +build: go build -ldflags "$(LDFLAGS)" ./cmd/telegraf +.PHONY: telegraf +telegraf: generate build generate-clean + # Used by dockerfile builds .PHONY: go-install go-install: @@ -312,7 +323,7 @@ darwin-arm64: include_packages := $(mips) $(mipsel) $(arm64) $(amd64) $(static) $(armel) $(armhf) $(riscv64) $(s390x) $(ppc64le) $(i386) $(windows) $(darwin-amd64) $(darwin-arm64) .PHONY: package -package: $(include_packages) +package: generate $(include_packages) generate-clean .PHONY: $(include_packages) $(include_packages): diff --git a/config/config.go b/config/config.go index b17bbad29edd3..ae52e14628a38 100644 --- a/config/config.go +++ b/config/config.go @@ -678,23 +678,24 @@ func printConfig(name string, p telegraf.PluginDescriber, op string, commented b if commented { comment = "# " } - fmt.Printf("\n%s# %s\n%s[[%s.%s]]", comment, p.Description(), comment, op, name) if di.Since != "" { removalNote := "" if di.RemovalIn != "" { removalNote = " and will be removed in " + di.RemovalIn } - fmt.Printf("\n%s ## DEPRECATED: The '%s' plugin is deprecated in version %s%s, %s.", comment, name, di.Since, removalNote, di.Notice) + fmt.Printf("\n%s ## DEPRECATED: The '%s' plugin is deprecated in version %s%s, %s.", comment, name, di.Since, removalNote, di.Notice) } config := p.SampleConfig() if config == "" { + fmt.Printf("\n#[[%s.%s]]", op, name) fmt.Printf("\n%s # no configuration\n\n", comment) } else { lines := strings.Split(config, "\n") + fmt.Print("\n") for i, line := range lines { - if i == 0 || i == len(lines)-1 { + if i == len(lines)-1 { fmt.Print("\n") continue } diff --git a/config/config_test.go b/config/config_test.go index 5a64cabcad424..03010098418f1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -678,7 +678,6 @@ type MockupInputPluginParserOld struct { } func (m *MockupInputPluginParserOld) SampleConfig() string { return "Mockup old parser test plugin" } -func (m *MockupInputPluginParserOld) Description() string { return "Mockup old parser test plugin" } func (m *MockupInputPluginParserOld) Gather(acc telegraf.Accumulator) error { return nil } func (m *MockupInputPluginParserOld) SetParser(parser parsers.Parser) { m.Parser = parser } func (m *MockupInputPluginParserOld) SetParserFunc(f parsers.ParserFunc) { m.ParserFunc = f } @@ -690,7 +689,6 @@ type MockupInputPluginParserNew struct { } func (m *MockupInputPluginParserNew) SampleConfig() string { return "Mockup old parser test plugin" } -func (m *MockupInputPluginParserNew) Description() string { return "Mockup old parser test plugin" } func (m *MockupInputPluginParserNew) Gather(acc telegraf.Accumulator) error { return nil } func (m *MockupInputPluginParserNew) SetParser(parser telegraf.Parser) { m.Parser = parser } func (m *MockupInputPluginParserNew) SetParserFunc(f telegraf.ParserFunc) { m.ParserFunc = f } @@ -714,7 +712,6 @@ type MockupInputPlugin struct { } func (m *MockupInputPlugin) SampleConfig() string { return "Mockup test input plugin" } -func (m *MockupInputPlugin) Description() string { return "Mockup test input plugin" } func (m *MockupInputPlugin) Gather(acc telegraf.Accumulator) error { return nil } func (m *MockupInputPlugin) SetParser(parser telegraf.Parser) { m.parser = parser } @@ -730,7 +727,6 @@ type MockupOuputPlugin struct { func (m *MockupOuputPlugin) Connect() error { return nil } func (m *MockupOuputPlugin) Close() error { return nil } -func (m *MockupOuputPlugin) Description() string { return "Mockup test output plugin" } func (m *MockupOuputPlugin) SampleConfig() string { return "Mockup test output plugin" } func (m *MockupOuputPlugin) Write(metrics []telegraf.Metric) error { return nil } diff --git a/docs/AGGREGATORS.md b/docs/AGGREGATORS.md index 265b9fa6893a9..d0604bc02ee02 100644 --- a/docs/AGGREGATORS.md +++ b/docs/AGGREGATORS.md @@ -9,10 +9,10 @@ This section is for developers who want to create a new aggregator plugin. register themselves. See below for a quick example. * To be available within Telegraf itself, plugins must add themselves to the `github.com/influxdata/telegraf/plugins/aggregators/all/all.go` file. -* The `SampleConfig` function should return valid toml that describes how the - plugin can be configured. This is included in `telegraf config`. Please - consult the [Sample Config][] page for the latest style guidelines. -* The `Description` function should say in one line what this aggregator does. +* Each plugin requires a file called `_sample_config.go`, where `` is replaced with the actual plugin name. + Copy the [example template](#sample-configuration-template) into this file, also updating `` were appropriate. + This file is automatically updated during the build process to include the sample configuration from the `README.md`. + Please consult the [Sample Config][] page for the latest style guidelines. * The Aggregator plugin will need to keep caches of metrics that have passed through it. This should be done using the builtin `HashID()` function of each metric. @@ -22,6 +22,8 @@ This section is for developers who want to create a new aggregator plugin. ### Aggregator Plugin Example ```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean package min // min.go @@ -44,26 +46,10 @@ func NewMin() telegraf.Aggregator { return m } -var sampleConfig = ` - ## period is the flush & clear interval of the aggregator. - period = "30s" - ## If true drop_original will drop the original metrics and - ## only send aggregates. - drop_original = false -` - func (m *Min) Init() error { return nil } -func (m *Min) SampleConfig() string { - return sampleConfig -} - -func (m *Min) Description() string { - return "Keep the aggregate min of each metric passing through." -} - func (m *Min) Add(in telegraf.Metric) { id := in.HashID() if _, ok := m.nameCache[id]; !ok { @@ -127,6 +113,19 @@ func init() { } ``` +### Sample Configuration Template + +```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean +// DON'T EDIT; This file is used as a template by tools/generate_plugindata +package + +func (k *) SampleConfig() string { + return `{{ .SampleConfig }}` +} +``` + [telegraf.Aggregator]: https://godoc.org/github.com/influxdata/telegraf#Aggregator [Sample Config]: https://github.com/influxdata/telegraf/blob/master/docs/developers/SAMPLE_CONFIG.md [Code Style]: https://github.com/influxdata/telegraf/blob/master/docs/developers/CODE_STYLE.md diff --git a/docs/INPUTS.md b/docs/INPUTS.md index 6f553b060aadb..e910578a36b2f 100644 --- a/docs/INPUTS.md +++ b/docs/INPUTS.md @@ -15,11 +15,10 @@ and submit new inputs. themselves. See below for a quick example. - Input Plugins must be added to the `github.com/influxdata/telegraf/plugins/inputs/all/all.go` file. -- The `SampleConfig` function should return valid toml that describes how the - plugin can be configured. This is included in `telegraf config`. Please - consult the [Sample Config][] page for the latest style - guidelines. -- The `Description` function should say in one line what this plugin does. +- Each plugin requires a file called `_sample_config.go`, where `` is replaced with the actual plugin name. + Copy the [example template](#sample-configuration-template) into this file, also updating `` were appropriate. + This file is automatically updated during the build process to include the sample configuration from the `README.md`. + Please consult the [Sample Config][] page for the latest style guidelines. - Follow the recommended [Code Style][]. Let's say you've written a plugin that emits metrics about processes on the @@ -28,10 +27,10 @@ current host. ## Input Plugin Example ```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean package simple -// simple.go - import ( "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" @@ -42,17 +41,6 @@ type Simple struct { Log telegraf.Logger `toml:"-"` } -func (s *Simple) Description() string { - return "a demo plugin" -} - -func (s *Simple) SampleConfig() string { - return ` - ## Indicate if everything is fine - ok = true -` -} - // Init is for setup, and validating config. func (s *Simple) Init() error { return nil @@ -73,6 +61,17 @@ func init() { } ``` +```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean +// DON'T EDIT; This file is used as a template by tools/generate_plugindata +package + +func (k *) SampleConfig() string { + return `{{ .SampleConfig }}` +} +``` + ### Development - Run `make static` followed by `make plugin-[pluginName]` to spin up a docker @@ -101,7 +100,7 @@ You can then utilize the parser internally in your plugin, parsing data as you see fit. Telegraf's configuration layer will take care of instantiating and creating the `Parser` object. -Add the following to the `SampleConfig()`: +Add the following to the sample configuration in the README.md: ```toml ## Data format to consume. diff --git a/docs/OUTPUTS.md b/docs/OUTPUTS.md index b9baa69a9d3f4..fd84b070b7d7f 100644 --- a/docs/OUTPUTS.md +++ b/docs/OUTPUTS.md @@ -11,15 +11,17 @@ similar constructs. themselves. See below for a quick example. - To be available within Telegraf itself, plugins must add themselves to the `github.com/influxdata/telegraf/plugins/outputs/all/all.go` file. -- The `SampleConfig` function should return valid toml that describes how the - plugin can be configured. This is included in `telegraf config`. Please - consult the [Sample Config][] page for the latest style guidelines. -- The `Description` function should say in one line what this output does. +- Each plugin requires a file called `_sample_config.go`, where `` is replaced with the actual plugin name. + Copy the [example template](#sample-configuration-template) into this file, also updating `` were appropriate. + This file is automatically updated during the build process to include the sample configuration from the `README.md`. + Please consult the [Sample Config][] page for the latest style guidelines. - Follow the recommended [Code Style][]. ## Output Plugin Example ```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean package simpleoutput // simpleoutput.go @@ -34,16 +36,6 @@ type Simple struct { Log telegraf.Logger `toml:"-"` } -func (s *Simple) Description() string { - return "a demo output" -} - -func (s *Simple) SampleConfig() string { - return ` - ok = true -` -} - // Init is for setup, and validating config. func (s *Simple) Init() error { return nil @@ -76,6 +68,19 @@ func init() { ``` +### Sample Configuration Template + +```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean +// DON'T EDIT; This file is used as a template by tools/generate_plugindata +package + +func (k *) SampleConfig() string { + return `{{ .SampleConfig }}` +} +``` + ## Data Formats Some output plugins, such as the [file][] plugin, can write in any supported diff --git a/docs/PROCESSORS.md b/docs/PROCESSORS.md index 44def8c9273bf..aeb40cf173cda 100644 --- a/docs/PROCESSORS.md +++ b/docs/PROCESSORS.md @@ -9,18 +9,17 @@ This section is for developers who want to create a new processor plugin. themselves. See below for a quick example. * To be available within Telegraf itself, plugins must add themselves to the `github.com/influxdata/telegraf/plugins/processors/all/all.go` file. -* The `SampleConfig` function should return valid toml that describes how the - processor can be configured. This is include in the output of `telegraf - config`. -* The `SampleConfig` function should return valid toml that describes how the - plugin can be configured. This is included in `telegraf config`. Please - consult the [Sample Config][] page for the latest style guidelines. -* The `Description` function should say in one line what this processor does. +* Each plugin requires a file called `_sample_config.go`, where `` is replaced with the actual plugin name. + Copy the [example template](#sample-configuration-template) into this file, also updating `` were appropriate. + This file is automatically updated during the build process to include the sample configuration from the `README.md`. + Please consult the [Sample Config][] page for the latest style guidelines. * Follow the recommended [Code Style][]. ## Processor Plugin Example ```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean package printer // printer.go @@ -36,17 +35,6 @@ type Printer struct { Log telegraf.Logger `toml:"-"` } -var sampleConfig = ` -` - -func (p *Printer) SampleConfig() string { - return sampleConfig -} - -func (p *Printer) Description() string { - return "Print all metrics that pass through this filter." -} - // Init is for setup, and validating config. func (p *Printer) Init() error { return nil @@ -66,6 +54,19 @@ func init() { } ``` +### Sample Configuration Template + +```go +//go:generate go run ../../../tools/generate_plugindata/main.go +//go:generate go run ../../../tools/generate_plugindata/main.go --clean +// DON'T EDIT; This file is used as a template by tools/generate_plugindata +package + +func (k *) SampleConfig() string { + return `{{ .SampleConfig }}` +} +``` + ## Streaming Processors Streaming processors are a new processor type available to you. They are @@ -102,17 +103,6 @@ type Printer struct { Log telegraf.Logger `toml:"-"` } -var sampleConfig = ` -` - -func (p *Printer) SampleConfig() string { - return sampleConfig -} - -func (p *Printer) Description() string { - return "Print all metrics that pass through this filter." -} - // Init is for setup, and validating config. func (p *Printer) Init() error { return nil diff --git a/docs/developers/SAMPLE_CONFIG.md b/docs/developers/SAMPLE_CONFIG.md index 2f67535de54b2..75ff181b6679d 100644 --- a/docs/developers/SAMPLE_CONFIG.md +++ b/docs/developers/SAMPLE_CONFIG.md @@ -1,7 +1,6 @@ # Sample Configuration -The sample config file is generated from a results of the `SampleConfig()` and -`Description()` functions of the plugins. +The sample config file is generated from a results of the `SampleConfig()` functions of the plugin. You can generate a full sample config: diff --git a/go.mod b/go.mod index 72de99e995ae5..d1cab392d35e7 100644 --- a/go.mod +++ b/go.mod @@ -137,6 +137,7 @@ require ( github.com/wavefronthq/wavefront-sdk-go v0.9.10 github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf github.com/xdg/scram v1.0.3 + github.com/yuin/goldmark v1.4.1 go.mongodb.org/mongo-driver v1.8.3 go.opentelemetry.io/collector/model v0.44.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.27.0 diff --git a/go.sum b/go.sum index 3eacacdb29c1d..64e1850dc178a 100644 --- a/go.sum +++ b/go.sum @@ -2261,6 +2261,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/gopher-lua v0.0.0-20200603152657-dc2b0ca8b37e/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da h1:NimzV1aGyq29m5ukMK0AMWEhFaL/lrEOaephfuoiARg= diff --git a/plugin.go b/plugin.go index 3f4004d766457..72421a5301276 100644 --- a/plugin.go +++ b/plugin.go @@ -26,11 +26,8 @@ type Initializer interface { // not part of the interface, but will receive an injected logger if it's set. // eg: Log telegraf.Logger `toml:"-"` type PluginDescriber interface { - // SampleConfig returns the default configuration of the Processor + // SampleConfig returns the default configuration of the Plugin SampleConfig() string - - // Description returns a one-sentence description on the Processor - Description() string } // Logger defines an plugin-related interface for logging. diff --git a/plugins/common/shim/config_test.go b/plugins/common/shim/config_test.go index ffe58a1d5de0a..69c18394ae274 100644 --- a/plugins/common/shim/config_test.go +++ b/plugins/common/shim/config_test.go @@ -34,16 +34,6 @@ func TestLoadConfig(t *testing.T) { require.Equal(t, `test"\test`, inp.SecretValue) } -func TestDefaultImportedPluginsSelfRegisters(t *testing.T) { - inputs.Add("test", func() telegraf.Input { - return &testInput{} - }) - - cfg, err := LoadConfig(nil) - require.NoError(t, err) - require.Equal(t, "test", cfg.Input.Description()) -} - func TestLoadingSpecialTypes(t *testing.T) { inputs.Add("test", func() telegraf.Input { return &testDurationInput{} diff --git a/plugins/common/shim/goshim_test.go b/plugins/common/shim/goshim_test.go index 0f2bd4c7d3bb9..1011f9e77293f 100644 --- a/plugins/common/shim/goshim_test.go +++ b/plugins/common/shim/goshim_test.go @@ -66,10 +66,6 @@ func (i *erroringInput) SampleConfig() string { return "" } -func (i *erroringInput) Description() string { - return "" -} - func (i *erroringInput) Gather(acc telegraf.Accumulator) error { acc.AddError(errors.New("intentional")) return nil diff --git a/plugins/processors/streamingprocessor.go b/plugins/processors/streamingprocessor.go index 95ebae2142b7a..185b8db9fdd75 100644 --- a/plugins/processors/streamingprocessor.go +++ b/plugins/processors/streamingprocessor.go @@ -24,10 +24,6 @@ func (sp *streamingProcessor) SampleConfig() string { return sp.processor.SampleConfig() } -func (sp *streamingProcessor) Description() string { - return sp.processor.Description() -} - func (sp *streamingProcessor) Start(acc telegraf.Accumulator) error { sp.acc = acc return nil diff --git a/tools/generate_plugindata/main.go b/tools/generate_plugindata/main.go new file mode 100644 index 0000000000000..bc096ea74527a --- /dev/null +++ b/tools/generate_plugindata/main.go @@ -0,0 +1,157 @@ +// generate_plugindata is a tool used to inject the sample configuration into all the plugins +// It extracts the sample configuration from the plugins README.md +// Then using the file plugin_name_sample_config.go as a template, and will be updated with the sample configuration +// This tool is then also used to revert these changes with the `--clean` flag +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "log" //nolint:revive + "os" + "strings" + "text/template" + + "github.com/yuin/goldmark" + gast "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/text" +) + +func createSourceName(packageName string) string { + return fmt.Sprintf("%s_sample_config.go", packageName) +} + +// extractPluginData reads the README.md to get the sample configuration +func extractPluginData() (string, error) { + readMe, err := os.ReadFile("README.md") + if err != nil { + return "", err + } + p := goldmark.DefaultParser() + r := text.NewReader(readMe) + root := p.Parse(r) + + var currentSection string + for n := root.FirstChild(); n != nil; n = n.NextSibling() { + switch tok := n.(type) { + case *gast.Heading: + if tok.FirstChild() != nil { + currentSection = string(tok.FirstChild().Text(readMe)) + } + case *gast.FencedCodeBlock: + if currentSection == "Configuration" && string(tok.Language(readMe)) == "toml" { + var config []byte + for i := 0; i < tok.Lines().Len(); i++ { + line := tok.Lines().At(i) + config = append(config, line.Value(readMe)...) + } + return string(config), nil + } + } + } + + fmt.Printf("No configuration found for plugin: %s\n", os.Getenv("GOPACKAGE")) + + return "", nil +} + +// generatePluginData parses the main source file of the plugin as a template and updates it with the sample configuration +// The original source file is saved so that these changes can be reverted +func generatePluginData(packageName string, sampleConfig string) error { + sourceName := createSourceName(packageName) + + plugin, err := os.ReadFile(sourceName) + if err != nil { + return err + } + + generatedTemplate := template.Must(template.New("").Parse(string(plugin))) + + f, err := os.Create(sourceName) + if err != nil { + return err + } + defer f.Close() + + err = generatedTemplate.Execute(f, struct { + SampleConfig string + }{ + SampleConfig: sampleConfig, + }) + if err != nil { + return err + } + + return nil +} + +var newSampleConfigFunc = ` return ` + "`{{ .SampleConfig }}`\n" + +// cleanGeneratedFiles will revert the changes made by generatePluginData +func cleanGeneratedFiles(packageName string) error { + sourceName := createSourceName(packageName) + sourcefile, err := os.Open(sourceName) + if err != nil { + return err + } + defer sourcefile.Close() + + var c []byte + buf := bytes.NewBuffer(c) + + scanner := bufio.NewScanner(sourcefile) + + var sampleconfigSection bool + for scanner.Scan() { + if sampleconfigSection && strings.TrimSpace(scanner.Text()) == "}" { + sampleconfigSection = false + if _, err := buf.Write([]byte(newSampleConfigFunc)); err != nil { + return err + } + } + + if !sampleconfigSection { + if _, err := buf.Write(scanner.Bytes()); err != nil { + return err + } + if _, err = buf.WriteString("\n"); err != nil { + return err + } + } + if !sampleconfigSection && strings.Contains(scanner.Text(), "SampleConfig() string") { + sampleconfigSection = true + } + } + + err = os.WriteFile(sourceName, buf.Bytes(), 0664) + if err != nil { + return err + } + return nil +} + +func main() { + clean := flag.Bool("clean", false, "Remove generated files") + flag.Parse() + + goPackage := os.Getenv("GOPACKAGE") + + if *clean { + err := cleanGeneratedFiles(goPackage) + if err != nil { + log.Fatal(err) + } + } else { + s, err := extractPluginData() + if err != nil { + log.Fatal(err) + } + + err = generatePluginData(goPackage, s) + if err != nil { + log.Fatal(err) + } + } +} diff --git a/tools/generate_plugindata/main_test.go b/tools/generate_plugindata/main_test.go new file mode 100644 index 0000000000000..24b34e87e542c --- /dev/null +++ b/tools/generate_plugindata/main_test.go @@ -0,0 +1,133 @@ +package main + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +var originalPlugin = `package main +func (*Plugin) SampleConfig() string { + return ` + "`{{ .SampleConfig }}`" + ` +} + +` + +func TestGeneratePluginData(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + readme := `# plugin + +## Configuration + +` + "```" + `toml +# test plugin +[[input.plugin]] + # No configuration +` + "```" + r, err := os.Create("README.md") + require.NoError(t, err) + _, err = r.Write([]byte(readme)) + require.NoError(t, err) + err = r.Close() + require.NoError(t, err) + + sourceFile, err := os.Create("test_sample_config.go") + require.NoError(t, err) + _, err = sourceFile.Write([]byte(originalPlugin)) + require.NoError(t, err) + err = sourceFile.Close() + require.NoError(t, err) + + defer func() { + err = os.Remove("test_sample_config.go") + require.NoError(t, err) + err = os.Remove("README.md") + require.NoError(t, err) + }() + + s, err := extractPluginData() + require.NoError(t, err) + + err = generatePluginData("test", s) + require.NoError(t, err) + + expected := `package main +func (*Plugin) SampleConfig() string { + return ` + "`" + `# test plugin +[[input.plugin]] + # No configuration +` + "`" + ` +} + +` + + newSourceFile, err := os.ReadFile("test_sample_config.go") + require.NoError(t, err) + + require.Equal(t, expected, string(newSourceFile)) +} + +func TestGeneratePluginDataNoConfig(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + readme := `# plugin` + + r, err := os.Create("README.md") + require.NoError(t, err) + _, err = r.Write([]byte(readme)) + require.NoError(t, err) + err = r.Close() + require.NoError(t, err) + + defer func() { + err = os.Remove("README.md") + require.NoError(t, err) + }() + + s, err := extractPluginData() + require.NoError(t, err) + require.Empty(t, s) +} + +func setupGeneratedPluginFile(t *testing.T, fileName string) { + // Create files that will be cleaned up + r, err := os.Create(fileName) + require.NoError(t, err) + defer r.Close() + + updatePlugin := `package main +func (*Plugin) SampleConfig() string { + return "I am a sample config" +} + +` + _, err = r.Write([]byte(updatePlugin)) + require.NoError(t, err) +} + +func TestCleanGeneratedFiles(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + filename := "testClean_sample_config.go" + + setupGeneratedPluginFile(t, filename) + + err := cleanGeneratedFiles("testClean") + require.NoError(t, err) + + b, err := os.ReadFile(filename) + require.NoError(t, err) + + require.Equal(t, originalPlugin, string(b)) + + err = os.Remove(filename) + require.NoError(t, err) +}