telegraf/cmd/telegraf/main_test.go

568 lines
13 KiB
Go

package main
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
"github.com/influxdata/telegraf/plugins/outputs"
)
var secrets = map[string]map[string][]byte{
"yoda": {
"episode1": []byte("member"),
"episode2": []byte("member"),
"episode3": []byte("member"),
},
"mace_windu": {
"episode1": []byte("member"),
"episode2": []byte("member"),
"episode3": []byte("member"),
},
"oppo_rancisis": {
"episode1": []byte("member"),
"episode2": []byte("member"),
},
"coleman_kcaj": {
"episode3": []byte("member"),
},
}
type MockTelegraf struct {
GlobalFlags
WindowFlags
}
func NewMockTelegraf() *MockTelegraf {
return &MockTelegraf{}
}
func (m *MockTelegraf) Init(_ <-chan error, _ Filters, g GlobalFlags, w WindowFlags) {
m.GlobalFlags = g
m.WindowFlags = w
}
func (m *MockTelegraf) Run() error {
return nil
}
func (m *MockTelegraf) ListSecretStores() ([]string, error) {
ids := make([]string, 0, len(secrets))
for k := range secrets {
ids = append(ids, k)
}
return ids, nil
}
func (m *MockTelegraf) GetSecretStore(id string) (telegraf.SecretStore, error) {
v, found := secrets[id]
if !found {
return nil, errors.New("unknown secret store")
}
s := &MockSecretStore{Secrets: v}
return s, nil
}
type MockSecretStore struct {
Secrets map[string][]byte
}
func (s *MockSecretStore) Init() error {
return nil
}
func (s *MockSecretStore) SampleConfig() string {
return "I'm just a dummy"
}
func (s *MockSecretStore) Get(key string) ([]byte, error) {
v, found := s.Secrets[key]
if !found {
return nil, errors.New("not found")
}
return v, nil
}
func (s *MockSecretStore) Set(key, value string) error {
if strings.HasPrefix(key, "darth") {
return errors.New("don't join the dark side")
}
s.Secrets[key] = []byte(value)
return nil
}
func (s *MockSecretStore) List() ([]string, error) {
keys := make([]string, 0, len(s.Secrets))
for k := range s.Secrets {
keys = append(keys, k)
}
return keys, nil
}
func (s *MockSecretStore) GetResolver(key string) (telegraf.ResolveFunc, error) {
return func() ([]byte, bool, error) {
v, err := s.Get(key)
return v, false, err
}, nil
}
type MockConfig struct {
Buffer io.Writer
ExpectedDeprecatedPlugins map[string][]config.PluginDeprecationInfo
}
func NewMockConfig(buffer io.Writer) *MockConfig {
return &MockConfig{
Buffer: buffer,
}
}
func (m *MockConfig) CollectDeprecationInfos(_, _, _, _ []string) map[string][]config.PluginDeprecationInfo {
return m.ExpectedDeprecatedPlugins
}
func (m *MockConfig) PrintDeprecationList(plugins []config.PluginDeprecationInfo) {
for _, p := range plugins {
_, _ = m.Buffer.Write([]byte(fmt.Sprintf("plugin name: %s\n", p.Name)))
}
}
type MockServer struct {
Address string
}
func NewMockServer() *MockServer {
return &MockServer{}
}
func (m *MockServer) Start(_ string) {
m.Address = "localhost:6060"
}
func (m *MockServer) ErrChan() <-chan error {
return nil
}
func TestUsageFlag(t *testing.T) {
tests := []struct {
PluginName string
ExpectedError string
ExpectedOutput string
}{
{
PluginName: "example",
ExpectedError: "input example not found and output example not found",
},
{
PluginName: "temp",
ExpectedOutput: `
# Read metrics about temperature
[[inputs.temp]]
# no configuration
`,
},
}
for _, test := range tests {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, "--usage", test.PluginName)
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf())
if test.ExpectedError != "" {
require.ErrorContains(t, err, test.ExpectedError)
continue
}
require.NoError(t, err)
// To run this test on windows and linux, remove windows carriage return
o := strings.Replace(buf.String(), "\r", "", -1)
require.Equal(t, test.ExpectedOutput, o)
}
}
func TestInputListFlag(t *testing.T) {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, "--input-list")
temp := inputs.Inputs
inputs.Inputs = map[string]inputs.Creator{
"test": func() telegraf.Input { return nil },
}
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf())
require.NoError(t, err)
expectedOutput := `DEPRECATED: use telegraf plugins inputs
Available Input Plugins:
test
`
require.Equal(t, expectedOutput, buf.String())
inputs.Inputs = temp
}
func TestOutputListFlag(t *testing.T) {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, "--output-list")
temp := outputs.Outputs
outputs.Outputs = map[string]outputs.Creator{
"test": func() telegraf.Output { return nil },
}
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf())
require.NoError(t, err)
expectedOutput := `DEPRECATED: use telegraf plugins outputs
Available Output Plugins:
test
`
require.Equal(t, expectedOutput, buf.String())
outputs.Outputs = temp
}
func TestDeprecationListFlag(t *testing.T) {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, "--deprecation-list")
mS := NewMockServer()
mC := NewMockConfig(buf)
mC.ExpectedDeprecatedPlugins = make(map[string][]config.PluginDeprecationInfo)
mC.ExpectedDeprecatedPlugins["inputs"] = []config.PluginDeprecationInfo{
{
DeprecationInfo: config.DeprecationInfo{
Name: "test",
},
},
}
err := runApp(args, buf, mS, mC, NewMockTelegraf())
require.NoError(t, err)
expectedOutput := `Deprecated Input Plugins:
plugin name: test
Deprecated Output Plugins:
Deprecated Processor Plugins:
Deprecated Aggregator Plugins:
`
require.Equal(t, expectedOutput, buf.String())
}
func TestPprofAddressFlag(t *testing.T) {
buf := new(bytes.Buffer)
args := os.Args[0:1]
address := "localhost:6060"
args = append(args, "--pprof-addr", address)
m := NewMockServer()
err := runApp(args, buf, m, NewMockConfig(buf), NewMockTelegraf())
require.NoError(t, err)
require.Equal(t, address, m.Address)
}
// !!! DEPRECATED !!!
// TestPluginDirectoryFlag tests `--plugin-directory`
func TestPluginDirectoryFlag(t *testing.T) {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, "--plugin-directory", ".")
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf())
require.ErrorContains(t, err, "go plugin support is not enabled")
}
func TestCommandConfig(t *testing.T) {
tests := []struct {
name string
commands []string
expectedHeaders []string
removedHeaders []string
expectedPlugins []string
removedPlugins []string
}{
{
name: "deprecated flag --sample-config",
commands: []string{"--sample-config"},
expectedHeaders: []string{
outputHeader,
inputHeader,
aggregatorHeader,
processorHeader,
serviceInputHeader,
},
},
{
name: "no filters",
commands: []string{"config"},
expectedHeaders: []string{
outputHeader,
inputHeader,
aggregatorHeader,
processorHeader,
serviceInputHeader,
},
},
{
name: "filter sections for inputs",
commands: []string{"config", "--section-filter", "inputs"},
expectedHeaders: []string{
inputHeader,
},
removedHeaders: []string{
outputHeader,
aggregatorHeader,
processorHeader,
},
},
{
name: "filter sections for inputs,outputs",
commands: []string{"config", "--section-filter", "inputs:outputs"},
expectedHeaders: []string{
inputHeader,
outputHeader,
},
removedHeaders: []string{
aggregatorHeader,
processorHeader,
},
},
{
name: "filter input plugins",
commands: []string{"config", "--input-filter", "cpu:file"},
expectedPlugins: []string{
"[[inputs.cpu]]",
"[[inputs.file]]",
},
removedPlugins: []string{
"[[inputs.disk]]",
},
},
{
name: "filter output plugins",
commands: []string{"config", "--output-filter", "influxdb:http"},
expectedPlugins: []string{
"[[outputs.influxdb]]",
"[[outputs.http]]",
},
removedPlugins: []string{
"[[outputs.file]]",
},
},
{
name: "filter processor plugins",
commands: []string{"config", "--processor-filter", "date:enum"},
expectedPlugins: []string{
"[[processors.date]]",
"[[processors.enum]]",
},
removedPlugins: []string{
"[[processors.parser]]",
},
},
{
name: "filter aggregator plugins",
commands: []string{"config", "--aggregator-filter", "basicstats:starlark"},
expectedPlugins: []string{
"[[aggregators.basicstats]]",
"[[aggregators.starlark]]",
},
removedPlugins: []string{
"[[aggregators.minmax]]",
},
},
{
name: "test filters before config",
commands: []string{"--input-filter", "cpu:file", "config"},
expectedPlugins: []string{
"[[inputs.cpu]]",
"[[inputs.file]]",
},
removedPlugins: []string{
"[[inputs.disk]]",
},
},
{
name: "test filters before and after config",
commands: []string{"--input-filter", "file", "config", "--input-filter", "cpu"},
expectedPlugins: []string{
"[[inputs.cpu]]",
"[[inputs.file]]",
},
removedPlugins: []string{
"[[inputs.disk]]",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, test.commands...)
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf())
require.NoError(t, err)
output := buf.String()
for _, e := range test.expectedHeaders {
require.Contains(t, output, e, "expected header not found")
}
for _, r := range test.removedHeaders {
require.NotContains(t, output, r, "removed header found")
}
for _, e := range test.expectedPlugins {
require.Contains(t, output, e, "expected plugin not found")
}
for _, r := range test.removedPlugins {
require.NotContains(t, output, r, "removed plugin found")
}
})
}
}
func TestCommandVersion(t *testing.T) {
tests := []struct {
Version string
Branch string
Commit string
ExpectedOutput string
}{
{
Version: "v2.0.0",
ExpectedOutput: "Telegraf v2.0.0\n",
},
{
ExpectedOutput: "Telegraf unknown\n",
},
{
Version: "v2.0.0",
Branch: "master",
ExpectedOutput: "Telegraf v2.0.0 (git: master@unknown)\n",
},
{
Version: "v2.0.0",
Branch: "master",
Commit: "123",
ExpectedOutput: "Telegraf v2.0.0 (git: master@123)\n",
},
{
Version: "v2.0.0",
Commit: "123",
ExpectedOutput: "Telegraf v2.0.0 (git: unknown@123)\n",
},
}
for _, test := range tests {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, "version")
internal.Version = test.Version
internal.Branch = test.Branch
internal.Commit = test.Commit
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf())
require.NoError(t, err)
require.Equal(t, test.ExpectedOutput, buf.String())
}
}
// Users should use the version subcommand
func TestFlagVersion(t *testing.T) {
tests := []struct {
Version string
Branch string
Commit string
ExpectedOutput string
}{
{
Version: "v2.0.0",
ExpectedOutput: "Telegraf v2.0.0\n",
},
{
ExpectedOutput: "Telegraf unknown\n",
},
{
Version: "v2.0.0",
Branch: "master",
ExpectedOutput: "Telegraf v2.0.0 (git: master@unknown)\n",
},
{
Version: "v2.0.0",
Branch: "master",
Commit: "123",
ExpectedOutput: "Telegraf v2.0.0 (git: master@123)\n",
},
{
Version: "v2.0.0",
Commit: "123",
ExpectedOutput: "Telegraf v2.0.0 (git: unknown@123)\n",
},
}
for _, test := range tests {
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, "--version")
internal.Version = test.Version
internal.Branch = test.Branch
internal.Commit = test.Commit
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf())
require.NoError(t, err)
require.Equal(t, test.ExpectedOutput, buf.String())
}
}
func TestGlobablBoolFlags(t *testing.T) {
commands := []string{
"--debug",
"--test",
"--quiet",
"--once",
}
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, commands...)
m := NewMockTelegraf()
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), m)
require.NoError(t, err)
require.True(t, m.debug)
require.True(t, m.test)
require.True(t, m.once)
require.True(t, m.quiet)
}
func TestFlagsAreSet(t *testing.T) {
expectedInt := 1
expectedString := "test"
commands := []string{
"--config", expectedString,
"--config-directory", expectedString,
"--debug",
"--test",
"--quiet",
"--once",
"--test-wait", strconv.Itoa(expectedInt),
"--watch-config", expectedString,
"--pidfile", expectedString,
}
buf := new(bytes.Buffer)
args := os.Args[0:1]
args = append(args, commands...)
m := NewMockTelegraf()
err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), m)
require.NoError(t, err)
require.Equal(t, []string{expectedString}, m.config)
require.Equal(t, []string{expectedString}, m.configDir)
require.True(t, m.debug)
require.True(t, m.test)
require.True(t, m.once)
require.True(t, m.quiet)
require.Equal(t, expectedInt, m.testWait)
require.Equal(t, expectedString, m.watchConfig)
require.Equal(t, expectedString, m.pidFile)
}