From 2e50c63607aa847dad1f7273d346d8c5425c595f Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Mon, 31 Aug 2026 12:51:31 +0200 Subject: [PATCH] fix(events): include alert details in all notify events Notification events did not include alert details like fingerprint in all cases. This made it defficult to track an alert end to end when querying events produced by event recorder. This change: - adds firing, muted, resolved alerts to notify context - emits the alerts in all notify events' details - drops GroupedAlerts by embedding alerts directly This is follow up for #5409 Signed-off-by: Siavash Safi --- eventrecorder/events.go | 43 +-- eventrecorder/events/v2/events.pb.go | 355 +++++++++------------ eventrecorder/events_test.go | 9 +- notify/context.go | 15 +- notify/event.go | 58 ++-- notify/mute.go | 11 +- notify/mute_test.go | 23 ++ notify/notify_test.go | 138 +++++--- notify/retry_stage.go | 2 +- proto/eventrecorder/events/v2/events.proto | 18 +- 10 files changed, 328 insertions(+), 344 deletions(-) diff --git a/eventrecorder/events.go b/eventrecorder/events.go index 929cade8eb..3f96918aa4 100644 --- a/eventrecorder/events.go +++ b/eventrecorder/events.go @@ -89,11 +89,6 @@ type AlertGroup struct { message *events.AlertGroupInfo } -// GroupedAlert is an immutable grouped-alert snapshot. -type GroupedAlert struct { - message *events.GroupedAlert -} - // InhibitRule is an immutable inhibition-rule snapshot. type InhibitRule struct { message *events.InhibitRule @@ -113,10 +108,10 @@ const ( // Notification contains the snapshots used to construct a notification event. type Notification struct { - Alerts []GroupedAlert - FiringAlerts []GroupedAlert - ResolvedAlerts []GroupedAlert - MutedAlerts []GroupedAlert + Alerts []*alert.Alert + FiringAlerts []*alert.Alert + ResolvedAlerts []*alert.Alert + MutedAlerts []*alert.Alert Group AlertGroup RepeatInterval time.Duration Reason NotificationReason @@ -133,16 +128,6 @@ func NewAlertGroup(groupKey string, groupLabels model.LabelSet, groupID, receive }} } -// NewGroupedAlert snapshots an alert and its notification-pipeline hash. -func NewGroupedAlert(hash uint64, a *alert.Alert) GroupedAlert { - return GroupedAlert{message: &events.GroupedAlert{Hash: hash, Details: alertToEvents(a)}} -} - -// NewGroupedAlertReference snapshots a hash-only grouped-alert reference. -func NewGroupedAlertReference(hash uint64) GroupedAlert { - return GroupedAlert{message: &events.GroupedAlert{Hash: hash}} -} - // NewAlertmanagerStartupEvent constructs startup event data. func NewAlertmanagerStartupEvent(version, buildContext string) EventData { return newEventData("alertmanager_startup_event", &events.EventData{EventType: &events.EventData_AlertmanagerStartupEvent{ @@ -165,16 +150,16 @@ func NewAlertCreatedEvent(a *alert.Alert) EventData { } // NewAlertGroupedEvent constructs alert-grouped event data. -func NewAlertGroupedEvent(group AlertGroup, groupedAlert GroupedAlert) EventData { +func NewAlertGroupedEvent(group AlertGroup, a *alert.Alert) EventData { return newEventData("alert_grouped", &events.EventData{EventType: &events.EventData_AlertGrouped{ - AlertGrouped: &events.AlertGroupedEvent{Alert: groupedAlert.message, GroupInfo: group.message}, + AlertGrouped: &events.AlertGroupedEvent{Alert: alertToEvents(a), GroupInfo: group.message}, }}) } // NewAlertResolvedEvent constructs alert-resolved event data. -func NewAlertResolvedEvent(group AlertGroup, groupedAlert GroupedAlert) EventData { +func NewAlertResolvedEvent(group AlertGroup, a *alert.Alert) EventData { return newEventData("alert_resolved", &events.EventData{EventType: &events.EventData_AlertResolved{ - AlertResolved: &events.AlertResolvedEvent{Alert: groupedAlert.message, GroupInfo: group.message}, + AlertResolved: &events.AlertResolvedEvent{Alert: alertToEvents(a), GroupInfo: group.message}, }}) } @@ -182,8 +167,8 @@ func NewAlertResolvedEvent(group AlertGroup, groupedAlert GroupedAlert) EventDat func NewNotificationEvent(notification Notification) EventData { return newEventData("notification", &events.EventData{EventType: &events.EventData_Notification{ Notification: &events.NotificationEvent{ - Alerts: groupedAlertsToEvents(notification.Alerts), FiringAlerts: groupedAlertsToEvents(notification.FiringAlerts), - ResolvedAlerts: groupedAlertsToEvents(notification.ResolvedAlerts), MutedAlerts: groupedAlertsToEvents(notification.MutedAlerts), + Alerts: alertsToEvents(notification.Alerts), FiringAlerts: alertsToEvents(notification.FiringAlerts), + ResolvedAlerts: alertsToEvents(notification.ResolvedAlerts), MutedAlerts: alertsToEvents(notification.MutedAlerts), GroupInfo: notification.Group.message, RepeatInterval: durationpb.New(notification.RepeatInterval), Reason: notificationReasonToEvents(notification.Reason), FlushId: notification.FlushID, Integration: &events.Integration{Name: notification.Integration, Index: notification.IntegrationIdx}, @@ -386,10 +371,10 @@ func cloneTimestamp(timestamp *timestamppb.Timestamp) *timestamppb.Timestamp { return timestamppb.New(timestamp.AsTime()) } -func groupedAlertsToEvents(alerts []GroupedAlert) []*events.GroupedAlert { - result := make([]*events.GroupedAlert, len(alerts)) - for i, groupedAlert := range alerts { - result[i] = groupedAlert.message +func alertsToEvents(alerts []*alert.Alert) []*events.Alert { + result := make([]*events.Alert, len(alerts)) + for i, a := range alerts { + result[i] = alertToEvents(a) } return result } diff --git a/eventrecorder/events/v2/events.pb.go b/eventrecorder/events/v2/events.pb.go index 374d5776e2..daeb5adf1f 100644 --- a/eventrecorder/events/v2/events.pb.go +++ b/eventrecorder/events/v2/events.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 +// protoc-gen-go v1.36.12 // protoc (unknown) // source: events/v2/events.proto @@ -134,7 +134,7 @@ func (x Matcher_Type) Number() protoreflect.EnumNumber { // Deprecated: Use Matcher_Type.Descriptor instead. func (Matcher_Type) EnumDescriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{13, 0} + return file_events_v2_events_proto_rawDescGZIP(), []int{12, 0} } // Event is the top-level envelope written to each event recorder output. @@ -600,59 +600,6 @@ func (x *Alert) GetResolved() bool { return false } -// GroupedAlert is a reference to an alert within an aggregation group. -type GroupedAlert struct { - state protoimpl.MessageState `protogen:"open.v1"` - Hash uint64 `protobuf:"varint,1,opt,name=hash,proto3" json:"hash,omitempty"` - Details *Alert `protobuf:"bytes,2,opt,name=details,proto3,oneof" json:"details,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GroupedAlert) Reset() { - *x = GroupedAlert{} - mi := &file_events_v2_events_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GroupedAlert) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GroupedAlert) ProtoMessage() {} - -func (x *GroupedAlert) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GroupedAlert.ProtoReflect.Descriptor instead. -func (*GroupedAlert) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{5} -} - -func (x *GroupedAlert) GetHash() uint64 { - if x != nil { - return x.Hash - } - return 0 -} - -func (x *GroupedAlert) GetDetails() *Alert { - if x != nil { - return x.Details - } - return nil -} - // AlertGroupInfo describes an alert's aggregation group context. type AlertGroupInfo struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -668,7 +615,7 @@ type AlertGroupInfo struct { func (x *AlertGroupInfo) Reset() { *x = AlertGroupInfo{} - mi := &file_events_v2_events_proto_msgTypes[6] + mi := &file_events_v2_events_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -680,7 +627,7 @@ func (x *AlertGroupInfo) String() string { func (*AlertGroupInfo) ProtoMessage() {} func (x *AlertGroupInfo) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[6] + mi := &file_events_v2_events_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -693,7 +640,7 @@ func (x *AlertGroupInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AlertGroupInfo.ProtoReflect.Descriptor instead. func (*AlertGroupInfo) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{6} + return file_events_v2_events_proto_rawDescGZIP(), []int{5} } func (x *AlertGroupInfo) GetGroupKey() string { @@ -748,7 +695,7 @@ type AlertCreatedEvent struct { func (x *AlertCreatedEvent) Reset() { *x = AlertCreatedEvent{} - mi := &file_events_v2_events_proto_msgTypes[7] + mi := &file_events_v2_events_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -760,7 +707,7 @@ func (x *AlertCreatedEvent) String() string { func (*AlertCreatedEvent) ProtoMessage() {} func (x *AlertCreatedEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[7] + mi := &file_events_v2_events_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -773,7 +720,7 @@ func (x *AlertCreatedEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use AlertCreatedEvent.ProtoReflect.Descriptor instead. func (*AlertCreatedEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{7} + return file_events_v2_events_proto_rawDescGZIP(), []int{6} } func (x *AlertCreatedEvent) GetAlert() *Alert { @@ -786,7 +733,7 @@ func (x *AlertCreatedEvent) GetAlert() *Alert { // AlertResolvedEvent is emitted when a resolved alert leaves its group. type AlertResolvedEvent struct { state protoimpl.MessageState `protogen:"open.v1"` - Alert *GroupedAlert `protobuf:"bytes,1,opt,name=alert,proto3" json:"alert,omitempty"` + Alert *Alert `protobuf:"bytes,1,opt,name=alert,proto3" json:"alert,omitempty"` GroupInfo *AlertGroupInfo `protobuf:"bytes,2,opt,name=group_info,json=groupInfo,proto3" json:"group_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -794,7 +741,7 @@ type AlertResolvedEvent struct { func (x *AlertResolvedEvent) Reset() { *x = AlertResolvedEvent{} - mi := &file_events_v2_events_proto_msgTypes[8] + mi := &file_events_v2_events_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -806,7 +753,7 @@ func (x *AlertResolvedEvent) String() string { func (*AlertResolvedEvent) ProtoMessage() {} func (x *AlertResolvedEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[8] + mi := &file_events_v2_events_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -819,10 +766,10 @@ func (x *AlertResolvedEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use AlertResolvedEvent.ProtoReflect.Descriptor instead. func (*AlertResolvedEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{8} + return file_events_v2_events_proto_rawDescGZIP(), []int{7} } -func (x *AlertResolvedEvent) GetAlert() *GroupedAlert { +func (x *AlertResolvedEvent) GetAlert() *Alert { if x != nil { return x.Alert } @@ -839,7 +786,7 @@ func (x *AlertResolvedEvent) GetGroupInfo() *AlertGroupInfo { // AlertGroupedEvent is emitted when an alert first enters a group. type AlertGroupedEvent struct { state protoimpl.MessageState `protogen:"open.v1"` - Alert *GroupedAlert `protobuf:"bytes,1,opt,name=alert,proto3" json:"alert,omitempty"` + Alert *Alert `protobuf:"bytes,1,opt,name=alert,proto3" json:"alert,omitempty"` GroupInfo *AlertGroupInfo `protobuf:"bytes,2,opt,name=group_info,json=groupInfo,proto3" json:"group_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -847,7 +794,7 @@ type AlertGroupedEvent struct { func (x *AlertGroupedEvent) Reset() { *x = AlertGroupedEvent{} - mi := &file_events_v2_events_proto_msgTypes[9] + mi := &file_events_v2_events_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -859,7 +806,7 @@ func (x *AlertGroupedEvent) String() string { func (*AlertGroupedEvent) ProtoMessage() {} func (x *AlertGroupedEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[9] + mi := &file_events_v2_events_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -872,10 +819,10 @@ func (x *AlertGroupedEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use AlertGroupedEvent.ProtoReflect.Descriptor instead. func (*AlertGroupedEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{9} + return file_events_v2_events_proto_rawDescGZIP(), []int{8} } -func (x *AlertGroupedEvent) GetAlert() *GroupedAlert { +func (x *AlertGroupedEvent) GetAlert() *Alert { if x != nil { return x.Alert } @@ -900,7 +847,7 @@ type Integration struct { func (x *Integration) Reset() { *x = Integration{} - mi := &file_events_v2_events_proto_msgTypes[10] + mi := &file_events_v2_events_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -912,7 +859,7 @@ func (x *Integration) String() string { func (*Integration) ProtoMessage() {} func (x *Integration) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[10] + mi := &file_events_v2_events_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -925,7 +872,7 @@ func (x *Integration) ProtoReflect() protoreflect.Message { // Deprecated: Use Integration.ProtoReflect.Descriptor instead. func (*Integration) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{10} + return file_events_v2_events_proto_rawDescGZIP(), []int{9} } func (x *Integration) GetName() string { @@ -945,10 +892,10 @@ func (x *Integration) GetIndex() int64 { // NotificationEvent is emitted after a notification is delivered. type NotificationEvent struct { state protoimpl.MessageState `protogen:"open.v1"` - Alerts []*GroupedAlert `protobuf:"bytes,1,rep,name=alerts,proto3" json:"alerts,omitempty"` - FiringAlerts []*GroupedAlert `protobuf:"bytes,2,rep,name=firing_alerts,json=firingAlerts,proto3" json:"firing_alerts,omitempty"` - ResolvedAlerts []*GroupedAlert `protobuf:"bytes,3,rep,name=resolved_alerts,json=resolvedAlerts,proto3" json:"resolved_alerts,omitempty"` - MutedAlerts []*GroupedAlert `protobuf:"bytes,4,rep,name=muted_alerts,json=mutedAlerts,proto3" json:"muted_alerts,omitempty"` + Alerts []*Alert `protobuf:"bytes,1,rep,name=alerts,proto3" json:"alerts,omitempty"` + FiringAlerts []*Alert `protobuf:"bytes,2,rep,name=firing_alerts,json=firingAlerts,proto3" json:"firing_alerts,omitempty"` + ResolvedAlerts []*Alert `protobuf:"bytes,3,rep,name=resolved_alerts,json=resolvedAlerts,proto3" json:"resolved_alerts,omitempty"` + MutedAlerts []*Alert `protobuf:"bytes,4,rep,name=muted_alerts,json=mutedAlerts,proto3" json:"muted_alerts,omitempty"` GroupInfo *AlertGroupInfo `protobuf:"bytes,5,opt,name=group_info,json=groupInfo,proto3" json:"group_info,omitempty"` RepeatInterval *durationpb.Duration `protobuf:"bytes,6,opt,name=repeat_interval,json=repeatInterval,proto3" json:"repeat_interval,omitempty"` Reason NotifyReason `protobuf:"varint,7,opt,name=reason,proto3,enum=events.v2.NotifyReason" json:"reason,omitempty"` @@ -960,7 +907,7 @@ type NotificationEvent struct { func (x *NotificationEvent) Reset() { *x = NotificationEvent{} - mi := &file_events_v2_events_proto_msgTypes[11] + mi := &file_events_v2_events_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -972,7 +919,7 @@ func (x *NotificationEvent) String() string { func (*NotificationEvent) ProtoMessage() {} func (x *NotificationEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[11] + mi := &file_events_v2_events_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -985,31 +932,31 @@ func (x *NotificationEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use NotificationEvent.ProtoReflect.Descriptor instead. func (*NotificationEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{11} + return file_events_v2_events_proto_rawDescGZIP(), []int{10} } -func (x *NotificationEvent) GetAlerts() []*GroupedAlert { +func (x *NotificationEvent) GetAlerts() []*Alert { if x != nil { return x.Alerts } return nil } -func (x *NotificationEvent) GetFiringAlerts() []*GroupedAlert { +func (x *NotificationEvent) GetFiringAlerts() []*Alert { if x != nil { return x.FiringAlerts } return nil } -func (x *NotificationEvent) GetResolvedAlerts() []*GroupedAlert { +func (x *NotificationEvent) GetResolvedAlerts() []*Alert { if x != nil { return x.ResolvedAlerts } return nil } -func (x *NotificationEvent) GetMutedAlerts() []*GroupedAlert { +func (x *NotificationEvent) GetMutedAlerts() []*Alert { if x != nil { return x.MutedAlerts } @@ -1070,7 +1017,7 @@ type Silence struct { func (x *Silence) Reset() { *x = Silence{} - mi := &file_events_v2_events_proto_msgTypes[12] + mi := &file_events_v2_events_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1082,7 +1029,7 @@ func (x *Silence) String() string { func (*Silence) ProtoMessage() {} func (x *Silence) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[12] + mi := &file_events_v2_events_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1095,7 +1042,7 @@ func (x *Silence) ProtoReflect() protoreflect.Message { // Deprecated: Use Silence.ProtoReflect.Descriptor instead. func (*Silence) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{12} + return file_events_v2_events_proto_rawDescGZIP(), []int{11} } func (x *Silence) GetId() string { @@ -1181,7 +1128,7 @@ type Matcher struct { func (x *Matcher) Reset() { *x = Matcher{} - mi := &file_events_v2_events_proto_msgTypes[13] + mi := &file_events_v2_events_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1193,7 +1140,7 @@ func (x *Matcher) String() string { func (*Matcher) ProtoMessage() {} func (x *Matcher) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[13] + mi := &file_events_v2_events_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1206,7 +1153,7 @@ func (x *Matcher) ProtoReflect() protoreflect.Message { // Deprecated: Use Matcher.ProtoReflect.Descriptor instead. func (*Matcher) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{13} + return file_events_v2_events_proto_rawDescGZIP(), []int{12} } func (x *Matcher) GetType() Matcher_Type { @@ -1247,7 +1194,7 @@ type MatcherSet struct { func (x *MatcherSet) Reset() { *x = MatcherSet{} - mi := &file_events_v2_events_proto_msgTypes[14] + mi := &file_events_v2_events_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1259,7 +1206,7 @@ func (x *MatcherSet) String() string { func (*MatcherSet) ProtoMessage() {} func (x *MatcherSet) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[14] + mi := &file_events_v2_events_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1272,7 +1219,7 @@ func (x *MatcherSet) ProtoReflect() protoreflect.Message { // Deprecated: Use MatcherSet.ProtoReflect.Descriptor instead. func (*MatcherSet) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{14} + return file_events_v2_events_proto_rawDescGZIP(), []int{13} } func (x *MatcherSet) GetMatchers() []*Matcher { @@ -1292,7 +1239,7 @@ type SilenceCreatedEvent struct { func (x *SilenceCreatedEvent) Reset() { *x = SilenceCreatedEvent{} - mi := &file_events_v2_events_proto_msgTypes[15] + mi := &file_events_v2_events_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1304,7 +1251,7 @@ func (x *SilenceCreatedEvent) String() string { func (*SilenceCreatedEvent) ProtoMessage() {} func (x *SilenceCreatedEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[15] + mi := &file_events_v2_events_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1317,7 +1264,7 @@ func (x *SilenceCreatedEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use SilenceCreatedEvent.ProtoReflect.Descriptor instead. func (*SilenceCreatedEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{15} + return file_events_v2_events_proto_rawDescGZIP(), []int{14} } func (x *SilenceCreatedEvent) GetSilence() *Silence { @@ -1337,7 +1284,7 @@ type SilenceUpdatedEvent struct { func (x *SilenceUpdatedEvent) Reset() { *x = SilenceUpdatedEvent{} - mi := &file_events_v2_events_proto_msgTypes[16] + mi := &file_events_v2_events_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1349,7 +1296,7 @@ func (x *SilenceUpdatedEvent) String() string { func (*SilenceUpdatedEvent) ProtoMessage() {} func (x *SilenceUpdatedEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[16] + mi := &file_events_v2_events_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1362,7 +1309,7 @@ func (x *SilenceUpdatedEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use SilenceUpdatedEvent.ProtoReflect.Descriptor instead. func (*SilenceUpdatedEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{16} + return file_events_v2_events_proto_rawDescGZIP(), []int{15} } func (x *SilenceUpdatedEvent) GetSilence() *Silence { @@ -1383,7 +1330,7 @@ type MutedAlert struct { func (x *MutedAlert) Reset() { *x = MutedAlert{} - mi := &file_events_v2_events_proto_msgTypes[17] + mi := &file_events_v2_events_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1395,7 +1342,7 @@ func (x *MutedAlert) String() string { func (*MutedAlert) ProtoMessage() {} func (x *MutedAlert) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[17] + mi := &file_events_v2_events_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1408,7 +1355,7 @@ func (x *MutedAlert) ProtoReflect() protoreflect.Message { // Deprecated: Use MutedAlert.ProtoReflect.Descriptor instead. func (*MutedAlert) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{17} + return file_events_v2_events_proto_rawDescGZIP(), []int{16} } func (x *MutedAlert) GetLabels() map[string]string { @@ -1436,7 +1383,7 @@ type SilenceMutedAlertEvent struct { func (x *SilenceMutedAlertEvent) Reset() { *x = SilenceMutedAlertEvent{} - mi := &file_events_v2_events_proto_msgTypes[18] + mi := &file_events_v2_events_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1448,7 +1395,7 @@ func (x *SilenceMutedAlertEvent) String() string { func (*SilenceMutedAlertEvent) ProtoMessage() {} func (x *SilenceMutedAlertEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[18] + mi := &file_events_v2_events_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1461,7 +1408,7 @@ func (x *SilenceMutedAlertEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use SilenceMutedAlertEvent.ProtoReflect.Descriptor instead. func (*SilenceMutedAlertEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{18} + return file_events_v2_events_proto_rawDescGZIP(), []int{17} } func (x *SilenceMutedAlertEvent) GetSilence() *Silence { @@ -1491,7 +1438,7 @@ type InhibitRule struct { func (x *InhibitRule) Reset() { *x = InhibitRule{} - mi := &file_events_v2_events_proto_msgTypes[19] + mi := &file_events_v2_events_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1503,7 +1450,7 @@ func (x *InhibitRule) String() string { func (*InhibitRule) ProtoMessage() {} func (x *InhibitRule) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[19] + mi := &file_events_v2_events_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1516,7 +1463,7 @@ func (x *InhibitRule) ProtoReflect() protoreflect.Message { // Deprecated: Use InhibitRule.ProtoReflect.Descriptor instead. func (*InhibitRule) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{19} + return file_events_v2_events_proto_rawDescGZIP(), []int{18} } func (x *InhibitRule) GetSourceMatchers() []*Matcher { @@ -1559,7 +1506,7 @@ type InhibitionMutedAlertEvent struct { func (x *InhibitionMutedAlertEvent) Reset() { *x = InhibitionMutedAlertEvent{} - mi := &file_events_v2_events_proto_msgTypes[20] + mi := &file_events_v2_events_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1571,7 +1518,7 @@ func (x *InhibitionMutedAlertEvent) String() string { func (*InhibitionMutedAlertEvent) ProtoMessage() {} func (x *InhibitionMutedAlertEvent) ProtoReflect() protoreflect.Message { - mi := &file_events_v2_events_proto_msgTypes[20] + mi := &file_events_v2_events_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1584,7 +1531,7 @@ func (x *InhibitionMutedAlertEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use InhibitionMutedAlertEvent.ProtoReflect.Descriptor instead. func (*InhibitionMutedAlertEvent) Descriptor() ([]byte, []int) { - return file_events_v2_events_proto_rawDescGZIP(), []int{20} + return file_events_v2_events_proto_rawDescGZIP(), []int{19} } func (x *InhibitionMutedAlertEvent) GetInhibitRules() []*InhibitRule { @@ -1650,12 +1597,7 @@ const file_events_v2_events_proto_rawDesc = "" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + "\x10AnnotationsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"_\n" + - "\fGroupedAlert\x12\x12\n" + - "\x04hash\x18\x01 \x01(\x04R\x04hash\x12/\n" + - "\adetails\x18\x02 \x01(\v2\x10.events.v2.AlertH\x00R\adetails\x88\x01\x01B\n" + - "\n" + - "\b_details\"\xcb\x02\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xcb\x02\n" + "\x0eAlertGroupInfo\x12\x1b\n" + "\tgroup_key\x18\x01 \x01(\tR\bgroupKey\x12M\n" + "\fgroup_labels\x18\x02 \x03(\v2*.events.v2.AlertGroupInfo.GroupLabelsEntryR\vgroupLabels\x12\x19\n" + @@ -1668,23 +1610,23 @@ const file_events_v2_events_proto_rawDesc = "" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\";\n" + "\x11AlertCreatedEvent\x12&\n" + - "\x05alert\x18\x01 \x01(\v2\x10.events.v2.AlertR\x05alert\"}\n" + - "\x12AlertResolvedEvent\x12-\n" + - "\x05alert\x18\x01 \x01(\v2\x17.events.v2.GroupedAlertR\x05alert\x128\n" + + "\x05alert\x18\x01 \x01(\v2\x10.events.v2.AlertR\x05alert\"v\n" + + "\x12AlertResolvedEvent\x12&\n" + + "\x05alert\x18\x01 \x01(\v2\x10.events.v2.AlertR\x05alert\x128\n" + "\n" + - "group_info\x18\x02 \x01(\v2\x19.events.v2.AlertGroupInfoR\tgroupInfo\"|\n" + - "\x11AlertGroupedEvent\x12-\n" + - "\x05alert\x18\x01 \x01(\v2\x17.events.v2.GroupedAlertR\x05alert\x128\n" + + "group_info\x18\x02 \x01(\v2\x19.events.v2.AlertGroupInfoR\tgroupInfo\"u\n" + + "\x11AlertGroupedEvent\x12&\n" + + "\x05alert\x18\x01 \x01(\v2\x10.events.v2.AlertR\x05alert\x128\n" + "\n" + "group_info\x18\x02 \x01(\v2\x19.events.v2.AlertGroupInfoR\tgroupInfo\"7\n" + "\vIntegration\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + - "\x05index\x18\x02 \x01(\x03R\x05index\"\x84\x04\n" + - "\x11NotificationEvent\x12/\n" + - "\x06alerts\x18\x01 \x03(\v2\x17.events.v2.GroupedAlertR\x06alerts\x12<\n" + - "\rfiring_alerts\x18\x02 \x03(\v2\x17.events.v2.GroupedAlertR\ffiringAlerts\x12@\n" + - "\x0fresolved_alerts\x18\x03 \x03(\v2\x17.events.v2.GroupedAlertR\x0eresolvedAlerts\x12:\n" + - "\fmuted_alerts\x18\x04 \x03(\v2\x17.events.v2.GroupedAlertR\vmutedAlerts\x128\n" + + "\x05index\x18\x02 \x01(\x03R\x05index\"\xe8\x03\n" + + "\x11NotificationEvent\x12(\n" + + "\x06alerts\x18\x01 \x03(\v2\x10.events.v2.AlertR\x06alerts\x125\n" + + "\rfiring_alerts\x18\x02 \x03(\v2\x10.events.v2.AlertR\ffiringAlerts\x129\n" + + "\x0fresolved_alerts\x18\x03 \x03(\v2\x10.events.v2.AlertR\x0eresolvedAlerts\x123\n" + + "\fmuted_alerts\x18\x04 \x03(\v2\x10.events.v2.AlertR\vmutedAlerts\x128\n" + "\n" + "group_info\x18\x05 \x01(\v2\x19.events.v2.AlertGroupInfoR\tgroupInfo\x12B\n" + "\x0frepeat_interval\x18\x06 \x01(\v2\x19.google.protobuf.DurationR\x0erepeatInterval\x12/\n" + @@ -1769,7 +1711,7 @@ func file_events_v2_events_proto_rawDescGZIP() []byte { } var file_events_v2_events_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_events_v2_events_proto_msgTypes = make([]protoimpl.MessageInfo, 26) +var file_events_v2_events_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_events_v2_events_proto_goTypes = []any{ (NotifyReason)(0), // 0: events.v2.NotifyReason (Matcher_Type)(0), // 1: events.v2.Matcher.Type @@ -1778,86 +1720,84 @@ var file_events_v2_events_proto_goTypes = []any{ (*AlertmanagerStartupEvent)(nil), // 4: events.v2.AlertmanagerStartupEvent (*AlertmanagerShutdownEvent)(nil), // 5: events.v2.AlertmanagerShutdownEvent (*Alert)(nil), // 6: events.v2.Alert - (*GroupedAlert)(nil), // 7: events.v2.GroupedAlert - (*AlertGroupInfo)(nil), // 8: events.v2.AlertGroupInfo - (*AlertCreatedEvent)(nil), // 9: events.v2.AlertCreatedEvent - (*AlertResolvedEvent)(nil), // 10: events.v2.AlertResolvedEvent - (*AlertGroupedEvent)(nil), // 11: events.v2.AlertGroupedEvent - (*Integration)(nil), // 12: events.v2.Integration - (*NotificationEvent)(nil), // 13: events.v2.NotificationEvent - (*Silence)(nil), // 14: events.v2.Silence - (*Matcher)(nil), // 15: events.v2.Matcher - (*MatcherSet)(nil), // 16: events.v2.MatcherSet - (*SilenceCreatedEvent)(nil), // 17: events.v2.SilenceCreatedEvent - (*SilenceUpdatedEvent)(nil), // 18: events.v2.SilenceUpdatedEvent - (*MutedAlert)(nil), // 19: events.v2.MutedAlert - (*SilenceMutedAlertEvent)(nil), // 20: events.v2.SilenceMutedAlertEvent - (*InhibitRule)(nil), // 21: events.v2.InhibitRule - (*InhibitionMutedAlertEvent)(nil), // 22: events.v2.InhibitionMutedAlertEvent - nil, // 23: events.v2.Alert.LabelsEntry - nil, // 24: events.v2.Alert.AnnotationsEntry - nil, // 25: events.v2.AlertGroupInfo.GroupLabelsEntry - nil, // 26: events.v2.Silence.AnnotationsEntry - nil, // 27: events.v2.MutedAlert.LabelsEntry - (*timestamppb.Timestamp)(nil), // 28: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 29: google.protobuf.Duration + (*AlertGroupInfo)(nil), // 7: events.v2.AlertGroupInfo + (*AlertCreatedEvent)(nil), // 8: events.v2.AlertCreatedEvent + (*AlertResolvedEvent)(nil), // 9: events.v2.AlertResolvedEvent + (*AlertGroupedEvent)(nil), // 10: events.v2.AlertGroupedEvent + (*Integration)(nil), // 11: events.v2.Integration + (*NotificationEvent)(nil), // 12: events.v2.NotificationEvent + (*Silence)(nil), // 13: events.v2.Silence + (*Matcher)(nil), // 14: events.v2.Matcher + (*MatcherSet)(nil), // 15: events.v2.MatcherSet + (*SilenceCreatedEvent)(nil), // 16: events.v2.SilenceCreatedEvent + (*SilenceUpdatedEvent)(nil), // 17: events.v2.SilenceUpdatedEvent + (*MutedAlert)(nil), // 18: events.v2.MutedAlert + (*SilenceMutedAlertEvent)(nil), // 19: events.v2.SilenceMutedAlertEvent + (*InhibitRule)(nil), // 20: events.v2.InhibitRule + (*InhibitionMutedAlertEvent)(nil), // 21: events.v2.InhibitionMutedAlertEvent + nil, // 22: events.v2.Alert.LabelsEntry + nil, // 23: events.v2.Alert.AnnotationsEntry + nil, // 24: events.v2.AlertGroupInfo.GroupLabelsEntry + nil, // 25: events.v2.Silence.AnnotationsEntry + nil, // 26: events.v2.MutedAlert.LabelsEntry + (*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 28: google.protobuf.Duration } var file_events_v2_events_proto_depIdxs = []int32{ - 28, // 0: events.v2.Event.timestamp:type_name -> google.protobuf.Timestamp + 27, // 0: events.v2.Event.timestamp:type_name -> google.protobuf.Timestamp 3, // 1: events.v2.Event.data:type_name -> events.v2.EventData 4, // 2: events.v2.EventData.alertmanager_startup_event:type_name -> events.v2.AlertmanagerStartupEvent 5, // 3: events.v2.EventData.alertmanager_shutdown_event:type_name -> events.v2.AlertmanagerShutdownEvent - 9, // 4: events.v2.EventData.alert_created:type_name -> events.v2.AlertCreatedEvent - 10, // 5: events.v2.EventData.alert_resolved:type_name -> events.v2.AlertResolvedEvent - 11, // 6: events.v2.EventData.alert_grouped:type_name -> events.v2.AlertGroupedEvent - 13, // 7: events.v2.EventData.notification:type_name -> events.v2.NotificationEvent - 17, // 8: events.v2.EventData.silence_created:type_name -> events.v2.SilenceCreatedEvent - 18, // 9: events.v2.EventData.silence_updated:type_name -> events.v2.SilenceUpdatedEvent - 20, // 10: events.v2.EventData.silence_muted_alert:type_name -> events.v2.SilenceMutedAlertEvent - 22, // 11: events.v2.EventData.inhibition_muted_alert:type_name -> events.v2.InhibitionMutedAlertEvent - 23, // 12: events.v2.Alert.labels:type_name -> events.v2.Alert.LabelsEntry - 24, // 13: events.v2.Alert.annotations:type_name -> events.v2.Alert.AnnotationsEntry - 28, // 14: events.v2.Alert.starts_at:type_name -> google.protobuf.Timestamp - 28, // 15: events.v2.Alert.ends_at:type_name -> google.protobuf.Timestamp - 6, // 16: events.v2.GroupedAlert.details:type_name -> events.v2.Alert - 25, // 17: events.v2.AlertGroupInfo.group_labels:type_name -> events.v2.AlertGroupInfo.GroupLabelsEntry - 15, // 18: events.v2.AlertGroupInfo.matchers:type_name -> events.v2.Matcher - 6, // 19: events.v2.AlertCreatedEvent.alert:type_name -> events.v2.Alert - 7, // 20: events.v2.AlertResolvedEvent.alert:type_name -> events.v2.GroupedAlert - 8, // 21: events.v2.AlertResolvedEvent.group_info:type_name -> events.v2.AlertGroupInfo - 7, // 22: events.v2.AlertGroupedEvent.alert:type_name -> events.v2.GroupedAlert - 8, // 23: events.v2.AlertGroupedEvent.group_info:type_name -> events.v2.AlertGroupInfo - 7, // 24: events.v2.NotificationEvent.alerts:type_name -> events.v2.GroupedAlert - 7, // 25: events.v2.NotificationEvent.firing_alerts:type_name -> events.v2.GroupedAlert - 7, // 26: events.v2.NotificationEvent.resolved_alerts:type_name -> events.v2.GroupedAlert - 7, // 27: events.v2.NotificationEvent.muted_alerts:type_name -> events.v2.GroupedAlert - 8, // 28: events.v2.NotificationEvent.group_info:type_name -> events.v2.AlertGroupInfo - 29, // 29: events.v2.NotificationEvent.repeat_interval:type_name -> google.protobuf.Duration - 0, // 30: events.v2.NotificationEvent.reason:type_name -> events.v2.NotifyReason - 12, // 31: events.v2.NotificationEvent.integration:type_name -> events.v2.Integration - 15, // 32: events.v2.Silence.matchers:type_name -> events.v2.Matcher - 26, // 33: events.v2.Silence.annotations:type_name -> events.v2.Silence.AnnotationsEntry - 28, // 34: events.v2.Silence.starts_at:type_name -> google.protobuf.Timestamp - 28, // 35: events.v2.Silence.ends_at:type_name -> google.protobuf.Timestamp - 28, // 36: events.v2.Silence.updated_at:type_name -> google.protobuf.Timestamp - 16, // 37: events.v2.Silence.matcher_sets:type_name -> events.v2.MatcherSet - 16, // 38: events.v2.Silence.receiver_matcher_sets:type_name -> events.v2.MatcherSet - 1, // 39: events.v2.Matcher.type:type_name -> events.v2.Matcher.Type - 15, // 40: events.v2.MatcherSet.matchers:type_name -> events.v2.Matcher - 14, // 41: events.v2.SilenceCreatedEvent.silence:type_name -> events.v2.Silence - 14, // 42: events.v2.SilenceUpdatedEvent.silence:type_name -> events.v2.Silence - 27, // 43: events.v2.MutedAlert.labels:type_name -> events.v2.MutedAlert.LabelsEntry - 14, // 44: events.v2.SilenceMutedAlertEvent.silence:type_name -> events.v2.Silence - 19, // 45: events.v2.SilenceMutedAlertEvent.muted_alert:type_name -> events.v2.MutedAlert - 15, // 46: events.v2.InhibitRule.source_matchers:type_name -> events.v2.Matcher - 15, // 47: events.v2.InhibitRule.target_matchers:type_name -> events.v2.Matcher - 21, // 48: events.v2.InhibitionMutedAlertEvent.inhibit_rules:type_name -> events.v2.InhibitRule - 19, // 49: events.v2.InhibitionMutedAlertEvent.muted_alert:type_name -> events.v2.MutedAlert - 50, // [50:50] is the sub-list for method output_type - 50, // [50:50] is the sub-list for method input_type - 50, // [50:50] is the sub-list for extension type_name - 50, // [50:50] is the sub-list for extension extendee - 0, // [0:50] is the sub-list for field type_name + 8, // 4: events.v2.EventData.alert_created:type_name -> events.v2.AlertCreatedEvent + 9, // 5: events.v2.EventData.alert_resolved:type_name -> events.v2.AlertResolvedEvent + 10, // 6: events.v2.EventData.alert_grouped:type_name -> events.v2.AlertGroupedEvent + 12, // 7: events.v2.EventData.notification:type_name -> events.v2.NotificationEvent + 16, // 8: events.v2.EventData.silence_created:type_name -> events.v2.SilenceCreatedEvent + 17, // 9: events.v2.EventData.silence_updated:type_name -> events.v2.SilenceUpdatedEvent + 19, // 10: events.v2.EventData.silence_muted_alert:type_name -> events.v2.SilenceMutedAlertEvent + 21, // 11: events.v2.EventData.inhibition_muted_alert:type_name -> events.v2.InhibitionMutedAlertEvent + 22, // 12: events.v2.Alert.labels:type_name -> events.v2.Alert.LabelsEntry + 23, // 13: events.v2.Alert.annotations:type_name -> events.v2.Alert.AnnotationsEntry + 27, // 14: events.v2.Alert.starts_at:type_name -> google.protobuf.Timestamp + 27, // 15: events.v2.Alert.ends_at:type_name -> google.protobuf.Timestamp + 24, // 16: events.v2.AlertGroupInfo.group_labels:type_name -> events.v2.AlertGroupInfo.GroupLabelsEntry + 14, // 17: events.v2.AlertGroupInfo.matchers:type_name -> events.v2.Matcher + 6, // 18: events.v2.AlertCreatedEvent.alert:type_name -> events.v2.Alert + 6, // 19: events.v2.AlertResolvedEvent.alert:type_name -> events.v2.Alert + 7, // 20: events.v2.AlertResolvedEvent.group_info:type_name -> events.v2.AlertGroupInfo + 6, // 21: events.v2.AlertGroupedEvent.alert:type_name -> events.v2.Alert + 7, // 22: events.v2.AlertGroupedEvent.group_info:type_name -> events.v2.AlertGroupInfo + 6, // 23: events.v2.NotificationEvent.alerts:type_name -> events.v2.Alert + 6, // 24: events.v2.NotificationEvent.firing_alerts:type_name -> events.v2.Alert + 6, // 25: events.v2.NotificationEvent.resolved_alerts:type_name -> events.v2.Alert + 6, // 26: events.v2.NotificationEvent.muted_alerts:type_name -> events.v2.Alert + 7, // 27: events.v2.NotificationEvent.group_info:type_name -> events.v2.AlertGroupInfo + 28, // 28: events.v2.NotificationEvent.repeat_interval:type_name -> google.protobuf.Duration + 0, // 29: events.v2.NotificationEvent.reason:type_name -> events.v2.NotifyReason + 11, // 30: events.v2.NotificationEvent.integration:type_name -> events.v2.Integration + 14, // 31: events.v2.Silence.matchers:type_name -> events.v2.Matcher + 25, // 32: events.v2.Silence.annotations:type_name -> events.v2.Silence.AnnotationsEntry + 27, // 33: events.v2.Silence.starts_at:type_name -> google.protobuf.Timestamp + 27, // 34: events.v2.Silence.ends_at:type_name -> google.protobuf.Timestamp + 27, // 35: events.v2.Silence.updated_at:type_name -> google.protobuf.Timestamp + 15, // 36: events.v2.Silence.matcher_sets:type_name -> events.v2.MatcherSet + 15, // 37: events.v2.Silence.receiver_matcher_sets:type_name -> events.v2.MatcherSet + 1, // 38: events.v2.Matcher.type:type_name -> events.v2.Matcher.Type + 14, // 39: events.v2.MatcherSet.matchers:type_name -> events.v2.Matcher + 13, // 40: events.v2.SilenceCreatedEvent.silence:type_name -> events.v2.Silence + 13, // 41: events.v2.SilenceUpdatedEvent.silence:type_name -> events.v2.Silence + 26, // 42: events.v2.MutedAlert.labels:type_name -> events.v2.MutedAlert.LabelsEntry + 13, // 43: events.v2.SilenceMutedAlertEvent.silence:type_name -> events.v2.Silence + 18, // 44: events.v2.SilenceMutedAlertEvent.muted_alert:type_name -> events.v2.MutedAlert + 14, // 45: events.v2.InhibitRule.source_matchers:type_name -> events.v2.Matcher + 14, // 46: events.v2.InhibitRule.target_matchers:type_name -> events.v2.Matcher + 20, // 47: events.v2.InhibitionMutedAlertEvent.inhibit_rules:type_name -> events.v2.InhibitRule + 18, // 48: events.v2.InhibitionMutedAlertEvent.muted_alert:type_name -> events.v2.MutedAlert + 49, // [49:49] is the sub-list for method output_type + 49, // [49:49] is the sub-list for method input_type + 49, // [49:49] is the sub-list for extension type_name + 49, // [49:49] is the sub-list for extension extendee + 0, // [0:49] is the sub-list for field type_name } func init() { file_events_v2_events_proto_init() } @@ -1877,14 +1817,13 @@ func file_events_v2_events_proto_init() { (*EventData_SilenceMutedAlert)(nil), (*EventData_InhibitionMutedAlert)(nil), } - file_events_v2_events_proto_msgTypes[5].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_events_v2_events_proto_rawDesc), len(file_events_v2_events_proto_rawDesc)), NumEnums: 2, - NumMessages: 26, + NumMessages: 25, NumExtensions: 0, NumServices: 0, }, diff --git a/eventrecorder/events_test.go b/eventrecorder/events_test.go index 57f36180ad..4de38fa618 100644 --- a/eventrecorder/events_test.go +++ b/eventrecorder/events_test.go @@ -32,12 +32,14 @@ func TestAlertEventSnapshotsLabels(t *testing.T) { Labels: model.LabelSet{"alertname": "Down", "severity": "warning"}, Annotations: model.LabelSet{"summary": "test"}, StartsAt: time.Now(), EndsAt: time.Now().Add(time.Hour), }} + fingerprint := a.Fingerprint() event := NewAlertCreatedEvent(a) a.Labels["severity"] = "critical" a.Annotations["summary"] = "changed" got := event.message.GetAlertCreated().Alert + require.Equal(t, uint64(fingerprint), got.Fingerprint) require.Equal(t, "warning", got.Labels["severity"]) require.Equal(t, "test", got.Annotations["summary"]) } @@ -129,7 +131,7 @@ func TestSilenceMatcherUnknownTypeHasNoRenderedValue(t *testing.T) { } func TestNilMatchersAreAbsentFromJSON(t *testing.T) { - event := NewAlertGroupedEvent(NewAlertGroup("", nil, "", "", labels.Matchers{nil}, ""), NewGroupedAlertReference(1)).withMetadata(nil, "", 0) + event := NewAlertGroupedEvent(NewAlertGroup("", nil, "", "", labels.Matchers{nil}, ""), &alert.Alert{}).withMetadata(nil, "", 0) data, err := event.MarshalJSON() require.NoError(t, err) @@ -141,14 +143,13 @@ func TestNilMatchersAreAbsentFromJSON(t *testing.T) { func TestEventConstructors(t *testing.T) { group := NewAlertGroup("", nil, "", "", nil, "") - grouped := NewGroupedAlertReference(1) rule := NewInhibitRule("", nil, nil, nil) constructed := []EventData{ NewAlertmanagerStartupEvent("", ""), NewAlertmanagerShutdownEvent(), NewAlertCreatedEvent(nil), - NewAlertGroupedEvent(group, grouped), - NewAlertResolvedEvent(group, grouped), + NewAlertGroupedEvent(group, nil), + NewAlertResolvedEvent(group, nil), NewNotificationEvent(Notification{Group: group}), NewSilenceMutedAlertEvent(nil, 0, nil), NewSilenceCreatedEvent(nil), diff --git a/notify/context.go b/notify/context.go index 2d67fbee97..a26f1e2c5a 100644 --- a/notify/context.go +++ b/notify/context.go @@ -18,6 +18,7 @@ import ( "github.com/prometheus/common/model" + "github.com/prometheus/alertmanager/alert" "github.com/prometheus/alertmanager/nflog" "github.com/prometheus/alertmanager/pkg/labels" ) @@ -189,14 +190,16 @@ func NotificationReason(ctx context.Context) (NotifyReason, bool) { return v, ok } -// WithMutedAlerts populates a context with a set of muted alert hashes. -func WithMutedAlerts(ctx context.Context, alerts map[uint64]struct{}) context.Context { - return context.WithValue(ctx, keyMutedAlerts, alerts) +func withMutedAlerts(ctx context.Context, alerts []*alert.Alert) context.Context { + existing, _ := mutedAlerts(ctx) + results := make([]*alert.Alert, 0, len(existing)+len(alerts)) + results = append(results, existing...) + results = append(results, alerts...) + return context.WithValue(ctx, keyMutedAlerts, results) } -// MutedAlerts extracts a set of muted alert hashes from the context. -func MutedAlerts(ctx context.Context) (map[uint64]struct{}, bool) { - v, ok := ctx.Value(keyMutedAlerts).(map[uint64]struct{}) +func mutedAlerts(ctx context.Context) ([]*alert.Alert, bool) { + v, ok := ctx.Value(keyMutedAlerts).([]*alert.Alert) return v, ok } diff --git a/notify/event.go b/notify/event.go index e93a47d396..98cbc46866 100644 --- a/notify/event.go +++ b/notify/event.go @@ -14,20 +14,15 @@ package notify // This file contains helpers for constructing event recorder events -// from the notification pipeline context. It lives in the notify package -// because it accesses unexported context keys (keyFiringAlerts, etc.). +// from notification pipeline state that is internal to the notify package. import ( "context" + "github.com/prometheus/alertmanager/alert" "github.com/prometheus/alertmanager/eventrecorder" - "github.com/prometheus/alertmanager/types" ) -func groupedAlertEvent(alert *types.Alert) eventrecorder.GroupedAlert { - return eventrecorder.NewGroupedAlert(hashAlert(alert), alert) -} - func extractAlertGroupInfo(ctx context.Context) eventrecorder.AlertGroup { groupKey, _ := ExtractGroupKey(ctx) receiverName, _ := ReceiverName(ctx) @@ -40,21 +35,19 @@ func extractAlertGroupInfo(ctx context.Context) eventrecorder.AlertGroup { ) } -func extractGroupedAlerts(ctx context.Context, key notifyKey) []eventrecorder.GroupedAlert { - var result []eventrecorder.GroupedAlert - if list, ok := ctx.Value(key).([]uint64); ok { - for _, hash := range list { - result = append(result, eventrecorder.NewGroupedAlertReference(hash)) - } +func alertDetailsByHash(alerts []*alert.Alert) map[uint64]*alert.Alert { + result := make(map[uint64]*alert.Alert, len(alerts)) + for _, alert := range alerts { + result[hashAlert(alert)] = alert } return result } -func extractMutedGroupedAlerts(ctx context.Context) []eventrecorder.GroupedAlert { - var result []eventrecorder.GroupedAlert - if muted, ok := MutedAlerts(ctx); ok { - for hash := range muted { - result = append(result, eventrecorder.NewGroupedAlertReference(hash)) +func alertDetailsForHashes(alerts map[uint64]*alert.Alert, hashes []uint64) []*alert.Alert { + result := make([]*alert.Alert, 0, len(hashes)) + for _, hash := range hashes { + if alert, ok := alerts[hash]; ok { + result = append(result, alert) } } return result @@ -77,23 +70,20 @@ func notifyReasonToEvent(reason NotifyReason) eventrecorder.NotificationReason { } } -// NewNotificationEvent constructs notification event data from the pipeline -// context after a successful notification delivery. -func NewNotificationEvent(ctx context.Context, alerts []*types.Alert, integration Integration) eventrecorder.EventData { - groupedAlerts := make([]eventrecorder.GroupedAlert, 0, len(alerts)) - for _, alert := range alerts { - groupedAlerts = append(groupedAlerts, groupedAlertEvent(alert)) - } - +func newNotificationEvent(ctx context.Context, sent, alerts []*alert.Alert, integration Integration) eventrecorder.EventData { notifyReason, _ := NotificationReason(ctx) repeatInterval, _ := RepeatInterval(ctx) flushID, _ := FlushID(ctx) + firingHashes, _ := FiringAlerts(ctx) + resolvedHashes, _ := ResolvedAlerts(ctx) + details := alertDetailsByHash(alerts) + muted, _ := mutedAlerts(ctx) return eventrecorder.NewNotificationEvent(eventrecorder.Notification{ - Alerts: groupedAlerts, - FiringAlerts: extractGroupedAlerts(ctx, keyFiringAlerts), - ResolvedAlerts: extractGroupedAlerts(ctx, keyResolvedAlerts), - MutedAlerts: extractMutedGroupedAlerts(ctx), + Alerts: sent, + FiringAlerts: alertDetailsForHashes(details, firingHashes), + ResolvedAlerts: alertDetailsForHashes(details, resolvedHashes), + MutedAlerts: muted, Group: extractAlertGroupInfo(ctx), RepeatInterval: repeatInterval, Reason: notifyReasonToEvent(notifyReason), @@ -104,11 +94,11 @@ func NewNotificationEvent(ctx context.Context, alerts []*types.Alert, integratio } // NewAlertResolvedEvent constructs alert-resolved event data. -func NewAlertResolvedEvent(groupInfo eventrecorder.AlertGroup, alert *types.Alert) eventrecorder.EventData { - return eventrecorder.NewAlertResolvedEvent(groupInfo, groupedAlertEvent(alert)) +func NewAlertResolvedEvent(groupInfo eventrecorder.AlertGroup, alert *alert.Alert) eventrecorder.EventData { + return eventrecorder.NewAlertResolvedEvent(groupInfo, alert) } // NewAlertGroupedEvent constructs alert-grouped event data. -func NewAlertGroupedEvent(groupInfo eventrecorder.AlertGroup, alert *types.Alert) eventrecorder.EventData { - return eventrecorder.NewAlertGroupedEvent(groupInfo, groupedAlertEvent(alert)) +func NewAlertGroupedEvent(groupInfo eventrecorder.AlertGroup, alert *alert.Alert) eventrecorder.EventData { + return eventrecorder.NewAlertGroupedEvent(groupInfo, alert) } diff --git a/notify/mute.go b/notify/mute.go index 12abc0ccbb..f4b94e01ce 100644 --- a/notify/mute.go +++ b/notify/mute.go @@ -102,16 +102,7 @@ func (n *MuteStage) Exec(ctx context.Context, logger *slog.Logger, alerts ...*al n.metrics.numNotificationSuppressedTotal.WithLabelValues(reason).Add(float64(len(muted))) logger.Debug("Notifications will not be sent for muted alerts", "alerts", fmt.Sprintf("%v", muted), "reason", reason) - // Record muted alert hashes in the context so downstream stages - // (e.g., the event recorder) can observe which alerts were muted. - mutedHashes, _ := MutedAlerts(ctx) - if mutedHashes == nil { - mutedHashes = make(map[uint64]struct{}, len(muted)) - } - for _, a := range muted { - mutedHashes[hashAlert(a)] = struct{}{} - } - ctx = WithMutedAlerts(ctx, mutedHashes) + ctx = withMutedAlerts(ctx, muted) } return ctx, filtered, nil diff --git a/notify/mute_test.go b/notify/mute_test.go index e92e241554..5e1b9a37ed 100644 --- a/notify/mute_test.go +++ b/notify/mute_test.go @@ -91,6 +91,29 @@ func TestMuteStage(t *testing.T) { } } +func TestMuteStageAccumulatesMutedAlertDetails(t *testing.T) { + metrics := NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}) + firstStage := NewMuteStage(MuteFunc(func(_ context.Context, lset model.LabelSet) bool { + return lset["muted_by"] == "first" + }), metrics) + secondStage := NewMuteStage(MuteFunc(func(_ context.Context, lset model.LabelSet) bool { + return lset["muted_by"] == "second" + }), metrics) + first := &alert.Alert{Alert: model.Alert{Labels: model.LabelSet{"alertname": "First", "muted_by": "first"}}} + second := &alert.Alert{Alert: model.Alert{Labels: model.LabelSet{"alertname": "Second", "muted_by": "second"}}} + active := &alert.Alert{Alert: model.Alert{Labels: model.LabelSet{"alertname": "Active"}}} + + ctx, alerts, err := firstStage.Exec(context.Background(), promslog.NewNopLogger(), first, second, active) + require.NoError(t, err) + ctx, alerts, err = secondStage.Exec(ctx, promslog.NewNopLogger(), alerts...) + require.NoError(t, err) + require.Equal(t, []*alert.Alert{active}, alerts) + + muted, ok := mutedAlerts(ctx) + require.True(t, ok) + require.Equal(t, []*alert.Alert{first, second}, muted) +} + func TestMuteStageWithSilences(t *testing.T) { silences, err := silence.New(silence.Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) if err != nil { diff --git a/notify/notify_test.go b/notify/notify_test.go index a62a15df73..bd7b59f667 100644 --- a/notify/notify_test.go +++ b/notify/notify_test.go @@ -19,7 +19,10 @@ import ( "fmt" "io" "log/slog" + "os" + "path/filepath" "reflect" + "strings" "testing" "time" @@ -28,13 +31,15 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/common/promslog" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/prometheus/alertmanager/alert" "github.com/prometheus/alertmanager/eventrecorder" + eventsv2 "github.com/prometheus/alertmanager/eventrecorder/events/v2" "github.com/prometheus/alertmanager/featurecontrol" "github.com/prometheus/alertmanager/nflog" "github.com/prometheus/alertmanager/nflog/nflogpb" - "github.com/prometheus/alertmanager/types" ) type sendResolved bool @@ -43,15 +48,15 @@ func (s sendResolved) SendResolved() bool { return bool(s) } -type notifierFunc func(ctx context.Context, alerts ...*types.Alert) (bool, error) +type notifierFunc func(ctx context.Context, alerts ...*alert.Alert) (bool, error) -func (f notifierFunc) Notify(ctx context.Context, alerts ...*types.Alert) (bool, error) { +func (f notifierFunc) Notify(ctx context.Context, alerts ...*alert.Alert) (bool, error) { return f(ctx, alerts...) } type failStage struct{} -func (s failStage) Exec(ctx context.Context, l *slog.Logger, as ...*types.Alert) (context.Context, []*types.Alert, error) { +func (s failStage) Exec(ctx context.Context, l *slog.Logger, as ...*alert.Alert) (context.Context, []*alert.Alert, error) { return ctx, nil, fmt.Errorf("some error") } @@ -216,7 +221,7 @@ func TestDedupStageNeedsUpdate(t *testing.T) { func TestDedupStageUsesContextNow(t *testing.T) { base := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) s := &DedupStage{ - hash: func(*types.Alert) uint64 { return 1 }, + hash: func(*alert.Alert) uint64 { return 1 }, now: func() time.Time { return base.Add(time.Hour) }, @@ -235,7 +240,7 @@ func TestDedupStageUsesContextNow(t *testing.T) { ctx = WithRepeatInterval(ctx, 30*time.Minute) ctx = WithNow(ctx, base.Add(10*time.Minute)) - alerts := []*types.Alert{{Alert: model.Alert{Labels: model.LabelSet{"alertname": "test"}}}} + alerts := []*alert.Alert{{Alert: model.Alert{Labels: model.LabelSet{"alertname": "test"}}}} _, res, err := s.Exec(ctx, promslog.NewNopLogger(), alerts...) require.NoError(t, err) @@ -246,7 +251,7 @@ func TestDedupStage(t *testing.T) { i := 0 now := utcNow() s := &DedupStage{ - hash: func(a *types.Alert) uint64 { + hash: func(a *alert.Alert) uint64 { res := uint64(i) i++ return res @@ -269,7 +274,7 @@ func TestDedupStage(t *testing.T) { ctx = WithRepeatInterval(ctx, time.Hour) - alerts := []*types.Alert{{}, {}, {}} + alerts := []*alert.Alert{{}, {}, {}} // Must catch notification log query errors. s.nflog = &testNflog{ @@ -338,13 +343,13 @@ func TestDedupStage(t *testing.T) { func TestMultiStage(t *testing.T) { var ( - alerts1 = []*types.Alert{{}} - alerts2 = []*types.Alert{{}, {}} - alerts3 = []*types.Alert{{}, {}, {}} + alerts1 = []*alert.Alert{{}} + alerts2 = []*alert.Alert{{}, {}} + alerts3 = []*alert.Alert{{}, {}, {}} ) stage := MultiStage{ - StageFunc(func(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { + StageFunc(func(ctx context.Context, l *slog.Logger, alerts ...*alert.Alert) (context.Context, []*alert.Alert, error) { if !reflect.DeepEqual(alerts, alerts1) { t.Fatal("Input not equal to input of MultiStage") } @@ -352,7 +357,7 @@ func TestMultiStage(t *testing.T) { ctx = context.WithValue(ctx, "key", "value") return ctx, alerts2, nil }), - StageFunc(func(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { + StageFunc(func(ctx context.Context, l *slog.Logger, alerts ...*alert.Alert) (context.Context, []*alert.Alert, error) { if !reflect.DeepEqual(alerts, alerts2) { t.Fatal("Input not equal to output of previous stage") } @@ -389,12 +394,12 @@ func TestMultiStageFailure(t *testing.T) { func TestRoutingStage(t *testing.T) { var ( - alerts1 = []*types.Alert{{}} - alerts2 = []*types.Alert{{}, {}} + alerts1 = []*alert.Alert{{}} + alerts2 = []*alert.Alert{{}, {}} ) stage := RoutingStage{ - "name": StageFunc(func(ctx context.Context, l *slog.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) { + "name": StageFunc(func(ctx context.Context, l *slog.Logger, alerts ...*alert.Alert) (context.Context, []*alert.Alert, error) { if !reflect.DeepEqual(alerts, alerts1) { t.Fatal("Input not equal to input of RoutingStage") } @@ -417,9 +422,9 @@ func TestRoutingStage(t *testing.T) { func TestRetryStageWithError(t *testing.T) { fail, retry := true, true - sent := []*types.Alert{} + sent := []*alert.Alert{} i := Integration{ - notifier: notifierFunc(func(ctx context.Context, alerts ...*types.Alert) (bool, error) { + notifier: notifierFunc(func(ctx context.Context, alerts ...*alert.Alert) (bool, error) { if fail { fail = false return retry, errors.New("fail to deliver notification") @@ -431,7 +436,7 @@ func TestRetryStageWithError(t *testing.T) { } r := NewRetryStage(i, "", NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}), eventrecorder.NopRecorder()) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ EndsAt: time.Now().Add(time.Hour), @@ -474,7 +479,7 @@ func TestRetryStageWithErrorCode(t *testing.T) { testData := testData i := Integration{ name: "test", - notifier: notifierFunc(func(ctx context.Context, alerts ...*types.Alert) (bool, error) { + notifier: notifierFunc(func(ctx context.Context, alerts ...*alert.Alert) (bool, error) { if !testData.isNewErrorWithReason { return retry, errors.New("fail to deliver notification") } @@ -484,7 +489,7 @@ func TestRetryStageWithErrorCode(t *testing.T) { } r := NewRetryStage(i, "", NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}), eventrecorder.NopRecorder()) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ EndsAt: time.Now().Add(time.Hour), @@ -511,7 +516,7 @@ func TestRetryStageWithContextCanceled(t *testing.T) { i := Integration{ name: "test", - notifier: notifierFunc(func(ctx context.Context, alerts ...*types.Alert) (bool, error) { + notifier: notifierFunc(func(ctx context.Context, alerts ...*alert.Alert) (bool, error) { cancel() return true, errors.New("request failed: context canceled") }), @@ -519,7 +524,7 @@ func TestRetryStageWithContextCanceled(t *testing.T) { } r := NewRetryStage(i, "", NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}), eventrecorder.NopRecorder()) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ EndsAt: time.Now().Add(time.Hour), @@ -541,9 +546,9 @@ func TestRetryStageWithContextCanceled(t *testing.T) { } func TestRetryStageNoResolved(t *testing.T) { - sent := []*types.Alert{} + sent := []*alert.Alert{} i := Integration{ - notifier: notifierFunc(func(ctx context.Context, alerts ...*types.Alert) (bool, error) { + notifier: notifierFunc(func(ctx context.Context, alerts ...*alert.Alert) (bool, error) { sent = append(sent, alerts...) return false, nil }), @@ -551,7 +556,7 @@ func TestRetryStageNoResolved(t *testing.T) { } r := NewRetryStage(i, "", NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}), eventrecorder.NopRecorder()) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ EndsAt: time.Now().Add(-time.Hour), @@ -576,7 +581,7 @@ func TestRetryStageNoResolved(t *testing.T) { resctx, res, err = r.Exec(ctx, promslog.NewNopLogger(), alerts...) require.NoError(t, err) require.Equal(t, alerts, res) - require.Equal(t, []*types.Alert{alerts[1]}, sent) + require.Equal(t, []*alert.Alert{alerts[1]}, sent) require.NotNil(t, resctx) // All alerts are resolved. @@ -587,14 +592,67 @@ func TestRetryStageNoResolved(t *testing.T) { resctx, res, err = r.Exec(ctx, promslog.NewNopLogger(), alerts...) require.NoError(t, err) require.Equal(t, alerts, res) - require.Equal(t, []*types.Alert{}, sent) + require.Equal(t, []*alert.Alert{}, sent) require.NotNil(t, resctx) } +func TestRetryStageNotificationEventUsesDedupAlertState(t *testing.T) { + path := filepath.Join(t.TempDir(), "events.jsonl") + recorder := eventrecorder.NewRecorderFromConfig(eventrecorder.Config{ + FileOutputs: []eventrecorder.FileOutputConfig{{Path: path}}, + }, "test", promslog.NewNopLogger(), nil) + t.Cleanup(func() { require.NoError(t, recorder.Close()) }) + firing := &alert.Alert{Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "Firing", "instance": "api-1"}, StartsAt: time.Now(), EndsAt: time.Now().Add(time.Hour), + }} + resolved := &alert.Alert{Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "Resolved", "instance": "api-2"}, StartsAt: time.Now().Add(-time.Hour), EndsAt: time.Now().Add(-time.Minute), + }} + muted := &alert.Alert{Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "Muted", "instance": "api-3"}, StartsAt: time.Now(), EndsAt: time.Now().Add(time.Hour), + }} + var sent []*alert.Alert + integration := NewIntegration(notifierFunc(func(_ context.Context, alerts ...*alert.Alert) (bool, error) { + sent = append(sent, alerts...) + firing.EndsAt = time.Now().Add(-time.Minute) + return false, nil + }), sendResolved(false), "webhook", 2, "test") + dedup := NewDedupStage(&integration, &testNflog{}, &nflogpb.Receiver{}) + stage := NewRetryStage(integration, "test", NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}), recorder) + ctx := eventrecorder.WithEventRecording(context.Background()) + ctx = WithGroupKey(ctx, "group") + ctx = WithRepeatInterval(ctx, time.Hour) + ctx = withMutedAlerts(ctx, []*alert.Alert{muted}) + ctx, alerts, err := dedup.Exec(ctx, promslog.NewNopLogger(), firing, resolved) + require.NoError(t, err) + + _, alerts, err = stage.Exec(ctx, promslog.NewNopLogger(), alerts...) + require.NoError(t, err) + require.Equal(t, []*alert.Alert{firing, resolved}, alerts) + require.Equal(t, []*alert.Alert{firing}, sent) + require.True(t, firing.Resolved()) + require.NoError(t, recorder.Close()) + + data, err := os.ReadFile(path) + require.NoError(t, err) + var recorded eventsv2.Event + require.NoError(t, protojson.Unmarshal([]byte(strings.TrimSpace(string(data))), &recorded)) + notification := recorded.GetData().GetNotification() + require.Len(t, notification.Alerts, 1) + require.Len(t, notification.FiringAlerts, 1) + require.Len(t, notification.ResolvedAlerts, 1) + require.Len(t, notification.MutedAlerts, 1) + require.Equal(t, uint64(firing.Fingerprint()), notification.Alerts[0].Fingerprint) + require.Equal(t, uint64(firing.Fingerprint()), notification.FiringAlerts[0].Fingerprint) + require.Equal(t, uint64(resolved.Fingerprint()), notification.ResolvedAlerts[0].Fingerprint) + require.Equal(t, uint64(muted.Fingerprint()), notification.MutedAlerts[0].Fingerprint) + require.Equal(t, "api-3", notification.MutedAlerts[0].Labels["instance"]) +} + func TestRetryStageSendResolved(t *testing.T) { - sent := []*types.Alert{} + sent := []*alert.Alert{} i := Integration{ - notifier: notifierFunc(func(ctx context.Context, alerts ...*types.Alert) (bool, error) { + notifier: notifierFunc(func(ctx context.Context, alerts ...*alert.Alert) (bool, error) { sent = append(sent, alerts...) return false, nil }), @@ -602,7 +660,7 @@ func TestRetryStageSendResolved(t *testing.T) { } r := NewRetryStage(i, "", NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}), eventrecorder.NopRecorder()) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ EndsAt: time.Now().Add(-time.Hour), @@ -642,7 +700,7 @@ func TestSetNotifiesStage(t *testing.T) { recv: &nflogpb.Receiver{GroupName: "test"}, nflog: tnflog, } - alerts := []*types.Alert{{}, {}, {}} + alerts := []*alert.Alert{{}, {}, {}} ctx := context.Background() resctx, res, err := s.Exec(ctx, promslog.NewNopLogger(), alerts...) @@ -713,7 +771,7 @@ func TestReceiverData_PreservationWhenNotifierDoesNotUpdate(t *testing.T) { recv := &nflogpb.Receiver{GroupName: "test"} dedupStage := NewDedupStage(sendResolved(true), tnflog, recv) - notifier := notifierFunc(func(ctx context.Context, alerts ...*types.Alert) (bool, error) { + notifier := notifierFunc(func(ctx context.Context, alerts ...*alert.Alert) (bool, error) { callCount++ if callCount == 1 { @@ -736,7 +794,7 @@ func TestReceiverData_PreservationWhenNotifierDoesNotUpdate(t *testing.T) { ctx = WithGroupKey(ctx, "testkey") ctx = WithRepeatInterval(ctx, time.Hour) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "test"}, @@ -828,7 +886,7 @@ func TestDedupStageExtractsReceiverData_DataPresent(t *testing.T) { ctx = WithGroupKey(ctx, "key") ctx = WithRepeatInterval(ctx, time.Hour) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "test"}, @@ -870,7 +928,7 @@ func TestDedupStageExtractsReceiverData_NilReceiverData(t *testing.T) { ctx = WithGroupKey(ctx, "key") ctx = WithRepeatInterval(ctx, time.Hour) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "test"}, @@ -897,7 +955,7 @@ func TestDedupStageExtractsReceiverData_NoEntry(t *testing.T) { ctx = WithGroupKey(ctx, "key") ctx = WithRepeatInterval(ctx, time.Hour) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "test"}, @@ -928,7 +986,7 @@ func TestNflogStore_NoLeakBetweenNotificationSequences(t *testing.T) { recv := &nflogpb.Receiver{GroupName: "test"} dedupStage := NewDedupStage(sendResolved(true), tnflog, recv) - notifier := notifierFunc(func(ctx context.Context, alerts ...*types.Alert) (bool, error) { + notifier := notifierFunc(func(ctx context.Context, alerts ...*alert.Alert) (bool, error) { callCount++ store, ok := NflogStore(ctx) require.True(t, ok, "Store should be available in context") @@ -947,7 +1005,7 @@ func TestNflogStore_NoLeakBetweenNotificationSequences(t *testing.T) { retryStage := NewRetryStage(integration, "test", NewMetrics(prometheus.NewRegistry(), featurecontrol.NoopFlags{}), eventrecorder.NopRecorder()) setNotifiesStage := NewSetNotifiesStage(tnflog, recv) - alerts := []*types.Alert{ + alerts := []*alert.Alert{ { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "test"}, @@ -1021,7 +1079,7 @@ func TestNflogStore_NoLeakBetweenNotificationSequences(t *testing.T) { } func BenchmarkHashAlert(b *testing.B) { - alert := &types.Alert{ + alert := &alert.Alert{ Alert: model.Alert{ Labels: model.LabelSet{"foo": "the_first_value", "bar": "the_second_value", "another": "value"}, }, diff --git a/notify/retry_stage.go b/notify/retry_stage.go index fb3b93ef0c..739be35bcc 100644 --- a/notify/retry_stage.go +++ b/notify/retry_stage.go @@ -179,7 +179,7 @@ func (r RetryStage) exec(ctx context.Context, l *slog.Logger, alerts ...*alert.A } r.recorder.RecordEvent(ctx, func() eventrecorder.EventData { - return NewNotificationEvent(ctx, sent, r.integration) + return newNotificationEvent(ctx, sent, alerts, r.integration) }) return ctx, alerts, nil } diff --git a/proto/eventrecorder/events/v2/events.proto b/proto/eventrecorder/events/v2/events.proto index 5d1ef36c46..84ef703f72 100644 --- a/proto/eventrecorder/events/v2/events.proto +++ b/proto/eventrecorder/events/v2/events.proto @@ -51,12 +51,6 @@ message Alert { bool resolved = 7; } -// GroupedAlert is a reference to an alert within an aggregation group. -message GroupedAlert { - uint64 hash = 1; - optional Alert details = 2; -} - // AlertGroupInfo describes an alert's aggregation group context. message AlertGroupInfo { string group_key = 1; @@ -74,13 +68,13 @@ message AlertCreatedEvent { // AlertResolvedEvent is emitted when a resolved alert leaves its group. message AlertResolvedEvent { - GroupedAlert alert = 1; + Alert alert = 1; AlertGroupInfo group_info = 2; } // AlertGroupedEvent is emitted when an alert first enters a group. message AlertGroupedEvent { - GroupedAlert alert = 1; + Alert alert = 1; AlertGroupInfo group_info = 2; } @@ -102,10 +96,10 @@ message Integration { // NotificationEvent is emitted after a notification is delivered. message NotificationEvent { - repeated GroupedAlert alerts = 1; - repeated GroupedAlert firing_alerts = 2; - repeated GroupedAlert resolved_alerts = 3; - repeated GroupedAlert muted_alerts = 4; + repeated Alert alerts = 1; + repeated Alert firing_alerts = 2; + repeated Alert resolved_alerts = 3; + repeated Alert muted_alerts = 4; AlertGroupInfo group_info = 5; google.protobuf.Duration repeat_interval = 6; NotifyReason reason = 7;