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

[internal/coreinternal] Supports pattern for delete and hash actions on attraction #5750

Closed
Closed
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
61 changes: 52 additions & 9 deletions internal/coreinternal/attraction/attraction.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,12 @@ const (
UPSERT Action = "upsert"

// DELETE deletes the attribute. If the key doesn't exist, no action is performed.
// Supports pattern which is matched against attribute key.
DELETE Action = "delete"

// HASH calculates the SHA-1 hash of an existing value and overwrites the
// value with it's SHA-1 hash result.
// Supports pattern which is matched against attribute key.
HASH Action = "hash"

// EXTRACT extracts values using a regular expression rule from the input
Expand Down Expand Up @@ -140,13 +142,22 @@ type AttrProc struct {
func NewAttrProc(settings *Settings) (*AttrProc, error) {
var attributeActions []attributeAction
for i, a := range settings.Actions {
// `key` is a required field
if a.Key == "" {
return nil, fmt.Errorf("error creating AttrProc due to missing required field \"key\" at the %d-th actions", i)
}

// Convert `action` to lowercase for comparison.
a.Action = Action(strings.ToLower(string(a.Action)))

switch a.Action {
case DELETE, HASH:
// requires `key` and/or `pattern`
if a.Key == "" && a.RegexPattern == "" {
return nil, fmt.Errorf("error creating AttrProc due to missing required field (at least one of \"key\" and \"pattern\" have to be used) at the %d-th actions", i)
}
default:
// `key` is a required field
if a.Key == "" {
return nil, fmt.Errorf("error creating AttrProc due to missing required field \"key\" at the %d-th actions", i)
}
}

action := attributeAction{
Key: a.Key,
Action: a.Action,
Expand Down Expand Up @@ -176,9 +187,17 @@ func NewAttrProc(settings *Settings) (*AttrProc, error) {
action.FromAttribute = a.FromAttribute
}
case HASH, DELETE:
if a.Value != nil || a.FromAttribute != "" || a.RegexPattern != "" {
if a.Value != nil || a.FromAttribute != "" {
return nil, fmt.Errorf("error creating AttrProc. Action \"%s\" does not use \"value\", \"pattern\" or \"from_attribute\" field. These must not be specified for %d-th action", a.Action, i)
}

if a.RegexPattern != "" {
re, err := regexp.Compile(a.RegexPattern)
if err != nil {
return nil, fmt.Errorf("error creating AttrProc. Field \"pattern\" has invalid pattern: \"%s\" to be set at the %d-th actions", a.RegexPattern, i)
}
action.Regex = re
}
case EXTRACT:
if a.Value != nil || a.FromAttribute != "" {
return nil, fmt.Errorf("error creating AttrProc. Action \"%s\" does not use \"value\" or \"from_attribute\" field. These must not be specified for %d-th action", a.Action, i)
Expand Down Expand Up @@ -222,6 +241,10 @@ func (ap *AttrProc) Process(attrs pdata.AttributeMap) {
switch action.Action {
case DELETE:
attrs.Delete(action.Key)

for _, k := range getMatchingKeys(action.Regex, attrs) {
attrs.Delete(k)
}
case INSERT:
av, found := getSourceAttributeValue(action, attrs)
if !found {
Expand All @@ -241,7 +264,11 @@ func (ap *AttrProc) Process(attrs pdata.AttributeMap) {
}
attrs.Upsert(action.Key, av)
case HASH:
hashAttribute(action, attrs)
hashAttribute(action.Key, attrs)

for _, k := range getMatchingKeys(action.Regex, attrs) {
hashAttribute(k, attrs)
}
case EXTRACT:
extractAttributes(action, attrs)
}
Expand All @@ -257,8 +284,8 @@ func getSourceAttributeValue(action attributeAction, attrs pdata.AttributeMap) (
return attrs.Get(action.FromAttribute)
}

func hashAttribute(action attributeAction, attrs pdata.AttributeMap) {
if value, exists := attrs.Get(action.Key); exists {
func hashAttribute(key string, attrs pdata.AttributeMap) {
if value, exists := attrs.Get(key); exists {
sha1Hasher(value)
}
}
Expand All @@ -284,3 +311,19 @@ func extractAttributes(action attributeAction, attrs pdata.AttributeMap) {
attrs.UpsertString(action.AttrNames[i], matches[i])
}
}

func getMatchingKeys(regexp *regexp.Regexp, attrs pdata.AttributeMap) []string {
keys := []string{}

if regexp == nil {
return keys
}

attrs.Range(func(k string, v pdata.AttributeValue) bool {
if regexp.MatchString(k) {
keys = append(keys, k)
}
return true
})
return keys
}
84 changes: 75 additions & 9 deletions internal/coreinternal/attraction/attraction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,67 @@ func TestAttributes_UpsertFromAttribute(t *testing.T) {
}

func TestAttributes_Delete(t *testing.T) {
testCases := []testCase{
// Ensure the span contains no changes.
{
name: "DeleteEmptyAttributes",
inputAttributes: map[string]pdata.AttributeValue{},
expectedAttributes: map[string]pdata.AttributeValue{},
},
// Ensure the span contains no changes because the key doesn't exist.
{
name: "DeleteAttributeNoExist",
inputAttributes: map[string]pdata.AttributeValue{
"boo": pdata.NewAttributeValueString("ghosts are scary"),
},
expectedAttributes: map[string]pdata.AttributeValue{
"boo": pdata.NewAttributeValueString("ghosts are scary"),
},
},
// Ensure `duplicate_key` is deleted for spans with the attribute set.
{
name: "DeleteAttributeExists",
inputAttributes: map[string]pdata.AttributeValue{
"duplicate_key": pdata.NewAttributeValueDouble(3245.6),
"original_key": pdata.NewAttributeValueDouble(3245.6),
},
expectedAttributes: map[string]pdata.AttributeValue{
"original_key": pdata.NewAttributeValueDouble(3245.6),
},
},
// Ensure `duplicate_key` is deleted by regexp for spans with the attribute set.
{
name: "DeleteAttributeExists",
inputAttributes: map[string]pdata.AttributeValue{
"duplicate_key_a": pdata.NewAttributeValueDouble(3245.6),
"duplicate_key_b": pdata.NewAttributeValueDouble(3245.6),
"duplicate_key_c": pdata.NewAttributeValueDouble(3245.6),
"original_key": pdata.NewAttributeValueDouble(3245.6),
"not_duplicate_key": pdata.NewAttributeValueDouble(3246.6),
},
expectedAttributes: map[string]pdata.AttributeValue{
"original_key": pdata.NewAttributeValueDouble(3245.6),
"not_duplicate_key": pdata.NewAttributeValueDouble(3246.6),
},
},
}

cfg := &Settings{
Actions: []ActionKeyValue{
{Key: "duplicate_key", RegexPattern: "^duplicate_key_.", Action: DELETE},
},
}

ap, err := NewAttrProc(cfg)
require.Nil(t, err)
require.NotNil(t, ap)

for _, tt := range testCases {
runIndividualTestCase(t, tt, ap)
}
}

func TestAttributes_Delete_Regexp(t *testing.T) {
testCases := []testCase{
// Ensure the span contains no changes.
{
Expand Down Expand Up @@ -521,7 +582,7 @@ func TestAttributes_Delete(t *testing.T) {

cfg := &Settings{
Actions: []ActionKeyValue{
{Key: "duplicate_key", Action: DELETE},
{RegexPattern: "duplicate.*", Action: DELETE},
},
}

Expand Down Expand Up @@ -611,11 +672,23 @@ func TestAttributes_HashValue(t *testing.T) {
"updateme": pdata.NewAttributeValueString(sha1Hash([]byte{0})),
},
},
// Ensure regex pattern is being used
{
name: "HashBoolFalse",
inputAttributes: map[string]pdata.AttributeValue{
"updatemebyregexp": pdata.NewAttributeValueBool(false),
"donotupdatemebyregexp": pdata.NewAttributeValueBool(false),
},
expectedAttributes: map[string]pdata.AttributeValue{
"updatemebyregexp": pdata.NewAttributeValueString(sha1Hash([]byte{0})),
"donotupdatemebyregexp": pdata.NewAttributeValueBool(false),
},
},
}

cfg := &Settings{
Actions: []ActionKeyValue{
{Key: "updateme", Action: HASH},
{Key: "updateme", RegexPattern: "^updatemeby.*", Action: HASH},
},
}

Expand Down Expand Up @@ -813,13 +886,6 @@ func TestInvalidConfig(t *testing.T) {
},
errorString: "error creating AttrProc. Field \"pattern\" has invalid pattern: \"(?P<invalid.regex>.*?)$\" to be set at the 0-th actions",
},
{
name: "delete with regex",
actionLists: []ActionKeyValue{
{RegexPattern: "(?P<operation_website>.*?)$", Key: "ab", Action: DELETE},
},
errorString: "error creating AttrProc. Action \"delete\" does not use \"value\", \"pattern\" or \"from_attribute\" field. These must not be specified for 0-th action",
},
{
name: "regex with unnamed capture group",
actionLists: []ActionKeyValue{
Expand Down
12 changes: 10 additions & 2 deletions processor/attributesprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,26 @@ For the actions `insert`, `update` and `upsert`,
```

For the `delete` action,
- `key` is required
- `key` and/or `pattern` is required
- `action: delete` is required.
```yaml
# Key specifies the attribute to act upon.
- key: <key>
action: delete
# Rule specifies the regex pattern for attribute names to act upon.
pattern: <regular pattern>
```


For the `hash` action,
- `key` is required
- `key` and/or `pattern` is required
- `action: hash` is required.
```yaml
# Key specifies the attribute to act upon.
- key: <key>
action: hash
# Rule specifies the regex pattern for attribute names to act upon.
pattern: <regular pattern>
```


Expand Down Expand Up @@ -97,8 +101,12 @@ processors:
value: 2245
- key: account_password
action: delete
- pattern: ^k8s\..*
action: delete
- key: account_email
action: hash
- pattern: ^secret\..*
action: delete

```

Expand Down
2 changes: 2 additions & 0 deletions processor/resourceprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ processors:
action: insert
- key: redundant-attribute
action: delete
- pattern: ^k8s\.pod\..*
action: delete
```

Refer to [config.yaml](./testdata/config.yaml) for detailed
Expand Down