From 90a28aa05f2a515164b1bdedd197401b7959b729 Mon Sep 17 00:00:00 2001 From: Kamil Oracz Date: Wed, 9 Oct 2024 15:23:37 +0200 Subject: [PATCH] Add banfield transformer --- transformer/auto.go | 7 ++++ transformer/ban_field.go | 75 +++++++++++++++++++++++++++++++++++ transformer/ban_field_test.go | 41 +++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 transformer/ban_field.go create mode 100644 transformer/ban_field_test.go diff --git a/transformer/auto.go b/transformer/auto.go index b96b03be..34b5b648 100644 --- a/transformer/auto.go +++ b/transformer/auto.go @@ -122,4 +122,11 @@ func init() { Help: "Ban values from nested structure using a path e.g. Path looking like this foo.bar.1 has a structure looking like this { foo: { bar: { 1: hello } } }. The last element in the path will get removed from the tree in this case 1: hello, you will end up having a tree looking like this { foo: { bar: {} } }.", AutoMake: false, }) + Auto.Add(skogul.Module{ + Name: "banfield", + Aliases: []string{}, + Alloc: func() interface{} { return &BanField{} }, + Help: "Remove single fields in a metric based on a regular expression criteria", + AutoMake: false, + }) } diff --git a/transformer/ban_field.go b/transformer/ban_field.go new file mode 100644 index 00000000..628f9b19 --- /dev/null +++ b/transformer/ban_field.go @@ -0,0 +1,75 @@ +package transformer + +import ( + "fmt" + "github.com/telenornms/skogul" + "regexp" + "sync" +) + +type BanField struct { + SourceData string `doc:"Data field to ban"` + RegexpData string `doc:"Regex to match value of source-data field"` + regexpData *regexp.Regexp + SourceMetadata string `doc:"Metadata field to ban"` + RegexpMetadata string `doc:"Regex to match value of source-metadata field"` + regexpMetadata *regexp.Regexp + errData error + errMetadata error + init sync.Once +} + +func (b *BanField) Transform(c *skogul.Container) error { + b.init.Do(func() { + b.regexpData, b.errData = regexp.Compile(b.RegexpData) + b.regexpMetadata, b.errMetadata = regexp.Compile(b.RegexpMetadata) + }) + + if b.errData != nil { + return fmt.Errorf("unable to compile regexp `%s': %w", b.RegexpData, b.errData) + } + if b.errMetadata != nil { + return fmt.Errorf("unable to compile regexp `%s': %w", b.RegexpMetadata, b.errMetadata) + } + + for _, metric := range c.Metrics { + if b.SourceData != "" { + if str, ok := metric.Data[b.SourceData]; ok { + if b.regexpData.Match([]byte(str.(string))) { + delete(metric.Data, b.SourceData) + } + } + } + if b.SourceMetadata != "" { + if str, ok := metric.Metadata[b.SourceMetadata]; ok { + if b.regexpMetadata.Match([]byte(str.(string))) { + delete(metric.Metadata, b.SourceMetadata) + } + } + } + } + + return nil +} + +func (b *BanField) Verify() error { + if b.SourceData != "" && b.RegexpData == "" { + return fmt.Errorf("regexpdata field has to have a value when sourcedata is provided") + } + if b.SourceMetadata != "" && b.RegexpMetadata == "" { + return fmt.Errorf("regexpmetadata field has to have a value when sourcemetadata is provided") + } + + var err error + + _, err = regexp.Compile(b.RegexpData) + if err != nil { + return fmt.Errorf("failed to compile regexp for regexpdata field %v %v", b.RegexpData, err) + } + + _, err = regexp.Compile(b.RegexpMetadata) + if err != nil { + return fmt.Errorf("failed to compile regexp for regexpmetadata field %v %v", b.RegexpMetadata, err) + } + return nil +} diff --git a/transformer/ban_field_test.go b/transformer/ban_field_test.go new file mode 100644 index 00000000..815db4f2 --- /dev/null +++ b/transformer/ban_field_test.go @@ -0,0 +1,41 @@ +package transformer_test + +import ( + "testing" + + "github.com/telenornms/skogul" + "github.com/telenornms/skogul/transformer" +) + +func TestBanField(t *testing.T) { + metric := skogul.Metric{} + metric.Metadata = make(map[string]interface{}) + metric.Metadata["foofoo"] = "barBAR" + metric.Data = make(map[string]interface{}) + metric.Data["foo"] = "BAR" + metric.Data["baz"] = "foobar" + c := skogul.Container{} + c.Metrics = []*skogul.Metric{&metric} + + ban := transformer.BanField{ + SourceData: "foo", + RegexpData: "BAR", + SourceMetadata: "foofoo", + RegexpMetadata: "barBAR", + } + + t.Logf("Container before transform:\n%v", c) + err := ban.Transform(&c) + if err != nil { + t.Errorf("ban_field returned non-nil err: %v", err) + } + + t.Logf("Container after transform:\n%v", c) + + if _, ok := c.Metrics[0].Metadata["foofoo"]; ok { + t.Fatal("ban_field transformer failed to ban key-value pair") + } + if _, ok := c.Metrics[0].Data["foo"]; ok { + t.Fatal("ban_field transformer failed to ban key-value pair") + } +}