2015-11-25 05:22:11 +08:00
package config
import (
2021-04-10 01:15:04 +08:00
"fmt"
2021-02-13 00:38:40 +08:00
"net/http"
"net/http/httptest"
2016-04-02 03:53:34 +08:00
"os"
2021-05-27 00:13:50 +08:00
"runtime"
2020-11-23 23:40:32 +08:00
"strings"
2015-11-25 05:22:11 +08:00
"testing"
"time"
2021-11-25 02:52:51 +08:00
"github.com/stretchr/testify/require"
2021-04-10 01:15:04 +08:00
"github.com/influxdata/telegraf"
2020-05-05 02:09:10 +08:00
"github.com/influxdata/telegraf/models"
2021-04-10 01:15:04 +08:00
"github.com/influxdata/telegraf/plugins/common/tls"
2016-01-21 02:57:35 +08:00
"github.com/influxdata/telegraf/plugins/inputs"
2021-04-10 01:15:04 +08:00
"github.com/influxdata/telegraf/plugins/outputs"
2016-02-06 08:36:35 +08:00
"github.com/influxdata/telegraf/plugins/parsers"
2015-11-25 05:22:11 +08:00
)
2016-04-02 03:53:34 +08:00
func TestConfig_LoadSingleInputWithEnvVars ( t * testing . T ) {
c := NewConfig ( )
2021-04-10 01:15:04 +08:00
require . NoError ( t , os . Setenv ( "MY_TEST_SERVER" , "192.168.1.1" ) )
require . NoError ( t , os . Setenv ( "TEST_INTERVAL" , "10s" ) )
2016-04-02 03:53:34 +08:00
c . LoadConfig ( "./testdata/single_plugin_env_vars.toml" )
2021-04-10 01:15:04 +08:00
input := inputs . Inputs [ "memcached" ] ( ) . ( * MockupInputPlugin )
input . Servers = [ ] string { "192.168.1.1" }
2016-04-02 03:53:34 +08:00
2016-07-28 19:31:11 +08:00
filter := models . Filter {
2016-04-13 07:06:27 +08:00
NameDrop : [ ] string { "metricname2" } ,
2019-03-30 07:02:10 +08:00
NamePass : [ ] string { "metricname1" , "ip_192.168.1.1_name" } ,
2016-04-13 07:06:27 +08:00
FieldDrop : [ ] string { "other" , "stuff" } ,
FieldPass : [ ] string { "some" , "strings" } ,
2016-07-28 19:31:11 +08:00
TagDrop : [ ] models . TagFilter {
2018-10-20 04:32:54 +08:00
{
2016-04-13 07:06:27 +08:00
Name : "badtag" ,
Filter : [ ] string { "othertag" } ,
2016-04-02 03:53:34 +08:00
} ,
2016-04-13 07:06:27 +08:00
} ,
2016-07-28 19:31:11 +08:00
TagPass : [ ] models . TagFilter {
2018-10-20 04:32:54 +08:00
{
2016-04-13 07:06:27 +08:00
Name : "goodtag" ,
Filter : [ ] string { "mytag" } ,
2016-04-02 03:53:34 +08:00
} ,
} ,
2016-04-13 07:06:27 +08:00
}
2021-04-10 01:15:04 +08:00
require . NoError ( t , filter . Compile ( ) )
inputConfig := & models . InputConfig {
2016-04-13 07:06:27 +08:00
Name : "memcached" ,
Filter : filter ,
2016-04-02 03:53:34 +08:00
Interval : 10 * time . Second ,
}
2021-04-10 01:15:04 +08:00
inputConfig . Tags = make ( map [ string ] string )
2016-04-02 03:53:34 +08:00
2021-04-10 01:15:04 +08:00
// 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." )
2016-04-02 03:53:34 +08:00
}
2016-01-08 04:39:43 +08:00
func TestConfig_LoadSingleInput ( t * testing . T ) {
2015-11-25 05:22:11 +08:00
c := NewConfig ( )
c . LoadConfig ( "./testdata/single_plugin.toml" )
2021-04-10 01:15:04 +08:00
input := inputs . Inputs [ "memcached" ] ( ) . ( * MockupInputPlugin )
input . Servers = [ ] string { "localhost" }
2015-11-25 05:22:11 +08:00
2016-07-28 19:31:11 +08:00
filter := models . Filter {
2016-04-13 07:06:27 +08:00
NameDrop : [ ] string { "metricname2" } ,
NamePass : [ ] string { "metricname1" } ,
FieldDrop : [ ] string { "other" , "stuff" } ,
FieldPass : [ ] string { "some" , "strings" } ,
2016-07-28 19:31:11 +08:00
TagDrop : [ ] models . TagFilter {
2018-10-20 04:32:54 +08:00
{
2016-04-13 07:06:27 +08:00
Name : "badtag" ,
Filter : [ ] string { "othertag" } ,
2015-11-25 05:22:11 +08:00
} ,
2016-04-13 07:06:27 +08:00
} ,
2016-07-28 19:31:11 +08:00
TagPass : [ ] models . TagFilter {
2018-10-20 04:32:54 +08:00
{
2016-04-13 07:06:27 +08:00
Name : "goodtag" ,
Filter : [ ] string { "mytag" } ,
2015-11-25 05:22:11 +08:00
} ,
} ,
2016-04-13 07:06:27 +08:00
}
2021-04-10 01:15:04 +08:00
require . NoError ( t , filter . Compile ( ) )
inputConfig := & models . InputConfig {
2016-04-13 07:06:27 +08:00
Name : "memcached" ,
Filter : filter ,
2015-11-25 05:22:11 +08:00
Interval : 5 * time . Second ,
}
2021-04-10 01:15:04 +08:00
inputConfig . Tags = make ( map [ string ] string )
2015-11-25 05:22:11 +08:00
2021-04-10 01:15:04 +08:00
// 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." )
2015-11-25 05:22:11 +08:00
}
func TestConfig_LoadDirectory ( t * testing . T ) {
c := NewConfig ( )
2021-04-10 01:15:04 +08:00
require . NoError ( t , c . LoadConfig ( "./testdata/single_plugin.toml" ) )
require . NoError ( t , c . LoadDirectory ( "./testdata/subconfig" ) )
2015-11-25 05:22:11 +08:00
2021-04-10 01:15:04 +08:00
// Create the expected data
expectedPlugins := make ( [ ] * MockupInputPlugin , 4 )
expectedConfigs := make ( [ ] * models . InputConfig , 4 )
2015-11-25 05:22:11 +08:00
2021-04-10 01:15:04 +08:00
expectedPlugins [ 0 ] = inputs . Inputs [ "memcached" ] ( ) . ( * MockupInputPlugin )
expectedPlugins [ 0 ] . Servers = [ ] string { "localhost" }
filterMockup := models . Filter {
2016-04-13 07:06:27 +08:00
NameDrop : [ ] string { "metricname2" } ,
NamePass : [ ] string { "metricname1" } ,
FieldDrop : [ ] string { "other" , "stuff" } ,
FieldPass : [ ] string { "some" , "strings" } ,
2016-07-28 19:31:11 +08:00
TagDrop : [ ] models . TagFilter {
2018-10-20 04:32:54 +08:00
{
2016-04-13 07:06:27 +08:00
Name : "badtag" ,
Filter : [ ] string { "othertag" } ,
2015-11-25 05:22:11 +08:00
} ,
2016-04-13 07:06:27 +08:00
} ,
2016-07-28 19:31:11 +08:00
TagPass : [ ] models . TagFilter {
2018-10-20 04:32:54 +08:00
{
2016-04-13 07:06:27 +08:00
Name : "goodtag" ,
Filter : [ ] string { "mytag" } ,
2015-11-25 05:22:11 +08:00
} ,
} ,
2016-04-13 07:06:27 +08:00
}
2021-04-10 01:15:04 +08:00
require . NoError ( t , filterMockup . Compile ( ) )
expectedConfigs [ 0 ] = & models . InputConfig {
2016-04-13 07:06:27 +08:00
Name : "memcached" ,
2021-04-10 01:15:04 +08:00
Filter : filterMockup ,
2015-11-25 05:22:11 +08:00
Interval : 5 * time . Second ,
}
2021-04-10 01:15:04 +08:00
expectedConfigs [ 0 ] . Tags = make ( map [ string ] string )
2015-11-25 05:22:11 +08:00
2021-04-10 01:15:04 +08:00
expectedPlugins [ 1 ] = inputs . Inputs [ "exec" ] ( ) . ( * MockupInputPlugin )
2021-11-25 02:52:51 +08:00
parserConfig := & parsers . Config {
2018-08-23 10:26:48 +08:00
MetricName : "exec" ,
DataFormat : "json" ,
2020-01-22 02:10:02 +08:00
JSONStrict : true ,
2021-11-25 02:52:51 +08:00
}
p , err := parsers . NewParser ( parserConfig )
2021-04-10 01:15:04 +08:00
require . NoError ( t , err )
2021-11-25 02:52:51 +08:00
// Inject logger to have proper struct for comparison
models . SetLoggerOnPlugin ( p , models . NewLogger ( "parsers" , parserConfig . DataFormat , parserConfig . MetricName ) )
2021-04-10 01:15:04 +08:00
expectedPlugins [ 1 ] . SetParser ( p )
expectedPlugins [ 1 ] . Command = "/usr/bin/myothercollector --foo=bar"
expectedConfigs [ 1 ] = & models . InputConfig {
2016-01-08 04:39:43 +08:00
Name : "exec" ,
MeasurementSuffix : "_myothercollector" ,
}
2021-04-10 01:15:04 +08:00
expectedConfigs [ 1 ] . Tags = make ( map [ string ] string )
2015-11-25 05:22:11 +08:00
2021-04-10 01:15:04 +08:00
expectedPlugins [ 2 ] = inputs . Inputs [ "memcached" ] ( ) . ( * MockupInputPlugin )
expectedPlugins [ 2 ] . Servers = [ ] string { "192.168.1.1" }
2015-11-25 05:22:11 +08:00
2021-04-10 01:15:04 +08:00
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
// Ignore the parser if not expected
if expectedPlugins [ i ] . parser == nil {
input . parser = nil
}
2015-11-25 05:22:11 +08:00
2021-04-10 01:15:04 +08:00
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 )
}
2015-11-25 05:22:11 +08:00
}
2019-04-26 11:19:58 +08:00
func TestConfig_LoadSpecialTypes ( t * testing . T ) {
c := NewConfig ( )
2021-04-10 01:15:04 +08:00
require . NoError ( t , c . LoadConfig ( "./testdata/special_types.toml" ) )
require . Len ( t , c . Inputs , 1 )
2019-04-26 11:19:58 +08:00
2021-04-10 01:15:04 +08:00
input , ok := c . Inputs [ 0 ] . Input . ( * MockupInputPlugin )
require . True ( t , ok )
2019-04-26 11:19:58 +08:00
// Tests telegraf duration parsing.
2021-04-10 01:15:04 +08:00
require . Equal ( t , Duration ( time . Second ) , input . WriteTimeout )
2019-04-26 11:19:58 +08:00
// Tests telegraf size parsing.
2021-04-10 01:15:04 +08:00
require . Equal ( t , Size ( 1024 * 1024 ) , input . MaxBodySize )
2019-04-26 11:19:58 +08:00
// Tests toml multiline basic strings.
2021-04-10 01:15:04 +08:00
require . Equal ( t , "/path/to/my/cert" , strings . TrimRight ( input . TLSCert , "\r\n" ) )
2019-04-26 11:19:58 +08:00
}
func TestConfig_FieldNotDefined ( t * testing . T ) {
c := NewConfig ( )
err := c . LoadConfig ( "./testdata/invalid_field.toml" )
require . Error ( t , err , "invalid field name" )
2021-04-10 01:15:04 +08:00
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 ( ) )
2019-04-26 11:19:58 +08:00
}
func TestConfig_WrongFieldType ( t * testing . T ) {
c := NewConfig ( )
err := c . LoadConfig ( "./testdata/wrong_field_type.toml" )
require . Error ( t , err , "invalid field type" )
2021-04-10 01:15:04 +08:00
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 ( ) )
2019-04-26 11:19:58 +08:00
c = NewConfig ( )
err = c . LoadConfig ( "./testdata/wrong_field_type2.toml" )
require . Error ( t , err , "invalid field type2" )
2021-04-10 01:15:04 +08:00
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 ( ) )
2019-04-26 11:19:58 +08:00
}
func TestConfig_InlineTables ( t * testing . T ) {
// #4098
c := NewConfig ( )
2021-04-10 01:15:04 +08:00
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 )
2019-04-26 11:19:58 +08:00
}
func TestConfig_SliceComment ( t * testing . T ) {
t . Skipf ( "Skipping until #3642 is resolved" )
c := NewConfig ( )
2021-04-10 01:15:04 +08:00
require . NoError ( t , c . LoadConfig ( "./testdata/slice_comment.toml" ) )
require . Len ( t , c . Outputs , 1 )
2019-04-26 11:19:58 +08:00
2021-04-10 01:15:04 +08:00
output , ok := c . Outputs [ 0 ] . Output . ( * MockupOuputPlugin )
require . True ( t , ok )
require . Equal ( t , [ ] string { "test" } , output . Scopes )
2019-04-26 11:19:58 +08:00
}
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" )
2021-04-10 01:15:04 +08:00
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 ( ) )
2019-04-26 11:19:58 +08:00
}
2020-10-20 22:16:22 +08:00
func TestConfig_AzureMonitorNamespacePrefix ( t * testing . T ) {
// #8256 Cannot use empty string as the namespace prefix
c := NewConfig ( )
2021-04-10 01:15:04 +08:00
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 )
}
2020-10-20 22:16:22 +08:00
}
2021-02-13 00:38:40 +08:00
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 ( )
2021-04-10 01:15:04 +08:00
expected := fmt . Sprintf ( "Error loading config file %s: Retry 3 of 3 failed to retrieve remote config: 404 Not Found" , ts . URL )
2021-02-13 00:38:40 +08:00
c := NewConfig ( )
err := c . LoadConfig ( ts . URL )
require . Error ( t , err )
2021-04-10 01:15:04 +08:00
require . Equal ( t , expected , err . Error ( ) )
2021-02-13 00:38:40 +08:00
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 ( )
2021-04-10 01:15:04 +08:00
require . NoError ( t , c . LoadConfig ( ts . URL ) )
2021-02-13 00:38:40 +08:00
require . Equal ( t , 4 , responseCounter )
}
2021-04-10 01:15:04 +08:00
2021-06-03 11:22:15 +08:00
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 )
}
2021-05-27 00:13:50 +08:00
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 ( ) )
}
}
2021-04-10 01:15:04 +08:00
/*** 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" `
Port int ` toml:"port" `
Command string
PidFile string
Log telegraf . Logger ` toml:"-" `
tls . ServerConfig
parser parsers . Parser
}
func ( m * MockupInputPlugin ) SampleConfig ( ) string { return "Mockup test intput plugin" }
func ( m * MockupInputPlugin ) Description ( ) string { return "Mockup test intput plugin" }
func ( m * MockupInputPlugin ) Gather ( acc telegraf . Accumulator ) error { return nil }
func ( m * MockupInputPlugin ) SetParser ( parser parsers . 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 ) Description ( ) string { return "Mockup test output plugin" }
func ( m * MockupOuputPlugin ) SampleConfig ( ) string { return "Mockup test output plugin" }
func ( m * MockupOuputPlugin ) Write ( metrics [ ] telegraf . Metric ) error { return nil }
// Register the mockup plugin on loading
func init ( ) {
// Register the mockup input plugin for the required names
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 { } } )
}