Allow using glob pattern in the command list in configuration. This enables for example placing all commands in a single directory and using /path/to/dir/*.sh as one of the commands to run all shell scripts in that directory. Glob patterns are applied on every run of the commands, so matching commands can be added without restarting telegraf. closes #1142
220 lines
5.2 KiB
Go
220 lines
5.2 KiB
Go
package exec
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
"github.com/influxdata/telegraf/plugins/parsers"
|
|
|
|
"github.com/influxdata/telegraf/testutil"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Midnight 9/22/2015
|
|
const baseTimeSeconds = 1442905200
|
|
|
|
const validJson = `
|
|
{
|
|
"status": "green",
|
|
"num_processes": 82,
|
|
"cpu": {
|
|
"status": "red",
|
|
"nil_status": null,
|
|
"used": 8234,
|
|
"free": 32
|
|
},
|
|
"percent": 0.81,
|
|
"users": [0, 1, 2, 3]
|
|
}`
|
|
|
|
const malformedJson = `
|
|
{
|
|
"status": "green",
|
|
`
|
|
|
|
const lineProtocol = "cpu,host=foo,datacenter=us-east usage_idle=99,usage_busy=1"
|
|
|
|
const lineProtocolMulti = `
|
|
cpu,cpu=cpu0,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
|
cpu,cpu=cpu1,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
|
cpu,cpu=cpu2,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
|
cpu,cpu=cpu3,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
|
cpu,cpu=cpu4,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
|
cpu,cpu=cpu5,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
|
cpu,cpu=cpu6,host=foo,datacenter=us-east usage_idle=99,usage_busy=1
|
|
`
|
|
|
|
type runnerMock struct {
|
|
out []byte
|
|
err error
|
|
}
|
|
|
|
func newRunnerMock(out []byte, err error) Runner {
|
|
return &runnerMock{
|
|
out: out,
|
|
err: err,
|
|
}
|
|
}
|
|
|
|
func (r runnerMock) Run(e *Exec, command string, acc telegraf.Accumulator) ([]byte, error) {
|
|
if r.err != nil {
|
|
return nil, r.err
|
|
}
|
|
return r.out, nil
|
|
}
|
|
|
|
func TestExec(t *testing.T) {
|
|
parser, _ := parsers.NewJSONParser("exec", []string{}, nil)
|
|
e := &Exec{
|
|
runner: newRunnerMock([]byte(validJson), nil),
|
|
Commands: []string{"testcommand arg1"},
|
|
parser: parser,
|
|
}
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, acc.NFields(), 8, "non-numeric measurements should be ignored")
|
|
|
|
fields := map[string]interface{}{
|
|
"num_processes": float64(82),
|
|
"cpu_used": float64(8234),
|
|
"cpu_free": float64(32),
|
|
"percent": float64(0.81),
|
|
"users_0": float64(0),
|
|
"users_1": float64(1),
|
|
"users_2": float64(2),
|
|
"users_3": float64(3),
|
|
}
|
|
acc.AssertContainsFields(t, "exec", fields)
|
|
}
|
|
|
|
func TestExecMalformed(t *testing.T) {
|
|
parser, _ := parsers.NewJSONParser("exec", []string{}, nil)
|
|
e := &Exec{
|
|
runner: newRunnerMock([]byte(malformedJson), nil),
|
|
Commands: []string{"badcommand arg1"},
|
|
parser: parser,
|
|
}
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.Error(t, err)
|
|
assert.Equal(t, acc.NFields(), 0, "No new points should have been added")
|
|
}
|
|
|
|
func TestCommandError(t *testing.T) {
|
|
parser, _ := parsers.NewJSONParser("exec", []string{}, nil)
|
|
e := &Exec{
|
|
runner: newRunnerMock(nil, fmt.Errorf("exit status code 1")),
|
|
Commands: []string{"badcommand"},
|
|
parser: parser,
|
|
}
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.Error(t, err)
|
|
assert.Equal(t, acc.NFields(), 0, "No new points should have been added")
|
|
}
|
|
|
|
func TestLineProtocolParse(t *testing.T) {
|
|
parser, _ := parsers.NewInfluxParser()
|
|
e := &Exec{
|
|
runner: newRunnerMock([]byte(lineProtocol), nil),
|
|
Commands: []string{"line-protocol"},
|
|
parser: parser,
|
|
}
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.NoError(t, err)
|
|
|
|
fields := map[string]interface{}{
|
|
"usage_idle": float64(99),
|
|
"usage_busy": float64(1),
|
|
}
|
|
tags := map[string]string{
|
|
"host": "foo",
|
|
"datacenter": "us-east",
|
|
}
|
|
acc.AssertContainsTaggedFields(t, "cpu", fields, tags)
|
|
}
|
|
|
|
func TestLineProtocolParseMultiple(t *testing.T) {
|
|
parser, _ := parsers.NewInfluxParser()
|
|
e := &Exec{
|
|
runner: newRunnerMock([]byte(lineProtocolMulti), nil),
|
|
Commands: []string{"line-protocol"},
|
|
parser: parser,
|
|
}
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.NoError(t, err)
|
|
|
|
fields := map[string]interface{}{
|
|
"usage_idle": float64(99),
|
|
"usage_busy": float64(1),
|
|
}
|
|
tags := map[string]string{
|
|
"host": "foo",
|
|
"datacenter": "us-east",
|
|
}
|
|
cpuTags := []string{"cpu0", "cpu1", "cpu2", "cpu3", "cpu4", "cpu5", "cpu6"}
|
|
|
|
for _, cpu := range cpuTags {
|
|
tags["cpu"] = cpu
|
|
acc.AssertContainsTaggedFields(t, "cpu", fields, tags)
|
|
}
|
|
}
|
|
|
|
func TestExecCommandWithGlob(t *testing.T) {
|
|
parser, _ := parsers.NewValueParser("metric", "string", nil)
|
|
e := NewExec()
|
|
e.Commands = []string{"/bin/ech* metric_value"}
|
|
e.SetParser(parser)
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.NoError(t, err)
|
|
|
|
fields := map[string]interface{}{
|
|
"value": "metric_value",
|
|
}
|
|
acc.AssertContainsFields(t, "metric", fields)
|
|
}
|
|
|
|
func TestExecCommandWithoutGlob(t *testing.T) {
|
|
parser, _ := parsers.NewValueParser("metric", "string", nil)
|
|
e := NewExec()
|
|
e.Commands = []string{"/bin/echo metric_value"}
|
|
e.SetParser(parser)
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.NoError(t, err)
|
|
|
|
fields := map[string]interface{}{
|
|
"value": "metric_value",
|
|
}
|
|
acc.AssertContainsFields(t, "metric", fields)
|
|
}
|
|
|
|
func TestExecCommandWithoutGlobAndPath(t *testing.T) {
|
|
parser, _ := parsers.NewValueParser("metric", "string", nil)
|
|
e := NewExec()
|
|
e.Commands = []string{"echo metric_value"}
|
|
e.SetParser(parser)
|
|
|
|
var acc testutil.Accumulator
|
|
err := e.Gather(&acc)
|
|
require.NoError(t, err)
|
|
|
|
fields := map[string]interface{}{
|
|
"value": "metric_value",
|
|
}
|
|
acc.AssertContainsFields(t, "metric", fields)
|
|
}
|