Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve the quality of starlark docs by executing them as tests #8020

Merged
merged 3 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions plugins/processors/starlark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Existing Python code is unlikely to work unmodified. The execution environment
is sandboxed, and it is not possible to do I/O operations such as reading from
files or sockets.

The Starlark [specification][] has details about the syntax and available
The **[Starlark specification][]** has details about the syntax and available
functions.

Telegraf minimum version: Telegraf 1.15.0
Expand Down Expand Up @@ -44,7 +44,7 @@ def apply(metric):
```

For a list of available types and functions that can be used in the code, see
the Starlark [specification][].
the [Starlark specification][].

In addition to these, the following InfluxDB-specific
types and functions are exposed to the script.
Expand Down Expand Up @@ -149,14 +149,17 @@ Attempting to modify the global scope will fail with an error.

### Examples

- [ratio](/plugins/processors/starlark/testdata/ratio.star)
- [rename](/plugins/processors/starlark/testdata/rename.star)
- [scale](/plugins/processors/starlark/testdata/scale.star)
- [number logic](/plugins/processors/starlark/testdata/number_logic.star)
- [pivot](/plugins/processors/starlark/testdata/pivot.star)
- [ratio](/plugins/processors/starlark/testdata/ratio.star) - Compute the ratio of two integer fields
- [rename](/plugins/processors/starlark/testdata/rename.star) - Rename tags or fields using a name mapping.
- [scale](/plugins/processors/starlark/testdata/scale.star) - Multiply any field by a number
- [number logic](/plugins/processors/starlark/testdata/number_logic.star) - transform a numerical value to another numerical value
- [pivot](/plugins/processors/starlark/testdata/pivot.star) - Pivots a key's value to be the key for another key.
- [value filter](plugins/processors/starlark/testdata/value_filter.star) - remove a metric based on a field value.

Open a [PR](https://github.com/influxdata/telegraf/compare) to add any other useful Starlark examples.
[All examples](/plugins/processors/starlark/testdata) are in the testdata folder.

[specification]: https://github.com/google/starlark-go/blob/master/doc/spec.md
Open a Pull Request to add any other useful Starlark examples.

[Starlark specification]: https://github.com/google/starlark-go/blob/master/doc/spec.md
[string]: https://github.com/google/starlark-go/blob/master/doc/spec.md#strings
[dict]: https://github.com/google/starlark-go/blob/master/doc/spec.md#dictionaries
75 changes: 75 additions & 0 deletions plugins/processors/starlark/starlark_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package starlark

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/parsers"
"github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -2787,3 +2793,72 @@ def apply(metric):
})
}
}
func TestAllScriptTestData(t *testing.T) {
// can be run from multiple folders
paths := []string{"testdata", "plugins/processors/starlark/testdata"}
for _, testdataPath := range paths {
filepath.Walk(testdataPath, func(path string, info os.FileInfo, err error) error {
if info == nil || info.IsDir() {
return nil
}
fn := path
t.Run(fn, func(t *testing.T) {
b, err := ioutil.ReadFile(fn)
require.NoError(t, err)
lines := strings.Split(string(b), "\n")
inputMetrics := parseMetricsFrom(t, lines, "Example Input:")
outputMetrics := parseMetricsFrom(t, lines, "Example Output:")
plugin := &Starlark{
Script: fn,
Log: testutil.Logger{},
}
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)
require.NoError(t, err)
}

err = plugin.Stop()
require.NoError(t, err)

testutil.RequireMetricsEqual(t, outputMetrics, acc.GetTelegrafMetrics(), testutil.SortMetrics(), testutil.IgnoreTime())
})
return nil
})
}
}

var parser, _ = parsers.NewInfluxParser() // literally never returns errors.

// 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) {
require.NotZero(t, len(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.NotEqual(t, -1, startIdx, fmt.Sprintf("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.NoError(t, err, fmt.Sprintf("Expected to be able to parse %q metric, but found error", header))
metrics = append(metrics, m)
}
return metrics
}
5 changes: 3 additions & 2 deletions plugins/processors/starlark/testdata/number_logic.star
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
# Example: Set any 'status' field between 1 and 6 to a value of 0
#
# Example Input:
# lb, http_method=GET status=5 1465839830100400201
# lb,http_method=GET status=5i 1465839830100400201
#
# Example Output:
# lb, http_method=GET status=0 1465839830100400201
# lb,http_method=GET status=0i 1465839830100400201


def apply(metric):
Expand Down
18 changes: 9 additions & 9 deletions plugins/processors/starlark/testdata/pivot.star
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ Pivots a key's value to be the key for another key.
In this example it pivots the value of key `sensor`
to be the key of the value in key `value`

Input:
temperature sensor=001A0,value=111.48
Example Input:
temperature sensor="001A0",value=111.48

Output:
Example Output:
temperature 001A0=111.48
'''
def apply(metric):
metric.fields[str(metric.fields['sensor'])] = metric.fields['value']
metric.fields.pop('value',None)
metric.fields.pop('sensor',None)
return metric

def apply(metric):
metric.fields[str(metric.fields['sensor'])] = metric.fields['value']
metric.fields.pop('value',None)
metric.fields.pop('sensor',None)
return metric
5 changes: 3 additions & 2 deletions plugins/processors/starlark/testdata/ratio.star
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
# Example: A new field 'usage' from an existing fields 'used' and 'total'
#
# Example Input:
# memory, host=hostname used=11038756864.4948,total=17179869184.1221 1597255082000000000
# memory,host=hostname used=11038756864.4948,total=17179869184.1221 1597255082000000000
#
# Example Output:
# memory, host=hostname used=11038756864.4948,total=17179869184.1221,usage=64.254021647 1597255082000000000
# memory,host=hostname used=11038756864.4948,total=17179869184.1221,usage=64.25402164701573 1597255082000000000

def apply(metric):
used = float(metric.fields['used'])
Expand Down
9 changes: 7 additions & 2 deletions plugins/processors/starlark/testdata/rename.star
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Rename any tags using the mapping in the renames dict.
#
# Example Input:
# measurement, host=hostname lower=0,upper=100 1597255410000000000
# measurement,host=hostname lower=0,upper=100 1597255410000000000
#
# Example Output:
# measurement, host=hostname min=0,max=100 1597255410000000000
# measurement,host=hostname min=0,max=100 1597255410000000000

renames = {
'lower': 'min',
Expand All @@ -15,4 +16,8 @@ def apply(metric):
if k in renames:
metric.tags[renames[k]] = v
metric.tags.pop(k)
for k, v in metric.fields.items():
if k in renames:
metric.fields[renames[k]] = v
metric.fields.pop(k)
return metric
1 change: 1 addition & 0 deletions plugins/processors/starlark/testdata/scale.star
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# Example Input:
# modbus,host=hostname Current=1.22,Energy=0,Frequency=60i,Power=0,Voltage=123.9000015258789 1554079521000000000
#
# Example Output:
# modbus,host=hostname Current=12.2,Energy=0,Frequency=60i,Power=0,Voltage=1239.000015258789 1554079521000000000

Expand Down
18 changes: 18 additions & 0 deletions plugins/processors/starlark/testdata/value_filter.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Filter metrics by value
'''
In this example we look at the `value` field of the metric.
If the value is zeor, we delete all the fields, effectively dropping the metric.

Example Input:
temperature sensor="001A0",value=111.48
temperature sensor="001B0",value=0.0

Example Output:
temperature sensor="001A0",value=111.48
'''

def apply(metric):
if metric.fields["value"] == 0.0:
# removing all fields deletes a metric
metric.fields.clear()
return metric