package input import ( _ "embed" "errors" "os" "path/filepath" "time" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" "github.com/influxdata/telegraf/plugins/parsers/influx/influx_upstream" "github.com/influxdata/telegraf/testutil" ) //go:embed sample.conf var sampleConfig string // Example struct should be named the same as the Plugin type Plugin struct { Files []string `toml:"files"` DefaultTags map[string]string `toml:"default_tag_defs"` AdditionalParams map[string]interface{} `toml:"additional_params"` Parser telegraf.Parser `toml:"-"` Log telegraf.Logger `toml:"-"` // Settings used by test-code Path string `toml:"-"` // start path for relative files ExpectedFilename string `toml:"-"` // filename of the expected metrics (default: expected.out) UseTypeTag string `toml:"-"` // if specified use this tag to infer metric type // Test-data derived from the files Expected []telegraf.Metric `toml:"-"` // expected metrics ExpectedErrors []string `toml:"-"` // expected errors ShouldIgnoreTimestamp bool `toml:"-"` // flag indicating if the expected metrics do have timestamps // Internal data inputFilenames []string } func (*Plugin) SampleConfig() string { return sampleConfig } func (p *Plugin) Init() error { // Setup the filenames p.inputFilenames = make([]string, 0, len(p.Files)) for _, fn := range p.Files { if !filepath.IsAbs(fn) && p.Path != "" { fn = filepath.Join(p.Path, fn) } p.inputFilenames = append(p.inputFilenames, fn) } // Setup an influx parser for reading the expected metrics expectedParser := &influx_upstream.Parser{} if err := expectedParser.Init(); err != nil { return err } expectedParser.SetTimeFunc(func() time.Time { return time.Time{} }) // Read the expected metrics if any expectedFn := "expected.out" if p.ExpectedFilename != "" { expectedFn = p.ExpectedFilename } if !filepath.IsAbs(expectedFn) && p.Path != "" { expectedFn = filepath.Join(p.Path, expectedFn) } if _, err := os.Stat(expectedFn); err == nil { var err error p.Expected, err = testutil.ParseMetricsFromFile(expectedFn, expectedParser) if err != nil { return err } } // Read the expected errors if any expectedErrorFn := "expected.err" if !filepath.IsAbs(expectedErrorFn) && p.Path != "" { expectedErrorFn = filepath.Join(p.Path, expectedErrorFn) } if _, err := os.Stat(expectedErrorFn); err == nil { var err error p.ExpectedErrors, err = testutil.ParseLinesFromFile(expectedErrorFn) if err != nil { return err } if len(p.ExpectedErrors) == 0 { return errors.New("got empty expected errors file") } } // Fixup the metric type if requested if p.UseTypeTag != "" { for i, m := range p.Expected { typeTag, found := m.GetTag(p.UseTypeTag) if !found { continue } var mtype telegraf.ValueType switch typeTag { case "counter": mtype = telegraf.Counter case "gauge": mtype = telegraf.Gauge case "untyped": mtype = telegraf.Untyped case "summary": mtype = telegraf.Summary case "histogram": mtype = telegraf.Histogram default: continue } m.SetType(mtype) m.RemoveTag(p.UseTypeTag) p.Expected[i] = m } } // Determine if we should check the timestamps indicated by a missing // timestamp in the expected input for i, m := range p.Expected { missingTimestamp := m.Time().IsZero() if i == 0 { p.ShouldIgnoreTimestamp = missingTimestamp continue } if missingTimestamp != p.ShouldIgnoreTimestamp { return errors.New("mixed timestamp and non-timestamp data in expected metrics") } } // Set the parser's default tags, just in case if p.Parser != nil { p.Parser.SetDefaultTags(p.DefaultTags) } return nil } func (p *Plugin) Gather(acc telegraf.Accumulator) error { if p.Parser == nil { return errors.New("no parser defined") } for _, fn := range p.inputFilenames { data, err := os.ReadFile(fn) if err != nil { return err } metrics, err := p.Parser.Parse(data) if err != nil { return err } for _, m := range metrics { acc.AddMetric(m) } } return nil } func (p *Plugin) SetParser(parser telegraf.Parser) { p.Parser = parser if len(p.DefaultTags) > 0 { p.Parser.SetDefaultTags(p.DefaultTags) } } // Register the plugin func init() { inputs.Add("test", func() telegraf.Input { return &Plugin{} }) }