Add InclusionList to Alert CRD

Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
Co-authored-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Matheus Pimenta 2023-04-29 17:05:56 +01:00
parent 18c49ad361
commit 9b7a80942f
7 changed files with 167 additions and 4 deletions

View File

@ -45,6 +45,11 @@ type AlertSpec struct {
// +required
EventSources []v1.CrossNamespaceObjectReference `json:"eventSources"`
// InclusionList specifies a list of Golang regular expressions
// to be used for including messages.
// +optional
InclusionList []string `json:"inclusionList,omitempty"`
// ExclusionList specifies a list of Golang regular expressions
// to be used for excluding messages.
// +optional

View File

@ -98,6 +98,11 @@ func (in *AlertSpec) DeepCopyInto(out *AlertSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.InclusionList != nil {
in, out := &in.InclusionList, &out.InclusionList
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ExclusionList != nil {
in, out := &in.ExclusionList, &out.ExclusionList
*out = make([]string, len(*in))

View File

@ -303,6 +303,12 @@ spec:
items:
type: string
type: array
inclusionList:
description: InclusionList specifies a list of Golang regular expressions
to be used for including messages.
items:
type: string
type: array
providerRef:
description: ProviderRef specifies which Provider this Alert should
use.

View File

@ -114,6 +114,19 @@ on the involved object kind, name and namespace.</p>
</tr>
<tr>
<td>
<code>inclusionList</code><br>
<em>
[]string
</em>
</td>
<td>
<em>(Optional)</em>
<p>InclusionList specifies a list of Golang regular expressions
to be used for including messages.</p>
</td>
</tr>
<tr>
<td>
<code>exclusionList</code><br>
<em>
[]string
@ -589,6 +602,19 @@ on the involved object kind, name and namespace.</p>
</tr>
<tr>
<td>
<code>inclusionList</code><br>
<em>
[]string
</em>
</td>
<td>
<em>(Optional)</em>
<p>InclusionList specifies a list of Golang regular expressions
to be used for including messages.</p>
</td>
</tr>
<tr>
<td>
<code>exclusionList</code><br>
<em>
[]string

View File

@ -171,7 +171,8 @@ To receive alerts only on errors, set the field value to `error`.
### Event exclusion
`.spec.exclusionList` is an optional field to specify a list of regex expressions to filter
events based on message content.
events based on message content. The event will be excluded if the message matches at least
one of the expressions in the list.
#### Example
@ -198,6 +199,39 @@ The above definition will not send alerts for transient Git clone errors like:
unable to clone 'ssh://git@ssh.dev.azure.com/v3/...', error: SSH could not read data: Error waiting on socket
```
### Event inclusion
`.spec.inclusionList` is an optional field to specify a list of regex expressions to filter
events based on message content. The event will be sent if the message matches at least one
of the expressions in the list, and discarded otherwise. If the message matches one of the
expressions in the inclusion list but also matches one of the expressions in the exclusion
list, then the event is still discarded (exclusion is stronger than inclusion).
#### Example
Alert if the message matches a [Go regex](https://golang.org/pkg/regexp/syntax)
from the inclusion list:
```yaml
---
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Alert
metadata:
name: <name>
spec:
eventSources:
- kind: HelmRelease
name: '*'
inclusionList:
- ".*succeeded.*"
exclusionList:
- ".*uninstall.*"
- ".*test.*"
```
The above definition will send alerts for successful Helm installs, upgrades and rollbacks,
but not uninstalls and tests.
### Suspend
`.spec.suspend` is an optional field to suspend the altering.

View File

@ -274,6 +274,8 @@ func TestAlertReconciler_EventHandler(t *testing.T) {
},
},
}
inclusionAlert := alert.DeepCopy()
inclusionAlert.Spec.InclusionList = []string{"^included"}
g.Expect(k8sClient.Create(context.Background(), alert)).To(Succeed())
@ -424,4 +426,71 @@ func TestAlertReconciler_EventHandler(t *testing.T) {
req = nil
})
}
// update alert for testing inclusion list
var obj apiv1beta2.Alert
g.Expect(k8sClient.Get(context.Background(), client.ObjectKeyFromObject(alert), &obj)).To(Succeed())
inclusionAlert.ResourceVersion = obj.ResourceVersion
g.Expect(k8sClient.Update(context.Background(), inclusionAlert)).To(Succeed())
// wait for ready
g.Eventually(func() bool {
var obj apiv1beta2.Alert
g.Expect(k8sClient.Get(context.Background(), client.ObjectKeyFromObject(inclusionAlert), &obj))
return conditions.IsReady(&obj)
}, 30*time.Second, time.Second).Should(BeTrue())
event = eventv1.Event{
InvolvedObject: corev1.ObjectReference{
Kind: "Bucket",
Name: "hyacinth",
Namespace: namespace,
},
Severity: "info",
Timestamp: metav1.Now(),
Message: "included",
Reason: "event-happened",
ReportingController: "source-controller",
}
tests = []struct {
name string
modifyEventFunc func(e eventv1.Event) eventv1.Event
forwarded bool
}{
{
name: "forwards when message matches inclusion list",
modifyEventFunc: func(e eventv1.Event) eventv1.Event { return e },
forwarded: true,
},
{
name: "drops when message does not match inclusion list",
modifyEventFunc: func(e eventv1.Event) eventv1.Event {
e.Message = "not included"
return e
},
forwarded: false,
},
{
name: "drops when message matches inclusion list and exclusion list",
modifyEventFunc: func(e eventv1.Event) eventv1.Event {
e.Message = "included excluded"
return e
},
forwarded: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event = tt.modifyEventFunc(event)
testSent()
if tt.forwarded {
testForwarded()
} else {
testFiltered()
}
req = nil
})
}
}

View File

@ -85,15 +85,33 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request)
continue each_alert
}
// skip alert if the message does not match any regex from the inclusion list
if len(alert.Spec.InclusionList) > 0 {
var include bool
for _, inclusionRegex := range alert.Spec.InclusionList {
if r, err := regexp.Compile(inclusionRegex); err == nil {
if r.Match([]byte(event.Message)) {
include = true
break
}
} else {
s.logger.Error(err, fmt.Sprintf("failed to compile inclusion regex: %s", inclusionRegex))
}
}
if !include {
continue each_alert
}
}
// skip alert if the message matches a regex from the exclusion list
if len(alert.Spec.ExclusionList) > 0 {
for _, exp := range alert.Spec.ExclusionList {
if r, err := regexp.Compile(exp); err == nil {
for _, exclusionRegex := range alert.Spec.ExclusionList {
if r, err := regexp.Compile(exclusionRegex); err == nil {
if r.Match([]byte(event.Message)) {
continue each_alert
}
} else {
s.logger.Error(err, fmt.Sprintf("failed to compile regex: %s", exp))
s.logger.Error(err, fmt.Sprintf("failed to compile exclusion regex: %s", exclusionRegex))
}
}
}