github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/alertmanager/merger/v2_alert_groups.go (about) 1 package merger 2 3 import ( 4 "errors" 5 "sort" 6 7 "github.com/go-openapi/swag" 8 v2 "github.com/prometheus/alertmanager/api/v2" 9 v2_models "github.com/prometheus/alertmanager/api/v2/models" 10 prom_model "github.com/prometheus/common/model" 11 ) 12 13 // V2AlertGroups implements the Merger interface for GET /v2/alerts/groups. It returns 14 // the union of alert groups over all the responses. When the same alert exists in the same 15 // group for multiple responses, the instance of that alert with the most recent UpdatedAt 16 // timestamp is returned in that group within the response. 17 type V2AlertGroups struct{} 18 19 func (V2AlertGroups) MergeResponses(in [][]byte) ([]byte, error) { 20 groups := make(v2_models.AlertGroups, 0) 21 for _, body := range in { 22 parsed := make(v2_models.AlertGroups, 0) 23 if err := swag.ReadJSON(body, &parsed); err != nil { 24 return nil, err 25 } 26 groups = append(groups, parsed...) 27 } 28 29 merged, err := mergeV2AlertGroups(groups) 30 if err != nil { 31 return nil, err 32 } 33 34 return swag.WriteJSON(merged) 35 } 36 37 func mergeV2AlertGroups(in v2_models.AlertGroups) (v2_models.AlertGroups, error) { 38 // Gather lists of all alerts for each distinct group. 39 groups := make(map[groupKey]*v2_models.AlertGroup) 40 for _, group := range in { 41 if group.Receiver == nil { 42 return nil, errors.New("unexpected nil receiver") 43 } 44 if group.Receiver.Name == nil { 45 return nil, errors.New("unexpected nil receiver name") 46 } 47 48 key := getGroupKey(group) 49 if current, ok := groups[key]; ok { 50 current.Alerts = append(current.Alerts, group.Alerts...) 51 } else { 52 groups[key] = group 53 } 54 } 55 56 // Merge duplicates of the same alert within each group. 57 for _, group := range groups { 58 var err error 59 group.Alerts, err = mergeV2Alerts(group.Alerts) 60 if err != nil { 61 return nil, err 62 } 63 } 64 65 result := make(v2_models.AlertGroups, 0, len(groups)) 66 for _, group := range groups { 67 result = append(result, group) 68 } 69 70 // Mimic Alertmanager which returns groups ordered by labels and receiver. 71 sort.Sort(byGroup(result)) 72 73 return result, nil 74 } 75 76 // getGroupKey returns an identity for a group which can be used to match it against other groups. 77 // Only the receiver name is necessary to ensure grouping by receiver, and for the labels, we again 78 // use the same method for matching the group labels as used internally, generating the fingerprint. 79 func getGroupKey(group *v2_models.AlertGroup) groupKey { 80 return groupKey{ 81 fingerprint: prom_model.LabelsToSignature(group.Labels), 82 receiver: *group.Receiver.Name, 83 } 84 } 85 86 type groupKey struct { 87 fingerprint uint64 88 receiver string 89 } 90 91 // byGroup implements the ordering of Alertmanager dispatch.AlertGroups on the OpenAPI type. 92 type byGroup v2_models.AlertGroups 93 94 func (ag byGroup) Swap(i, j int) { ag[i], ag[j] = ag[j], ag[i] } 95 func (ag byGroup) Less(i, j int) bool { 96 iLabels := v2.APILabelSetToModelLabelSet(ag[i].Labels) 97 jLabels := v2.APILabelSetToModelLabelSet(ag[j].Labels) 98 99 if iLabels.Equal(jLabels) { 100 return *ag[i].Receiver.Name < *ag[j].Receiver.Name 101 } 102 return iLabels.Before(jLabels) 103 } 104 func (ag byGroup) Len() int { return len(ag) }