github.com/influxdata/telegraf@v1.30.3/config/config_test.go (about)

     1  package config_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"reflect"
    12  	"regexp"
    13  	"runtime"
    14  	"strings"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/google/go-cmp/cmp"
    20  	"github.com/google/go-cmp/cmp/cmpopts"
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/influxdata/telegraf"
    24  	"github.com/influxdata/telegraf/config"
    25  	"github.com/influxdata/telegraf/metric"
    26  	"github.com/influxdata/telegraf/models"
    27  	"github.com/influxdata/telegraf/persister"
    28  	"github.com/influxdata/telegraf/plugins/common/tls"
    29  	"github.com/influxdata/telegraf/plugins/inputs"
    30  	"github.com/influxdata/telegraf/plugins/outputs"
    31  	"github.com/influxdata/telegraf/plugins/parsers"
    32  	_ "github.com/influxdata/telegraf/plugins/parsers/all" // Blank import to have all parsers for testing
    33  	"github.com/influxdata/telegraf/plugins/parsers/json"
    34  	"github.com/influxdata/telegraf/plugins/processors"
    35  	"github.com/influxdata/telegraf/plugins/serializers"
    36  	_ "github.com/influxdata/telegraf/plugins/serializers/all" // Blank import to have all serializers for testing
    37  	promserializer "github.com/influxdata/telegraf/plugins/serializers/prometheus"
    38  	"github.com/influxdata/telegraf/testutil"
    39  )
    40  
    41  func TestReadBinaryFile(t *testing.T) {
    42  	// Create a temporary binary file using the Telegraf tool custom_builder to pass as a config
    43  	wd, err := os.Getwd()
    44  	require.NoError(t, err)
    45  	t.Cleanup(func() {
    46  		err := os.Chdir(wd)
    47  		require.NoError(t, err)
    48  	})
    49  
    50  	err = os.Chdir("../")
    51  	require.NoError(t, err)
    52  	tmpdir := t.TempDir()
    53  	binaryFile := filepath.Join(tmpdir, "custom_builder")
    54  	cmd := exec.Command("go", "build", "-o", binaryFile, "./tools/custom_builder")
    55  	var outb, errb bytes.Buffer
    56  	cmd.Stdout = &outb
    57  	cmd.Stderr = &errb
    58  	err = cmd.Run()
    59  
    60  	require.NoError(t, err, fmt.Sprintf("stdout: %s, stderr: %s", outb.String(), errb.String()))
    61  	c := config.NewConfig()
    62  	err = c.LoadConfig(binaryFile)
    63  	require.Error(t, err)
    64  	require.ErrorContains(t, err, "provided config is not a TOML file")
    65  }
    66  
    67  func TestConfig_LoadSingleInputWithEnvVars(t *testing.T) {
    68  	c := config.NewConfig()
    69  	t.Setenv("MY_TEST_SERVER", "192.168.1.1")
    70  	t.Setenv("TEST_INTERVAL", "10s")
    71  	require.NoError(t, c.LoadConfig("./testdata/single_plugin_env_vars.toml"))
    72  
    73  	input := inputs.Inputs["memcached"]().(*MockupInputPlugin)
    74  	input.Servers = []string{"192.168.1.1"}
    75  	input.Command = `Raw command which may or may not contain # in it
    76  # is unique`
    77  
    78  	filter := models.Filter{
    79  		NameDrop:     []string{"metricname2"},
    80  		NamePass:     []string{"metricname1", "ip_192.168.1.1_name"},
    81  		FieldExclude: []string{"other", "stuff"},
    82  		FieldInclude: []string{"some", "strings"},
    83  		TagDropFilters: []models.TagFilter{
    84  			{
    85  				Name:   "badtag",
    86  				Values: []string{"othertag"},
    87  			},
    88  		},
    89  		TagPassFilters: []models.TagFilter{
    90  			{
    91  				Name:   "goodtag",
    92  				Values: []string{"mytag", "tagwith#value", "TagWithMultilineSyntax"},
    93  			},
    94  		},
    95  	}
    96  	require.NoError(t, filter.Compile())
    97  	inputConfig := &models.InputConfig{
    98  		Name:     "memcached",
    99  		Filter:   filter,
   100  		Interval: 10 * time.Second,
   101  	}
   102  	inputConfig.Tags = make(map[string]string)
   103  
   104  	// Ignore Log, Parser and ID
   105  	c.Inputs[0].Input.(*MockupInputPlugin).Log = nil
   106  	c.Inputs[0].Input.(*MockupInputPlugin).parser = nil
   107  	c.Inputs[0].Config.ID = ""
   108  	require.Equal(t, input, c.Inputs[0].Input, "Testdata did not produce a correct mockup struct.")
   109  	require.Equal(t, inputConfig, c.Inputs[0].Config, "Testdata did not produce correct input metadata.")
   110  }
   111  
   112  func TestConfig_LoadSingleInput(t *testing.T) {
   113  	c := config.NewConfig()
   114  	require.NoError(t, c.LoadConfig("./testdata/single_plugin.toml"))
   115  
   116  	input := inputs.Inputs["memcached"]().(*MockupInputPlugin)
   117  	input.Servers = []string{"localhost"}
   118  
   119  	filter := models.Filter{
   120  		NameDrop:     []string{"metricname2"},
   121  		NamePass:     []string{"metricname1"},
   122  		FieldExclude: []string{"other", "stuff"},
   123  		FieldInclude: []string{"some", "strings"},
   124  		TagDropFilters: []models.TagFilter{
   125  			{
   126  				Name:   "badtag",
   127  				Values: []string{"othertag"},
   128  			},
   129  		},
   130  		TagPassFilters: []models.TagFilter{
   131  			{
   132  				Name:   "goodtag",
   133  				Values: []string{"mytag"},
   134  			},
   135  		},
   136  	}
   137  	require.NoError(t, filter.Compile())
   138  	inputConfig := &models.InputConfig{
   139  		Name:     "memcached",
   140  		Filter:   filter,
   141  		Interval: 5 * time.Second,
   142  	}
   143  	inputConfig.Tags = make(map[string]string)
   144  
   145  	// Ignore Log, Parser and ID
   146  	c.Inputs[0].Input.(*MockupInputPlugin).Log = nil
   147  	c.Inputs[0].Input.(*MockupInputPlugin).parser = nil
   148  	c.Inputs[0].Config.ID = ""
   149  	require.Equal(t, input, c.Inputs[0].Input, "Testdata did not produce a correct memcached struct.")
   150  	require.Equal(t, inputConfig, c.Inputs[0].Config, "Testdata did not produce correct memcached metadata.")
   151  }
   152  
   153  func TestConfig_LoadSingleInput_WithSeparators(t *testing.T) {
   154  	c := config.NewConfig()
   155  	require.NoError(t, c.LoadConfig("./testdata/single_plugin_with_separators.toml"))
   156  
   157  	input := inputs.Inputs["memcached"]().(*MockupInputPlugin)
   158  	input.Servers = []string{"localhost"}
   159  
   160  	filter := models.Filter{
   161  		NameDrop:           []string{"metricname2"},
   162  		NameDropSeparators: ".",
   163  		NamePass:           []string{"metricname1"},
   164  		NamePassSeparators: ".",
   165  		FieldExclude:       []string{"other", "stuff"},
   166  		FieldInclude:       []string{"some", "strings"},
   167  		TagDropFilters: []models.TagFilter{
   168  			{
   169  				Name:   "badtag",
   170  				Values: []string{"othertag"},
   171  			},
   172  		},
   173  		TagPassFilters: []models.TagFilter{
   174  			{
   175  				Name:   "goodtag",
   176  				Values: []string{"mytag"},
   177  			},
   178  		},
   179  	}
   180  	require.NoError(t, filter.Compile())
   181  	inputConfig := &models.InputConfig{
   182  		Name:     "memcached",
   183  		Filter:   filter,
   184  		Interval: 5 * time.Second,
   185  	}
   186  	inputConfig.Tags = make(map[string]string)
   187  
   188  	// Ignore Log, Parser and ID
   189  	c.Inputs[0].Input.(*MockupInputPlugin).Log = nil
   190  	c.Inputs[0].Input.(*MockupInputPlugin).parser = nil
   191  	c.Inputs[0].Config.ID = ""
   192  	require.Equal(t, input, c.Inputs[0].Input, "Testdata did not produce a correct memcached struct.")
   193  	require.Equal(t, inputConfig, c.Inputs[0].Config, "Testdata did not produce correct memcached metadata.")
   194  }
   195  
   196  func TestConfig_LoadSingleInput_WithCommentInArray(t *testing.T) {
   197  	c := config.NewConfig()
   198  	require.NoError(t, c.LoadConfig("./testdata/single_plugin_with_comment_in_array.toml"))
   199  	require.Len(t, c.Inputs, 1)
   200  
   201  	input := c.Inputs[0].Input.(*MockupInputPlugin)
   202  	require.ElementsMatch(t, input.Servers, []string{"localhost"})
   203  }
   204  
   205  func TestConfig_LoadDirectory(t *testing.T) {
   206  	c := config.NewConfig()
   207  
   208  	files, err := config.WalkDirectory("./testdata/subconfig")
   209  	files = append([]string{"./testdata/single_plugin.toml"}, files...)
   210  	require.NoError(t, err)
   211  	require.NoError(t, c.LoadAll(files...))
   212  
   213  	// Create the expected data
   214  	expectedPlugins := make([]*MockupInputPlugin, 4)
   215  	expectedConfigs := make([]*models.InputConfig, 4)
   216  
   217  	expectedPlugins[0] = inputs.Inputs["memcached"]().(*MockupInputPlugin)
   218  	expectedPlugins[0].Servers = []string{"localhost"}
   219  
   220  	filterMockup := models.Filter{
   221  		NameDrop:     []string{"metricname2"},
   222  		NamePass:     []string{"metricname1"},
   223  		FieldExclude: []string{"other", "stuff"},
   224  		FieldInclude: []string{"some", "strings"},
   225  		TagDropFilters: []models.TagFilter{
   226  			{
   227  				Name:   "badtag",
   228  				Values: []string{"othertag"},
   229  			},
   230  		},
   231  		TagPassFilters: []models.TagFilter{
   232  			{
   233  				Name:   "goodtag",
   234  				Values: []string{"mytag"},
   235  			},
   236  		},
   237  	}
   238  	require.NoError(t, filterMockup.Compile())
   239  	expectedConfigs[0] = &models.InputConfig{
   240  		Name:     "memcached",
   241  		Filter:   filterMockup,
   242  		Interval: 5 * time.Second,
   243  	}
   244  	expectedConfigs[0].Tags = make(map[string]string)
   245  
   246  	expectedPlugins[1] = inputs.Inputs["exec"]().(*MockupInputPlugin)
   247  	parser := &json.Parser{
   248  		MetricName: "exec",
   249  		Strict:     true,
   250  	}
   251  	require.NoError(t, parser.Init())
   252  
   253  	expectedPlugins[1].SetParser(parser)
   254  	expectedPlugins[1].Command = "/usr/bin/myothercollector --foo=bar"
   255  	expectedConfigs[1] = &models.InputConfig{
   256  		Name:              "exec",
   257  		MeasurementSuffix: "_myothercollector",
   258  	}
   259  	expectedConfigs[1].Tags = make(map[string]string)
   260  
   261  	expectedPlugins[2] = inputs.Inputs["memcached"]().(*MockupInputPlugin)
   262  	expectedPlugins[2].Servers = []string{"192.168.1.1"}
   263  
   264  	filterMemcached := models.Filter{
   265  		NameDrop:     []string{"metricname2"},
   266  		NamePass:     []string{"metricname1"},
   267  		FieldExclude: []string{"other", "stuff"},
   268  		FieldInclude: []string{"some", "strings"},
   269  		TagDropFilters: []models.TagFilter{
   270  			{
   271  				Name:   "badtag",
   272  				Values: []string{"othertag"},
   273  			},
   274  		},
   275  		TagPassFilters: []models.TagFilter{
   276  			{
   277  				Name:   "goodtag",
   278  				Values: []string{"mytag"},
   279  			},
   280  		},
   281  	}
   282  	require.NoError(t, filterMemcached.Compile())
   283  	expectedConfigs[2] = &models.InputConfig{
   284  		Name:     "memcached",
   285  		Filter:   filterMemcached,
   286  		Interval: 5 * time.Second,
   287  	}
   288  	expectedConfigs[2].Tags = make(map[string]string)
   289  
   290  	expectedPlugins[3] = inputs.Inputs["procstat"]().(*MockupInputPlugin)
   291  	expectedPlugins[3].PidFile = "/var/run/grafana-server.pid"
   292  	expectedConfigs[3] = &models.InputConfig{Name: "procstat"}
   293  	expectedConfigs[3].Tags = make(map[string]string)
   294  
   295  	// Check the generated plugins
   296  	require.Len(t, c.Inputs, len(expectedPlugins))
   297  	require.Len(t, c.Inputs, len(expectedConfigs))
   298  	for i, plugin := range c.Inputs {
   299  		input := plugin.Input.(*MockupInputPlugin)
   300  		// Check the logger and ignore it for comparison
   301  		require.NotNil(t, input.Log)
   302  		input.Log = nil
   303  
   304  		// Check the parsers if any
   305  		if expectedPlugins[i].parser != nil {
   306  			runningParser, ok := input.parser.(*models.RunningParser)
   307  			require.True(t, ok)
   308  
   309  			// We only use the JSON parser here
   310  			parser, ok := runningParser.Parser.(*json.Parser)
   311  			require.True(t, ok)
   312  
   313  			// Prepare parser for comparison
   314  			require.NoError(t, parser.Init())
   315  			parser.Log = nil
   316  
   317  			// Compare the parser
   318  			require.Equalf(t, expectedPlugins[i].parser, parser, "Plugin %d: incorrect parser produced", i)
   319  		}
   320  
   321  		// Ignore the parsers for further comparisons
   322  		input.parser = nil
   323  		expectedPlugins[i].parser = nil
   324  
   325  		// Ignore the ID
   326  		plugin.Config.ID = ""
   327  
   328  		require.Equalf(t, expectedPlugins[i], plugin.Input, "Plugin %d: incorrect struct produced", i)
   329  		require.Equalf(t, expectedConfigs[i], plugin.Config, "Plugin %d: incorrect config produced", i)
   330  	}
   331  }
   332  
   333  func TestConfig_WrongCertPath(t *testing.T) {
   334  	c := config.NewConfig()
   335  	require.Error(t, c.LoadConfig("./testdata/wrong_cert_path.toml"))
   336  }
   337  
   338  func TestConfig_DefaultParser(t *testing.T) {
   339  	c := config.NewConfig()
   340  	require.NoError(t, c.LoadConfig("./testdata/default_parser.toml"))
   341  }
   342  
   343  func TestConfig_DefaultExecParser(t *testing.T) {
   344  	c := config.NewConfig()
   345  	require.NoError(t, c.LoadConfig("./testdata/default_parser_exec.toml"))
   346  }
   347  
   348  func TestConfig_LoadSpecialTypes(t *testing.T) {
   349  	c := config.NewConfig()
   350  	require.NoError(t, c.LoadConfig("./testdata/special_types.toml"))
   351  	require.Len(t, c.Inputs, 1)
   352  
   353  	input, ok := c.Inputs[0].Input.(*MockupInputPlugin)
   354  	require.True(t, ok)
   355  	// Tests telegraf config.Duration parsing.
   356  	require.Equal(t, config.Duration(time.Second), input.WriteTimeout)
   357  	// Tests telegraf size parsing.
   358  	require.Equal(t, config.Size(1024*1024), input.MaxBodySize)
   359  	// Tests toml multiline basic strings on single line.
   360  	require.Equal(t, "./testdata/special_types.pem", input.TLSCert)
   361  	// Tests toml multiline basic strings on single line.
   362  	require.Equal(t, "./testdata/special_types.key", input.TLSKey)
   363  	// Tests toml multiline basic strings on multiple lines.
   364  	require.Equal(t, "/path/", strings.TrimRight(input.Paths[0], "\r\n"))
   365  }
   366  
   367  func TestConfig_DeprecatedFilters(t *testing.T) {
   368  	c := config.NewConfig()
   369  	require.NoError(t, c.LoadConfig("./testdata/deprecated_field_filter.toml"))
   370  
   371  	require.Len(t, c.Inputs, 1)
   372  	require.Equal(t, []string{"foo", "bar", "baz"}, c.Inputs[0].Config.Filter.FieldInclude)
   373  	require.Equal(t, []string{"foo", "bar", "baz"}, c.Inputs[0].Config.Filter.FieldExclude)
   374  }
   375  
   376  func TestConfig_FieldNotDefined(t *testing.T) {
   377  	tests := []struct {
   378  		name     string
   379  		filename string
   380  		expected string
   381  	}{
   382  		{
   383  			name:     "in input plugin without parser",
   384  			filename: "./testdata/invalid_field.toml",
   385  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   386  				"This is either a typo or this config option does not exist in this version.",
   387  		},
   388  		{
   389  			name:     "in input plugin with parser",
   390  			filename: "./testdata/invalid_field_with_parser.toml",
   391  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   392  				"This is either a typo or this config option does not exist in this version.",
   393  		},
   394  		{
   395  			name:     "in input plugin with parser func",
   396  			filename: "./testdata/invalid_field_with_parserfunc.toml",
   397  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   398  				"This is either a typo or this config option does not exist in this version.",
   399  		},
   400  		{
   401  			name:     "in parser of input plugin",
   402  			filename: "./testdata/invalid_field_in_parser_table.toml",
   403  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   404  				"This is either a typo or this config option does not exist in this version.",
   405  		},
   406  		{
   407  			name:     "in parser of input plugin with parser-func",
   408  			filename: "./testdata/invalid_field_in_parserfunc_table.toml",
   409  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   410  				"This is either a typo or this config option does not exist in this version.",
   411  		},
   412  		{
   413  			name:     "in processor plugin without parser",
   414  			filename: "./testdata/invalid_field_processor.toml",
   415  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   416  				"This is either a typo or this config option does not exist in this version.",
   417  		},
   418  		{
   419  			name:     "in processor plugin with parser",
   420  			filename: "./testdata/invalid_field_processor_with_parser.toml",
   421  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   422  				"This is either a typo or this config option does not exist in this version.",
   423  		},
   424  		{
   425  			name:     "in processor plugin with parser func",
   426  			filename: "./testdata/invalid_field_processor_with_parserfunc.toml",
   427  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   428  				"This is either a typo or this config option does not exist in this version.",
   429  		},
   430  		{
   431  			name:     "in parser of processor plugin",
   432  			filename: "./testdata/invalid_field_processor_in_parser_table.toml",
   433  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   434  				"This is either a typo or this config option does not exist in this version.",
   435  		},
   436  		{
   437  			name:     "in parser of processor plugin with parser-func",
   438  			filename: "./testdata/invalid_field_processor_in_parserfunc_table.toml",
   439  			expected: "line 1: configuration specified the fields [\"not_a_field\"], but they were not used. " +
   440  				"This is either a typo or this config option does not exist in this version.",
   441  		},
   442  	}
   443  
   444  	for _, tt := range tests {
   445  		t.Run(tt.name, func(t *testing.T) {
   446  			c := config.NewConfig()
   447  			err := c.LoadConfig(tt.filename)
   448  			require.ErrorContains(t, err, tt.expected)
   449  		})
   450  	}
   451  }
   452  
   453  func TestConfig_WrongFieldType(t *testing.T) {
   454  	c := config.NewConfig()
   455  	err := c.LoadConfig("./testdata/wrong_field_type.toml")
   456  	require.Error(t, err, "invalid field type")
   457  	require.ErrorContains(t, err, "cannot unmarshal TOML string into int")
   458  
   459  	c = config.NewConfig()
   460  	err = c.LoadConfig("./testdata/wrong_field_type2.toml")
   461  	require.Error(t, err, "invalid field type2")
   462  	require.ErrorContains(t, err, "cannot unmarshal TOML string into []string")
   463  }
   464  
   465  func TestConfig_InlineTables(t *testing.T) {
   466  	// #4098
   467  	t.Setenv("TOKEN", "test")
   468  
   469  	c := config.NewConfig()
   470  	require.NoError(t, c.LoadConfig("./testdata/inline_table.toml"))
   471  	require.Len(t, c.Outputs, 2)
   472  
   473  	output, ok := c.Outputs[1].Output.(*MockupOuputPlugin)
   474  	require.True(t, ok)
   475  	require.Equal(t, map[string]string{"Authorization": "Token test", "Content-Type": "application/json"}, output.Headers)
   476  	require.Equal(t, []string{"org_id"}, c.Outputs[0].Config.Filter.TagInclude)
   477  }
   478  
   479  func TestConfig_SliceComment(t *testing.T) {
   480  	t.Skipf("Skipping until #3642 is resolved")
   481  
   482  	c := config.NewConfig()
   483  	require.NoError(t, c.LoadConfig("./testdata/slice_comment.toml"))
   484  	require.Len(t, c.Outputs, 1)
   485  
   486  	output, ok := c.Outputs[0].Output.(*MockupOuputPlugin)
   487  	require.True(t, ok)
   488  	require.Equal(t, []string{"test"}, output.Scopes)
   489  }
   490  
   491  func TestConfig_BadOrdering(t *testing.T) {
   492  	// #3444: when not using inline tables, care has to be taken so subsequent configuration
   493  	// doesn't become part of the table. This is not a bug, but TOML syntax.
   494  	c := config.NewConfig()
   495  	err := c.LoadConfig("./testdata/non_slice_slice.toml")
   496  	require.Error(t, err, "bad ordering")
   497  	require.Equal(
   498  		t,
   499  		"error loading config file ./testdata/non_slice_slice.toml: error parsing http array, line 4: cannot unmarshal TOML array into string (need slice)",
   500  		err.Error(),
   501  	)
   502  }
   503  
   504  func TestConfig_AzureMonitorNamespacePrefix(t *testing.T) {
   505  	// #8256 Cannot use empty string as the namespace prefix
   506  	c := config.NewConfig()
   507  	require.NoError(t, c.LoadConfig("./testdata/azure_monitor.toml"))
   508  	require.Len(t, c.Outputs, 2)
   509  
   510  	expectedPrefix := []string{"Telegraf/", ""}
   511  	for i, plugin := range c.Outputs {
   512  		output, ok := plugin.Output.(*MockupOuputPlugin)
   513  		require.True(t, ok)
   514  		require.Equal(t, expectedPrefix[i], output.NamespacePrefix)
   515  	}
   516  }
   517  
   518  func TestGetDefaultConfigPathFromEnvURL(t *testing.T) {
   519  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
   520  		w.WriteHeader(http.StatusOK)
   521  		_, _ = w.Write([]byte("[agent]\ndebug = true"))
   522  	}))
   523  	defer ts.Close()
   524  
   525  	c := config.NewConfig()
   526  	t.Setenv("TELEGRAF_CONFIG_PATH", ts.URL)
   527  	configPath, err := config.GetDefaultConfigPath()
   528  	require.NoError(t, err)
   529  	require.Equal(t, []string{ts.URL}, configPath)
   530  	require.NoError(t, c.LoadConfig(configPath[0]))
   531  }
   532  
   533  func TestConfig_URLLikeFileName(t *testing.T) {
   534  	c := config.NewConfig()
   535  	err := c.LoadConfig("http:##www.example.com.conf")
   536  	require.Error(t, err)
   537  
   538  	if runtime.GOOS == "windows" {
   539  		// The error file not found error message is different on Windows
   540  		require.Equal(
   541  			t,
   542  			"error loading config file http:##www.example.com.conf: open http:##www.example.com.conf: The system cannot find the file specified.",
   543  			err.Error(),
   544  		)
   545  	} else {
   546  		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())
   547  	}
   548  }
   549  
   550  func TestConfig_Filtering(t *testing.T) {
   551  	c := config.NewConfig()
   552  	require.NoError(t, c.LoadAll("./testdata/filter_metricpass.toml"))
   553  	require.Len(t, c.Processors, 1)
   554  
   555  	in := []telegraf.Metric{
   556  		metric.New(
   557  			"machine",
   558  			map[string]string{"state": "on"},
   559  			map[string]interface{}{"value": 42.0},
   560  			time.Date(2023, time.April, 23, 01, 15, 30, 0, time.UTC),
   561  		),
   562  		metric.New(
   563  			"machine",
   564  			map[string]string{"state": "off"},
   565  			map[string]interface{}{"value": 23.0},
   566  			time.Date(2023, time.April, 23, 23, 59, 01, 0, time.UTC),
   567  		),
   568  		metric.New(
   569  			"temperature",
   570  			map[string]string{},
   571  			map[string]interface{}{"value": 23.5},
   572  			time.Date(2023, time.April, 24, 02, 15, 30, 0, time.UTC),
   573  		),
   574  	}
   575  	expected := []telegraf.Metric{
   576  		metric.New(
   577  			"machine",
   578  			map[string]string{
   579  				"state":     "on",
   580  				"processed": "yes",
   581  			},
   582  			map[string]interface{}{"value": 42.0},
   583  			time.Date(2023, time.April, 23, 01, 15, 30, 0, time.UTC),
   584  		),
   585  		metric.New(
   586  			"machine",
   587  			map[string]string{"state": "off"},
   588  			map[string]interface{}{"value": 23.0},
   589  			time.Date(2023, time.April, 23, 23, 59, 01, 0, time.UTC),
   590  		),
   591  		metric.New(
   592  			"temperature",
   593  			map[string]string{
   594  				"processed": "yes",
   595  			},
   596  			map[string]interface{}{"value": 23.5},
   597  			time.Date(2023, time.April, 24, 02, 15, 30, 0, time.UTC),
   598  		),
   599  	}
   600  
   601  	plugin := c.Processors[0]
   602  	var acc testutil.Accumulator
   603  	for _, m := range in {
   604  		require.NoError(t, plugin.Add(m, &acc))
   605  	}
   606  	actual := acc.GetTelegrafMetrics()
   607  	testutil.RequireMetricsEqual(t, expected, actual, testutil.SortMetrics())
   608  }
   609  
   610  func TestConfig_SerializerInterfaceNewFormat(t *testing.T) {
   611  	formats := []string{
   612  		"carbon2",
   613  		"csv",
   614  		"graphite",
   615  		"influx",
   616  		"json",
   617  		"msgpack",
   618  		"nowmetric",
   619  		"prometheus",
   620  		"prometheusremotewrite",
   621  		"splunkmetric",
   622  		"wavefront",
   623  	}
   624  
   625  	c := config.NewConfig()
   626  	require.NoError(t, c.LoadConfig("./testdata/serializers_new.toml"))
   627  	require.Len(t, c.Outputs, len(formats))
   628  
   629  	cfg := serializers.Config{}
   630  	override := map[string]struct {
   631  		param map[string]interface{}
   632  		mask  []string
   633  	}{}
   634  
   635  	expected := make([]telegraf.Serializer, 0, len(formats))
   636  	for _, format := range formats {
   637  		formatCfg := &cfg
   638  		formatCfg.DataFormat = format
   639  
   640  		logger := models.NewLogger("serializers", format, "test")
   641  
   642  		var serializer telegraf.Serializer
   643  		if creator, found := serializers.Serializers[format]; found {
   644  			t.Logf("new-style %q", format)
   645  			serializer = creator()
   646  		} else {
   647  			t.Logf("old-style %q", format)
   648  			var err error
   649  			serializer, err = serializers.NewSerializer(formatCfg)
   650  			require.NoErrorf(t, err, "No serializer for format %q", format)
   651  		}
   652  
   653  		if settings, found := override[format]; found {
   654  			s := reflect.Indirect(reflect.ValueOf(serializer))
   655  			for key, value := range settings.param {
   656  				v := reflect.ValueOf(value)
   657  				s.FieldByName(key).Set(v)
   658  			}
   659  		}
   660  		models.SetLoggerOnPlugin(serializer, logger)
   661  		if s, ok := serializer.(telegraf.Initializer); ok {
   662  			require.NoError(t, s.Init())
   663  		}
   664  		expected = append(expected, serializer)
   665  	}
   666  	require.Len(t, expected, len(formats))
   667  
   668  	actual := make([]interface{}, 0)
   669  	for _, plugin := range c.Outputs {
   670  		output, ok := plugin.Output.(*MockupOutputPluginSerializerNew)
   671  		require.True(t, ok)
   672  		// Get the parser set with 'SetParser()'
   673  		if p, ok := output.Serializer.(*models.RunningSerializer); ok {
   674  			actual = append(actual, p.Serializer)
   675  		} else {
   676  			actual = append(actual, output.Serializer)
   677  		}
   678  	}
   679  	require.Len(t, actual, len(formats))
   680  
   681  	for i, format := range formats {
   682  		// Determine the underlying type of the serializer
   683  		stype := reflect.Indirect(reflect.ValueOf(expected[i])).Interface()
   684  		// Ignore all unexported fields and fields not relevant for functionality
   685  		options := []cmp.Option{
   686  			cmpopts.IgnoreUnexported(stype),
   687  			cmpopts.IgnoreUnexported(reflect.Indirect(reflect.ValueOf(promserializer.MetricTypes{})).Interface()),
   688  			cmpopts.IgnoreTypes(sync.Mutex{}, regexp.Regexp{}),
   689  			cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}),
   690  		}
   691  		if settings, found := override[format]; found {
   692  			options = append(options, cmpopts.IgnoreFields(stype, settings.mask...))
   693  		}
   694  
   695  		// Do a manual comparison as require.EqualValues will also work on unexported fields
   696  		// that cannot be cleared or ignored.
   697  		diff := cmp.Diff(expected[i], actual[i], options...)
   698  		require.Emptyf(t, diff, "Difference in SetSerializer() for %q", format)
   699  	}
   700  }
   701  
   702  func TestConfig_SerializerInterfaceOldFormat(t *testing.T) {
   703  	formats := []string{
   704  		"carbon2",
   705  		"csv",
   706  		"graphite",
   707  		"influx",
   708  		"json",
   709  		"msgpack",
   710  		"nowmetric",
   711  		"prometheus",
   712  		"prometheusremotewrite",
   713  		"splunkmetric",
   714  		"wavefront",
   715  	}
   716  
   717  	c := config.NewConfig()
   718  	require.NoError(t, c.LoadConfig("./testdata/serializers_old.toml"))
   719  	require.Len(t, c.Outputs, len(formats))
   720  
   721  	cfg := serializers.Config{}
   722  	override := map[string]struct {
   723  		param map[string]interface{}
   724  		mask  []string
   725  	}{}
   726  
   727  	expected := make([]telegraf.Serializer, 0, len(formats))
   728  	for _, format := range formats {
   729  		formatCfg := &cfg
   730  		formatCfg.DataFormat = format
   731  
   732  		logger := models.NewLogger("serializers", format, "test")
   733  
   734  		var serializer serializers.Serializer
   735  		if creator, found := serializers.Serializers[format]; found {
   736  			t.Logf("new-style %q", format)
   737  			serializer = creator()
   738  		} else {
   739  			t.Logf("old-style %q", format)
   740  			var err error
   741  			serializer, err = serializers.NewSerializer(formatCfg)
   742  			require.NoErrorf(t, err, "No serializer for format %q", format)
   743  		}
   744  
   745  		if settings, found := override[format]; found {
   746  			s := reflect.Indirect(reflect.ValueOf(serializer))
   747  			for key, value := range settings.param {
   748  				v := reflect.ValueOf(value)
   749  				s.FieldByName(key).Set(v)
   750  			}
   751  		}
   752  		models.SetLoggerOnPlugin(serializer, logger)
   753  		if s, ok := serializer.(telegraf.Initializer); ok {
   754  			require.NoError(t, s.Init())
   755  		}
   756  		expected = append(expected, serializer)
   757  	}
   758  	require.Len(t, expected, len(formats))
   759  
   760  	actual := make([]interface{}, 0)
   761  	for _, plugin := range c.Outputs {
   762  		output, ok := plugin.Output.(*MockupOutputPluginSerializerOld)
   763  		require.True(t, ok)
   764  		// Get the parser set with 'SetParser()'
   765  		if p, ok := output.Serializer.(*models.RunningSerializer); ok {
   766  			actual = append(actual, p.Serializer)
   767  		} else {
   768  			actual = append(actual, output.Serializer)
   769  		}
   770  	}
   771  	require.Len(t, actual, len(formats))
   772  
   773  	for i, format := range formats {
   774  		// Determine the underlying type of the serializer
   775  		stype := reflect.Indirect(reflect.ValueOf(expected[i])).Interface()
   776  		// Ignore all unexported fields and fields not relevant for functionality
   777  		options := []cmp.Option{
   778  			cmpopts.IgnoreUnexported(stype),
   779  			cmpopts.IgnoreUnexported(reflect.Indirect(reflect.ValueOf(promserializer.MetricTypes{})).Interface()),
   780  			cmpopts.IgnoreTypes(sync.Mutex{}, regexp.Regexp{}),
   781  			cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}),
   782  		}
   783  		if settings, found := override[format]; found {
   784  			options = append(options, cmpopts.IgnoreFields(stype, settings.mask...))
   785  		}
   786  
   787  		// Do a manual comparison as require.EqualValues will also work on unexported fields
   788  		// that cannot be cleared or ignored.
   789  		diff := cmp.Diff(expected[i], actual[i], options...)
   790  		require.Emptyf(t, diff, "Difference in SetSerializer() for %q", format)
   791  	}
   792  }
   793  
   794  func TestConfig_ParserInterface(t *testing.T) {
   795  	formats := []string{
   796  		"collectd",
   797  		"csv",
   798  		"dropwizard",
   799  		"form_urlencoded",
   800  		"graphite",
   801  		"grok",
   802  		"influx",
   803  		"json",
   804  		"json_v2",
   805  		"logfmt",
   806  		"nagios",
   807  		"prometheus",
   808  		"prometheusremotewrite",
   809  		"value",
   810  		"wavefront",
   811  		"xml", "xpath_json", "xpath_msgpack", "xpath_protobuf",
   812  	}
   813  
   814  	c := config.NewConfig()
   815  	require.NoError(t, c.LoadConfig("./testdata/parsers_new.toml"))
   816  	require.Len(t, c.Inputs, len(formats))
   817  
   818  	override := map[string]struct {
   819  		param map[string]interface{}
   820  		mask  []string
   821  	}{
   822  		"csv": {
   823  			param: map[string]interface{}{
   824  				"HeaderRowCount": 42,
   825  			},
   826  			mask: []string{"TimeFunc", "ResetMode"},
   827  		},
   828  		"xpath_protobuf": {
   829  			param: map[string]interface{}{
   830  				"ProtobufMessageDef":  "testdata/addressbook.proto",
   831  				"ProtobufMessageType": "addressbook.AddressBook",
   832  			},
   833  		},
   834  	}
   835  
   836  	expected := make([]telegraf.Parser, 0, len(formats))
   837  	for _, format := range formats {
   838  		logger := models.NewLogger("parsers", format, "parser_test_new")
   839  
   840  		creator, found := parsers.Parsers[format]
   841  		require.Truef(t, found, "No parser for format %q", format)
   842  
   843  		parser := creator("parser_test_new")
   844  		if settings, found := override[format]; found {
   845  			s := reflect.Indirect(reflect.ValueOf(parser))
   846  			for key, value := range settings.param {
   847  				v := reflect.ValueOf(value)
   848  				s.FieldByName(key).Set(v)
   849  			}
   850  		}
   851  		models.SetLoggerOnPlugin(parser, logger)
   852  		if p, ok := parser.(telegraf.Initializer); ok {
   853  			require.NoError(t, p.Init())
   854  		}
   855  		expected = append(expected, parser)
   856  	}
   857  	require.Len(t, expected, len(formats))
   858  
   859  	actual := make([]interface{}, 0)
   860  	generated := make([]interface{}, 0)
   861  	for _, plugin := range c.Inputs {
   862  		input, ok := plugin.Input.(*MockupInputPluginParserNew)
   863  		require.True(t, ok)
   864  		// Get the parser set with 'SetParser()'
   865  		if p, ok := input.Parser.(*models.RunningParser); ok {
   866  			actual = append(actual, p.Parser)
   867  		} else {
   868  			actual = append(actual, input.Parser)
   869  		}
   870  		// Get the parser set with 'SetParserFunc()'
   871  		g, err := input.ParserFunc()
   872  		require.NoError(t, err)
   873  		if rp, ok := g.(*models.RunningParser); ok {
   874  			generated = append(generated, rp.Parser)
   875  		} else {
   876  			generated = append(generated, g)
   877  		}
   878  	}
   879  	require.Len(t, actual, len(formats))
   880  
   881  	for i, format := range formats {
   882  		// Determine the underlying type of the parser
   883  		stype := reflect.Indirect(reflect.ValueOf(expected[i])).Interface()
   884  		// Ignore all unexported fields and fields not relevant for functionality
   885  		options := []cmp.Option{
   886  			cmpopts.IgnoreUnexported(stype),
   887  			cmpopts.IgnoreTypes(sync.Mutex{}),
   888  			cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}),
   889  		}
   890  		if settings, found := override[format]; found {
   891  			options = append(options, cmpopts.IgnoreFields(stype, settings.mask...))
   892  		}
   893  
   894  		// Do a manual comparison as require.EqualValues will also work on unexported fields
   895  		// that cannot be cleared or ignored.
   896  		diff := cmp.Diff(expected[i], actual[i], options...)
   897  		require.Emptyf(t, diff, "Difference in SetParser() for %q", format)
   898  		diff = cmp.Diff(expected[i], generated[i], options...)
   899  		require.Emptyf(t, diff, "Difference in SetParserFunc() for %q", format)
   900  	}
   901  }
   902  
   903  func TestConfig_MultipleProcessorsOrder(t *testing.T) {
   904  	tests := []struct {
   905  		name          string
   906  		filename      []string
   907  		expectedOrder []string
   908  	}{
   909  		{
   910  			name:     "Test the order of multiple unique processosr",
   911  			filename: []string{"multiple_processors.toml"},
   912  			expectedOrder: []string{
   913  				"processor",
   914  				"parser_test",
   915  				"processor_parser",
   916  				"processor_parserfunc",
   917  			},
   918  		},
   919  		{
   920  			name:     "Test using a single 'order' configuration",
   921  			filename: []string{"multiple_processors_simple_order.toml"},
   922  			expectedOrder: []string{
   923  				"parser_test",
   924  				"processor_parser",
   925  				"processor_parserfunc",
   926  				"processor",
   927  			},
   928  		},
   929  		{
   930  			name:     "Test using multiple 'order' configurations",
   931  			filename: []string{"multiple_processors_messy_order.toml"},
   932  			expectedOrder: []string{
   933  				"parser_test",
   934  				"processor_parserfunc",
   935  				"processor",
   936  				"processor_parser",
   937  				"processor_parser",
   938  				"processor_parserfunc",
   939  			},
   940  		},
   941  		{
   942  			name: "Test loading multiple configuration files",
   943  			filename: []string{
   944  				"multiple_processors.toml",
   945  				"multiple_processors_simple_order.toml",
   946  			},
   947  			expectedOrder: []string{
   948  				"processor",
   949  				"parser_test",
   950  				"processor_parser",
   951  				"processor_parserfunc",
   952  				"parser_test",
   953  				"processor_parser",
   954  				"processor_parserfunc",
   955  				"processor",
   956  			},
   957  		},
   958  		{
   959  			name: "Test loading multiple configuration files both with order",
   960  			filename: []string{
   961  				"multiple_processors_simple_order.toml",
   962  				"multiple_processors_messy_order.toml",
   963  			},
   964  			expectedOrder: []string{
   965  				"parser_test",
   966  				"processor_parser",
   967  				"processor_parserfunc",
   968  				"parser_test",
   969  				"processor_parserfunc",
   970  				"processor",
   971  				"processor",
   972  				"processor_parser",
   973  				"processor_parser",
   974  				"processor_parserfunc",
   975  			},
   976  		},
   977  	}
   978  
   979  	for _, test := range tests {
   980  		t.Run(test.name, func(t *testing.T) {
   981  			c := config.NewConfig()
   982  			filenames := make([]string, 0, len(test.filename))
   983  			for _, fn := range test.filename {
   984  				filenames = append(filenames, filepath.Join(".", "testdata", "processor_order", fn))
   985  			}
   986  			require.NoError(t, c.LoadAll(filenames...))
   987  
   988  			require.Equal(t, len(test.expectedOrder), len(c.Processors))
   989  
   990  			var order []string
   991  			for _, p := range c.Processors {
   992  				order = append(order, p.Config.Name)
   993  			}
   994  
   995  			require.Equal(t, test.expectedOrder, order)
   996  		})
   997  	}
   998  }
   999  
  1000  func TestConfig_ProcessorsWithParsers(t *testing.T) {
  1001  	formats := []string{
  1002  		"collectd",
  1003  		"csv",
  1004  		"dropwizard",
  1005  		"form_urlencoded",
  1006  		"graphite",
  1007  		"grok",
  1008  		"influx",
  1009  		"json",
  1010  		"json_v2",
  1011  		"logfmt",
  1012  		"nagios",
  1013  		"prometheus",
  1014  		"prometheusremotewrite",
  1015  		"value",
  1016  		"wavefront",
  1017  		"xml", "xpath_json", "xpath_msgpack", "xpath_protobuf",
  1018  	}
  1019  
  1020  	c := config.NewConfig()
  1021  	require.NoError(t, c.LoadAll("./testdata/processors_with_parsers.toml"))
  1022  	require.Len(t, c.Processors, len(formats))
  1023  
  1024  	override := map[string]struct {
  1025  		param map[string]interface{}
  1026  		mask  []string
  1027  	}{
  1028  		"csv": {
  1029  			param: map[string]interface{}{
  1030  				"HeaderRowCount": 42,
  1031  			},
  1032  			mask: []string{"TimeFunc", "ResetMode"},
  1033  		},
  1034  		"xpath_protobuf": {
  1035  			param: map[string]interface{}{
  1036  				"ProtobufMessageDef":  "testdata/addressbook.proto",
  1037  				"ProtobufMessageType": "addressbook.AddressBook",
  1038  			},
  1039  		},
  1040  	}
  1041  
  1042  	expected := make([]telegraf.Parser, 0, len(formats))
  1043  	for _, format := range formats {
  1044  		logger := models.NewLogger("parsers", format, "processors_with_parsers")
  1045  
  1046  		creator, found := parsers.Parsers[format]
  1047  		require.Truef(t, found, "No parser for format %q", format)
  1048  
  1049  		parser := creator("parser_test")
  1050  		if settings, found := override[format]; found {
  1051  			s := reflect.Indirect(reflect.ValueOf(parser))
  1052  			for key, value := range settings.param {
  1053  				v := reflect.ValueOf(value)
  1054  				s.FieldByName(key).Set(v)
  1055  			}
  1056  		}
  1057  		models.SetLoggerOnPlugin(parser, logger)
  1058  		if p, ok := parser.(telegraf.Initializer); ok {
  1059  			require.NoError(t, p.Init())
  1060  		}
  1061  		expected = append(expected, parser)
  1062  	}
  1063  	require.Len(t, expected, len(formats))
  1064  
  1065  	actual := make([]interface{}, 0)
  1066  	generated := make([]interface{}, 0)
  1067  	for _, plugin := range c.Processors {
  1068  		var processorIF telegraf.Processor
  1069  		if p, ok := plugin.Processor.(processors.HasUnwrap); ok {
  1070  			processorIF = p.Unwrap()
  1071  		} else {
  1072  			processorIF = plugin.Processor.(telegraf.Processor)
  1073  		}
  1074  		require.NotNil(t, processorIF)
  1075  
  1076  		processor, ok := processorIF.(*MockupProcessorPluginParser)
  1077  		require.True(t, ok)
  1078  
  1079  		// Get the parser set with 'SetParser()'
  1080  		if p, ok := processor.Parser.(*models.RunningParser); ok {
  1081  			actual = append(actual, p.Parser)
  1082  		} else {
  1083  			actual = append(actual, processor.Parser)
  1084  		}
  1085  		// Get the parser set with 'SetParserFunc()'
  1086  		if processor.ParserFunc != nil {
  1087  			g, err := processor.ParserFunc()
  1088  			require.NoError(t, err)
  1089  			if rp, ok := g.(*models.RunningParser); ok {
  1090  				generated = append(generated, rp.Parser)
  1091  			} else {
  1092  				generated = append(generated, g)
  1093  			}
  1094  		} else {
  1095  			generated = append(generated, nil)
  1096  		}
  1097  	}
  1098  	require.Len(t, actual, len(formats))
  1099  
  1100  	for i, format := range formats {
  1101  		// Determine the underlying type of the parser
  1102  		stype := reflect.Indirect(reflect.ValueOf(expected[i])).Interface()
  1103  		// Ignore all unexported fields and fields not relevant for functionality
  1104  		options := []cmp.Option{
  1105  			cmpopts.IgnoreUnexported(stype),
  1106  			cmpopts.IgnoreTypes(sync.Mutex{}),
  1107  			cmpopts.IgnoreInterfaces(struct{ telegraf.Logger }{}),
  1108  		}
  1109  		if settings, found := override[format]; found {
  1110  			options = append(options, cmpopts.IgnoreFields(stype, settings.mask...))
  1111  		}
  1112  
  1113  		// Do a manual comparison as require.EqualValues will also work on unexported fields
  1114  		// that cannot be cleared or ignored.
  1115  		diff := cmp.Diff(expected[i], actual[i], options...)
  1116  		require.Emptyf(t, diff, "Difference in SetParser() for %q", format)
  1117  		diff = cmp.Diff(expected[i], generated[i], options...)
  1118  		require.Emptyf(t, diff, "Difference in SetParserFunc() for %q", format)
  1119  	}
  1120  }
  1121  
  1122  func TestConfigPluginIDsDifferent(t *testing.T) {
  1123  	c := config.NewConfig()
  1124  	c.Agent.Statefile = "/dev/null"
  1125  	require.NoError(t, c.LoadConfig("./testdata/state_persistence_input_all_different.toml"))
  1126  	require.NotEmpty(t, c.Inputs)
  1127  
  1128  	// Compare generated IDs
  1129  	for i, pi := range c.Inputs {
  1130  		refid := pi.Config.ID
  1131  		require.NotEmpty(t, refid)
  1132  
  1133  		// Cross-comparison
  1134  		for j, pj := range c.Inputs {
  1135  			testid := pj.Config.ID
  1136  			if i == j {
  1137  				require.Equal(t, refid, testid)
  1138  				continue
  1139  			}
  1140  			require.NotEqualf(t, refid, testid, "equal for %d, %d", i, j)
  1141  		}
  1142  	}
  1143  }
  1144  
  1145  func TestConfigPluginIDsSame(t *testing.T) {
  1146  	c := config.NewConfig()
  1147  	c.Agent.Statefile = "/dev/null"
  1148  	require.NoError(t, c.LoadConfig("./testdata/state_persistence_input_all_same.toml"))
  1149  	require.NotEmpty(t, c.Inputs)
  1150  
  1151  	// Compare generated IDs
  1152  	for i, pi := range c.Inputs {
  1153  		refid := pi.Config.ID
  1154  		require.NotEmpty(t, refid)
  1155  
  1156  		// Cross-comparison
  1157  		for j, pj := range c.Inputs {
  1158  			testid := pj.Config.ID
  1159  			require.Equal(t, refid, testid, "not equal for %d, %d", i, j)
  1160  		}
  1161  	}
  1162  }
  1163  
  1164  func TestPersisterInputStoreLoad(t *testing.T) {
  1165  	// Reserve a temporary state file
  1166  	file, err := os.CreateTemp("", "telegraf_state-*.json")
  1167  	require.NoError(t, err)
  1168  	filename := file.Name()
  1169  	require.NoError(t, file.Close())
  1170  	defer os.Remove(filename)
  1171  
  1172  	// Load the plugins
  1173  	cstore := config.NewConfig()
  1174  	require.NoError(t, cstore.LoadConfig("testdata/state_persistence_input_store_load.toml"))
  1175  
  1176  	// Initialize the persister for storing the state
  1177  	persisterStore := persister.Persister{
  1178  		Filename: filename,
  1179  	}
  1180  	require.NoError(t, persisterStore.Init())
  1181  
  1182  	expected := make(map[string]interface{})
  1183  	for i, plugin := range cstore.Inputs {
  1184  		require.NoError(t, plugin.Init())
  1185  
  1186  		// Register
  1187  		p := plugin.Input.(*MockupStatePlugin)
  1188  		require.NoError(t, persisterStore.Register(plugin.ID(), p))
  1189  
  1190  		// Change the state
  1191  		p.state.Name += "_" + strings.Repeat("a", i+1)
  1192  		p.state.Version++
  1193  		p.state.Offset += uint64(i + 1)
  1194  		p.state.Bits = append(p.state.Bits, len(p.state.Bits))
  1195  		p.state.Modified, _ = time.Parse(time.RFC3339, "2022-11-03T16:49:00+02:00")
  1196  
  1197  		// Store the state for later comparison
  1198  		expected[plugin.ID()] = p.GetState()
  1199  	}
  1200  
  1201  	// Write state
  1202  	require.NoError(t, persisterStore.Store())
  1203  
  1204  	// Load the plugins
  1205  	cload := config.NewConfig()
  1206  	require.NoError(t, cload.LoadConfig("testdata/state_persistence_input_store_load.toml"))
  1207  	require.Len(t, cload.Inputs, len(expected))
  1208  
  1209  	// Initialize the persister for loading the state
  1210  	persisterLoad := persister.Persister{
  1211  		Filename: filename,
  1212  	}
  1213  	require.NoError(t, persisterLoad.Init())
  1214  
  1215  	for _, plugin := range cload.Inputs {
  1216  		require.NoError(t, plugin.Init())
  1217  
  1218  		// Register
  1219  		p := plugin.Input.(*MockupStatePlugin)
  1220  		require.NoError(t, persisterLoad.Register(plugin.ID(), p))
  1221  
  1222  		// Check that the states are not yet restored
  1223  		require.NotNil(t, expected[plugin.ID()])
  1224  		require.NotEqual(t, expected[plugin.ID()], p.GetState())
  1225  	}
  1226  
  1227  	// Restore states
  1228  	require.NoError(t, persisterLoad.Load())
  1229  
  1230  	// Check we got what we saved.
  1231  	for _, plugin := range cload.Inputs {
  1232  		p := plugin.Input.(*MockupStatePlugin)
  1233  		require.Equal(t, expected[plugin.ID()], p.GetState())
  1234  	}
  1235  }
  1236  
  1237  func TestPersisterProcessorRegistration(t *testing.T) {
  1238  	// Load the plugins
  1239  	c := config.NewConfig()
  1240  	require.NoError(t, c.LoadConfig("testdata/state_persistence_processors.toml"))
  1241  	require.NotEmpty(t, c.Processors)
  1242  	require.NotEmpty(t, c.AggProcessors)
  1243  
  1244  	// Initialize the persister for test
  1245  	dut := persister.Persister{
  1246  		Filename: "/tmp/doesn_t_matter.json",
  1247  	}
  1248  	require.NoError(t, dut.Init())
  1249  
  1250  	// Register the processors
  1251  	for _, plugin := range c.Processors {
  1252  		unwrapped := plugin.Processor.(processors.HasUnwrap).Unwrap()
  1253  
  1254  		p := unwrapped.(*MockupProcessorPlugin)
  1255  		require.NoError(t, dut.Register(plugin.ID(), p))
  1256  	}
  1257  
  1258  	// Register the after-aggregator processors
  1259  	for _, plugin := range c.AggProcessors {
  1260  		unwrapped := plugin.Processor.(processors.HasUnwrap).Unwrap()
  1261  
  1262  		p := unwrapped.(*MockupProcessorPlugin)
  1263  		require.NoError(t, dut.Register(plugin.ID(), p))
  1264  	}
  1265  }
  1266  
  1267  /*** Mockup INPUT plugin for (new) parser testing to avoid cyclic dependencies ***/
  1268  type MockupInputPluginParserNew struct {
  1269  	Parser     telegraf.Parser
  1270  	ParserFunc telegraf.ParserFunc
  1271  }
  1272  
  1273  func (m *MockupInputPluginParserNew) SampleConfig() string {
  1274  	return "Mockup old parser test plugin"
  1275  }
  1276  func (m *MockupInputPluginParserNew) Gather(_ telegraf.Accumulator) error {
  1277  	return nil
  1278  }
  1279  func (m *MockupInputPluginParserNew) SetParser(parser telegraf.Parser) {
  1280  	m.Parser = parser
  1281  }
  1282  func (m *MockupInputPluginParserNew) SetParserFunc(f telegraf.ParserFunc) {
  1283  	m.ParserFunc = f
  1284  }
  1285  
  1286  /*** Mockup INPUT plugin for testing to avoid cyclic dependencies ***/
  1287  type MockupInputPlugin struct {
  1288  	Servers      []string        `toml:"servers"`
  1289  	Methods      []string        `toml:"methods"`
  1290  	Timeout      config.Duration `toml:"timeout"`
  1291  	ReadTimeout  config.Duration `toml:"read_timeout"`
  1292  	WriteTimeout config.Duration `toml:"write_timeout"`
  1293  	MaxBodySize  config.Size     `toml:"max_body_size"`
  1294  	Paths        []string        `toml:"paths"`
  1295  	Port         int             `toml:"port"`
  1296  	Password     config.Secret   `toml:"password"`
  1297  	Command      string
  1298  	Files        []string
  1299  	PidFile      string
  1300  	Log          telegraf.Logger `toml:"-"`
  1301  	tls.ServerConfig
  1302  
  1303  	parser telegraf.Parser
  1304  }
  1305  
  1306  func (m *MockupInputPlugin) SampleConfig() string {
  1307  	return "Mockup test input plugin"
  1308  }
  1309  func (m *MockupInputPlugin) Gather(_ telegraf.Accumulator) error {
  1310  	return nil
  1311  }
  1312  func (m *MockupInputPlugin) SetParser(parser telegraf.Parser) {
  1313  	m.parser = parser
  1314  }
  1315  
  1316  /*** Mockup INPUT plugin with ParserFunc interface ***/
  1317  type MockupInputPluginParserFunc struct {
  1318  	parserFunc telegraf.ParserFunc
  1319  }
  1320  
  1321  func (m *MockupInputPluginParserFunc) SampleConfig() string {
  1322  	return "Mockup test input plugin"
  1323  }
  1324  func (m *MockupInputPluginParserFunc) Gather(_ telegraf.Accumulator) error {
  1325  	return nil
  1326  }
  1327  func (m *MockupInputPluginParserFunc) SetParserFunc(pf telegraf.ParserFunc) {
  1328  	m.parserFunc = pf
  1329  }
  1330  
  1331  /*** Mockup INPUT plugin without ParserFunc interface ***/
  1332  type MockupInputPluginParserOnly struct {
  1333  	parser telegraf.Parser
  1334  }
  1335  
  1336  func (m *MockupInputPluginParserOnly) SampleConfig() string {
  1337  	return "Mockup test input plugin"
  1338  }
  1339  func (m *MockupInputPluginParserOnly) Gather(_ telegraf.Accumulator) error {
  1340  	return nil
  1341  }
  1342  func (m *MockupInputPluginParserOnly) SetParser(p telegraf.Parser) {
  1343  	m.parser = p
  1344  }
  1345  
  1346  /*** Mockup PROCESSOR plugin for testing to avoid cyclic dependencies ***/
  1347  type MockupProcessorPluginParser struct {
  1348  	Parser     telegraf.Parser
  1349  	ParserFunc telegraf.ParserFunc
  1350  }
  1351  
  1352  func (m *MockupProcessorPluginParser) Start(_ telegraf.Accumulator) error {
  1353  	return nil
  1354  }
  1355  func (m *MockupProcessorPluginParser) Stop() {
  1356  }
  1357  func (m *MockupProcessorPluginParser) SampleConfig() string {
  1358  	return "Mockup test processor plugin with parser"
  1359  }
  1360  func (m *MockupProcessorPluginParser) Apply(_ ...telegraf.Metric) []telegraf.Metric {
  1361  	return nil
  1362  }
  1363  func (m *MockupProcessorPluginParser) Add(_ telegraf.Metric, _ telegraf.Accumulator) error {
  1364  	return nil
  1365  }
  1366  func (m *MockupProcessorPluginParser) SetParser(parser telegraf.Parser) {
  1367  	m.Parser = parser
  1368  }
  1369  func (m *MockupProcessorPluginParser) SetParserFunc(f telegraf.ParserFunc) {
  1370  	m.ParserFunc = f
  1371  }
  1372  
  1373  /*** Mockup PROCESSOR plugin without parser ***/
  1374  type MockupProcessorPlugin struct {
  1375  	Option string `toml:"option"`
  1376  	state  []uint64
  1377  }
  1378  
  1379  func (m *MockupProcessorPlugin) Start(_ telegraf.Accumulator) error {
  1380  	return nil
  1381  }
  1382  func (m *MockupProcessorPlugin) Stop() {
  1383  }
  1384  func (m *MockupProcessorPlugin) SampleConfig() string {
  1385  	return "Mockup test processor plugin with parser"
  1386  }
  1387  func (m *MockupProcessorPlugin) Apply(in ...telegraf.Metric) []telegraf.Metric {
  1388  	out := make([]telegraf.Metric, 0, len(in))
  1389  	for _, m := range in {
  1390  		m.AddTag("processed", "yes")
  1391  		out = append(out, m)
  1392  	}
  1393  	return out
  1394  }
  1395  func (m *MockupProcessorPlugin) GetState() interface{} {
  1396  	return m.state
  1397  }
  1398  func (m *MockupProcessorPlugin) SetState(state interface{}) error {
  1399  	s, ok := state.([]uint64)
  1400  	if !ok {
  1401  		return fmt.Errorf("invalid state type %T", state)
  1402  	}
  1403  	m.state = s
  1404  
  1405  	return nil
  1406  }
  1407  
  1408  /*** Mockup PROCESSOR plugin with parser ***/
  1409  type MockupProcessorPluginParserOnly struct {
  1410  	Parser telegraf.Parser
  1411  }
  1412  
  1413  func (m *MockupProcessorPluginParserOnly) Start(_ telegraf.Accumulator) error {
  1414  	return nil
  1415  }
  1416  func (m *MockupProcessorPluginParserOnly) Stop() {
  1417  }
  1418  func (m *MockupProcessorPluginParserOnly) SampleConfig() string {
  1419  	return "Mockup test processor plugin with parser"
  1420  }
  1421  func (m *MockupProcessorPluginParserOnly) Apply(_ ...telegraf.Metric) []telegraf.Metric {
  1422  	return nil
  1423  }
  1424  func (m *MockupProcessorPluginParserOnly) Add(_ telegraf.Metric, _ telegraf.Accumulator) error {
  1425  	return nil
  1426  }
  1427  func (m *MockupProcessorPluginParserOnly) SetParser(parser telegraf.Parser) {
  1428  	m.Parser = parser
  1429  }
  1430  
  1431  /*** Mockup PROCESSOR plugin with parser-function ***/
  1432  type MockupProcessorPluginParserFunc struct {
  1433  	Parser telegraf.ParserFunc
  1434  }
  1435  
  1436  func (m *MockupProcessorPluginParserFunc) Start(_ telegraf.Accumulator) error {
  1437  	return nil
  1438  }
  1439  func (m *MockupProcessorPluginParserFunc) Stop() {
  1440  }
  1441  func (m *MockupProcessorPluginParserFunc) SampleConfig() string {
  1442  	return "Mockup test processor plugin with parser"
  1443  }
  1444  func (m *MockupProcessorPluginParserFunc) Apply(_ ...telegraf.Metric) []telegraf.Metric {
  1445  	return nil
  1446  }
  1447  func (m *MockupProcessorPluginParserFunc) Add(_ telegraf.Metric, _ telegraf.Accumulator) error {
  1448  	return nil
  1449  }
  1450  func (m *MockupProcessorPluginParserFunc) SetParserFunc(pf telegraf.ParserFunc) {
  1451  	m.Parser = pf
  1452  }
  1453  
  1454  /*** Mockup OUTPUT plugin for testing to avoid cyclic dependencies ***/
  1455  type MockupOuputPlugin struct {
  1456  	URL             string            `toml:"url"`
  1457  	Headers         map[string]string `toml:"headers"`
  1458  	Scopes          []string          `toml:"scopes"`
  1459  	NamespacePrefix string            `toml:"namespace_prefix"`
  1460  	Log             telegraf.Logger   `toml:"-"`
  1461  	tls.ClientConfig
  1462  }
  1463  
  1464  func (m *MockupOuputPlugin) Connect() error {
  1465  	return nil
  1466  }
  1467  func (m *MockupOuputPlugin) Close() error {
  1468  	return nil
  1469  }
  1470  func (m *MockupOuputPlugin) SampleConfig() string {
  1471  	return "Mockup test output plugin"
  1472  }
  1473  func (m *MockupOuputPlugin) Write(_ []telegraf.Metric) error {
  1474  	return nil
  1475  }
  1476  
  1477  /*** Mockup OUTPUT plugin for serializer testing to avoid cyclic dependencies ***/
  1478  type MockupOutputPluginSerializerOld struct {
  1479  	Serializer serializers.Serializer
  1480  }
  1481  
  1482  func (m *MockupOutputPluginSerializerOld) SetSerializer(s serializers.Serializer) {
  1483  	m.Serializer = s
  1484  }
  1485  func (*MockupOutputPluginSerializerOld) Connect() error {
  1486  	return nil
  1487  }
  1488  func (*MockupOutputPluginSerializerOld) Close() error {
  1489  	return nil
  1490  }
  1491  func (*MockupOutputPluginSerializerOld) SampleConfig() string {
  1492  	return "Mockup test output plugin"
  1493  }
  1494  func (*MockupOutputPluginSerializerOld) Write(_ []telegraf.Metric) error {
  1495  	return nil
  1496  }
  1497  
  1498  type MockupOutputPluginSerializerNew struct {
  1499  	Serializer telegraf.Serializer
  1500  }
  1501  
  1502  func (m *MockupOutputPluginSerializerNew) SetSerializer(s telegraf.Serializer) {
  1503  	m.Serializer = s
  1504  }
  1505  func (*MockupOutputPluginSerializerNew) Connect() error {
  1506  	return nil
  1507  }
  1508  func (*MockupOutputPluginSerializerNew) Close() error {
  1509  	return nil
  1510  }
  1511  func (*MockupOutputPluginSerializerNew) SampleConfig() string {
  1512  	return "Mockup test output plugin"
  1513  }
  1514  func (*MockupOutputPluginSerializerNew) Write(_ []telegraf.Metric) error {
  1515  	return nil
  1516  }
  1517  
  1518  /*** Mockup INPUT plugin with state for testing to avoid cyclic dependencies ***/
  1519  type MockupState struct {
  1520  	Name     string
  1521  	Version  uint64
  1522  	Offset   uint64
  1523  	Bits     []int
  1524  	Modified time.Time
  1525  }
  1526  
  1527  type MockupStatePluginSettings struct {
  1528  	Name     string  `toml:"name"`
  1529  	Factor   float64 `toml:"factor"`
  1530  	Enabled  bool    `toml:"enabled"`
  1531  	BitField []int   `toml:"bits"`
  1532  }
  1533  
  1534  type MockupStatePlugin struct {
  1535  	Servers  []string                    `toml:"servers"`
  1536  	Method   string                      `toml:"method"`
  1537  	Settings map[string]string           `toml:"params"`
  1538  	Port     int                         `toml:"port"`
  1539  	Setups   []MockupStatePluginSettings `toml:"setup"`
  1540  	state    MockupState
  1541  }
  1542  
  1543  func (m *MockupStatePlugin) Init() error {
  1544  	t0, _ := time.Parse(time.RFC3339, "2021-04-24T23:42:00+02:00")
  1545  	m.state = MockupState{
  1546  		Name:     "mockup",
  1547  		Bits:     []int{},
  1548  		Modified: t0,
  1549  	}
  1550  
  1551  	return nil
  1552  }
  1553  
  1554  func (m *MockupStatePlugin) GetState() interface{} {
  1555  	return m.state
  1556  }
  1557  
  1558  func (m *MockupStatePlugin) SetState(state interface{}) error {
  1559  	s, ok := state.(MockupState)
  1560  	if !ok {
  1561  		return fmt.Errorf("invalid state type %T", state)
  1562  	}
  1563  	m.state = s
  1564  
  1565  	return nil
  1566  }
  1567  
  1568  func (m *MockupStatePlugin) SampleConfig() string {
  1569  	return "Mockup test plugin"
  1570  }
  1571  
  1572  func (m *MockupStatePlugin) Gather(_ telegraf.Accumulator) error {
  1573  	return nil
  1574  }
  1575  
  1576  // Register the mockup plugin on loading
  1577  func init() {
  1578  	// Register the mockup input plugin for the required names
  1579  	inputs.Add("parser_test_new", func() telegraf.Input {
  1580  		return &MockupInputPluginParserNew{}
  1581  	})
  1582  	inputs.Add("parser", func() telegraf.Input {
  1583  		return &MockupInputPluginParserOnly{}
  1584  	})
  1585  	inputs.Add("parser_func", func() telegraf.Input {
  1586  		return &MockupInputPluginParserFunc{}
  1587  	})
  1588  	inputs.Add("exec", func() telegraf.Input {
  1589  		return &MockupInputPlugin{Timeout: config.Duration(time.Second * 5)}
  1590  	})
  1591  	inputs.Add("file", func() telegraf.Input {
  1592  		return &MockupInputPlugin{}
  1593  	})
  1594  	inputs.Add("http_listener_v2", func() telegraf.Input {
  1595  		return &MockupInputPlugin{}
  1596  	})
  1597  	inputs.Add("memcached", func() telegraf.Input {
  1598  		return &MockupInputPlugin{}
  1599  	})
  1600  	inputs.Add("procstat", func() telegraf.Input {
  1601  		return &MockupInputPlugin{}
  1602  	})
  1603  	inputs.Add("statetest", func() telegraf.Input {
  1604  		return &MockupStatePlugin{}
  1605  	})
  1606  
  1607  	// Register the mockup processor plugin for the required names
  1608  	processors.Add("parser_test", func() telegraf.Processor {
  1609  		return &MockupProcessorPluginParser{}
  1610  	})
  1611  	processors.Add("processor", func() telegraf.Processor {
  1612  		return &MockupProcessorPlugin{}
  1613  	})
  1614  	processors.Add("processor_parser", func() telegraf.Processor {
  1615  		return &MockupProcessorPluginParserOnly{}
  1616  	})
  1617  	processors.Add("processor_parserfunc", func() telegraf.Processor {
  1618  		return &MockupProcessorPluginParserFunc{}
  1619  	})
  1620  	processors.Add("statetest", func() telegraf.Processor {
  1621  		return &MockupProcessorPlugin{}
  1622  	})
  1623  
  1624  	// Register the mockup output plugin for the required names
  1625  	outputs.Add("azure_monitor", func() telegraf.Output {
  1626  		return &MockupOuputPlugin{NamespacePrefix: "Telegraf/"}
  1627  	})
  1628  	outputs.Add("http", func() telegraf.Output {
  1629  		return &MockupOuputPlugin{}
  1630  	})
  1631  	outputs.Add("serializer_test_new", func() telegraf.Output {
  1632  		return &MockupOutputPluginSerializerNew{}
  1633  	})
  1634  	outputs.Add("serializer_test_old", func() telegraf.Output {
  1635  		return &MockupOutputPluginSerializerOld{}
  1636  	})
  1637  }