Added the option removecr to inputs.exec to remove all carraige returns (CR, ASCII 0x0D, Unicode codepoint \u0D, ^M). The option is boolean and not enabled if not present in the config file. closes #1606 Updated CHANGELOG.md with information about removecr Ran go fmt ./... Moved removal of CRs to internal/internal.go Moved the code to remove carriage returns from plugins/inputs/exec/exec.go to internal/internal.go. Additionally changed the conditional on which it gets applied from using a configuration file option to checking if it is running on Windows. Moved Carriage Return check to correct place Moved the carriage return removal back to the exec plugin. Added unit testing for it. Fixed a bug (removing too many characters). Ran go fmt ./... Reverted CHANGELOG to master Updated Changelog
263 lines
6.7 KiB
Go
263 lines
6.7 KiB
Go
package exec
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"runtime"
|
|
"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 CarriageReturnTest struct {
|
|
input []byte
|
|
output []byte
|
|
}
|
|
|
|
var crTests = []CarriageReturnTest{
|
|
{[]byte{0x4c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x0d, 0x0a, 0x4c, 0x69,
|
|
0x6e, 0x65, 0x20, 0x32, 0x0d, 0x0a, 0x4c, 0x69, 0x6e, 0x65,
|
|
0x20, 0x33},
|
|
[]byte{0x4c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x0a, 0x4c, 0x69, 0x6e,
|
|
0x65, 0x20, 0x32, 0x0a, 0x4c, 0x69, 0x6e, 0x65, 0x20, 0x33}},
|
|
{[]byte{0x4c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x0a, 0x4c, 0x69, 0x6e,
|
|
0x65, 0x20, 0x32, 0x0a, 0x4c, 0x69, 0x6e, 0x65, 0x20, 0x33},
|
|
[]byte{0x4c, 0x69, 0x6e, 0x65, 0x20, 0x31, 0x0a, 0x4c, 0x69, 0x6e,
|
|
0x65, 0x20, 0x32, 0x0a, 0x4c, 0x69, 0x6e, 0x65, 0x20, 0x33}},
|
|
{[]byte{0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c,
|
|
0x6c, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x62, 0x69, 0x67, 0x20,
|
|
0x6c, 0x69, 0x6e, 0x65},
|
|
[]byte{0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c,
|
|
0x6c, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x62, 0x69, 0x67, 0x20,
|
|
0x6c, 0x69, 0x6e, 0x65}},
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
func TestRemoveCarriageReturns(t *testing.T) {
|
|
if runtime.GOOS == "windows" {
|
|
// Test that all carriage returns are removed
|
|
for _, test := range crTests {
|
|
b := bytes.NewBuffer(test.input)
|
|
out := removeCarriageReturns(*b)
|
|
assert.True(t, bytes.Equal(test.output, out.Bytes()))
|
|
}
|
|
} else {
|
|
// Test that the buffer is returned unaltered
|
|
for _, test := range crTests {
|
|
b := bytes.NewBuffer(test.input)
|
|
out := removeCarriageReturns(*b)
|
|
assert.True(t, bytes.Equal(test.input, out.Bytes()))
|
|
}
|
|
}
|
|
}
|