mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
232 lines
6.9 KiB
Go
232 lines
6.9 KiB
Go
package alertmanager
|
|
|
|
import (
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/prymitive/karma/internal/config"
|
|
"github.com/prymitive/karma/internal/models"
|
|
"github.com/prymitive/karma/internal/slices"
|
|
"github.com/prymitive/karma/internal/transform"
|
|
)
|
|
|
|
// DedupAlerts will collect alert groups from all defined Alertmanager
|
|
// upstreams and deduplicate them, so we only return unique alerts
|
|
func DedupAlerts() []models.AlertGroup {
|
|
uniqueGroups := map[string][]models.AlertGroup{}
|
|
|
|
upstreams := GetAlertmanagers()
|
|
for _, am := range upstreams {
|
|
groups := am.Alerts()
|
|
for _, ag := range groups {
|
|
if _, found := uniqueGroups[ag.ID]; !found {
|
|
uniqueGroups[ag.ID] = []models.AlertGroup{}
|
|
}
|
|
uniqueGroups[ag.ID] = append(uniqueGroups[ag.ID], ag)
|
|
}
|
|
}
|
|
|
|
dedupedGroups := []models.AlertGroup{}
|
|
alertStates := map[string][]string{}
|
|
for _, agList := range uniqueGroups {
|
|
alerts := map[string]models.Alert{}
|
|
for _, ag := range agList {
|
|
for _, alert := range ag.Alerts {
|
|
alert := alert // scopelint pin
|
|
// remove all alerts for receiver(s) that the user doesn't
|
|
// want to see in the UI
|
|
if transform.StripReceivers(config.Config.Receivers.Keep, config.Config.Receivers.Strip, alert.Receiver) {
|
|
continue
|
|
}
|
|
alertLFP := alert.LabelsFingerprint()
|
|
a, found := alerts[alertLFP]
|
|
if found {
|
|
// if we already have an alert with the same fp then just append
|
|
// alertmanager instances to it, this way we end up with all instances
|
|
// for each unique alert merged into a single alert with all
|
|
// alertmanager instances attached to it
|
|
a.Alertmanager = append(a.Alertmanager, alert.Alertmanager...)
|
|
// set startsAt to the earliest value we have
|
|
if alert.StartsAt.Before(a.StartsAt) {
|
|
a.StartsAt = alert.StartsAt
|
|
}
|
|
// update map
|
|
alerts[alertLFP] = a
|
|
// and append alert state to the slice
|
|
alertStates[alertLFP] = append(alertStates[alertLFP], alert.State)
|
|
} else {
|
|
alerts[alertLFP] = models.Alert(alert)
|
|
// seed alert state slice
|
|
alertStates[alertLFP] = []string{alert.State}
|
|
}
|
|
}
|
|
}
|
|
// skip empty groups
|
|
if len(alerts) == 0 {
|
|
continue
|
|
}
|
|
ag := models.AlertGroup(agList[0])
|
|
ag.Alerts = make(models.AlertList, 0, len(alerts))
|
|
for _, alert := range alerts {
|
|
alert := alert // scopelint pin
|
|
// strip labels and annotations user doesn't want to see in the UI
|
|
alert.Labels = transform.StripLables(config.Config.Labels.Keep, config.Config.Labels.Strip, alert.Labels)
|
|
alert.Annotations = transform.StripAnnotations(config.Config.Annotations.Keep, config.Config.Annotations.Strip, alert.Annotations)
|
|
// calculate final alert state based on the most important value found
|
|
// in the list of states from all instances
|
|
alertLFP := alert.LabelsFingerprint()
|
|
if slices.StringInSlice(alertStates[alertLFP], models.AlertStateActive) {
|
|
alert.State = models.AlertStateActive
|
|
} else if slices.StringInSlice(alertStates[alertLFP], models.AlertStateSuppressed) {
|
|
alert.State = models.AlertStateSuppressed
|
|
} else {
|
|
alert.State = models.AlertStateUnprocessed
|
|
}
|
|
// sort Alertmanager instances for every alert
|
|
sort.Slice(alert.Alertmanager, func(i, j int) bool {
|
|
return alert.Alertmanager[i].Name < alert.Alertmanager[j].Name
|
|
})
|
|
ag.Alerts = append(ag.Alerts, alert)
|
|
}
|
|
ag.Hash = ag.ContentFingerprint()
|
|
dedupedGroups = append(dedupedGroups, ag)
|
|
}
|
|
|
|
return dedupedGroups
|
|
}
|
|
|
|
// DedupKnownLabels returns a deduplicated slice of all known label names
|
|
func DedupSilences() []models.ManagedSilence {
|
|
silenceByCluster := map[string]map[string]models.Silence{}
|
|
upstreams := GetAlertmanagers()
|
|
|
|
for _, am := range upstreams {
|
|
for id, silence := range am.Silences() {
|
|
cluster := am.ClusterName()
|
|
|
|
if _, found := silenceByCluster[cluster]; !found {
|
|
silenceByCluster[cluster] = map[string]models.Silence{}
|
|
}
|
|
|
|
if _, ok := silenceByCluster[cluster][id]; !ok {
|
|
silenceByCluster[cluster][id] = silence
|
|
}
|
|
}
|
|
}
|
|
|
|
now := time.Now()
|
|
dedupedSilences := []models.ManagedSilence{}
|
|
for cluster, silenceMap := range silenceByCluster {
|
|
s := make([]models.ManagedSilence, 0, len(silenceMap))
|
|
for _, silence := range silenceMap {
|
|
managedSilence := models.ManagedSilence{
|
|
Cluster: cluster,
|
|
IsExpired: silence.EndsAt.Before(now),
|
|
Silence: silence,
|
|
}
|
|
s = append(s, managedSilence)
|
|
}
|
|
dedupedSilences = append(dedupedSilences, s...)
|
|
}
|
|
return dedupedSilences
|
|
}
|
|
|
|
// DedupColors returns a color map merged from all Alertmanager upstream color
|
|
// maps
|
|
func DedupColors() models.LabelsColorMap {
|
|
dedupedColors := models.LabelsColorMap{}
|
|
|
|
upstreams := GetAlertmanagers()
|
|
|
|
for _, am := range upstreams {
|
|
colors := am.Colors()
|
|
// map[string]map[string]LabelColors
|
|
for labelName, valueMap := range colors {
|
|
if _, found := dedupedColors[labelName]; !found {
|
|
dedupedColors[labelName] = make(map[string]models.LabelColors, len(valueMap))
|
|
}
|
|
for labelVal, labelColors := range valueMap {
|
|
if _, found := dedupedColors[labelName][labelVal]; !found {
|
|
dedupedColors[labelName][labelVal] = labelColors
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return dedupedColors
|
|
}
|
|
|
|
// DedupAutocomplete returns a list of autocomplete hints merged from all
|
|
// Alertmanager upstreams
|
|
func DedupAutocomplete() []models.Autocomplete {
|
|
uniqueAutocomplete := map[string]*models.Autocomplete{}
|
|
|
|
upstreams := GetAlertmanagers()
|
|
|
|
for _, am := range upstreams {
|
|
ac := am.Autocomplete()
|
|
for _, hint := range ac {
|
|
h, found := uniqueAutocomplete[hint.Value]
|
|
if found {
|
|
for _, token := range hint.Tokens {
|
|
if !slices.StringInSlice(h.Tokens, token) {
|
|
h.Tokens = append(h.Tokens, token)
|
|
}
|
|
}
|
|
} else {
|
|
uniqueAutocomplete[hint.Value] = &models.Autocomplete{
|
|
Value: hint.Value,
|
|
Tokens: hint.Tokens,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
dedupedAutocomplete := make([]models.Autocomplete, 0, len(uniqueAutocomplete))
|
|
for _, hint := range uniqueAutocomplete {
|
|
dedupedAutocomplete = append(dedupedAutocomplete, *hint)
|
|
}
|
|
|
|
return dedupedAutocomplete
|
|
}
|
|
|
|
// DedupKnownLabels returns a deduplicated slice of all known label names
|
|
func DedupKnownLabels() []string {
|
|
dedupedLabels := map[string]bool{}
|
|
upstreams := GetAlertmanagers()
|
|
|
|
for _, am := range upstreams {
|
|
for _, key := range am.KnownLabels() {
|
|
dedupedLabels[key] = true
|
|
}
|
|
}
|
|
|
|
flatLabels := make([]string, 0, len(dedupedLabels))
|
|
for key := range dedupedLabels {
|
|
flatLabels = append(flatLabels, key)
|
|
}
|
|
return flatLabels
|
|
}
|
|
|
|
// DedupKnownLabelValues returns a list of all known values for label $name
|
|
func DedupKnownLabelValues(name string) []string {
|
|
dedupedValues := map[string]bool{}
|
|
upstreams := GetAlertmanagers()
|
|
|
|
for _, am := range upstreams {
|
|
for _, ag := range am.Alerts() {
|
|
for _, alert := range ag.Alerts {
|
|
if val, found := alert.Labels[name]; found {
|
|
dedupedValues[val] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
flatValues := make([]string, 0, len(dedupedValues))
|
|
for key := range dedupedValues {
|
|
flatValues = append(flatValues, key)
|
|
}
|
|
return flatValues
|
|
}
|