Skip to content

Commit 3139b30

Browse files
authored
Add new filters allowing FLP-based dedup (#640)
* Add new filters allowing FLP-based dedup - New "remove_entry_all_satisfied" filter type: entry is removed only if all the conditions (represented by nested rules) are satisfied. This allows to have logical AND in filter conditions, whereas previously it was only possible to have logical OR - New "conditional_sampling" filter type: allows to have random sampling based on conditions. For example, a flow matching conditions A and B may have a sampling ratio of 1:10 whereas a flow matching condition C has 1:100 sampling and all other flows are 1:1 - Introduced a "preprocess" function on rules; currently it's only used to be able to cast the `Value interface{}` as an int (otherwise it comes as a float64); but could be also used in the future for other purpose, e.g. regex pre-compiling - Add tests * Fix tests
1 parent 14e9b59 commit 3139b30

File tree

6 files changed

+344
-112
lines changed

6 files changed

+344
-112
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ pipeline:
376376
filter:
377377
rules:
378378
- type: remove_entry_if_exists
379-
removeEntryIfExists:
379+
removeEntry:
380380
input: SrcPort
381381
```
382382

docs/api.md

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,33 +159,40 @@ Following is the supported API format for filter transformations:
159159
remove_entry_if_doesnt_exist: removes the entry if the field does not exist
160160
remove_entry_if_equal: removes the entry if the field value equals specified value
161161
remove_entry_if_not_equal: removes the entry if the field value does not equal specified value
162+
remove_entry_all_satisfied: removes the entry if all of the defined rules are satisfied
162163
add_field: adds (input) field to the entry; overrides previous value if present (key=input, value=value)
163164
add_field_if_doesnt_exist: adds a field to the entry if the field does not exist
164165
add_field_if: add output field set to assignee if input field satisfies criteria from parameters field
165166
add_regex_if: add output field if input field satisfies regex pattern from parameters field
166167
add_label: add (input) field to list of labels with value taken from Value field (key=input, value=value)
167168
add_label_if: add output field to list of labels with value taken from assignee field if input field satisfies criteria from parameters field
169+
conditional_sampling: define conditional sampling rules
168170
removeField: configuration for remove_field rule
169171
input: entry input field
170172
value: specified value of input field:
171-
removeEntryIfExists: configuration for remove_entry_if_exists rule
172-
input: entry input field
173-
value: specified value of input field:
174-
removeEntryIfDoesntExist: configuration for remove_entry_if_doesnt_exist rule
175-
input: entry input field
176-
value: specified value of input field:
177-
removeEntryIfEqual: configuration for remove_entry_if_equal rule
178-
input: entry input field
179-
value: specified value of input field:
180-
removeEntryIfNotEqual: configuration for remove_entry_if_not_equal rule
173+
castInt: set true to cast the value field as an int (numeric values are float64 otherwise)
174+
removeEntry: configuration for remove_entry_* rules
181175
input: entry input field
182176
value: specified value of input field:
177+
castInt: set true to cast the value field as an int (numeric values are float64 otherwise)
178+
removeEntryAllSatisfied: configuration for remove_entry_all_satisfied rule
179+
type: (enum) one of the following:
180+
remove_entry_if_exists: removes the entry if the field exists
181+
remove_entry_if_doesnt_exist: removes the entry if the field does not exist
182+
remove_entry_if_equal: removes the entry if the field value equals specified value
183+
remove_entry_if_not_equal: removes the entry if the field value does not equal specified value
184+
removeEntry: configuration for remove_entry_* rules
185+
input: entry input field
186+
value: specified value of input field:
187+
castInt: set true to cast the value field as an int (numeric values are float64 otherwise)
183188
addField: configuration for add_field rule
184189
input: entry input field
185190
value: specified value of input field:
191+
castInt: set true to cast the value field as an int (numeric values are float64 otherwise)
186192
addFieldIfDoesntExist: configuration for add_field_if_doesnt_exist rule
187193
input: entry input field
188194
value: specified value of input field:
195+
castInt: set true to cast the value field as an int (numeric values are float64 otherwise)
189196
addFieldIf: configuration for add_field_if rule
190197
input: entry input field
191198
output: entry output field
@@ -199,11 +206,24 @@ Following is the supported API format for filter transformations:
199206
addLabel: configuration for add_label rule
200207
input: entry input field
201208
value: specified value of input field:
209+
castInt: set true to cast the value field as an int (numeric values are float64 otherwise)
202210
addLabelIf: configuration for add_label_if rule
203211
input: entry input field
204212
output: entry output field
205213
parameters: parameters specific to type
206214
assignee: value needs to assign to output field
215+
conditionalSampling: sampling configuration rules
216+
value: sampling value: 1 flow on <sampling> is kept
217+
rules: rules to be satisfied for this sampling configuration
218+
type: (enum) one of the following:
219+
remove_entry_if_exists: removes the entry if the field exists
220+
remove_entry_if_doesnt_exist: removes the entry if the field does not exist
221+
remove_entry_if_equal: removes the entry if the field value equals specified value
222+
remove_entry_if_not_equal: removes the entry if the field value does not equal specified value
223+
removeEntry: configuration for remove_entry_* rules
224+
input: entry input field
225+
value: specified value of input field:
226+
castInt: set true to cast the value field as an int (numeric values are float64 otherwise)
207227
</pre>
208228
## Transform Network API
209229
Following is the supported API format for network transformations:

pkg/api/transform_filter.go

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ type TransformFilter struct {
2121
Rules []TransformFilterRule `yaml:"rules,omitempty" json:"rules,omitempty" doc:"list of filter rules, each includes:"`
2222
}
2323

24+
func (tf *TransformFilter) Preprocess() {
25+
for i := range tf.Rules {
26+
tf.Rules[i].preprocess()
27+
}
28+
}
29+
2430
type TransformFilterEnum string
2531

2632
const (
@@ -30,32 +36,66 @@ const (
3036
RemoveEntryIfDoesntExist TransformFilterEnum = "remove_entry_if_doesnt_exist" // removes the entry if the field does not exist
3137
RemoveEntryIfEqual TransformFilterEnum = "remove_entry_if_equal" // removes the entry if the field value equals specified value
3238
RemoveEntryIfNotEqual TransformFilterEnum = "remove_entry_if_not_equal" // removes the entry if the field value does not equal specified value
39+
RemoveEntryAllSatisfied TransformFilterEnum = "remove_entry_all_satisfied" // removes the entry if all of the defined rules are satisfied
3340
AddField TransformFilterEnum = "add_field" // adds (input) field to the entry; overrides previous value if present (key=input, value=value)
3441
AddFieldIfDoesntExist TransformFilterEnum = "add_field_if_doesnt_exist" // adds a field to the entry if the field does not exist
3542
AddFieldIf TransformFilterEnum = "add_field_if" // add output field set to assignee if input field satisfies criteria from parameters field
3643
AddRegExIf TransformFilterEnum = "add_regex_if" // add output field if input field satisfies regex pattern from parameters field
3744
AddLabel TransformFilterEnum = "add_label" // add (input) field to list of labels with value taken from Value field (key=input, value=value)
3845
AddLabelIf TransformFilterEnum = "add_label_if" // add output field to list of labels with value taken from assignee field if input field satisfies criteria from parameters field
46+
ConditionalSampling TransformFilterEnum = "conditional_sampling" // define conditional sampling rules
47+
)
48+
49+
type TransformFilterRemoveEntryEnum string
50+
51+
const (
52+
RemoveEntryIfExistsD TransformFilterRemoveEntryEnum = "remove_entry_if_exists" // removes the entry if the field exists
53+
RemoveEntryIfDoesntExistD TransformFilterRemoveEntryEnum = "remove_entry_if_doesnt_exist" // removes the entry if the field does not exist
54+
RemoveEntryIfEqualD TransformFilterRemoveEntryEnum = "remove_entry_if_equal" // removes the entry if the field value equals specified value
55+
RemoveEntryIfNotEqualD TransformFilterRemoveEntryEnum = "remove_entry_if_not_equal" // removes the entry if the field value does not equal specified value
3956
)
4057

4158
type TransformFilterRule struct {
42-
Type TransformFilterEnum `yaml:"type,omitempty" json:"type,omitempty" doc:"(enum) one of the following:"`
43-
RemoveField *TransformFilterGenericRule `yaml:"removeField,omitempty" json:"removeField,omitempty" doc:"configuration for remove_field rule"`
44-
RemoveEntryIfExists *TransformFilterGenericRule `yaml:"removeEntryIfExists,omitempty" json:"removeEntryIfExists,omitempty" doc:"configuration for remove_entry_if_exists rule"`
45-
RemoveEntryIfDoesntExist *TransformFilterGenericRule `yaml:"removeEntryIfDoesntExist,omitempty" json:"removeEntryIfDoesntExist,omitempty" doc:"configuration for remove_entry_if_doesnt_exist rule"`
46-
RemoveEntryIfEqual *TransformFilterGenericRule `yaml:"removeEntryIfEqual,omitempty" json:"removeEntryIfEqual,omitempty" doc:"configuration for remove_entry_if_equal rule"`
47-
RemoveEntryIfNotEqual *TransformFilterGenericRule `yaml:"removeEntryIfNotEqual,omitempty" json:"removeEntryIfNotEqual,omitempty" doc:"configuration for remove_entry_if_not_equal rule"`
48-
AddField *TransformFilterGenericRule `yaml:"addField,omitempty" json:"addField,omitempty" doc:"configuration for add_field rule"`
49-
AddFieldIfDoesntExist *TransformFilterGenericRule `yaml:"addFieldIfDoesntExist,omitempty" json:"addFieldIfDoesntExist,omitempty" doc:"configuration for add_field_if_doesnt_exist rule"`
50-
AddFieldIf *TransformFilterRuleWithAssignee `yaml:"addFieldIf,omitempty" json:"addFieldIf,omitempty" doc:"configuration for add_field_if rule"`
51-
AddRegExIf *TransformFilterRuleWithAssignee `yaml:"addRegexIf,omitempty" json:"addRegexIf,omitempty" doc:"configuration for add_regex_if rule"`
52-
AddLabel *TransformFilterGenericRule `yaml:"addLabel,omitempty" json:"addLabel,omitempty" doc:"configuration for add_label rule"`
53-
AddLabelIf *TransformFilterRuleWithAssignee `yaml:"addLabelIf,omitempty" json:"addLabelIf,omitempty" doc:"configuration for add_label_if rule"`
59+
Type TransformFilterEnum `yaml:"type,omitempty" json:"type,omitempty" doc:"(enum) one of the following:"`
60+
RemoveField *TransformFilterGenericRule `yaml:"removeField,omitempty" json:"removeField,omitempty" doc:"configuration for remove_field rule"`
61+
RemoveEntry *TransformFilterGenericRule `yaml:"removeEntry,omitempty" json:"removeEntry,omitempty" doc:"configuration for remove_entry_* rules"`
62+
RemoveEntryAllSatisfied []*RemoveEntryRule `yaml:"removeEntryAllSatisfied,omitempty" json:"removeEntryAllSatisfied,omitempty" doc:"configuration for remove_entry_all_satisfied rule"`
63+
AddField *TransformFilterGenericRule `yaml:"addField,omitempty" json:"addField,omitempty" doc:"configuration for add_field rule"`
64+
AddFieldIfDoesntExist *TransformFilterGenericRule `yaml:"addFieldIfDoesntExist,omitempty" json:"addFieldIfDoesntExist,omitempty" doc:"configuration for add_field_if_doesnt_exist rule"`
65+
AddFieldIf *TransformFilterRuleWithAssignee `yaml:"addFieldIf,omitempty" json:"addFieldIf,omitempty" doc:"configuration for add_field_if rule"`
66+
AddRegExIf *TransformFilterRuleWithAssignee `yaml:"addRegexIf,omitempty" json:"addRegexIf,omitempty" doc:"configuration for add_regex_if rule"`
67+
AddLabel *TransformFilterGenericRule `yaml:"addLabel,omitempty" json:"addLabel,omitempty" doc:"configuration for add_label rule"`
68+
AddLabelIf *TransformFilterRuleWithAssignee `yaml:"addLabelIf,omitempty" json:"addLabelIf,omitempty" doc:"configuration for add_label_if rule"`
69+
ConditionalSampling []*SamplingCondition `yaml:"conditionalSampling,omitempty" json:"conditionalSampling,omitempty" doc:"sampling configuration rules"`
70+
}
71+
72+
func (r *TransformFilterRule) preprocess() {
73+
if r.RemoveField != nil {
74+
r.RemoveField.preprocess()
75+
}
76+
if r.RemoveEntry != nil {
77+
r.RemoveEntry.preprocess()
78+
}
79+
for i := range r.RemoveEntryAllSatisfied {
80+
r.RemoveEntryAllSatisfied[i].RemoveEntry.preprocess()
81+
}
82+
for i := range r.ConditionalSampling {
83+
r.ConditionalSampling[i].preprocess()
84+
}
5485
}
5586

5687
type TransformFilterGenericRule struct {
57-
Input string `yaml:"input,omitempty" json:"input,omitempty" doc:"entry input field"`
58-
Value interface{} `yaml:"value,omitempty" json:"value,omitempty" doc:"specified value of input field:"`
88+
Input string `yaml:"input,omitempty" json:"input,omitempty" doc:"entry input field"`
89+
Value interface{} `yaml:"value,omitempty" json:"value,omitempty" doc:"specified value of input field:"`
90+
CastInt bool `yaml:"castInt,omitempty" json:"castInt,omitempty" doc:"set true to cast the value field as an int (numeric values are float64 otherwise)"`
91+
}
92+
93+
func (r *TransformFilterGenericRule) preprocess() {
94+
if r.CastInt {
95+
if f, ok := r.Value.(float64); ok {
96+
r.Value = int(f)
97+
}
98+
}
5999
}
60100

61101
type TransformFilterRuleWithAssignee struct {
@@ -64,3 +104,19 @@ type TransformFilterRuleWithAssignee struct {
64104
Parameters string `yaml:"parameters,omitempty" json:"parameters,omitempty" doc:"parameters specific to type"`
65105
Assignee string `yaml:"assignee,omitempty" json:"assignee,omitempty" doc:"value needs to assign to output field"`
66106
}
107+
108+
type RemoveEntryRule struct {
109+
Type TransformFilterRemoveEntryEnum `yaml:"type,omitempty" json:"type,omitempty" doc:"(enum) one of the following:"`
110+
RemoveEntry *TransformFilterGenericRule `yaml:"removeEntry,omitempty" json:"removeEntry,omitempty" doc:"configuration for remove_entry_* rules"`
111+
}
112+
113+
type SamplingCondition struct {
114+
Value uint16 `yaml:"value,omitempty" json:"value,omitempty" doc:"sampling value: 1 flow on <sampling> is kept"`
115+
Rules []*RemoveEntryRule `yaml:"rules,omitempty" json:"rules,omitempty" doc:"rules to be satisfied for this sampling configuration"`
116+
}
117+
118+
func (s *SamplingCondition) preprocess() {
119+
for i := range s.Rules {
120+
s.Rules[i].RemoveEntry.preprocess()
121+
}
122+
}

pkg/config/pipeline_builder_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ func TestGRPCPipeline(t *testing.T) {
6969
pl := NewGRPCPipeline("grpc", api.IngestGRPCProto{Port: 9050, BufferLen: 50})
7070
pl = pl.TransformFilter("filter", api.TransformFilter{
7171
Rules: []api.TransformFilterRule{{
72-
Type: "remove_entry_if_doesnt_exist",
73-
RemoveEntryIfDoesntExist: &api.TransformFilterGenericRule{Input: "doesnt_exist"},
72+
Type: "remove_entry_if_doesnt_exist",
73+
RemoveEntry: &api.TransformFilterGenericRule{Input: "doesnt_exist"},
7474
}},
7575
})
7676
pl = pl.WriteStdout("stdout", api.WriteStdout{Format: "json"})
@@ -90,7 +90,7 @@ func TestGRPCPipeline(t *testing.T) {
9090

9191
b, err = json.Marshal(params[1])
9292
require.NoError(t, err)
93-
require.JSONEq(t, `{"name":"filter","transform":{"type":"filter","filter":{"rules":[{"removeEntryIfDoesntExist":{"input":"doesnt_exist"},"type":"remove_entry_if_doesnt_exist"}]}}}`, string(b))
93+
require.JSONEq(t, `{"name":"filter","transform":{"type":"filter","filter":{"rules":[{"removeEntry":{"input":"doesnt_exist"},"type":"remove_entry_if_doesnt_exist"}]}}}`, string(b))
9494

9595
b, err = json.Marshal(params[2])
9696
require.NoError(t, err)
@@ -110,8 +110,8 @@ func TestKafkaPromPipeline(t *testing.T) {
110110
})
111111
pl = pl.TransformFilter("filter", api.TransformFilter{
112112
Rules: []api.TransformFilterRule{{
113-
Type: "remove_entry_if_doesnt_exist",
114-
RemoveEntryIfDoesntExist: &api.TransformFilterGenericRule{Input: "doesnt_exist"},
113+
Type: "remove_entry_if_doesnt_exist",
114+
RemoveEntry: &api.TransformFilterGenericRule{Input: "doesnt_exist"},
115115
}},
116116
})
117117
pl = pl.ConnTrack("conntrack", api.ConnTrack{
@@ -158,7 +158,7 @@ func TestKafkaPromPipeline(t *testing.T) {
158158

159159
b, err = json.Marshal(params[1])
160160
require.NoError(t, err)
161-
require.JSONEq(t, `{"name":"filter","transform":{"type":"filter","filter":{"rules":[{"removeEntryIfDoesntExist":{"input":"doesnt_exist"},"type":"remove_entry_if_doesnt_exist"}]}}}`, string(b))
161+
require.JSONEq(t, `{"name":"filter","transform":{"type":"filter","filter":{"rules":[{"removeEntry":{"input":"doesnt_exist"},"type":"remove_entry_if_doesnt_exist"}]}}}`, string(b))
162162

163163
b, err = json.Marshal(params[2])
164164
require.NoError(t, err)

0 commit comments

Comments
 (0)