Skip to content

Commit

Permalink
[pkg/ottl] Changed replacement string to be a path expression to a st…
Browse files Browse the repository at this point in the history
…ring telemetry field or a literal string
  • Loading branch information
rnishtala-sumo committed Jun 8, 2023
1 parent 8dc41a8 commit 44ceeb8
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 48 deletions.
20 changes: 20 additions & 0 deletions .chloggen/ottl_function_changes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use this changelog template to create an entry for release notes.
# If your change doesn't affect end users, such as a test fix or a tooling change,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: pkg/ottl

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Changed replacement_pattern (replacement) string to be a path expression to a string telemetry field or a literal string

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [22787]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
4 changes: 2 additions & 2 deletions pkg/ottl/ottlfuncs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ If using OTTL outside of collector configuration, `$` should not be escaped and

The `replace_match` function allows replacing entire strings if they match a glob pattern.

`target` is a path expression to a telemetry field. `pattern` is a string following [filepath.Match syntax](https://pkg.go.dev/path/filepath#Match). `replacement` is a string.
`target` is a path expression to a telemetry field. `pattern` is a string following [filepath.Match syntax](https://pkg.go.dev/path/filepath#Match). `replacement` is either a path expression to a string telemetry field or a literal string.

If `target` matches `pattern` it will get replaced with `replacement`.

Expand All @@ -200,7 +200,7 @@ Examples:

The `replace_pattern` function allows replacing all string sections that match a regex pattern with a new value.

`target` is a path expression to a telemetry field. `regex` is a regex string indicating a segment to replace. `replacement` is a string.
`target` is a path expression to a telemetry field. `regex` is a regex string indicating a segment to replace. `replacement` is either a path expression to a string telemetry field or a literal string.

If one or more sections of `target` match `regex` they will get replaced with `replacement`.

Expand Down
14 changes: 9 additions & 5 deletions pkg/ottl/ottlfuncs/func_replace_match.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import (
)

type ReplaceMatchArguments[K any] struct {
Target ottl.GetSetter[K] `ottlarg:"0"`
Pattern string `ottlarg:"1"`
Replacement string `ottlarg:"2"`
Target ottl.GetSetter[K] `ottlarg:"0"`
Pattern string `ottlarg:"1"`
Replacement ottl.StringGetter[K] `ottlarg:"2"`
}

func NewReplaceMatchFactory[K any]() ottl.Factory[K] {
Expand All @@ -32,22 +32,26 @@ func createReplaceMatchFunction[K any](_ ottl.FunctionContext, oArgs ottl.Argume
return replaceMatch(args.Target, args.Pattern, args.Replacement)
}

func replaceMatch[K any](target ottl.GetSetter[K], pattern string, replacement string) (ottl.ExprFunc[K], error) {
func replaceMatch[K any](target ottl.GetSetter[K], pattern string, replacement ottl.StringGetter[K]) (ottl.ExprFunc[K], error) {
glob, err := glob.Compile(pattern)
if err != nil {
return nil, fmt.Errorf("the pattern supplied to replace_match is not a valid pattern: %w", err)
}
return func(ctx context.Context, tCtx K) (interface{}, error) {
val, err := target.Get(ctx, tCtx)
replacementVal, errRepl := replacement.Get(ctx, tCtx)
if err != nil {
return nil, err
}
if errRepl != nil {
return nil, errRepl
}
if val == nil {
return nil, nil
}
if valStr, ok := val.(string); ok {
if glob.Match(valStr) {
err = target.Set(ctx, tCtx, replacement)
err = target.Set(ctx, tCtx, replacementVal)
if err != nil {
return nil, err
}
Expand Down
40 changes: 29 additions & 11 deletions pkg/ottl/ottlfuncs/func_replace_match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,31 @@ func Test_replaceMatch(t *testing.T) {
name string
target ottl.GetSetter[pcommon.Value]
pattern string
replacement string
replacement ottl.StringGetter[pcommon.Value]
want func(pcommon.Value)
}{
{
name: "replace match",
target: target,
pattern: "hello*",
replacement: "hello {universe}",
name: "replace match",
target: target,
pattern: "hello*",
replacement: ottl.StandardStringGetter[pcommon.Value]{
Getter: func(context.Context, pcommon.Value) (interface{}, error) {
return "hello {universe}", nil
},
},
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("hello {universe}")
},
},
{
name: "no match",
target: target,
pattern: "goodbye*",
replacement: "goodbye {universe}",
name: "no match",
target: target,
pattern: "goodbye*",
replacement: ottl.StandardStringGetter[pcommon.Value]{
Getter: func(context.Context, pcommon.Value) (interface{}, error) {
return "goodbye {universe}", nil
},
},
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("hello world")
},
Expand Down Expand Up @@ -81,8 +89,13 @@ func Test_replaceMatch_bad_input(t *testing.T) {
return nil
},
}
replacement := &ottl.StandardStringGetter[interface{}]{
Getter: func(context.Context, interface{}) (interface{}, error) {
return "{replacement}", nil
},
}

exprFunc, err := replaceMatch[interface{}](target, "*", "{replacement}")
exprFunc, err := replaceMatch[interface{}](target, "*", replacement)
assert.NoError(t, err)

result, err := exprFunc(nil, input)
Expand All @@ -102,8 +115,13 @@ func Test_replaceMatch_get_nil(t *testing.T) {
return nil
},
}
replacement := &ottl.StandardStringGetter[interface{}]{
Getter: func(context.Context, interface{}) (interface{}, error) {
return "{anything}", nil
},
}

exprFunc, err := replaceMatch[interface{}](target, "*", "{anything}")
exprFunc, err := replaceMatch[interface{}](target, "*", replacement)
assert.NoError(t, err)

result, err := exprFunc(nil, nil)
Expand Down
14 changes: 9 additions & 5 deletions pkg/ottl/ottlfuncs/func_replace_pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
)

type ReplacePatternArguments[K any] struct {
Target ottl.GetSetter[K] `ottlarg:"0"`
RegexPattern string `ottlarg:"1"`
Replacement string `ottlarg:"2"`
Target ottl.GetSetter[K] `ottlarg:"0"`
RegexPattern string `ottlarg:"1"`
Replacement ottl.StringGetter[K] `ottlarg:"2"`
}

func NewReplacePatternFactory[K any]() ottl.Factory[K] {
Expand All @@ -31,22 +31,26 @@ func createReplacePatternFunction[K any](_ ottl.FunctionContext, oArgs ottl.Argu
return replacePattern(args.Target, args.RegexPattern, args.Replacement)
}

func replacePattern[K any](target ottl.GetSetter[K], regexPattern string, replacement string) (ottl.ExprFunc[K], error) {
func replacePattern[K any](target ottl.GetSetter[K], regexPattern string, replacement ottl.StringGetter[K]) (ottl.ExprFunc[K], error) {
compiledPattern, err := regexp.Compile(regexPattern)
if err != nil {
return nil, fmt.Errorf("the regex pattern supplied to replace_pattern is not a valid pattern: %w", err)
}
return func(ctx context.Context, tCtx K) (interface{}, error) {
originalVal, err := target.Get(ctx, tCtx)
replacementVal, errRepl := replacement.Get(ctx, tCtx)
if err != nil {
return nil, err
}
if errRepl != nil {
return nil, errRepl
}
if originalVal == nil {
return nil, nil
}
if originalValStr, ok := originalVal.(string); ok {
if compiledPattern.MatchString(originalValStr) {
updatedStr := compiledPattern.ReplaceAllString(originalValStr, replacement)
updatedStr := compiledPattern.ReplaceAllString(originalValStr, replacementVal)
err = target.Set(ctx, tCtx, updatedStr)
if err != nil {
return nil, err
Expand Down
84 changes: 59 additions & 25 deletions pkg/ottl/ottlfuncs/func_replace_pattern_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,50 +31,70 @@ func Test_replacePattern(t *testing.T) {
name string
target ottl.GetSetter[pcommon.Value]
pattern string
replacement string
replacement ottl.StringGetter[pcommon.Value]
want func(pcommon.Value)
}{
{
name: "replace regex match",
target: target,
pattern: `passwd\=[^\s]*(\s?)`,
replacement: "passwd=*** ",
name: "replace regex match",
target: target,
pattern: `passwd\=[^\s]*(\s?)`,
replacement: ottl.StandardStringGetter[pcommon.Value]{
Getter: func(context.Context, pcommon.Value) (interface{}, error) {
return "passwd=*** ", nil
},
},
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("application passwd=*** otherarg=notsensitive key1 key2")
},
},
{
name: "no regex match",
target: target,
pattern: `nomatch\=[^\s]*(\s?)`,
replacement: "shouldnotbeinoutput",
name: "no regex match",
target: target,
pattern: `nomatch\=[^\s]*(\s?)`,
replacement: ottl.StandardStringGetter[pcommon.Value]{
Getter: func(context.Context, pcommon.Value) (interface{}, error) {
return "shouldnotbeinoutput", nil
},
},
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("application passwd=sensitivedtata otherarg=notsensitive key1 key2")
},
},
{
name: "multiple regex match",
target: target,
pattern: `key[^\s]*(\s?)`,
replacement: "**** ",
name: "multiple regex match",
target: target,
pattern: `key[^\s]*(\s?)`,
replacement: ottl.StandardStringGetter[pcommon.Value]{
Getter: func(context.Context, pcommon.Value) (interface{}, error) {
return "**** ", nil
},
},
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("application passwd=sensitivedtata otherarg=notsensitive **** **** ")
},
},
{
name: "expand capturing groups",
target: target,
pattern: `(\w+)=(\w+)`,
replacement: "$1:$2",
name: "expand capturing groups",
target: target,
pattern: `(\w+)=(\w+)`,
replacement: ottl.StandardStringGetter[pcommon.Value]{
Getter: func(context.Context, pcommon.Value) (interface{}, error) {
return "$1:$2", nil
},
},
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("application passwd:sensitivedtata otherarg:notsensitive key1 key2")
},
},
{
name: "replacement with literal $",
target: target,
pattern: `passwd\=[^\s]*(\s?)`,
replacement: "passwd=$$$$$$ ",
name: "replacement with literal $",
target: target,
pattern: `passwd\=[^\s]*(\s?)`,
replacement: ottl.StandardStringGetter[pcommon.Value]{
Getter: func(context.Context, pcommon.Value) (interface{}, error) {
return "passwd=$$$$$$ ", nil
},
},
want: func(expectedValue pcommon.Value) {
expectedValue.SetStr("application passwd=$$$ otherarg=notsensitive key1 key2")
},
Expand All @@ -83,7 +103,6 @@ func Test_replacePattern(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
scenarioValue := pcommon.NewValueStr(input.Str())

exprFunc, err := replacePattern(tt.target, tt.pattern, tt.replacement)
assert.NoError(t, err)

Expand All @@ -110,8 +129,13 @@ func Test_replacePattern_bad_input(t *testing.T) {
return nil
},
}
replacement := &ottl.StandardStringGetter[interface{}]{
Getter: func(context.Context, interface{}) (interface{}, error) {
return "{replacement}", nil
},
}

exprFunc, err := replacePattern[interface{}](target, "regexp", "{replacement}")
exprFunc, err := replacePattern[interface{}](target, "regexp", replacement)
assert.NoError(t, err)

result, err := exprFunc(nil, input)
Expand All @@ -130,8 +154,13 @@ func Test_replacePattern_get_nil(t *testing.T) {
return nil
},
}
replacement := &ottl.StandardStringGetter[interface{}]{
Getter: func(context.Context, interface{}) (interface{}, error) {
return "{anything}", nil
},
}

exprFunc, err := replacePattern[interface{}](target, `nomatch\=[^\s]*(\s?)`, "{anything}")
exprFunc, err := replacePattern[interface{}](target, `nomatch\=[^\s]*(\s?)`, replacement)
assert.NoError(t, err)

result, err := exprFunc(nil, nil)
Expand All @@ -150,9 +179,14 @@ func Test_replacePatterns_invalid_pattern(t *testing.T) {
return nil
},
}
replacement := &ottl.StandardStringGetter[interface{}]{
Getter: func(context.Context, interface{}) (interface{}, error) {
return "{anything}", nil
},
}

invalidRegexPattern := "*"
_, err := replacePattern[interface{}](target, invalidRegexPattern, "{anything}")
_, err := replacePattern[interface{}](target, invalidRegexPattern, replacement)
require.Error(t, err)
assert.ErrorContains(t, err, "error parsing regexp:")
}

0 comments on commit 44ceeb8

Please sign in to comment.