730 lines
24 KiB
Go
730 lines
24 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
"github.com/influxdata/telegraf/models"
|
|
"github.com/influxdata/telegraf/plugins/common/tls"
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
|
"github.com/influxdata/telegraf/plugins/outputs"
|
|
"github.com/influxdata/telegraf/plugins/parsers"
|
|
_ "github.com/influxdata/telegraf/plugins/parsers/all" // Blank import to have all parsers for testing
|
|
"github.com/influxdata/telegraf/plugins/parsers/json"
|
|
)
|
|
|
|
func TestReadBinaryFile(t *testing.T) {
|
|
// Create a temporary binary file using the Telegraf tool custom_builder to pass as a config
|
|
wd, err := os.Getwd()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := os.Chdir(wd)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
err = os.Chdir("../")
|
|
require.NoError(t, err)
|
|
tmpdir := t.TempDir()
|
|
binaryFile := filepath.Join(tmpdir, "custom_builder")
|
|
cmd := exec.Command("go", "build", "-o", binaryFile, "./tools/custom_builder")
|
|
var outb, errb bytes.Buffer
|
|
cmd.Stdout = &outb
|
|
cmd.Stderr = &errb
|
|
err = cmd.Run()
|
|
|
|
require.NoError(t, err, fmt.Sprintf("stdout: %s, stderr: %s", outb.String(), errb.String()))
|
|
c := NewConfig()
|
|
err = c.LoadConfig(binaryFile)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "provided config is not a TOML file")
|
|
}
|
|
|
|
func TestConfig_LoadSingleInputWithEnvVars(t *testing.T) {
|
|
c := NewConfig()
|
|
require.NoError(t, os.Setenv("MY_TEST_SERVER", "192.168.1.1"))
|
|
require.NoError(t, os.Setenv("TEST_INTERVAL", "10s"))
|
|
require.NoError(t, c.LoadConfig("./testdata/single_plugin_env_vars.toml"))
|
|
|
|
input := inputs.Inputs["memcached"]().(*MockupInputPlugin)
|
|
input.Servers = []string{"192.168.1.1"}
|
|
|
|
filter := models.Filter{
|
|
NameDrop: []string{"metricname2"},
|
|
NamePass: []string{"metricname1", "ip_192.168.1.1_name"},
|
|
FieldDrop: []string{"other", "stuff"},
|
|
FieldPass: []string{"some", "strings"},
|
|
TagDrop: []models.TagFilter{
|
|
{
|
|
Name: "badtag",
|
|
Filter: []string{"othertag"},
|
|
},
|
|
},
|
|
TagPass: []models.TagFilter{
|
|
{
|
|
Name: "goodtag",
|
|
Filter: []string{"mytag"},
|
|
},
|
|
},
|
|
}
|
|
require.NoError(t, filter.Compile())
|
|
inputConfig := &models.InputConfig{
|
|
Name: "memcached",
|
|
Filter: filter,
|
|
Interval: 10 * time.Second,
|
|
}
|
|
inputConfig.Tags = make(map[string]string)
|
|
|
|
// Ignore Log and Parser
|
|
c.Inputs[0].Input.(*MockupInputPlugin).Log = nil
|
|
c.Inputs[0].Input.(*MockupInputPlugin).parser = nil
|
|
require.Equal(t, input, c.Inputs[0].Input, "Testdata did not produce a correct mockup struct.")
|
|
require.Equal(t, inputConfig, c.Inputs[0].Config, "Testdata did not produce correct input metadata.")
|
|
}
|
|
|
|
func TestConfig_LoadSingleInput(t *testing.T) {
|
|
c := NewConfig()
|
|
c.LoadConfig("./testdata/single_plugin.toml")
|
|
|
|
input := inputs.Inputs["memcached"]().(*MockupInputPlugin)
|
|
input.Servers = []string{"localhost"}
|
|
|
|
filter := models.Filter{
|
|
NameDrop: []string{"metricname2"},
|
|
NamePass: []string{"metricname1"},
|
|
FieldDrop: []string{"other", "stuff"},
|
|
FieldPass: []string{"some", "strings"},
|
|
TagDrop: []models.TagFilter{
|
|
{
|
|
Name: "badtag",
|
|
Filter: []string{"othertag"},
|
|
},
|
|
},
|
|
TagPass: []models.TagFilter{
|
|
{
|
|
Name: "goodtag",
|
|
Filter: []string{"mytag"},
|
|
},
|
|
},
|
|
}
|
|
require.NoError(t, filter.Compile())
|
|
inputConfig := &models.InputConfig{
|
|
Name: "memcached",
|
|
Filter: filter,
|
|
Interval: 5 * time.Second,
|
|
}
|
|
inputConfig.Tags = make(map[string]string)
|
|
|
|
// Ignore Log and Parser
|
|
c.Inputs[0].Input.(*MockupInputPlugin).Log = nil
|
|
c.Inputs[0].Input.(*MockupInputPlugin).parser = nil
|
|
require.Equal(t, input, c.Inputs[0].Input, "Testdata did not produce a correct memcached struct.")
|
|
require.Equal(t, inputConfig, c.Inputs[0].Config, "Testdata did not produce correct memcached metadata.")
|
|
}
|
|
|
|
func TestConfig_LoadDirectory(t *testing.T) {
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig("./testdata/single_plugin.toml"))
|
|
require.NoError(t, c.LoadDirectory("./testdata/subconfig"))
|
|
|
|
// Create the expected data
|
|
expectedPlugins := make([]*MockupInputPlugin, 4)
|
|
expectedConfigs := make([]*models.InputConfig, 4)
|
|
|
|
expectedPlugins[0] = inputs.Inputs["memcached"]().(*MockupInputPlugin)
|
|
expectedPlugins[0].Servers = []string{"localhost"}
|
|
|
|
filterMockup := models.Filter{
|
|
NameDrop: []string{"metricname2"},
|
|
NamePass: []string{"metricname1"},
|
|
FieldDrop: []string{"other", "stuff"},
|
|
FieldPass: []string{"some", "strings"},
|
|
TagDrop: []models.TagFilter{
|
|
{
|
|
Name: "badtag",
|
|
Filter: []string{"othertag"},
|
|
},
|
|
},
|
|
TagPass: []models.TagFilter{
|
|
{
|
|
Name: "goodtag",
|
|
Filter: []string{"mytag"},
|
|
},
|
|
},
|
|
}
|
|
require.NoError(t, filterMockup.Compile())
|
|
expectedConfigs[0] = &models.InputConfig{
|
|
Name: "memcached",
|
|
Filter: filterMockup,
|
|
Interval: 5 * time.Second,
|
|
}
|
|
expectedConfigs[0].Tags = make(map[string]string)
|
|
|
|
expectedPlugins[1] = inputs.Inputs["exec"]().(*MockupInputPlugin)
|
|
parser := &json.Parser{
|
|
MetricName: "exec",
|
|
Strict: true,
|
|
}
|
|
require.NoError(t, parser.Init())
|
|
|
|
expectedPlugins[1].SetParser(parser)
|
|
expectedPlugins[1].Command = "/usr/bin/myothercollector --foo=bar"
|
|
expectedConfigs[1] = &models.InputConfig{
|
|
Name: "exec",
|
|
MeasurementSuffix: "_myothercollector",
|
|
}
|
|
expectedConfigs[1].Tags = make(map[string]string)
|
|
|
|
expectedPlugins[2] = inputs.Inputs["memcached"]().(*MockupInputPlugin)
|
|
expectedPlugins[2].Servers = []string{"192.168.1.1"}
|
|
|
|
filterMemcached := models.Filter{
|
|
NameDrop: []string{"metricname2"},
|
|
NamePass: []string{"metricname1"},
|
|
FieldDrop: []string{"other", "stuff"},
|
|
FieldPass: []string{"some", "strings"},
|
|
TagDrop: []models.TagFilter{
|
|
{
|
|
Name: "badtag",
|
|
Filter: []string{"othertag"},
|
|
},
|
|
},
|
|
TagPass: []models.TagFilter{
|
|
{
|
|
Name: "goodtag",
|
|
Filter: []string{"mytag"},
|
|
},
|
|
},
|
|
}
|
|
require.NoError(t, filterMemcached.Compile())
|
|
expectedConfigs[2] = &models.InputConfig{
|
|
Name: "memcached",
|
|
Filter: filterMemcached,
|
|
Interval: 5 * time.Second,
|
|
}
|
|
expectedConfigs[2].Tags = make(map[string]string)
|
|
|
|
expectedPlugins[3] = inputs.Inputs["procstat"]().(*MockupInputPlugin)
|
|
expectedPlugins[3].PidFile = "/var/run/grafana-server.pid"
|
|
expectedConfigs[3] = &models.InputConfig{Name: "procstat"}
|
|
expectedConfigs[3].Tags = make(map[string]string)
|
|
|
|
// Check the generated plugins
|
|
require.Len(t, c.Inputs, len(expectedPlugins))
|
|
require.Len(t, c.Inputs, len(expectedConfigs))
|
|
for i, plugin := range c.Inputs {
|
|
input := plugin.Input.(*MockupInputPlugin)
|
|
// Check the logger and ignore it for comparison
|
|
require.NotNil(t, input.Log)
|
|
input.Log = nil
|
|
|
|
// Check the parsers if any
|
|
if expectedPlugins[i].parser != nil {
|
|
runningParser, ok := input.parser.(*models.RunningParser)
|
|
require.True(t, ok)
|
|
|
|
// We only use the JSON parser here
|
|
parser, ok := runningParser.Parser.(*json.Parser)
|
|
require.True(t, ok)
|
|
|
|
// Prepare parser for comparison
|
|
require.NoError(t, parser.Init())
|
|
parser.Log = nil
|
|
|
|
// Compare the parser
|
|
require.Equalf(t, expectedPlugins[i].parser, parser, "Plugin %d: incorrect parser produced", i)
|
|
}
|
|
|
|
// Ignore the parsers for further comparisons
|
|
input.parser = nil
|
|
expectedPlugins[i].parser = nil
|
|
|
|
require.Equalf(t, expectedPlugins[i], plugin.Input, "Plugin %d: incorrect struct produced", i)
|
|
require.Equalf(t, expectedConfigs[i], plugin.Config, "Plugin %d: incorrect config produced", i)
|
|
}
|
|
}
|
|
|
|
func TestConfig_WrongCertPath(t *testing.T) {
|
|
c := NewConfig()
|
|
require.Error(t, c.LoadConfig("./testdata/wrong_cert_path.toml"))
|
|
}
|
|
|
|
func TestConfig_LoadSpecialTypes(t *testing.T) {
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig("./testdata/special_types.toml"))
|
|
require.Len(t, c.Inputs, 1)
|
|
|
|
input, ok := c.Inputs[0].Input.(*MockupInputPlugin)
|
|
require.True(t, ok)
|
|
// Tests telegraf duration parsing.
|
|
require.Equal(t, Duration(time.Second), input.WriteTimeout)
|
|
// Tests telegraf size parsing.
|
|
require.Equal(t, Size(1024*1024), input.MaxBodySize)
|
|
// Tests toml multiline basic strings on single line.
|
|
require.Equal(t, "./testdata/special_types.pem", input.TLSCert)
|
|
// Tests toml multiline basic strings on single line.
|
|
require.Equal(t, "./testdata/special_types.key", input.TLSKey)
|
|
// Tests toml multiline basic strings on multiple lines.
|
|
require.Equal(t, "/path/", strings.TrimRight(input.Paths[0], "\r\n"))
|
|
}
|
|
|
|
func TestConfig_FieldNotDefined(t *testing.T) {
|
|
c := NewConfig()
|
|
err := c.LoadConfig("./testdata/invalid_field.toml")
|
|
require.Error(t, err, "invalid field name")
|
|
require.Equal(t, "Error loading config file ./testdata/invalid_field.toml: plugin inputs.http_listener_v2: line 1: configuration specified the fields [\"not_a_field\"], but they weren't used", err.Error())
|
|
}
|
|
|
|
func TestConfig_WrongFieldType(t *testing.T) {
|
|
c := NewConfig()
|
|
err := c.LoadConfig("./testdata/wrong_field_type.toml")
|
|
require.Error(t, err, "invalid field type")
|
|
require.Equal(t, "Error loading config file ./testdata/wrong_field_type.toml: error parsing http_listener_v2, line 2: (config.MockupInputPlugin.Port) cannot unmarshal TOML string into int", err.Error())
|
|
|
|
c = NewConfig()
|
|
err = c.LoadConfig("./testdata/wrong_field_type2.toml")
|
|
require.Error(t, err, "invalid field type2")
|
|
require.Equal(t, "Error loading config file ./testdata/wrong_field_type2.toml: error parsing http_listener_v2, line 2: (config.MockupInputPlugin.Methods) cannot unmarshal TOML string into []string", err.Error())
|
|
}
|
|
|
|
func TestConfig_InlineTables(t *testing.T) {
|
|
// #4098
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig("./testdata/inline_table.toml"))
|
|
require.Len(t, c.Outputs, 2)
|
|
|
|
output, ok := c.Outputs[1].Output.(*MockupOuputPlugin)
|
|
require.True(t, ok)
|
|
require.Equal(t, map[string]string{"Authorization": "Token $TOKEN", "Content-Type": "application/json"}, output.Headers)
|
|
require.Equal(t, []string{"org_id"}, c.Outputs[0].Config.Filter.TagInclude)
|
|
}
|
|
|
|
func TestConfig_SliceComment(t *testing.T) {
|
|
t.Skipf("Skipping until #3642 is resolved")
|
|
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig("./testdata/slice_comment.toml"))
|
|
require.Len(t, c.Outputs, 1)
|
|
|
|
output, ok := c.Outputs[0].Output.(*MockupOuputPlugin)
|
|
require.True(t, ok)
|
|
require.Equal(t, []string{"test"}, output.Scopes)
|
|
}
|
|
|
|
func TestConfig_BadOrdering(t *testing.T) {
|
|
// #3444: when not using inline tables, care has to be taken so subsequent configuration
|
|
// doesn't become part of the table. This is not a bug, but TOML syntax.
|
|
c := NewConfig()
|
|
err := c.LoadConfig("./testdata/non_slice_slice.toml")
|
|
require.Error(t, err, "bad ordering")
|
|
require.Equal(t, "Error loading config file ./testdata/non_slice_slice.toml: error parsing http array, line 4: cannot unmarshal TOML array into string (need slice)", err.Error())
|
|
}
|
|
|
|
func TestConfig_AzureMonitorNamespacePrefix(t *testing.T) {
|
|
// #8256 Cannot use empty string as the namespace prefix
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig("./testdata/azure_monitor.toml"))
|
|
require.Len(t, c.Outputs, 2)
|
|
|
|
expectedPrefix := []string{"Telegraf/", ""}
|
|
for i, plugin := range c.Outputs {
|
|
output, ok := plugin.Output.(*MockupOuputPlugin)
|
|
require.True(t, ok)
|
|
require.Equal(t, expectedPrefix[i], output.NamespacePrefix)
|
|
}
|
|
}
|
|
|
|
func TestConfig_URLRetries3Fails(t *testing.T) {
|
|
httpLoadConfigRetryInterval = 0 * time.Second
|
|
responseCounter := 0
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
responseCounter++
|
|
}))
|
|
defer ts.Close()
|
|
|
|
expected := fmt.Sprintf("Error loading config file %s: Retry 3 of 3 failed to retrieve remote config: 404 Not Found", ts.URL)
|
|
|
|
c := NewConfig()
|
|
err := c.LoadConfig(ts.URL)
|
|
require.Error(t, err)
|
|
require.Equal(t, expected, err.Error())
|
|
require.Equal(t, 4, responseCounter)
|
|
}
|
|
|
|
func TestConfig_URLRetries3FailsThenPasses(t *testing.T) {
|
|
httpLoadConfigRetryInterval = 0 * time.Second
|
|
responseCounter := 0
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if responseCounter <= 2 {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
} else {
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
responseCounter++
|
|
}))
|
|
defer ts.Close()
|
|
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig(ts.URL))
|
|
require.Equal(t, 4, responseCounter)
|
|
}
|
|
|
|
func TestConfig_getDefaultConfigPathFromEnvURL(t *testing.T) {
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer ts.Close()
|
|
|
|
c := NewConfig()
|
|
err := os.Setenv("TELEGRAF_CONFIG_PATH", ts.URL)
|
|
require.NoError(t, err)
|
|
configPath, err := getDefaultConfigPath()
|
|
require.NoError(t, err)
|
|
require.Equal(t, ts.URL, configPath)
|
|
err = c.LoadConfig("")
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestConfig_URLLikeFileName(t *testing.T) {
|
|
c := NewConfig()
|
|
err := c.LoadConfig("http:##www.example.com.conf")
|
|
require.Error(t, err)
|
|
|
|
if runtime.GOOS == "windows" {
|
|
// The error file not found error message is different on windows
|
|
require.Equal(t, "Error loading config file http:##www.example.com.conf: open http:##www.example.com.conf: The system cannot find the file specified.", err.Error())
|
|
} else {
|
|
require.Equal(t, "Error loading config file http:##www.example.com.conf: open http:##www.example.com.conf: no such file or directory", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestConfig_ParserInterfaceNewFormat(t *testing.T) {
|
|
formats := []string{
|
|
"collectd",
|
|
"csv",
|
|
"dropwizard",
|
|
"form_urlencoded",
|
|
"graphite",
|
|
"grok",
|
|
"influx",
|
|
"json",
|
|
"json_v2",
|
|
"logfmt",
|
|
"nagios",
|
|
"prometheus",
|
|
"prometheusremotewrite",
|
|
"value",
|
|
"wavefront",
|
|
"xml", "xpath_json", "xpath_msgpack", "xpath_protobuf",
|
|
}
|
|
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig("./testdata/parsers_new.toml"))
|
|
require.Len(t, c.Inputs, len(formats))
|
|
|
|
cfg := parsers.Config{
|
|
CSVHeaderRowCount: 42,
|
|
DropwizardTagPathsMap: make(map[string]string),
|
|
GrokPatterns: []string{"%{COMBINED_LOG_FORMAT}"},
|
|
JSONStrict: true,
|
|
MetricName: "parser_test_new",
|
|
}
|
|
|
|
override := map[string]struct {
|
|
param map[string]interface{}
|
|
mask []string
|
|
}{
|
|
"csv": {
|
|
param: map[string]interface{}{
|
|
"HeaderRowCount": cfg.CSVHeaderRowCount,
|
|
},
|
|
mask: []string{"TimeFunc", "ResetMode"},
|
|
},
|
|
"xpath_protobuf": {
|
|
param: map[string]interface{}{
|
|
"ProtobufMessageDef": "testdata/addressbook.proto",
|
|
"ProtobufMessageType": "addressbook.AddressBook",
|
|
},
|
|
},
|
|
}
|
|
|
|
expected := make([]telegraf.Parser, 0, len(formats))
|
|
for _, format := range formats {
|
|
formatCfg := &cfg
|
|
formatCfg.DataFormat = format
|
|
|
|
logger := models.NewLogger("parsers", format, cfg.MetricName)
|
|
|
|
creator, found := parsers.Parsers[format]
|
|
require.Truef(t, found, "No parser for format %q", format)
|
|
|
|
parser := creator(formatCfg.MetricName)
|
|
if settings, found := override[format]; found {
|
|
s := reflect.Indirect(reflect.ValueOf(parser))
|
|
for key, value := range settings.param {
|
|
v := reflect.ValueOf(value)
|
|
s.FieldByName(key).Set(v)
|
|
}
|
|
}
|
|
models.SetLoggerOnPlugin(parser, logger)
|
|
if p, ok := parser.(telegraf.Initializer); ok {
|
|
require.NoError(t, p.Init())
|
|
}
|
|
expected = append(expected, parser)
|
|
}
|
|
require.Len(t, expected, len(formats))
|
|
|
|
actual := make([]interface{}, 0)
|
|
generated := make([]interface{}, 0)
|
|
for _, plugin := range c.Inputs {
|
|
input, ok := plugin.Input.(*MockupInputPluginParserNew)
|
|
require.True(t, ok)
|
|
// Get the parser set with 'SetParser()'
|
|
if p, ok := input.Parser.(*models.RunningParser); ok {
|
|
actual = append(actual, p.Parser)
|
|
} else {
|
|
actual = append(actual, input.Parser)
|
|
}
|
|
// Get the parser set with 'SetParserFunc()'
|
|
g, err := input.ParserFunc()
|
|
require.NoError(t, err)
|
|
if rp, ok := g.(*models.RunningParser); ok {
|
|
generated = append(generated, rp.Parser)
|
|
} else {
|
|
generated = append(generated, g)
|
|
}
|
|
}
|
|
require.Len(t, actual, len(formats))
|
|
|
|
for i, format := range formats {
|
|
// Determine the underlying type of the parser
|
|
stype := reflect.Indirect(reflect.ValueOf(expected[i])).Interface()
|
|
// Ignore all unexported fields and fields not relevant for functionality
|
|
options := []cmp.Option{
|
|
cmpopts.IgnoreUnexported(stype),
|
|
cmpopts.IgnoreTypes(sync.Mutex{}),
|
|
cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}),
|
|
}
|
|
if settings, found := override[format]; found {
|
|
options = append(options, cmpopts.IgnoreFields(stype, settings.mask...))
|
|
}
|
|
|
|
// Do a manual comparision as require.EqualValues will also work on unexported fields
|
|
// that cannot be cleared or ignored.
|
|
diff := cmp.Diff(expected[i], actual[i], options...)
|
|
require.Emptyf(t, diff, "Difference in SetParser() for %q", format)
|
|
diff = cmp.Diff(expected[i], generated[i], options...)
|
|
require.Emptyf(t, diff, "Difference in SetParserFunc() for %q", format)
|
|
}
|
|
}
|
|
|
|
func TestConfig_ParserInterfaceOldFormat(t *testing.T) {
|
|
formats := []string{
|
|
"collectd",
|
|
"csv",
|
|
"dropwizard",
|
|
"form_urlencoded",
|
|
"graphite",
|
|
"grok",
|
|
"influx",
|
|
"json",
|
|
"json_v2",
|
|
"logfmt",
|
|
"nagios",
|
|
"prometheus",
|
|
"prometheusremotewrite",
|
|
"value",
|
|
"wavefront",
|
|
"xml", "xpath_json", "xpath_msgpack", "xpath_protobuf",
|
|
}
|
|
|
|
c := NewConfig()
|
|
require.NoError(t, c.LoadConfig("./testdata/parsers_old.toml"))
|
|
require.Len(t, c.Inputs, len(formats))
|
|
|
|
cfg := parsers.Config{
|
|
CSVHeaderRowCount: 42,
|
|
DropwizardTagPathsMap: make(map[string]string),
|
|
GrokPatterns: []string{"%{COMBINED_LOG_FORMAT}"},
|
|
JSONStrict: true,
|
|
MetricName: "parser_test_old",
|
|
}
|
|
|
|
override := map[string]struct {
|
|
param map[string]interface{}
|
|
mask []string
|
|
}{
|
|
"csv": {
|
|
param: map[string]interface{}{
|
|
"HeaderRowCount": cfg.CSVHeaderRowCount,
|
|
},
|
|
mask: []string{"TimeFunc", "ResetMode"},
|
|
},
|
|
"xpath_protobuf": {
|
|
param: map[string]interface{}{
|
|
"ProtobufMessageDef": "testdata/addressbook.proto",
|
|
"ProtobufMessageType": "addressbook.AddressBook",
|
|
},
|
|
},
|
|
}
|
|
|
|
expected := make([]telegraf.Parser, 0, len(formats))
|
|
for _, format := range formats {
|
|
formatCfg := &cfg
|
|
formatCfg.DataFormat = format
|
|
|
|
logger := models.NewLogger("parsers", format, cfg.MetricName)
|
|
|
|
creator, found := parsers.Parsers[format]
|
|
require.Truef(t, found, "No parser for format %q", format)
|
|
|
|
parser := creator(formatCfg.MetricName)
|
|
if settings, found := override[format]; found {
|
|
s := reflect.Indirect(reflect.ValueOf(parser))
|
|
for key, value := range settings.param {
|
|
v := reflect.ValueOf(value)
|
|
s.FieldByName(key).Set(v)
|
|
}
|
|
}
|
|
models.SetLoggerOnPlugin(parser, logger)
|
|
if p, ok := parser.(telegraf.Initializer); ok {
|
|
require.NoError(t, p.Init())
|
|
}
|
|
expected = append(expected, parser)
|
|
}
|
|
require.Len(t, expected, len(formats))
|
|
|
|
actual := make([]interface{}, 0)
|
|
generated := make([]interface{}, 0)
|
|
for _, plugin := range c.Inputs {
|
|
input, ok := plugin.Input.(*MockupInputPluginParserOld)
|
|
require.True(t, ok)
|
|
// Get the parser set with 'SetParser()'
|
|
if p, ok := input.Parser.(*models.RunningParser); ok {
|
|
actual = append(actual, p.Parser)
|
|
} else {
|
|
actual = append(actual, input.Parser)
|
|
}
|
|
// Get the parser set with 'SetParserFunc()'
|
|
g, err := input.ParserFunc()
|
|
require.NoError(t, err)
|
|
if rp, ok := g.(*models.RunningParser); ok {
|
|
generated = append(generated, rp.Parser)
|
|
} else {
|
|
generated = append(generated, g)
|
|
}
|
|
}
|
|
require.Len(t, actual, len(formats))
|
|
|
|
for i, format := range formats {
|
|
// Determine the underlying type of the parser
|
|
stype := reflect.Indirect(reflect.ValueOf(expected[i])).Interface()
|
|
// Ignore all unexported fields and fields not relevant for functionality
|
|
options := []cmp.Option{
|
|
cmpopts.IgnoreUnexported(stype),
|
|
cmpopts.IgnoreTypes(sync.Mutex{}),
|
|
cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}),
|
|
}
|
|
if settings, found := override[format]; found {
|
|
options = append(options, cmpopts.IgnoreFields(stype, settings.mask...))
|
|
}
|
|
|
|
// Do a manual comparison as require.EqualValues will also work on unexported fields
|
|
// that cannot be cleared or ignored.
|
|
diff := cmp.Diff(expected[i], actual[i], options...)
|
|
require.Emptyf(t, diff, "Difference in SetParser() for %q", format)
|
|
diff = cmp.Diff(expected[i], generated[i], options...)
|
|
require.Emptyf(t, diff, "Difference in SetParserFunc() for %q", format)
|
|
}
|
|
}
|
|
|
|
/*** Mockup INPUT plugin for (old) parser testing to avoid cyclic dependencies ***/
|
|
type MockupInputPluginParserOld struct {
|
|
Parser parsers.Parser
|
|
ParserFunc parsers.ParserFunc
|
|
}
|
|
|
|
func (m *MockupInputPluginParserOld) SampleConfig() 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 }
|
|
|
|
/*** Mockup INPUT plugin for (new) parser testing to avoid cyclic dependencies ***/
|
|
type MockupInputPluginParserNew struct {
|
|
Parser telegraf.Parser
|
|
ParserFunc telegraf.ParserFunc
|
|
}
|
|
|
|
func (m *MockupInputPluginParserNew) SampleConfig() 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 }
|
|
|
|
/*** Mockup INPUT plugin for testing to avoid cyclic dependencies ***/
|
|
type MockupInputPlugin struct {
|
|
Servers []string `toml:"servers"`
|
|
Methods []string `toml:"methods"`
|
|
Timeout Duration `toml:"timeout"`
|
|
ReadTimeout Duration `toml:"read_timeout"`
|
|
WriteTimeout Duration `toml:"write_timeout"`
|
|
MaxBodySize Size `toml:"max_body_size"`
|
|
Paths []string `toml:"paths"`
|
|
Port int `toml:"port"`
|
|
Command string
|
|
PidFile string
|
|
Log telegraf.Logger `toml:"-"`
|
|
tls.ServerConfig
|
|
|
|
parser telegraf.Parser
|
|
}
|
|
|
|
func (m *MockupInputPlugin) SampleConfig() 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 }
|
|
|
|
/*** Mockup OUTPUT plugin for testing to avoid cyclic dependencies ***/
|
|
type MockupOuputPlugin struct {
|
|
URL string `toml:"url"`
|
|
Headers map[string]string `toml:"headers"`
|
|
Scopes []string `toml:"scopes"`
|
|
NamespacePrefix string `toml:"namespace_prefix"`
|
|
Log telegraf.Logger `toml:"-"`
|
|
tls.ClientConfig
|
|
}
|
|
|
|
func (m *MockupOuputPlugin) Connect() error { return nil }
|
|
func (m *MockupOuputPlugin) Close() error { return nil }
|
|
func (m *MockupOuputPlugin) SampleConfig() string { return "Mockup test output plugin" }
|
|
func (m *MockupOuputPlugin) Write(metrics []telegraf.Metric) error { return nil }
|
|
|
|
// Register the mockup plugin on loading
|
|
func init() {
|
|
// Register the mockup input plugin for the required names
|
|
inputs.Add("parser_test_new", func() telegraf.Input { return &MockupInputPluginParserNew{} })
|
|
inputs.Add("parser_test_old", func() telegraf.Input { return &MockupInputPluginParserOld{} })
|
|
inputs.Add("exec", func() telegraf.Input { return &MockupInputPlugin{Timeout: Duration(time.Second * 5)} })
|
|
inputs.Add("http_listener_v2", func() telegraf.Input { return &MockupInputPlugin{} })
|
|
inputs.Add("memcached", func() telegraf.Input { return &MockupInputPlugin{} })
|
|
inputs.Add("procstat", func() telegraf.Input { return &MockupInputPlugin{} })
|
|
|
|
// Register the mockup output plugin for the required names
|
|
outputs.Add("azure_monitor", func() telegraf.Output { return &MockupOuputPlugin{NamespacePrefix: "Telegraf/"} })
|
|
outputs.Add("http", func() telegraf.Output { return &MockupOuputPlugin{} })
|
|
}
|