3825 lines
78 KiB
Go
3825 lines
78 KiB
Go
|
package starlark
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/gob"
|
||
|
"errors"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
starlarktime "go.starlark.net/lib/time"
|
||
|
"go.starlark.net/starlark"
|
||
|
"go.starlark.net/starlarkstruct"
|
||
|
|
||
|
"github.com/influxdata/telegraf"
|
||
|
"github.com/influxdata/telegraf/config"
|
||
|
"github.com/influxdata/telegraf/metric"
|
||
|
common "github.com/influxdata/telegraf/plugins/common/starlark"
|
||
|
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
||
|
"github.com/influxdata/telegraf/testutil"
|
||
|
)
|
||
|
|
||
|
// Tests for runtime errors in the processors Init function.
|
||
|
func TestInitError(t *testing.T) {
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
constants map[string]interface{}
|
||
|
plugin *Starlark
|
||
|
}{
|
||
|
{
|
||
|
name: "source must define apply",
|
||
|
plugin: newStarlarkFromSource(""),
|
||
|
},
|
||
|
{
|
||
|
name: "apply must be a function",
|
||
|
plugin: newStarlarkFromSource(`
|
||
|
apply = 42
|
||
|
`),
|
||
|
},
|
||
|
{
|
||
|
name: "apply function must take one arg",
|
||
|
plugin: newStarlarkFromSource(`
|
||
|
def apply():
|
||
|
pass
|
||
|
`),
|
||
|
},
|
||
|
{
|
||
|
name: "package scope must have valid syntax",
|
||
|
plugin: newStarlarkFromSource(`
|
||
|
for
|
||
|
`),
|
||
|
},
|
||
|
{
|
||
|
name: "no source no script",
|
||
|
plugin: newStarlarkNoScript(),
|
||
|
},
|
||
|
{
|
||
|
name: "source and script",
|
||
|
plugin: newStarlarkFromSource(`
|
||
|
def apply():
|
||
|
pass
|
||
|
`),
|
||
|
},
|
||
|
{
|
||
|
name: "script file not found",
|
||
|
plugin: newStarlarkFromScript("testdata/file_not_found.star"),
|
||
|
},
|
||
|
{
|
||
|
name: "source and script",
|
||
|
plugin: newStarlarkFromSource(`
|
||
|
def apply(metric):
|
||
|
metric.fields["p1"] = unsupported_type
|
||
|
return metric
|
||
|
`),
|
||
|
constants: map[string]interface{}{
|
||
|
"unsupported_type": time.Now(),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
tt.plugin.Constants = tt.constants
|
||
|
err := tt.plugin.Init()
|
||
|
require.Error(t, err)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestApply(t *testing.T) {
|
||
|
// Tests for the behavior of the processors Apply function.
|
||
|
var applyTests = []struct {
|
||
|
name string
|
||
|
source string
|
||
|
input []telegraf.Metric
|
||
|
expected []telegraf.Metric
|
||
|
expectedErrorStr string
|
||
|
}{
|
||
|
{
|
||
|
name: "drop metric",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return None
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "passthrough",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "read value from global scope",
|
||
|
source: `
|
||
|
names = {
|
||
|
'cpu': 'cpu2',
|
||
|
'mem': 'mem2',
|
||
|
}
|
||
|
|
||
|
def apply(metric):
|
||
|
metric.name = names[metric.name]
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu2",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "cannot write to frozen global scope",
|
||
|
source: `
|
||
|
cache = []
|
||
|
|
||
|
def apply(metric):
|
||
|
cache.append(deepcopy(metric))
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 1.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "append: cannot append to frozen list",
|
||
|
},
|
||
|
{
|
||
|
name: "cannot return multiple references to same metric",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
# Should be return [metric, deepcopy(metric)]
|
||
|
return [metric, metric]
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range applyTests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
plugin := newStarlarkFromSource(tt.source)
|
||
|
err := plugin.Init()
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
var acc testutil.Accumulator
|
||
|
|
||
|
err = plugin.Start(&acc)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
for _, m := range tt.input {
|
||
|
err = plugin.Add(m, &acc)
|
||
|
if tt.expectedErrorStr != "" {
|
||
|
require.EqualError(t, err, tt.expectedErrorStr)
|
||
|
} else {
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plugin.Stop()
|
||
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tests for the behavior of the Metric type.
|
||
|
func TestMetric(t *testing.T) {
|
||
|
var tests = []struct {
|
||
|
name string
|
||
|
source string
|
||
|
constants map[string]interface{}
|
||
|
input []telegraf.Metric
|
||
|
expected []telegraf.Metric
|
||
|
expectedErrorStr string
|
||
|
}{
|
||
|
{
|
||
|
name: "create new metric",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
m = Metric('cpu')
|
||
|
m.fields['time_guest'] = 2.0
|
||
|
m.time = 0
|
||
|
return m
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 2.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "deepcopy",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return [metric, deepcopy(metric)]
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set name",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.name = "howdy"
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("howdy",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set name wrong type",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.name = 42
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "type error",
|
||
|
},
|
||
|
{
|
||
|
name: "get name",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['measurement'] = metric.name
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"measurement": "cpu",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "getattr tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "setattr tags is not allowed",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags = {}
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot set tags",
|
||
|
},
|
||
|
{
|
||
|
name: "empty tags are false",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if not metric.tags:
|
||
|
return metric
|
||
|
return None
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "non-empty tags are true",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if metric.tags:
|
||
|
return metric
|
||
|
return None
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tags in operator",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if 'host' not in metric.tags:
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup tag",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['result'] = metric.tags['host']
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"result": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup tag not set",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['foo']
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: `key "foo" not in Tags`,
|
||
|
},
|
||
|
{
|
||
|
name: "get tag",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['result'] = metric.tags.get('host')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"result": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "get tag default",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['result'] = metric.tags.get('foo', 'example.org')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"result": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "get tag not set returns none",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if metric.tags.get('foo') != None:
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set tag",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['host'] = 'example.org'
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set tag type error",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['host'] = 42
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "tag value must be of type 'str'",
|
||
|
},
|
||
|
{
|
||
|
name: "pop tag",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['host2'] = metric.tags.pop('host')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host2": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "pop tag (default)",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['host2'] = metric.tags.pop('url', 'foo.org')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"url": "bar.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"host2": "foo.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"host2": "bar.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "popitem tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['result'] = '='.join(metric.tags.popitem())
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"result": "host=example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "popitem tags empty dict",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.popitem()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "popitem(): tag dictionary is empty",
|
||
|
},
|
||
|
{
|
||
|
name: "tags setdefault key not set",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.setdefault('a', 'b')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tags setdefault key already set",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.setdefault('a', 'c')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tags update list of tuple",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.update([('b', 'y'), ('c', 'z')])
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
"b": "y",
|
||
|
"c": "z",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tags update kwargs",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.update(b='y', c='z')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
"b": "y",
|
||
|
"c": "z",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tags update dict",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.update({'b': 'y', 'c': 'z'})
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
"b": "y",
|
||
|
"c": "z",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tags update list tuple and kwargs",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.update([('b', 'y'), ('c', 'z')], d='zz')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "x",
|
||
|
"b": "y",
|
||
|
"c": "z",
|
||
|
"d": "zz",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
"foo": "bar",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
"foo": "bar",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tags and copy to fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
metric.fields[k] = k
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"host": "host",
|
||
|
"cpu": "cpu",
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tag keys",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags.keys():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
"foo": "bar",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
"foo": "bar",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tag keys and copy to fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags.keys():
|
||
|
metric.fields[k] = k
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"host": "host",
|
||
|
"cpu": "cpu",
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tag items",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k, v in metric.tags.items():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tag items and copy to fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k, v in metric.tags.items():
|
||
|
metric.fields[k] = v
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tag values",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for v in metric.tags.values():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tag values and copy to fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for v in metric.tags.values():
|
||
|
metric.fields[v] = v
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
"cpu": "cpu0",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
"example.org": "example.org",
|
||
|
"cpu0": "cpu0",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "clear tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.clear()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tags cannot pop while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
metric.tags.pop(k)
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "pop: cannot delete during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "tags cannot popitem while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
metric.tags.popitem()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot delete during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "tags cannot clear while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
metric.tags.clear()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot delete during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "tags cannot insert while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
metric.tags['i'] = 'j'
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot insert during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "tags can be cleared after iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
pass
|
||
|
metric.tags.clear()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "getattr fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "setattr fields is not allowed",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields = {}
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot set fields",
|
||
|
},
|
||
|
{
|
||
|
name: "empty fields are false",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if not metric.fields:
|
||
|
metric.fields["time_idle"] = 42
|
||
|
return metric
|
||
|
return None
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "non-empty fields are true",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if metric.fields:
|
||
|
return metric
|
||
|
return None
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fields in operator",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if 'time_idle' not in metric.fields:
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup string field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
value = metric.fields['value']
|
||
|
if value != "xyzzy" and type(value) != "str":
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": "xyzzy"},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": "xyzzy"},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup integer field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
value = metric.fields['value']
|
||
|
if value != 42 and type(value) != "int":
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup unsigned field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
value = metric.fields['value']
|
||
|
if value != 42 and type(value) != "int":
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": uint64(42)},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": uint64(42)},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup bool field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
value = metric.fields['value']
|
||
|
if value != True and type(value) != "bool":
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": true},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": true},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup float field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
value = metric.fields['value']
|
||
|
if value != 42.0 and type(value) != "float":
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "lookup field not set",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['foo']
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: `key "foo" not in Fields`,
|
||
|
},
|
||
|
{
|
||
|
name: "get field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['result'] = metric.fields.get('time_idle')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
"result": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "get field default",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['result'] = metric.fields.get('foo', 'example.org')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
"result": "example.org",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "get field not set returns none",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if metric.fields.get('foo') != None:
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set string field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['host'] = 'example.org'
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set integer field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['time_idle'] = 42
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set float field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['time_idle'] = 42.0
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set bool field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['time_idle'] = True
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": true,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set field type error",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['time_idle'] = {}
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "invalid starlark type",
|
||
|
},
|
||
|
{
|
||
|
name: "pop field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
time_idle = metric.fields.pop('time_idle')
|
||
|
if time_idle != 0:
|
||
|
return
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 0,
|
||
|
"time_guest": 0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_guest": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "pop field (default)",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['idle_count'] = metric.fields.pop('count', 10)
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 0,
|
||
|
"time_guest": 0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 0,
|
||
|
"time_guest": 0,
|
||
|
"count": 0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 0,
|
||
|
"time_guest": 0,
|
||
|
"idle_count": 10,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 0,
|
||
|
"time_guest": 0,
|
||
|
"idle_count": 0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "popitem field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
item = metric.fields.popitem()
|
||
|
if item != ("time_idle", 0):
|
||
|
return
|
||
|
metric.fields['time_guest'] = 0
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_guest": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "popitem fields empty dict",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.popitem()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "popitem(): field dictionary is empty",
|
||
|
},
|
||
|
{
|
||
|
name: "fields setdefault key not set",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.setdefault('a', 'b')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"a": "b"},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fields setdefault key already set",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.setdefault('a', 'c')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"a": "b"},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"a": "b"},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fields update list of tuple",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.update([('a', 'b'), ('c', 'd')])
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fields update kwargs",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.update(a='b', c='d')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fields update dict",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.update({'a': 'b', 'c': 'd'})
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fields update list tuple and kwargs",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.update([('a', 'b'), ('c', 'd')], e='f')
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate field keys",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields.keys():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate field keys and copy to tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields.keys():
|
||
|
metric.tags[k] = k
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"time_guest": "time_guest",
|
||
|
"time_idle": "time_idle",
|
||
|
"time_system": "time_system",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate field items",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k, v in metric.fields.items():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.0,
|
||
|
"time_idle": 2.0,
|
||
|
"time_system": 3.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate field items and copy to tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k, v in metric.fields.items():
|
||
|
metric.tags[k] = str(v)
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.1,
|
||
|
"time_idle": 2.1,
|
||
|
"time_system": 3.1,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"time_guest": "1.1",
|
||
|
"time_idle": "2.1",
|
||
|
"time_system": "3.1",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"time_guest": 1.1,
|
||
|
"time_idle": 2.1,
|
||
|
"time_system": 3.1,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate field values",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for v in metric.fields.values():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate field values and copy to tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for v in metric.fields.values():
|
||
|
metric.tags[str(v)] = str(v)
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"b": "b",
|
||
|
"d": "d",
|
||
|
"f": "f",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "clear fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields.clear()
|
||
|
metric.fields['notempty'] = 0
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 0,
|
||
|
"time_guest": 0,
|
||
|
"time_system": 0,
|
||
|
"time_user": 0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"notempty": 0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fields cannot pop while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
metric.fields.pop(k)
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "pop: cannot delete during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "fields cannot popitem while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
metric.fields.popitem()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot delete during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "fields cannot clear while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
metric.fields.clear()
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot delete during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "fields cannot insert while iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
metric.fields['time_guest'] = 0
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "cannot insert during iteration",
|
||
|
},
|
||
|
{
|
||
|
name: "fields can be cleared after iterating",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
pass
|
||
|
metric.fields.clear()
|
||
|
metric.fields['notempty'] = 0
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"notempty": 0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set time",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.time = 42
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(0, 0).UTC(),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(0, 42).UTC(),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set time wrong type",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.time = 'howdy'
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(0, 0).UTC(),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "type error",
|
||
|
},
|
||
|
{
|
||
|
name: "get time",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.time -= metric.time % 100000000
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(42, 11).UTC(),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42,
|
||
|
},
|
||
|
time.Unix(42, 0).UTC(),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "support errors",
|
||
|
source: `
|
||
|
load("json.star", "json")
|
||
|
|
||
|
def apply(metric):
|
||
|
msg = catch(lambda: process(metric))
|
||
|
if msg != None:
|
||
|
metric.fields["error"] = msg
|
||
|
metric.fields["value"] = "default"
|
||
|
return metric
|
||
|
|
||
|
def process(metric):
|
||
|
metric.fields["field1"] = "value1"
|
||
|
metric.tags["tags1"] = "value2"
|
||
|
# Throw an error
|
||
|
json.decode(metric.fields.get('value'))
|
||
|
# Should never be called
|
||
|
metric.fields["msg"] = "value4"
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": "non-json-content", "msg": "value3"},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{"tags1": "value2"},
|
||
|
map[string]interface{}{
|
||
|
"value": "default",
|
||
|
"field1": "value1",
|
||
|
"msg": "value3",
|
||
|
"error": "json.decode: at offset 0, unexpected character 'n'",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "support constants",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields["p1"] = max_size
|
||
|
metric.fields["p2"] = threshold
|
||
|
metric.fields["p3"] = default_name
|
||
|
metric.fields["p4"] = debug_mode
|
||
|
metric.fields["p5"] = supported_values[0]
|
||
|
metric.fields["p6"] = supported_values[1]
|
||
|
metric.fields["p7"] = supported_entries[2]
|
||
|
metric.fields["p8"] = supported_entries["3"]
|
||
|
return metric
|
||
|
`,
|
||
|
constants: map[string]interface{}{
|
||
|
"max_size": 10,
|
||
|
"threshold": 0.75,
|
||
|
"default_name": "Julia",
|
||
|
"debug_mode": true,
|
||
|
"supported_values": []interface{}{2, "3"},
|
||
|
"supported_entries": map[interface{}]interface{}{
|
||
|
2: "two",
|
||
|
"3": "three",
|
||
|
},
|
||
|
},
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"p1": 10,
|
||
|
"p2": 0.75,
|
||
|
"p3": "Julia",
|
||
|
"p4": true,
|
||
|
"p5": 2,
|
||
|
"p6": "3",
|
||
|
"p7": "two",
|
||
|
"p8": "three",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
plugin := newStarlarkFromSource(tt.source)
|
||
|
plugin.Constants = tt.constants
|
||
|
err := plugin.Init()
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
var acc testutil.Accumulator
|
||
|
|
||
|
err = plugin.Start(&acc)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
for _, m := range tt.input {
|
||
|
err = plugin.Add(m, &acc)
|
||
|
if tt.expectedErrorStr != "" {
|
||
|
require.ErrorContains(t, err, tt.expectedErrorStr)
|
||
|
} else {
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plugin.Stop()
|
||
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tests the behavior of the plugin according the provided TOML configuration.
|
||
|
func TestConfig(t *testing.T) {
|
||
|
var tests = []struct {
|
||
|
name string
|
||
|
config string
|
||
|
input []telegraf.Metric
|
||
|
expected []telegraf.Metric
|
||
|
}{
|
||
|
{
|
||
|
name: "support constants from configuration",
|
||
|
config: `
|
||
|
[[processors.starlark]]
|
||
|
source = '''
|
||
|
def apply(metric):
|
||
|
metric.fields["p1"] = max_size
|
||
|
metric.fields["p2"] = threshold
|
||
|
metric.fields["p3"] = default_name
|
||
|
metric.fields["p4"] = debug_mode
|
||
|
metric.fields["p5"] = supported_values[0]
|
||
|
metric.fields["p6"] = supported_values[1]
|
||
|
metric.fields["p7"] = supported_entries["2"]
|
||
|
metric.fields["p8"] = supported_entries["3"]
|
||
|
return metric
|
||
|
'''
|
||
|
[processors.starlark.constants]
|
||
|
max_size = 10
|
||
|
threshold = 0.75
|
||
|
default_name = "Elsa"
|
||
|
debug_mode = true
|
||
|
supported_values = ["2", "3"]
|
||
|
supported_entries = { "2" = "two", "3" = "three" }
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"p1": 10,
|
||
|
"p2": 0.75,
|
||
|
"p3": "Elsa",
|
||
|
"p4": true,
|
||
|
"p5": "2",
|
||
|
"p6": "3",
|
||
|
"p7": "two",
|
||
|
"p8": "three",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
plugin, err := buildPlugin(tt.config)
|
||
|
require.NoError(t, err)
|
||
|
err = plugin.Init()
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
var acc testutil.Accumulator
|
||
|
|
||
|
err = plugin.Start(&acc)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
for _, m := range tt.input {
|
||
|
err = plugin.Add(m, &acc)
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
|
||
|
plugin.Stop()
|
||
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Build a Starlark plugin from the provided configuration.
|
||
|
func buildPlugin(configContent string) (*Starlark, error) {
|
||
|
c := config.NewConfig()
|
||
|
err := c.LoadConfigData([]byte(configContent), config.EmptySourcePath)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if len(c.Processors) != 1 {
|
||
|
return nil, errors.New("only one processor was expected")
|
||
|
}
|
||
|
plugin, ok := (c.Processors[0].Processor).(*Starlark)
|
||
|
if !ok {
|
||
|
return nil, errors.New("only a Starlark processor was expected")
|
||
|
}
|
||
|
plugin.Log = testutil.Logger{}
|
||
|
return plugin, nil
|
||
|
}
|
||
|
|
||
|
func TestScript(t *testing.T) {
|
||
|
var tests = []struct {
|
||
|
name string
|
||
|
plugin *Starlark
|
||
|
input []telegraf.Metric
|
||
|
expected []telegraf.Metric
|
||
|
expectedErrorStr string
|
||
|
}{
|
||
|
{
|
||
|
name: "rename",
|
||
|
plugin: newStarlarkFromScript("testdata/rename.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"lower": "0",
|
||
|
"upper": "10",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"min": "0",
|
||
|
"max": "10",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "drop fields by type",
|
||
|
plugin: newStarlarkFromScript("testdata/drop_string_fields.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("device",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": 42,
|
||
|
"b": "42",
|
||
|
"c": 42.0,
|
||
|
"d": "42.0",
|
||
|
"e": true,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("device",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": 42,
|
||
|
"c": 42.0,
|
||
|
"e": true,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "drop fields with unexpected type",
|
||
|
plugin: newStarlarkFromScript("testdata/drop_fields_with_unexpected_type.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("device",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": 42,
|
||
|
"b": "42",
|
||
|
"c": 42.0,
|
||
|
"d": "42.0",
|
||
|
"e": true,
|
||
|
"f": 23.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("device",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": 42,
|
||
|
"c": 42.0,
|
||
|
"d": "42.0",
|
||
|
"e": true,
|
||
|
"f": 23.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "scale",
|
||
|
plugin: newStarlarkFromScript("testdata/scale.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 10.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 100.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "ratio",
|
||
|
plugin: newStarlarkFromScript("testdata/ratio.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("mem",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"used": 2,
|
||
|
"total": 10,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("mem",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"used": 2,
|
||
|
"total": 10,
|
||
|
"usage": 20.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "logging",
|
||
|
plugin: newStarlarkFromScript("testdata/logging.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("log",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"debug": "a debug message",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("log",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"debug": "a debug message",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "multiple_metrics",
|
||
|
plugin: newStarlarkFromScript("testdata/multiple_metrics.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("mm",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"value": "a",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("mm2",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"value": "b",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
testutil.MustMetric("mm1",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"value": "a",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "multiple_metrics_with_json",
|
||
|
plugin: newStarlarkFromScript("testdata/multiple_metrics_with_json.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("json",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"value": "[{\"label\": \"hello\"}, {\"label\": \"world\"}]",
|
||
|
},
|
||
|
time.Unix(1618488000, 999),
|
||
|
),
|
||
|
},
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric("json",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"value": "hello",
|
||
|
},
|
||
|
time.Unix(1618488000, 999),
|
||
|
),
|
||
|
testutil.MustMetric("json",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"value": "world",
|
||
|
},
|
||
|
time.Unix(1618488000, 999),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "fail",
|
||
|
plugin: newStarlarkFromScript("testdata/fail.star"),
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("fail",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"value": 1,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
expectedErrorStr: "fail: The field value should be greater than 1",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
err := tt.plugin.Init()
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
var acc testutil.Accumulator
|
||
|
|
||
|
err = tt.plugin.Start(&acc)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
for _, m := range tt.input {
|
||
|
err = tt.plugin.Add(m, &acc)
|
||
|
if tt.expectedErrorStr != "" {
|
||
|
require.EqualError(t, err, tt.expectedErrorStr)
|
||
|
} else {
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tt.plugin.Stop()
|
||
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Benchmarks modify the metric in place, so the scripts shouldn't modify the
|
||
|
// metric.
|
||
|
func Benchmark(b *testing.B) {
|
||
|
var tests = []struct {
|
||
|
name string
|
||
|
source string
|
||
|
input []telegraf.Metric
|
||
|
}{
|
||
|
{
|
||
|
name: "passthrough",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "create new metric",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
m = Metric('cpu')
|
||
|
m.fields['time_guest'] = 2.0
|
||
|
m.time = 0
|
||
|
return m
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set name",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.name = "cpu"
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set tag",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags['host'] = 'example.org'
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "tag in operator",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
if 'c' in metric.tags:
|
||
|
return metric
|
||
|
return None
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42.0},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
// This should be faster than calling items()
|
||
|
name: "iterate tags and get values",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.tags:
|
||
|
v = metric.tags[k]
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate tag items",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k, v in metric.tags.items():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
map[string]interface{}{"time_idle": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "set string field",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.fields['host'] = 'example.org'
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"host": "example.org",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate fields",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
"time_user": 42.0,
|
||
|
"time_guest": 42.0,
|
||
|
"time_system": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
// This should be faster than calling items()
|
||
|
name: "iterate fields and get values",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k in metric.fields:
|
||
|
v = metric.fields[k]
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"time_idle": 42.0,
|
||
|
"time_user": 42.0,
|
||
|
"time_guest": 42.0,
|
||
|
"time_system": 42.0,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "iterate field items",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
for k, v in metric.fields.items():
|
||
|
pass
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"a": "b",
|
||
|
"c": "d",
|
||
|
"e": "f",
|
||
|
"g": "h",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "concatenate 2 tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags["result"] = '_'.join(metric.tags.values())
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"tag_1": "a",
|
||
|
"tag_2": "b",
|
||
|
},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "concatenate 4 tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags["result"] = '_'.join(metric.tags.values())
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"tag_1": "a",
|
||
|
"tag_2": "b",
|
||
|
"tag_3": "c",
|
||
|
"tag_4": "d",
|
||
|
},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "concatenate 8 tags",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags["result"] = '_'.join(metric.tags.values())
|
||
|
return metric
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"tag_1": "a",
|
||
|
"tag_2": "b",
|
||
|
"tag_3": "c",
|
||
|
"tag_4": "d",
|
||
|
"tag_5": "e",
|
||
|
"tag_6": "f",
|
||
|
"tag_7": "g",
|
||
|
"tag_8": "h",
|
||
|
},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "filter by field value",
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
match = metric.tags.get("bar") == "yeah" or metric.tags.get("tag_1") == "foo"
|
||
|
match = match and metric.fields.get("value_1") > 5 and metric.fields.get("value_2") < 3.5
|
||
|
if match:
|
||
|
return metric
|
||
|
return None
|
||
|
`,
|
||
|
input: []telegraf.Metric{
|
||
|
testutil.MustMetric("cpu",
|
||
|
map[string]string{
|
||
|
"tag_1": "foo",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"value_1": 42,
|
||
|
"value_2": 3.1415,
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range tests {
|
||
|
b.Run(tt.name, func(b *testing.B) {
|
||
|
plugin := newStarlarkFromSource(tt.source)
|
||
|
|
||
|
err := plugin.Init()
|
||
|
require.NoError(b, err)
|
||
|
|
||
|
var acc testutil.NopAccumulator
|
||
|
|
||
|
err = plugin.Start(&acc)
|
||
|
require.NoError(b, err)
|
||
|
|
||
|
b.ResetTimer()
|
||
|
for n := 0; n < b.N; n++ {
|
||
|
for _, m := range tt.input {
|
||
|
err = plugin.Add(m, &acc)
|
||
|
require.NoError(b, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plugin.Stop()
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestAllScriptTestData(t *testing.T) {
|
||
|
// can be run from multiple folders
|
||
|
paths := []string{"testdata", "plugins/processors/starlark/testdata"}
|
||
|
for _, testdataPath := range paths {
|
||
|
err := filepath.Walk(testdataPath, func(path string, info os.FileInfo, _ error) error {
|
||
|
if info == nil || info.IsDir() {
|
||
|
return nil
|
||
|
}
|
||
|
fn := path
|
||
|
t.Run(fn, func(t *testing.T) {
|
||
|
b, err := os.ReadFile(fn)
|
||
|
require.NoError(t, err)
|
||
|
lines := strings.Split(string(b), "\n")
|
||
|
inputMetrics := parseMetricsFrom(t, lines, "Example Input:")
|
||
|
expectedErrorStr := parseErrorMessage(t, lines, "Example Output Error:")
|
||
|
var outputMetrics []telegraf.Metric
|
||
|
if expectedErrorStr == "" {
|
||
|
outputMetrics = parseMetricsFrom(t, lines, "Example Output:")
|
||
|
}
|
||
|
plugin := newStarlarkFromScript(fn)
|
||
|
require.NoError(t, plugin.Init())
|
||
|
|
||
|
acc := &testutil.Accumulator{}
|
||
|
|
||
|
err = plugin.Start(acc)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
for _, m := range inputMetrics {
|
||
|
err = plugin.Add(m, acc)
|
||
|
if expectedErrorStr != "" {
|
||
|
require.EqualError(t, err, expectedErrorStr)
|
||
|
} else {
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plugin.Stop()
|
||
|
testutil.RequireMetricsEqual(t, outputMetrics, acc.GetTelegrafMetrics(), testutil.SortMetrics())
|
||
|
})
|
||
|
return nil
|
||
|
})
|
||
|
require.NoError(t, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTracking(t *testing.T) {
|
||
|
var testCases = []struct {
|
||
|
name string
|
||
|
source string
|
||
|
numMetrics int
|
||
|
}{
|
||
|
{
|
||
|
name: "return none",
|
||
|
numMetrics: 0,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return None
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "return empty list of metrics",
|
||
|
numMetrics: 0,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return []
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "return original metric",
|
||
|
numMetrics: 1,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return metric
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "return original metric in a list",
|
||
|
numMetrics: 1,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return [metric]
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "return new metric",
|
||
|
numMetrics: 1,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
newmetric = Metric("new_metric")
|
||
|
newmetric.fields["value"] = 42
|
||
|
return newmetric
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "return new metric in a list",
|
||
|
numMetrics: 1,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
newmetric = Metric("new_metric")
|
||
|
newmetric.fields["value"] = 42
|
||
|
return [newmetric]
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "return original and new metric in a list",
|
||
|
numMetrics: 2,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
newmetric = Metric("new_metric")
|
||
|
newmetric.fields["value"] = 42
|
||
|
return [metric, newmetric]
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "return original and deep-copy",
|
||
|
numMetrics: 2,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
return [metric, deepcopy(metric, track=True)]
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "deep-copy but do not return",
|
||
|
numMetrics: 1,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
x = deepcopy(metric)
|
||
|
return [metric]
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "deep-copy but do not return original metric",
|
||
|
numMetrics: 1,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
x = deepcopy(metric, track=True)
|
||
|
return [x]
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "issue #14484",
|
||
|
numMetrics: 1,
|
||
|
source: `
|
||
|
def apply(metric):
|
||
|
metric.tags.pop("tag1")
|
||
|
return [metric]
|
||
|
`,
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range testCases {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
// Create a tracking metric and tap the delivery information
|
||
|
var mu sync.Mutex
|
||
|
delivered := make([]telegraf.DeliveryInfo, 0, 1)
|
||
|
notify := func(di telegraf.DeliveryInfo) {
|
||
|
mu.Lock()
|
||
|
defer mu.Unlock()
|
||
|
delivered = append(delivered, di)
|
||
|
}
|
||
|
|
||
|
// Configure the plugin
|
||
|
plugin := newStarlarkFromSource(tt.source)
|
||
|
require.NoError(t, plugin.Init())
|
||
|
acc := &testutil.Accumulator{}
|
||
|
require.NoError(t, plugin.Start(acc))
|
||
|
|
||
|
// Process expected metrics and compare with resulting metrics
|
||
|
input, _ := metric.WithTracking(testutil.TestMetric(1.23), notify)
|
||
|
require.NoError(t, plugin.Add(input, acc))
|
||
|
plugin.Stop()
|
||
|
|
||
|
// Ensure we get back the correct number of metrics
|
||
|
actual := acc.GetTelegrafMetrics()
|
||
|
require.Lenf(t, actual, tt.numMetrics, "expected %d metrics but got %d", tt.numMetrics, len(actual))
|
||
|
for _, m := range actual {
|
||
|
m.Accept()
|
||
|
}
|
||
|
|
||
|
// Simulate output acknowledging delivery of metrics and check delivery
|
||
|
require.Eventuallyf(t, func() bool {
|
||
|
mu.Lock()
|
||
|
defer mu.Unlock()
|
||
|
return len(delivered) == 1
|
||
|
}, 1*time.Second, 100*time.Millisecond, "original metric not delivered")
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTrackingStateful(t *testing.T) {
|
||
|
var testCases = []struct {
|
||
|
name string
|
||
|
source string
|
||
|
results int
|
||
|
loops int
|
||
|
delivery int
|
||
|
}{
|
||
|
{
|
||
|
name: "delayed release",
|
||
|
loops: 4,
|
||
|
results: 3,
|
||
|
delivery: 4,
|
||
|
source: `
|
||
|
state = {"last": None}
|
||
|
|
||
|
def apply(metric):
|
||
|
previous = state["last"]
|
||
|
state["last"] = deepcopy(metric)
|
||
|
return previous
|
||
|
`,
|
||
|
},
|
||
|
{
|
||
|
name: "delayed release with tracking",
|
||
|
loops: 4,
|
||
|
results: 3,
|
||
|
delivery: 3,
|
||
|
source: `
|
||
|
state = {"last": None}
|
||
|
|
||
|
def apply(metric):
|
||
|
previous = state["last"]
|
||
|
state["last"] = deepcopy(metric, track=True)
|
||
|
return previous
|
||
|
`,
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range testCases {
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
// Create a tracking metric and tap the delivery information
|
||
|
var mu sync.Mutex
|
||
|
delivered := make([]telegraf.TrackingID, 0, tt.delivery)
|
||
|
notify := func(di telegraf.DeliveryInfo) {
|
||
|
mu.Lock()
|
||
|
defer mu.Unlock()
|
||
|
delivered = append(delivered, di.ID())
|
||
|
}
|
||
|
|
||
|
// Configure the plugin
|
||
|
plugin := newStarlarkFromSource(tt.source)
|
||
|
require.NoError(t, plugin.Init())
|
||
|
acc := &testutil.Accumulator{}
|
||
|
require.NoError(t, plugin.Start(acc))
|
||
|
|
||
|
// Do the requested number of loops
|
||
|
expected := make([]telegraf.TrackingID, 0, tt.loops)
|
||
|
for i := 0; i < tt.loops; i++ {
|
||
|
// Process expected metrics and compare with resulting metrics
|
||
|
input, tid := metric.WithTracking(testutil.TestMetric(i), notify)
|
||
|
expected = append(expected, tid)
|
||
|
require.NoError(t, plugin.Add(input, acc))
|
||
|
}
|
||
|
plugin.Stop()
|
||
|
expected = expected[:tt.delivery]
|
||
|
|
||
|
// Simulate output acknowledging delivery of metrics and check delivery
|
||
|
actual := acc.GetTelegrafMetrics()
|
||
|
// Ensure we get back the correct number of metrics
|
||
|
require.Lenf(t, actual, tt.results, "expected %d metrics but got %d", tt.results, len(actual))
|
||
|
for _, m := range actual {
|
||
|
m.Accept()
|
||
|
}
|
||
|
|
||
|
require.Eventuallyf(t, func() bool {
|
||
|
mu.Lock()
|
||
|
defer mu.Unlock()
|
||
|
return len(delivered) >= tt.delivery
|
||
|
}, 1*time.Second, 100*time.Millisecond, "original metric(s) not delivered")
|
||
|
|
||
|
mu.Lock()
|
||
|
defer mu.Unlock()
|
||
|
require.ElementsMatch(t, expected, delivered, "mismatch in delivered metrics")
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGlobalState(t *testing.T) {
|
||
|
source := `
|
||
|
def apply(metric):
|
||
|
count = state.get("count", 0)
|
||
|
count += 1
|
||
|
state["count"] = count
|
||
|
|
||
|
metric.fields["count"] = count
|
||
|
|
||
|
return metric
|
||
|
`
|
||
|
// Define the metrics
|
||
|
input := []telegraf.Metric{
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(1713188113, 10),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(1713188113, 20),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(1713188113, 30),
|
||
|
)}
|
||
|
expected := []telegraf.Metric{
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42, "count": 1},
|
||
|
time.Unix(1713188113, 10),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42, "count": 2},
|
||
|
time.Unix(1713188113, 20),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42, "count": 3},
|
||
|
time.Unix(1713188113, 30),
|
||
|
),
|
||
|
}
|
||
|
|
||
|
// Configure the plugin
|
||
|
plugin := &Starlark{
|
||
|
Common: common.Common{
|
||
|
StarlarkLoadFunc: testLoadFunc,
|
||
|
Source: source,
|
||
|
Log: testutil.Logger{},
|
||
|
},
|
||
|
}
|
||
|
require.NoError(t, plugin.Init())
|
||
|
|
||
|
var acc testutil.Accumulator
|
||
|
require.NoError(t, plugin.Start(&acc))
|
||
|
|
||
|
// Do the processing
|
||
|
for _, m := range input {
|
||
|
require.NoError(t, plugin.Add(m, &acc))
|
||
|
}
|
||
|
plugin.Stop()
|
||
|
|
||
|
// Check
|
||
|
actual := acc.GetTelegrafMetrics()
|
||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||
|
}
|
||
|
|
||
|
func TestStatePersistence(t *testing.T) {
|
||
|
source := `
|
||
|
def apply(metric):
|
||
|
count = state.get("count", 0)
|
||
|
count += 1
|
||
|
state["count"] = count
|
||
|
|
||
|
metric.fields["count"] = count
|
||
|
metric.tags["instance"] = state.get("instance", "unknown")
|
||
|
|
||
|
return metric
|
||
|
`
|
||
|
// Define the metrics
|
||
|
input := []telegraf.Metric{
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(1713188113, 10),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(1713188113, 20),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{"value": 42},
|
||
|
time.Unix(1713188113, 30),
|
||
|
)}
|
||
|
expected := []telegraf.Metric{
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{"instance": "myhost"},
|
||
|
map[string]interface{}{"value": 42, "count": 1},
|
||
|
time.Unix(1713188113, 10),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{"instance": "myhost"},
|
||
|
map[string]interface{}{"value": 42, "count": 2},
|
||
|
time.Unix(1713188113, 20),
|
||
|
),
|
||
|
metric.New(
|
||
|
"test",
|
||
|
map[string]string{"instance": "myhost"},
|
||
|
map[string]interface{}{"value": 42, "count": 3},
|
||
|
time.Unix(1713188113, 30),
|
||
|
),
|
||
|
}
|
||
|
|
||
|
// Configure the plugin
|
||
|
plugin := &Starlark{
|
||
|
Common: common.Common{
|
||
|
StarlarkLoadFunc: testLoadFunc,
|
||
|
Source: source,
|
||
|
Log: testutil.Logger{},
|
||
|
},
|
||
|
}
|
||
|
require.NoError(t, plugin.Init())
|
||
|
|
||
|
// Setup the "persisted" state
|
||
|
var pi telegraf.StatefulPlugin = plugin
|
||
|
var buf bytes.Buffer
|
||
|
require.NoError(t, gob.NewEncoder(&buf).Encode(map[string]interface{}{"instance": "myhost"}))
|
||
|
require.NoError(t, pi.SetState(buf.Bytes()))
|
||
|
|
||
|
var acc testutil.Accumulator
|
||
|
require.NoError(t, plugin.Start(&acc))
|
||
|
|
||
|
// Do the processing
|
||
|
for _, m := range input {
|
||
|
require.NoError(t, plugin.Add(m, &acc))
|
||
|
}
|
||
|
plugin.Stop()
|
||
|
|
||
|
// Check
|
||
|
actual := acc.GetTelegrafMetrics()
|
||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||
|
|
||
|
// Check getting the persisted state
|
||
|
expectedState := map[string]interface{}{"instance": "myhost", "count": int64(3)}
|
||
|
|
||
|
var actualState map[string]interface{}
|
||
|
stateData, ok := pi.GetState().([]byte)
|
||
|
require.True(t, ok, "state is not a bytes array")
|
||
|
require.NoError(t, gob.NewDecoder(bytes.NewBuffer(stateData)).Decode(&actualState))
|
||
|
require.EqualValues(t, expectedState, actualState, "mismatch in state")
|
||
|
}
|
||
|
|
||
|
func TestUsePredefinedStateName(t *testing.T) {
|
||
|
source := `
|
||
|
def apply(metric):
|
||
|
return metric
|
||
|
`
|
||
|
// Configure the plugin
|
||
|
plugin := &Starlark{
|
||
|
Common: common.Common{
|
||
|
StarlarkLoadFunc: testLoadFunc,
|
||
|
Source: source,
|
||
|
Constants: map[string]interface{}{"state": "invalid"},
|
||
|
Log: testutil.Logger{},
|
||
|
},
|
||
|
}
|
||
|
require.ErrorContains(t, plugin.Init(), "'state' constant uses reserved name")
|
||
|
}
|
||
|
|
||
|
// parses metric lines out of line protocol following a header, with a trailing blank line
|
||
|
func parseMetricsFrom(t *testing.T, lines []string, header string) (metrics []telegraf.Metric) {
|
||
|
parser := &influx.Parser{}
|
||
|
require.NoError(t, parser.Init())
|
||
|
|
||
|
require.NotEmpty(t, lines, "Expected some lines to parse from .star file, found none")
|
||
|
startIdx := -1
|
||
|
endIdx := len(lines)
|
||
|
for i := range lines {
|
||
|
if strings.TrimLeft(lines[i], "# ") == header {
|
||
|
startIdx = i + 1
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
require.NotEqualf(t, -1, startIdx, "Header %q must exist in file", header)
|
||
|
for i := startIdx; i < len(lines); i++ {
|
||
|
line := strings.TrimLeft(lines[i], "# ")
|
||
|
if line == "" || line == "'''" {
|
||
|
endIdx = i
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
for i := startIdx; i < endIdx; i++ {
|
||
|
m, err := parser.ParseLine(strings.TrimLeft(lines[i], "# "))
|
||
|
require.NoErrorf(t, err, "Expected to be able to parse %q metric, but found error", header)
|
||
|
metrics = append(metrics, m)
|
||
|
}
|
||
|
return metrics
|
||
|
}
|
||
|
|
||
|
// parses error message out of line protocol following a header
|
||
|
func parseErrorMessage(t *testing.T, lines []string, header string) string {
|
||
|
require.NotEmpty(t, lines, "Expected some lines to parse from .star file, found none")
|
||
|
startIdx := -1
|
||
|
for i := range lines {
|
||
|
if strings.TrimLeft(lines[i], "# ") == header {
|
||
|
startIdx = i + 1
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if startIdx == -1 {
|
||
|
return ""
|
||
|
}
|
||
|
require.Lessf(t, startIdx, len(lines), "Expected to find the error message after %q, but found none", header)
|
||
|
return strings.TrimLeft(lines[startIdx], "# ")
|
||
|
}
|
||
|
|
||
|
func testLoadFunc(module string, logger telegraf.Logger) (starlark.StringDict, error) {
|
||
|
result, err := common.LoadFunc(module, logger)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if module == "time.star" {
|
||
|
customModule := result["time"].(*starlarkstruct.Module)
|
||
|
customModule.Members["now"] = starlark.NewBuiltin("now", testNow)
|
||
|
result["time"] = customModule
|
||
|
}
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
func testNow(_ *starlark.Thread, _ *starlark.Builtin, _ starlark.Tuple, _ []starlark.Tuple) (starlark.Value, error) {
|
||
|
return starlarktime.Time(time.Date(2021, 4, 15, 12, 0, 0, 999, time.UTC)), nil
|
||
|
}
|
||
|
|
||
|
func newStarlarkFromSource(source string) *Starlark {
|
||
|
return &Starlark{
|
||
|
Common: common.Common{
|
||
|
StarlarkLoadFunc: testLoadFunc,
|
||
|
Log: testutil.Logger{},
|
||
|
Source: source,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func newStarlarkFromScript(script string) *Starlark {
|
||
|
return &Starlark{
|
||
|
Common: common.Common{
|
||
|
StarlarkLoadFunc: testLoadFunc,
|
||
|
Log: testutil.Logger{},
|
||
|
Script: script,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func newStarlarkNoScript() *Starlark {
|
||
|
return &Starlark{
|
||
|
Common: common.Common{
|
||
|
StarlarkLoadFunc: testLoadFunc,
|
||
|
Log: testutil.Logger{},
|
||
|
},
|
||
|
}
|
||
|
}
|