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) }