diff --git a/api/v1beta1/alert_types.go b/api/v1beta1/alert_types.go index 58d2e1bc3..3adf54d42 100644 --- a/api/v1beta1/alert_types.go +++ b/api/v1beta1/alert_types.go @@ -38,6 +38,10 @@ type AlertSpec struct { // +required EventSources []CrossNamespaceObjectReference `json:"eventSources"` + // Short description of the impact and affected cluster. + // +optional + Summary string `json:"summary,omitempty"` + // This flag tells the controller to suspend subsequent events dispatching. // Defaults to false. // +optional diff --git a/config/crd/bases/notification.toolkit.fluxcd.io_alerts.yaml b/config/crd/bases/notification.toolkit.fluxcd.io_alerts.yaml index d15a57eac..be432235a 100644 --- a/config/crd/bases/notification.toolkit.fluxcd.io_alerts.yaml +++ b/config/crd/bases/notification.toolkit.fluxcd.io_alerts.yaml @@ -96,6 +96,9 @@ spec: TODO: Add other useful fields. apiVersion, kind, uid?' type: string type: object + summary: + description: Short description of the impact and affected cluster. + type: string suspend: description: This flag tells the controller to suspend subsequent events dispatching. Defaults to false. diff --git a/docs/api/notification.md b/docs/api/notification.md index 11348f5ba..8a0f45271 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -113,6 +113,18 @@ If set to ‘info’ no events will be filtered.

+summary
+ +string + + + +(Optional) +

Short description of the impact and affected cluster.

+ + + + suspend
bool @@ -491,6 +503,18 @@ If set to ‘info’ no events will be filtered.

+summary
+ +string + + + +(Optional) +

Short description of the impact and affected cluster.

+ + + + suspend
bool diff --git a/docs/spec/v1beta1/alert.md b/docs/spec/v1beta1/alert.md index cbc3f6cd9..8c4024655 100644 --- a/docs/spec/v1beta1/alert.md +++ b/docs/spec/v1beta1/alert.md @@ -21,6 +21,10 @@ type AlertSpec struct { // +required EventSources []CrossNamespaceObjectReference `json:"eventSources"` + // Short description of the impact and affected cluster. + // +optional + Summary string `json:"summary,omitempty"` + // This flag tells the controller to suspend subsequent events dispatching. // Defaults to false. // +optional @@ -93,3 +97,21 @@ spec: ``` If you don't specify an event source namespace, the alert namespace will be used. + +You can add a summary to describe the impact of an event: + +```yaml +apiVersion: notification.toolkit.fluxcd.io/v1beta1 +kind: Alert +metadata: + name: ingress + namespace: nginx +spec: + summary: "Ingress traffic affected in production (us-west-2)" + providerRef: + name: on-call-slack + eventSeverity: error + eventSources: + - kind: HelmRelease + name: nginx-ingress +``` diff --git a/internal/server/event_handlers.go b/internal/server/event_handlers.go index 77dc62ba8..03a01c73a 100644 --- a/internal/server/event_handlers.go +++ b/internal/server/event_handlers.go @@ -88,8 +88,6 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request) } } - // find providers - alertProviders := make([]notifier.Interface, 0) if len(alerts) == 0 { s.logger.Info("Discarding event, no alerts found for the involved object", "object", event.InvolvedObject.Namespace+"/"+event.InvolvedObject.Name, @@ -98,7 +96,12 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request) return } - // find providers + s.logger.Info("Dispatching event", + "object", event.InvolvedObject.Namespace+"/"+event.InvolvedObject.Name, + "kind", event.InvolvedObject.Kind, + "message", event.Message) + + // dispatch notifications for _, alert := range alerts { var provider v1beta1.Provider providerName := types.NamespacedName{Namespace: alert.Namespace, Name: alert.Spec.ProviderRef.Name} @@ -107,8 +110,7 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request) if err != nil { s.logger.Error(err, "failed to read provider", "provider", providerName) - w.WriteHeader(http.StatusBadRequest) - return + continue } webhook := provider.Spec.Address @@ -122,8 +124,7 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request) s.logger.Error(err, "failed to read secret", "provider", providerName, "secret", secretName.Name) - w.WriteHeader(http.StatusBadRequest) - return + continue } if address, ok := secret.Data["address"]; ok { @@ -138,8 +139,7 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request) if webhook == "" { s.logger.Error(nil, "provider has no address", "provider", providerName) - w.WriteHeader(http.StatusBadRequest) - return + continue } factory := notifier.NewFactory(webhook, provider.Spec.Proxy, provider.Spec.Username, provider.Spec.Channel, token) @@ -148,27 +148,27 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request) s.logger.Error(err, "failed to initialise provider", "provider", providerName, "type", provider.Spec.Type) - w.WriteHeader(http.StatusBadRequest) - return + continue } - alertProviders = append(alertProviders, sender) - } - - s.logger.Info("Dispatching event", - "object", event.InvolvedObject.Namespace+"/"+event.InvolvedObject.Name, - "kind", event.InvolvedObject.Kind, - "message", event.Message) + notification := *event + if alert.Spec.Summary != "" { + if notification.Metadata == nil { + notification.Metadata = map[string]string{ + "summary": alert.Spec.Summary, + } + } else { + notification.Metadata["summary"] = alert.Spec.Summary + } + } - // send notifications in the background - for _, provider := range alertProviders { - go func(p notifier.Interface, e recorder.Event) { - if err := p.Post(e); err != nil { + go func(n notifier.Interface, e recorder.Event) { + if err := n.Post(e); err != nil { s.logger.Error(err, "failed to send notification", "object", e.InvolvedObject.Namespace+"/"+e.InvolvedObject.Name, - "kind", event.InvolvedObject.Kind) + "kind", e.InvolvedObject.Kind) } - }(provider, *event) + }(sender, notification) } w.WriteHeader(http.StatusAccepted)