Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
305
plugins/common/starlark/builtins.go
Normal file
305
plugins/common/starlark/builtins.go
Normal file
|
@ -0,0 +1,305 @@
|
|||
package starlark
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
)
|
||||
|
||||
func newMetric(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
var (
|
||||
name starlark.String
|
||||
tags, fields starlark.Value
|
||||
)
|
||||
if err := starlark.UnpackArgs("Metric", args, kwargs, "name", &name, "tags?", &tags, "fields?", &fields); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allFields, err := toFields(fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allTags, err := toTags(tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := metric.New(string(name), allTags, allFields, time.Now())
|
||||
|
||||
return &Metric{metric: m}, nil
|
||||
}
|
||||
|
||||
func toString(value starlark.Value, errorMsg string) (string, error) {
|
||||
if value, ok := value.(starlark.String); ok {
|
||||
return string(value), nil
|
||||
}
|
||||
return "", fmt.Errorf(errorMsg, value)
|
||||
}
|
||||
|
||||
func items(value starlark.Value, errorMsg string) ([]starlark.Tuple, error) {
|
||||
if iter, ok := value.(starlark.IterableMapping); ok {
|
||||
return iter.Items(), nil
|
||||
}
|
||||
return nil, fmt.Errorf(errorMsg, value)
|
||||
}
|
||||
|
||||
func deepcopy(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
var sm *Metric
|
||||
var track bool
|
||||
if err := starlark.UnpackArgs("deepcopy", args, kwargs, "source", &sm, "track?", &track); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// In case we copy a tracking metric but do not want to track the result,
|
||||
// we have to strip the tracking information. This can be done by unwrapping
|
||||
// the metric.
|
||||
if tm, ok := sm.metric.(telegraf.TrackingMetric); ok && !track {
|
||||
return &Metric{metric: tm.Unwrap().Copy()}, nil
|
||||
}
|
||||
|
||||
// Copy the whole metric including potential tracking information
|
||||
return &Metric{metric: sm.metric.Copy()}, nil
|
||||
}
|
||||
|
||||
// catch(f) evaluates f() and returns its evaluation error message
|
||||
// if it failed or None if it succeeded.
|
||||
func catch(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
var fn starlark.Callable
|
||||
if err := starlark.UnpackArgs("catch", args, kwargs, "fn", &fn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := starlark.Call(thread, fn, nil, nil); err != nil {
|
||||
//nolint:nilerr // nil returned on purpose, error put inside starlark.Value
|
||||
return starlark.String(err.Error()), nil
|
||||
}
|
||||
return starlark.None, nil
|
||||
}
|
||||
|
||||
type builtinMethod func(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error)
|
||||
|
||||
func builtinAttr(recv starlark.Value, name string, methods map[string]builtinMethod) (starlark.Value, error) {
|
||||
method := methods[name]
|
||||
if method == nil {
|
||||
return starlark.None, fmt.Errorf("no such method %q", name)
|
||||
}
|
||||
|
||||
// Allocate a closure over 'method'.
|
||||
impl := func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
return method(b, args, kwargs)
|
||||
}
|
||||
return starlark.NewBuiltin(name, impl).BindReceiver(recv), nil
|
||||
}
|
||||
|
||||
func builtinAttrNames(methods map[string]builtinMethod) []string {
|
||||
names := make([]string, 0, len(methods))
|
||||
for name := range methods {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// --- dictionary methods ---
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·clear
|
||||
func dictClear(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
|
||||
type HasClear interface {
|
||||
Clear() error
|
||||
}
|
||||
return starlark.None, b.Receiver().(HasClear).Clear()
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·pop
|
||||
func dictPop(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
var k, d starlark.Value
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &k, &d); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
|
||||
type HasDelete interface {
|
||||
Delete(k starlark.Value) (starlark.Value, bool, error)
|
||||
}
|
||||
if v, found, err := b.Receiver().(HasDelete).Delete(k); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err) // dict is frozen or key is unhashable
|
||||
} else if found {
|
||||
return v, nil
|
||||
} else if d != nil {
|
||||
return d, nil
|
||||
}
|
||||
return starlark.None, fmt.Errorf("%s: missing key", b.Name())
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·popitem
|
||||
func dictPopitem(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
|
||||
type HasPopItem interface {
|
||||
PopItem() (starlark.Value, error)
|
||||
}
|
||||
return b.Receiver().(HasPopItem).PopItem()
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·get
|
||||
func dictGet(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
var key, dflt starlark.Value
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
if v, ok, err := b.Receiver().(starlark.Mapping).Get(key); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
} else if ok {
|
||||
return v, nil
|
||||
} else if dflt != nil {
|
||||
return dflt, nil
|
||||
}
|
||||
return starlark.None, nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·setdefault
|
||||
func dictSetdefault(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
var key, dflt starlark.Value = nil, starlark.None
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
|
||||
recv := b.Receiver().(starlark.HasSetKey)
|
||||
v, found, err := recv.Get(key)
|
||||
if err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
if !found {
|
||||
v = dflt
|
||||
if err := recv.SetKey(key, dflt); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update
|
||||
func dictUpdate(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
// Unpack the arguments
|
||||
if len(args) > 1 {
|
||||
return nil, fmt.Errorf("update: got %d arguments, want at most 1", len(args))
|
||||
}
|
||||
|
||||
// Get the target
|
||||
dict := b.Receiver().(starlark.HasSetKey)
|
||||
|
||||
if len(args) == 1 {
|
||||
switch updates := args[0].(type) {
|
||||
case starlark.IterableMapping:
|
||||
// Iterate over dict's key/value pairs, not just keys.
|
||||
for _, item := range updates.Items() {
|
||||
if err := dict.SetKey(item[0], item[1]); err != nil {
|
||||
return nil, err // dict is frozen
|
||||
}
|
||||
}
|
||||
default:
|
||||
// all other sequences
|
||||
iter := starlark.Iterate(updates)
|
||||
if iter == nil {
|
||||
return nil, fmt.Errorf("got %s, want iterable", updates.Type())
|
||||
}
|
||||
defer iter.Done()
|
||||
var pair starlark.Value
|
||||
for i := 0; iter.Next(&pair); i++ {
|
||||
iterErr := func() error {
|
||||
iter2 := starlark.Iterate(pair)
|
||||
if iter2 == nil {
|
||||
return fmt.Errorf("dictionary update sequence element #%d is not iterable (%s)", i, pair.Type())
|
||||
}
|
||||
defer iter2.Done()
|
||||
length := starlark.Len(pair)
|
||||
if length < 0 {
|
||||
return fmt.Errorf("dictionary update sequence element #%d has unknown length (%s)", i, pair.Type())
|
||||
} else if length != 2 {
|
||||
return fmt.Errorf("dictionary update sequence element #%d has length %d, want 2", i, length)
|
||||
}
|
||||
var k, v starlark.Value
|
||||
iter2.Next(&k)
|
||||
iter2.Next(&v)
|
||||
|
||||
return dict.SetKey(k, v)
|
||||
}()
|
||||
|
||||
if iterErr != nil {
|
||||
return nil, iterErr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then add the kwargs.
|
||||
before := starlark.Len(dict)
|
||||
for _, pair := range kwargs {
|
||||
if err := dict.SetKey(pair[0], pair[1]); err != nil {
|
||||
return nil, err // dict is frozen
|
||||
}
|
||||
}
|
||||
// In the common case, each kwarg will add another dict entry.
|
||||
// If that's not so, check whether it is because there was a duplicate kwarg.
|
||||
if starlark.Len(dict) < before+len(kwargs) {
|
||||
keys := make(map[starlark.String]bool, len(kwargs))
|
||||
for _, kv := range kwargs {
|
||||
k := kv[0].(starlark.String)
|
||||
if keys[k] {
|
||||
return nil, fmt.Errorf("duplicate keyword arg: %v", k)
|
||||
}
|
||||
keys[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
return starlark.None, nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·items
|
||||
func dictItems(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
items := b.Receiver().(starlark.IterableMapping).Items()
|
||||
res := make([]starlark.Value, 0, len(items))
|
||||
for _, item := range items {
|
||||
res = append(res, item) // convert [2]starlark.Value to starlark.Value
|
||||
}
|
||||
return starlark.NewList(res), nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·keys
|
||||
func dictKeys(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
|
||||
items := b.Receiver().(starlark.IterableMapping).Items()
|
||||
res := make([]starlark.Value, 0, len(items))
|
||||
for _, item := range items {
|
||||
res = append(res, item[0])
|
||||
}
|
||||
return starlark.NewList(res), nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update
|
||||
func dictValues(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
items := b.Receiver().(starlark.IterableMapping).Items()
|
||||
res := make([]starlark.Value, 0, len(items))
|
||||
for _, item := range items {
|
||||
res = append(res, item[1])
|
||||
}
|
||||
return starlark.NewList(res), nil
|
||||
}
|
308
plugins/common/starlark/field_dict.go
Normal file
308
plugins/common/starlark/field_dict.go
Normal file
|
@ -0,0 +1,308 @@
|
|||
package starlark
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
// FieldDict is a starlark.Value for the metric fields. It is heavily based on the
|
||||
// starlark.Dict.
|
||||
type FieldDict struct {
|
||||
*Metric
|
||||
}
|
||||
|
||||
func (d FieldDict) String() string {
|
||||
buf := new(strings.Builder)
|
||||
buf.WriteString("{")
|
||||
sep := ""
|
||||
for _, item := range d.Items() {
|
||||
k, v := item[0], item[1]
|
||||
buf.WriteString(sep)
|
||||
buf.WriteString(k.String())
|
||||
buf.WriteString(": ")
|
||||
buf.WriteString(v.String())
|
||||
sep = ", "
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (FieldDict) Type() string {
|
||||
return "Fields"
|
||||
}
|
||||
|
||||
func (d FieldDict) Freeze() {
|
||||
// Disable linter check as the frozen variable is modified despite
|
||||
// passing a value instead of a pointer, because `FieldDict` holds
|
||||
// a pointer to the underlying metric containing the `frozen` field.
|
||||
//revive:disable:modifies-value-receiver
|
||||
d.frozen = true
|
||||
}
|
||||
|
||||
func (d FieldDict) Truth() starlark.Bool {
|
||||
return len(d.metric.FieldList()) != 0
|
||||
}
|
||||
|
||||
func (FieldDict) Hash() (uint32, error) {
|
||||
return 0, errors.New("not hashable")
|
||||
}
|
||||
|
||||
// AttrNames implements the starlark.HasAttrs interface.
|
||||
func (FieldDict) AttrNames() []string {
|
||||
return builtinAttrNames(FieldDictMethods)
|
||||
}
|
||||
|
||||
// Attr implements the starlark.HasAttrs interface.
|
||||
func (d FieldDict) Attr(name string) (starlark.Value, error) {
|
||||
return builtinAttr(d, name, FieldDictMethods)
|
||||
}
|
||||
|
||||
var FieldDictMethods = map[string]builtinMethod{
|
||||
"clear": dictClear,
|
||||
"get": dictGet,
|
||||
"items": dictItems,
|
||||
"keys": dictKeys,
|
||||
"pop": dictPop,
|
||||
"popitem": dictPopitem,
|
||||
"setdefault": dictSetdefault,
|
||||
"update": dictUpdate,
|
||||
"values": dictValues,
|
||||
}
|
||||
|
||||
// Get implements the starlark.Mapping interface.
|
||||
func (d FieldDict) Get(key starlark.Value) (v starlark.Value, found bool, err error) {
|
||||
if k, ok := key.(starlark.String); ok {
|
||||
gv, found := d.metric.GetField(k.GoString())
|
||||
if !found {
|
||||
return starlark.None, false, nil
|
||||
}
|
||||
|
||||
v, err := asStarlarkValue(gv)
|
||||
if err != nil {
|
||||
return starlark.None, false, err
|
||||
}
|
||||
return v, true, nil
|
||||
}
|
||||
|
||||
return starlark.None, false, errors.New("key must be of type 'str'")
|
||||
}
|
||||
|
||||
// SetKey implements the starlark.HasSetKey interface to support map update
|
||||
// using x[k]=v syntax, like a dictionary.
|
||||
func (d FieldDict) SetKey(k, v starlark.Value) error {
|
||||
if d.fieldIterCount > 0 {
|
||||
return errors.New("cannot insert during iteration")
|
||||
}
|
||||
|
||||
key, ok := k.(starlark.String)
|
||||
if !ok {
|
||||
return errors.New("field key must be of type 'str'")
|
||||
}
|
||||
|
||||
gv, err := asGoValue(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.metric.AddField(key.GoString(), gv)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Items implements the starlark.IterableMapping interface.
|
||||
func (d FieldDict) Items() []starlark.Tuple {
|
||||
items := make([]starlark.Tuple, 0, len(d.metric.FieldList()))
|
||||
for _, field := range d.metric.FieldList() {
|
||||
key := starlark.String(field.Key)
|
||||
sv, err := asStarlarkValue(field.Value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
pair := starlark.Tuple{key, sv}
|
||||
items = append(items, pair)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (d FieldDict) Clear() error {
|
||||
if d.fieldIterCount > 0 {
|
||||
return errors.New("cannot delete during iteration")
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(d.metric.FieldList()))
|
||||
for _, field := range d.metric.FieldList() {
|
||||
keys = append(keys, field.Key)
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
d.metric.RemoveField(key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d FieldDict) PopItem() (starlark.Value, error) {
|
||||
if d.fieldIterCount > 0 {
|
||||
return nil, errors.New("cannot delete during iteration")
|
||||
}
|
||||
|
||||
if len(d.metric.FieldList()) == 0 {
|
||||
return nil, errors.New("popitem(): field dictionary is empty")
|
||||
}
|
||||
|
||||
field := d.metric.FieldList()[0]
|
||||
k := field.Key
|
||||
v := field.Value
|
||||
|
||||
d.metric.RemoveField(k)
|
||||
|
||||
sk := starlark.String(k)
|
||||
sv, err := asStarlarkValue(v)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not convert to starlark value")
|
||||
}
|
||||
|
||||
return starlark.Tuple{sk, sv}, nil
|
||||
}
|
||||
|
||||
func (d FieldDict) Delete(k starlark.Value) (v starlark.Value, found bool, err error) {
|
||||
if d.fieldIterCount > 0 {
|
||||
return nil, false, errors.New("cannot delete during iteration")
|
||||
}
|
||||
|
||||
if key, ok := k.(starlark.String); ok {
|
||||
value, ok := d.metric.GetField(key.GoString())
|
||||
if ok {
|
||||
d.metric.RemoveField(key.GoString())
|
||||
sv, err := asStarlarkValue(value)
|
||||
return sv, ok, err
|
||||
}
|
||||
return starlark.None, false, nil
|
||||
}
|
||||
|
||||
return starlark.None, false, errors.New("key must be of type 'str'")
|
||||
}
|
||||
|
||||
// Iterate implements the starlark.Iterator interface.
|
||||
func (d FieldDict) Iterate() starlark.Iterator {
|
||||
d.fieldIterCount++
|
||||
return &FieldIterator{Metric: d.Metric, fields: d.metric.FieldList()}
|
||||
}
|
||||
|
||||
type FieldIterator struct {
|
||||
*Metric
|
||||
fields []*telegraf.Field
|
||||
}
|
||||
|
||||
// Next implements the starlark.Iterator interface.
|
||||
func (i *FieldIterator) Next(p *starlark.Value) bool {
|
||||
if len(i.fields) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
field := i.fields[0]
|
||||
i.fields = i.fields[1:]
|
||||
*p = starlark.String(field.Key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Done implements the starlark.Iterator interface.
|
||||
func (i *FieldIterator) Done() {
|
||||
i.fieldIterCount--
|
||||
}
|
||||
|
||||
// AsStarlarkValue converts a field value to a starlark.Value.
|
||||
func asStarlarkValue(value interface{}) (starlark.Value, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
length := v.Len()
|
||||
array := make([]starlark.Value, 0, length)
|
||||
for i := 0; i < length; i++ {
|
||||
sVal, err := asStarlarkValue(v.Index(i).Interface())
|
||||
if err != nil {
|
||||
return starlark.None, err
|
||||
}
|
||||
array = append(array, sVal)
|
||||
}
|
||||
return starlark.NewList(array), nil
|
||||
case reflect.Map:
|
||||
dict := starlark.NewDict(v.Len())
|
||||
iter := v.MapRange()
|
||||
for iter.Next() {
|
||||
sKey, err := asStarlarkValue(iter.Key().Interface())
|
||||
if err != nil {
|
||||
return starlark.None, err
|
||||
}
|
||||
sValue, err := asStarlarkValue(iter.Value().Interface())
|
||||
if err != nil {
|
||||
return starlark.None, err
|
||||
}
|
||||
if err := dict.SetKey(sKey, sValue); err != nil {
|
||||
return starlark.None, err
|
||||
}
|
||||
}
|
||||
return dict, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return starlark.Float(v.Float()), nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return starlark.MakeInt64(v.Int()), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return starlark.MakeUint64(v.Uint()), nil
|
||||
case reflect.String:
|
||||
return starlark.String(v.String()), nil
|
||||
case reflect.Bool:
|
||||
return starlark.Bool(v.Bool()), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid type %T", value)
|
||||
}
|
||||
|
||||
// AsGoValue converts a starlark.Value to a field value.
|
||||
func asGoValue(value interface{}) (interface{}, error) {
|
||||
switch v := value.(type) {
|
||||
case starlark.Float:
|
||||
return float64(v), nil
|
||||
case starlark.Int:
|
||||
n, ok := v.Int64()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot represent integer %v as int64", v)
|
||||
}
|
||||
return n, nil
|
||||
case starlark.String:
|
||||
return string(v), nil
|
||||
case starlark.Bool:
|
||||
return bool(v), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid starlark type %T", value)
|
||||
}
|
||||
|
||||
// ToFields converts a starlark.Value to a map of values.
|
||||
func toFields(value starlark.Value) (map[string]interface{}, error) {
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
items, err := items(value, "The type %T is unsupported as type of collection of fields")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]interface{}, len(items))
|
||||
for _, item := range items {
|
||||
key, err := toString(item[0], "The type %T is unsupported as type of key for fields")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := asGoValue(item[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[key] = value
|
||||
}
|
||||
return result, nil
|
||||
}
|
48
plugins/common/starlark/logging.go
Normal file
48
plugins/common/starlark/logging.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package starlark
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/starlarkstruct"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
// Builds a module that defines all the supported logging functions which will log using the provided logger
|
||||
func LogModule(logger telegraf.Logger) *starlarkstruct.Module {
|
||||
var logFunc = func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||
return log(b, args, kwargs, logger)
|
||||
}
|
||||
return &starlarkstruct.Module{
|
||||
Name: "log",
|
||||
Members: starlark.StringDict{
|
||||
"debug": starlark.NewBuiltin("log.debug", logFunc),
|
||||
"info": starlark.NewBuiltin("log.info", logFunc),
|
||||
"warn": starlark.NewBuiltin("log.warn", logFunc),
|
||||
"error": starlark.NewBuiltin("log.error", logFunc),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Logs the provided message according to the level chosen
|
||||
func log(b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple, logger telegraf.Logger) (starlark.Value, error) {
|
||||
var msg starlark.String
|
||||
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &msg); err != nil {
|
||||
return starlark.None, fmt.Errorf("%s: %w", b.Name(), err)
|
||||
}
|
||||
switch b.Name() {
|
||||
case "log.debug":
|
||||
logger.Debug(string(msg))
|
||||
case "log.info":
|
||||
logger.Info(string(msg))
|
||||
case "log.warn":
|
||||
logger.Warn(string(msg))
|
||||
case "log.error":
|
||||
logger.Error(string(msg))
|
||||
default:
|
||||
return nil, errors.New("method " + b.Name() + " is unknown")
|
||||
}
|
||||
return starlark.None, nil
|
||||
}
|
156
plugins/common/starlark/metric.go
Normal file
156
plugins/common/starlark/metric.go
Normal file
|
@ -0,0 +1,156 @@
|
|||
package starlark
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
type Metric struct {
|
||||
ID telegraf.TrackingID
|
||||
metric telegraf.Metric
|
||||
tagIterCount int
|
||||
fieldIterCount int
|
||||
frozen bool
|
||||
}
|
||||
|
||||
// Wrap updates the starlark.Metric to wrap a new telegraf.Metric.
|
||||
func (m *Metric) Wrap(metric telegraf.Metric) {
|
||||
if tm, ok := metric.(telegraf.TrackingMetric); ok {
|
||||
m.ID = tm.TrackingID()
|
||||
}
|
||||
m.metric = metric
|
||||
m.tagIterCount = 0
|
||||
m.fieldIterCount = 0
|
||||
m.frozen = false
|
||||
}
|
||||
|
||||
// Unwrap removes the telegraf.Metric from the startlark.Metric.
|
||||
func (m *Metric) Unwrap() telegraf.Metric {
|
||||
return m.metric
|
||||
}
|
||||
|
||||
// String returns the starlark representation of the Metric.
|
||||
//
|
||||
// The String function is called by both the repr() and str() functions, and so
|
||||
// it behaves more like the repr function would in Python.
|
||||
func (m *Metric) String() string {
|
||||
buf := new(strings.Builder)
|
||||
buf.WriteString("Metric(")
|
||||
buf.WriteString(m.Name().String())
|
||||
buf.WriteString(", tags=")
|
||||
buf.WriteString(m.Tags().String())
|
||||
buf.WriteString(", fields=")
|
||||
buf.WriteString(m.Fields().String())
|
||||
buf.WriteString(", time=")
|
||||
buf.WriteString(m.Time().String())
|
||||
buf.WriteString(")")
|
||||
if m.ID != 0 {
|
||||
fmt.Fprintf(buf, "[tracking ID=%v]", m.ID)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (*Metric) Type() string {
|
||||
return "Metric"
|
||||
}
|
||||
|
||||
func (m *Metric) Freeze() {
|
||||
m.frozen = true
|
||||
}
|
||||
|
||||
func (*Metric) Truth() starlark.Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (*Metric) Hash() (uint32, error) {
|
||||
return 0, errors.New("not hashable")
|
||||
}
|
||||
|
||||
// AttrNames implements the starlark.HasAttrs interface.
|
||||
func (*Metric) AttrNames() []string {
|
||||
return []string{"name", "tags", "fields", "time"}
|
||||
}
|
||||
|
||||
// Attr implements the starlark.HasAttrs interface.
|
||||
func (m *Metric) Attr(name string) (starlark.Value, error) {
|
||||
switch name {
|
||||
case "name":
|
||||
return m.Name(), nil
|
||||
case "tags":
|
||||
return m.Tags(), nil
|
||||
case "fields":
|
||||
return m.Fields(), nil
|
||||
case "time":
|
||||
return m.Time(), nil
|
||||
default:
|
||||
// Returning nil, nil indicates "no such field or method"
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetField implements the starlark.HasSetField interface.
|
||||
func (m *Metric) SetField(name string, value starlark.Value) error {
|
||||
if m.frozen {
|
||||
return errors.New("cannot modify frozen metric")
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "name":
|
||||
return m.SetName(value)
|
||||
case "time":
|
||||
return m.SetTime(value)
|
||||
case "tags":
|
||||
return errors.New("cannot set tags")
|
||||
case "fields":
|
||||
return errors.New("cannot set fields")
|
||||
default:
|
||||
return starlark.NoSuchAttrError(
|
||||
fmt.Sprintf("cannot assign to field %q", name))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Metric) Name() starlark.String {
|
||||
return starlark.String(m.metric.Name())
|
||||
}
|
||||
|
||||
func (m *Metric) SetName(value starlark.Value) error {
|
||||
if str, ok := value.(starlark.String); ok {
|
||||
m.metric.SetName(str.GoString())
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("type error")
|
||||
}
|
||||
|
||||
func (m *Metric) Tags() TagDict {
|
||||
return TagDict{m}
|
||||
}
|
||||
|
||||
func (m *Metric) Fields() FieldDict {
|
||||
return FieldDict{m}
|
||||
}
|
||||
|
||||
func (m *Metric) Time() starlark.Int {
|
||||
return starlark.MakeInt64(m.metric.Time().UnixNano())
|
||||
}
|
||||
|
||||
func (m *Metric) SetTime(value starlark.Value) error {
|
||||
switch v := value.(type) {
|
||||
case starlark.Int:
|
||||
ns, ok := v.Int64()
|
||||
if !ok {
|
||||
return errors.New("type error: unrepresentable time")
|
||||
}
|
||||
tm := time.Unix(0, ns)
|
||||
m.metric.SetTime(tm)
|
||||
return nil
|
||||
default:
|
||||
return errors.New("type error")
|
||||
}
|
||||
}
|
282
plugins/common/starlark/starlark.go
Normal file
282
plugins/common/starlark/starlark.go
Normal file
|
@ -0,0 +1,282 @@
|
|||
package starlark
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"go.starlark.net/lib/json"
|
||||
"go.starlark.net/lib/math"
|
||||
"go.starlark.net/lib/time"
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/syntax"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
type Common struct {
|
||||
Source string `toml:"source"`
|
||||
Script string `toml:"script"`
|
||||
Constants map[string]interface{} `toml:"constants"`
|
||||
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
StarlarkLoadFunc func(module string, logger telegraf.Logger) (starlark.StringDict, error)
|
||||
|
||||
thread *starlark.Thread
|
||||
builtins starlark.StringDict
|
||||
globals starlark.StringDict
|
||||
functions map[string]*starlark.Function
|
||||
parameters map[string]starlark.Tuple
|
||||
state *starlark.Dict
|
||||
}
|
||||
|
||||
func (s *Common) GetState() interface{} {
|
||||
// Return the actual byte-type instead of nil allowing the persister
|
||||
// to guess instantiate variable of the appropriate type
|
||||
if s.state == nil {
|
||||
return make([]byte, 0)
|
||||
}
|
||||
|
||||
// Convert the starlark dict into a golang dictionary for serialization
|
||||
state := make(map[string]interface{}, s.state.Len())
|
||||
items := s.state.Items()
|
||||
for _, item := range items {
|
||||
if len(item) != 2 {
|
||||
// We do expect key-value pairs in the state so there should be
|
||||
// two items.
|
||||
s.Log.Errorf("state item %+v does not contain a key-value pair", item)
|
||||
continue
|
||||
}
|
||||
k, ok := item.Index(0).(starlark.String)
|
||||
if !ok {
|
||||
s.Log.Errorf("state item %+v has invalid key type %T", item, item.Index(0))
|
||||
continue
|
||||
}
|
||||
v, err := asGoValue(item.Index(1))
|
||||
if err != nil {
|
||||
s.Log.Errorf("state item %+v value cannot be converted: %v", item, err)
|
||||
continue
|
||||
}
|
||||
state[k.GoString()] = v
|
||||
}
|
||||
|
||||
// Do a binary GOB encoding to preserve types
|
||||
var buf bytes.Buffer
|
||||
if err := gob.NewEncoder(&buf).Encode(state); err != nil {
|
||||
s.Log.Errorf("encoding state failed: %v", err)
|
||||
return make([]byte, 0)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (s *Common) SetState(state interface{}) error {
|
||||
data, ok := state.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for state", state)
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode the binary GOB encoding
|
||||
var dict map[string]interface{}
|
||||
if err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(&dict); err != nil {
|
||||
return fmt.Errorf("decoding state failed: %w", err)
|
||||
}
|
||||
|
||||
// Convert the golang dict back to starlark types
|
||||
s.state = starlark.NewDict(len(dict))
|
||||
for k, v := range dict {
|
||||
sv, err := asStarlarkValue(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("value %v of state item %q cannot be set: %w", v, k, err)
|
||||
}
|
||||
if err := s.state.SetKey(starlark.String(k), sv); err != nil {
|
||||
return fmt.Errorf("state item %q cannot be set: %w", k, err)
|
||||
}
|
||||
}
|
||||
s.builtins["state"] = s.state
|
||||
|
||||
return s.InitProgram()
|
||||
}
|
||||
|
||||
func (s *Common) Init() error {
|
||||
if s.Source == "" && s.Script == "" {
|
||||
return errors.New("one of source or script must be set")
|
||||
}
|
||||
if s.Source != "" && s.Script != "" {
|
||||
return errors.New("both source or script cannot be set")
|
||||
}
|
||||
|
||||
s.builtins = starlark.StringDict{}
|
||||
s.builtins["Metric"] = starlark.NewBuiltin("Metric", newMetric)
|
||||
s.builtins["deepcopy"] = starlark.NewBuiltin("deepcopy", deepcopy)
|
||||
s.builtins["catch"] = starlark.NewBuiltin("catch", catch)
|
||||
|
||||
if err := s.addConstants(&s.builtins); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize the program
|
||||
if err := s.InitProgram(); err != nil {
|
||||
// Try again with a declared state. This might be necessary for
|
||||
// state persistence.
|
||||
s.state = starlark.NewDict(0)
|
||||
s.builtins["state"] = s.state
|
||||
if serr := s.InitProgram(); serr != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.functions = make(map[string]*starlark.Function)
|
||||
s.parameters = make(map[string]starlark.Tuple)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Common) InitProgram() error {
|
||||
// Load the program. In case of an error we can try to insert the state
|
||||
// which can be used implicitly e.g. when persisting states
|
||||
program, err := s.sourceProgram(s.builtins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute source
|
||||
s.thread = &starlark.Thread{
|
||||
Print: func(_ *starlark.Thread, msg string) { s.Log.Debug(msg) },
|
||||
Load: func(_ *starlark.Thread, module string) (starlark.StringDict, error) {
|
||||
return s.StarlarkLoadFunc(module, s.Log)
|
||||
},
|
||||
}
|
||||
globals, err := program.Init(s.thread, s.builtins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// In case the program declares a global "state" we should insert it to
|
||||
// avoid warnings about inserting into a frozen variable
|
||||
if _, found := globals["state"]; found {
|
||||
globals["state"] = starlark.NewDict(0)
|
||||
}
|
||||
|
||||
// Freeze the global state. This prevents modifications to the processor
|
||||
// state and prevents scripts from containing errors storing tracking
|
||||
// metrics. Tasks that require global state will not be possible due to
|
||||
// this, so maybe we should relax this in the future.
|
||||
globals.Freeze()
|
||||
s.globals = globals
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Common) GetParameters(name string) (starlark.Tuple, bool) {
|
||||
parameters, found := s.parameters[name]
|
||||
return parameters, found
|
||||
}
|
||||
|
||||
func (s *Common) AddFunction(name string, params ...starlark.Value) error {
|
||||
globalFn, found := s.globals[name]
|
||||
if !found {
|
||||
return fmt.Errorf("%s is not defined", name)
|
||||
}
|
||||
|
||||
fn, found := globalFn.(*starlark.Function)
|
||||
if !found {
|
||||
return fmt.Errorf("%s is not a function", name)
|
||||
}
|
||||
|
||||
if fn.NumParams() != len(params) {
|
||||
return fmt.Errorf("%s function must take %d parameter(s)", name, len(params))
|
||||
}
|
||||
p := make(starlark.Tuple, len(params))
|
||||
copy(p, params)
|
||||
|
||||
s.functions[name] = fn
|
||||
s.parameters[name] = params
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add all the constants defined in the plugin as constants of the script
|
||||
func (s *Common) addConstants(builtins *starlark.StringDict) error {
|
||||
for key, val := range s.Constants {
|
||||
if key == "state" {
|
||||
return errors.New("'state' constant uses reserved name")
|
||||
}
|
||||
sVal, err := asStarlarkValue(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("converting type %T failed: %w", val, err)
|
||||
}
|
||||
(*builtins)[key] = sVal
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Common) sourceProgram(builtins starlark.StringDict) (*starlark.Program, error) {
|
||||
var src interface{}
|
||||
if s.Source != "" {
|
||||
src = s.Source
|
||||
}
|
||||
|
||||
// AllowFloat - obsolete, no effect
|
||||
// AllowNestedDef - always on https://github.com/google/starlark-go/pull/328
|
||||
// AllowLambda - always on https://github.com/google/starlark-go/pull/328
|
||||
options := syntax.FileOptions{
|
||||
Recursion: true,
|
||||
GlobalReassign: true,
|
||||
Set: true,
|
||||
}
|
||||
|
||||
_, program, err := starlark.SourceProgramOptions(&options, s.Script, src, builtins.Has)
|
||||
return program, err
|
||||
}
|
||||
|
||||
// Call calls the function corresponding to the given name.
|
||||
func (s *Common) Call(name string) (starlark.Value, error) {
|
||||
fn, ok := s.functions[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("function %q does not exist", name)
|
||||
}
|
||||
args, ok := s.parameters[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("params for function %q do not exist", name)
|
||||
}
|
||||
return starlark.Call(s.thread, fn, args, nil)
|
||||
}
|
||||
|
||||
func (s *Common) LogError(err error) {
|
||||
var evalErr *starlark.EvalError
|
||||
if errors.As(err, &evalErr) {
|
||||
for _, line := range strings.Split(evalErr.Backtrace(), "\n") {
|
||||
s.Log.Error(line)
|
||||
}
|
||||
} else {
|
||||
s.Log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func LoadFunc(module string, logger telegraf.Logger) (starlark.StringDict, error) {
|
||||
switch module {
|
||||
case "json.star":
|
||||
return starlark.StringDict{
|
||||
"json": json.Module,
|
||||
}, nil
|
||||
case "logging.star":
|
||||
return starlark.StringDict{
|
||||
"log": LogModule(logger),
|
||||
}, nil
|
||||
case "math.star":
|
||||
return starlark.StringDict{
|
||||
"math": math.Module,
|
||||
}, nil
|
||||
case "time.star":
|
||||
return starlark.StringDict{
|
||||
"time": time.Module,
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New("module " + module + " is not available")
|
||||
}
|
||||
}
|
226
plugins/common/starlark/tag_dict.go
Normal file
226
plugins/common/starlark/tag_dict.go
Normal file
|
@ -0,0 +1,226 @@
|
|||
package starlark
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
// TagDict is a starlark.Value for the metric tags. It is heavily based on the
|
||||
// starlark.Dict.
|
||||
type TagDict struct {
|
||||
*Metric
|
||||
}
|
||||
|
||||
func (d TagDict) String() string {
|
||||
buf := new(strings.Builder)
|
||||
buf.WriteString("{")
|
||||
sep := ""
|
||||
for _, item := range d.Items() {
|
||||
k, v := item[0], item[1]
|
||||
buf.WriteString(sep)
|
||||
buf.WriteString(k.String())
|
||||
buf.WriteString(": ")
|
||||
buf.WriteString(v.String())
|
||||
sep = ", "
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (TagDict) Type() string {
|
||||
return "Tags"
|
||||
}
|
||||
|
||||
func (d TagDict) Freeze() {
|
||||
// Disable linter check as the frozen variable is modified despite
|
||||
// passing a value instead of a pointer, because `TagDict` holds
|
||||
// a pointer to the underlying metric containing the `frozen` field.
|
||||
//revive:disable:modifies-value-receiver
|
||||
d.frozen = true
|
||||
}
|
||||
|
||||
func (d TagDict) Truth() starlark.Bool {
|
||||
return len(d.metric.TagList()) != 0
|
||||
}
|
||||
|
||||
func (TagDict) Hash() (uint32, error) {
|
||||
return 0, errors.New("not hashable")
|
||||
}
|
||||
|
||||
// AttrNames implements the starlark.HasAttrs interface.
|
||||
func (TagDict) AttrNames() []string {
|
||||
return builtinAttrNames(TagDictMethods)
|
||||
}
|
||||
|
||||
// Attr implements the starlark.HasAttrs interface.
|
||||
func (d TagDict) Attr(name string) (starlark.Value, error) {
|
||||
return builtinAttr(d, name, TagDictMethods)
|
||||
}
|
||||
|
||||
var TagDictMethods = map[string]builtinMethod{
|
||||
"clear": dictClear,
|
||||
"get": dictGet,
|
||||
"items": dictItems,
|
||||
"keys": dictKeys,
|
||||
"pop": dictPop,
|
||||
"popitem": dictPopitem,
|
||||
"setdefault": dictSetdefault,
|
||||
"update": dictUpdate,
|
||||
"values": dictValues,
|
||||
}
|
||||
|
||||
// Get implements the starlark.Mapping interface.
|
||||
func (d TagDict) Get(key starlark.Value) (v starlark.Value, found bool, err error) {
|
||||
if k, ok := key.(starlark.String); ok {
|
||||
gv, found := d.metric.GetTag(k.GoString())
|
||||
if !found {
|
||||
return starlark.None, false, nil
|
||||
}
|
||||
return starlark.String(gv), true, err
|
||||
}
|
||||
|
||||
return starlark.None, false, errors.New("key must be of type 'str'")
|
||||
}
|
||||
|
||||
// SetKey implements the starlark.HasSetKey interface to support map update
|
||||
// using x[k]=v syntax, like a dictionary.
|
||||
func (d TagDict) SetKey(k, v starlark.Value) error {
|
||||
if d.tagIterCount > 0 {
|
||||
return errors.New("cannot insert during iteration")
|
||||
}
|
||||
|
||||
key, ok := k.(starlark.String)
|
||||
if !ok {
|
||||
return errors.New("tag key must be of type 'str'")
|
||||
}
|
||||
|
||||
value, ok := v.(starlark.String)
|
||||
if !ok {
|
||||
return errors.New("tag value must be of type 'str'")
|
||||
}
|
||||
|
||||
d.metric.AddTag(key.GoString(), value.GoString())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Items implements the starlark.IterableMapping interface.
|
||||
func (d TagDict) Items() []starlark.Tuple {
|
||||
items := make([]starlark.Tuple, 0, len(d.metric.TagList()))
|
||||
for _, tag := range d.metric.TagList() {
|
||||
key := starlark.String(tag.Key)
|
||||
value := starlark.String(tag.Value)
|
||||
pair := starlark.Tuple{key, value}
|
||||
items = append(items, pair)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (d TagDict) Clear() error {
|
||||
if d.tagIterCount > 0 {
|
||||
return errors.New("cannot delete during iteration")
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(d.metric.TagList()))
|
||||
for _, tag := range d.metric.TagList() {
|
||||
keys = append(keys, tag.Key)
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
d.metric.RemoveTag(key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d TagDict) PopItem() (v starlark.Value, err error) {
|
||||
if d.tagIterCount > 0 {
|
||||
return nil, errors.New("cannot delete during iteration")
|
||||
}
|
||||
|
||||
for _, tag := range d.metric.TagList() {
|
||||
k := tag.Key
|
||||
v := tag.Value
|
||||
|
||||
d.metric.RemoveTag(k)
|
||||
|
||||
sk := starlark.String(k)
|
||||
sv := starlark.String(v)
|
||||
return starlark.Tuple{sk, sv}, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("popitem(): tag dictionary is empty")
|
||||
}
|
||||
|
||||
func (d TagDict) Delete(k starlark.Value) (v starlark.Value, found bool, err error) {
|
||||
if d.tagIterCount > 0 {
|
||||
return nil, false, errors.New("cannot delete during iteration")
|
||||
}
|
||||
|
||||
if key, ok := k.(starlark.String); ok {
|
||||
value, ok := d.metric.GetTag(key.GoString())
|
||||
if ok {
|
||||
d.metric.RemoveTag(key.GoString())
|
||||
v := starlark.String(value)
|
||||
return v, ok, err
|
||||
}
|
||||
return starlark.None, false, nil
|
||||
}
|
||||
|
||||
return starlark.None, false, errors.New("key must be of type 'str'")
|
||||
}
|
||||
|
||||
// Iterate implements the starlark.Iterator interface.
|
||||
func (d TagDict) Iterate() starlark.Iterator {
|
||||
d.tagIterCount++
|
||||
return &TagIterator{Metric: d.Metric, tags: d.metric.TagList()}
|
||||
}
|
||||
|
||||
type TagIterator struct {
|
||||
*Metric
|
||||
tags []*telegraf.Tag
|
||||
}
|
||||
|
||||
// Next implements the starlark.Iterator interface.
|
||||
func (i *TagIterator) Next(p *starlark.Value) bool {
|
||||
if len(i.tags) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
tag := i.tags[0]
|
||||
i.tags = i.tags[1:]
|
||||
*p = starlark.String(tag.Key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Done implements the starlark.Iterator interface.
|
||||
func (i *TagIterator) Done() {
|
||||
i.tagIterCount--
|
||||
}
|
||||
|
||||
// ToTags converts a starlark.Value to a map of string.
|
||||
func toTags(value starlark.Value) (map[string]string, error) {
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
items, err := items(value, "The type %T is unsupported as type of collection of tags")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]string, len(items))
|
||||
for _, item := range items {
|
||||
key, err := toString(item[0], "The type %T is unsupported as type of key for tags")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := toString(item[1], "The type %T is unsupported as type of value for tags")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[key] = value
|
||||
}
|
||||
return result, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue