2021-03-16 04:44:52 -05:00
package notifier
import (
"context"
2021-04-13 07:02:44 -05:00
"crypto/md5"
2024-01-16 10:12:24 -06:00
"encoding/binary"
2021-04-13 07:02:44 -05:00
"encoding/json"
"fmt"
2021-03-16 04:44:52 -05:00
"path/filepath"
2021-08-12 08:04:09 -05:00
"strconv"
2021-03-16 04:44:52 -05:00
"time"
2023-02-03 10:36:49 -06:00
alertingNotify "github.com/grafana/alerting/notify"
"github.com/grafana/alerting/receivers"
alertingTemplates "github.com/grafana/alerting/templates"
2023-06-15 03:18:01 -05:00
"github.com/prometheus/alertmanager/config"
2023-02-03 10:36:49 -06:00
2023-01-30 02:55:35 -06:00
amv2 "github.com/prometheus/alertmanager/api/v2/models"
2021-09-09 11:25:22 -05:00
"github.com/grafana/grafana/pkg/infra/kvstore"
2021-03-16 04:44:52 -05:00
"github.com/grafana/grafana/pkg/infra/log"
2021-04-22 10:12:18 -05:00
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
2021-04-30 11:28:06 -05:00
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
2021-03-31 15:00:56 -05:00
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
2021-03-24 09:20:44 -05:00
"github.com/grafana/grafana/pkg/services/ngalert/store"
2022-01-26 09:42:40 -06:00
"github.com/grafana/grafana/pkg/services/notifications"
2021-03-16 04:44:52 -05:00
"github.com/grafana/grafana/pkg/setting"
)
2021-03-24 09:20:44 -05:00
const (
2023-11-29 05:49:39 -06:00
NotificationLogFilename = "notifications"
SilencesFilename = "silences"
2021-09-09 11:25:22 -05:00
2021-08-24 05:28:09 -05:00
workingDir = "alerting"
2023-01-23 04:30:10 -06:00
// maintenanceNotificationAndSilences how often should we flush and garbage collect notifications
2023-01-12 10:31:38 -06:00
notificationLogMaintenanceInterval = 15 * time . Minute
2021-03-24 09:20:44 -05:00
)
2022-03-23 03:49:02 -05:00
// How long should we keep silences and notification entries on-disk after they've served their purpose.
var retentionNotificationsAndSilences = 5 * 24 * time . Hour
var silenceMaintenanceInterval = 15 * time . Minute
2022-05-23 01:24:20 -05:00
type AlertingStore interface {
store . AlertingStore
2022-05-26 00:29:56 -05:00
store . ImageStore
2024-02-15 08:45:10 -06:00
autogenRuleStore
2022-05-23 01:24:20 -05:00
}
2023-09-06 05:59:29 -05:00
type alertmanager struct {
2023-02-03 10:36:49 -06:00
Base * alertingNotify . GrafanaAlertmanager
2022-01-07 02:40:09 -06:00
logger log . Logger
2021-05-17 12:06:47 -05:00
2023-06-15 03:18:01 -05:00
ConfigMetrics * metrics . AlertmanagerConfigMetrics
2022-01-26 09:42:40 -06:00
Settings * setting . Cfg
2022-05-23 01:24:20 -05:00
Store AlertingStore
2022-01-26 09:42:40 -06:00
fileStore * FileStore
NotificationService notifications . Service
2021-03-16 04:44:52 -05:00
2023-04-25 12:39:46 -05:00
decryptFn alertingNotify . GetDecryptedValueFn
2023-01-23 04:30:10 -06:00
orgID int64
2024-02-15 08:45:10 -06:00
withAutogen bool
2021-03-16 04:44:52 -05:00
}
2023-01-12 10:31:38 -06:00
// maintenanceOptions represent the options for components that need maintenance on a frequency within the Alertmanager.
// It implements the alerting.MaintenanceOptions interface.
type maintenanceOptions struct {
filepath string
retention time . Duration
maintenanceFrequency time . Duration
2023-02-03 10:36:49 -06:00
maintenanceFunc func ( alertingNotify . State ) ( int64 , error )
2023-01-12 10:31:38 -06:00
}
func ( m maintenanceOptions ) Filepath ( ) string {
return m . filepath
}
func ( m maintenanceOptions ) Retention ( ) time . Duration {
return m . retention
}
func ( m maintenanceOptions ) MaintenanceFrequency ( ) time . Duration {
return m . maintenanceFrequency
}
2023-02-03 10:36:49 -06:00
func ( m maintenanceOptions ) MaintenanceFunc ( state alertingNotify . State ) ( int64 , error ) {
2023-01-12 10:31:38 -06:00
return m . maintenanceFunc ( state )
}
2023-12-21 08:26:31 -06:00
func NewAlertmanager ( ctx context . Context , orgID int64 , cfg * setting . Cfg , store AlertingStore , kvStore kvstore . KVStore ,
2023-04-25 12:39:46 -05:00
peer alertingNotify . ClusterPeer , decryptFn alertingNotify . GetDecryptedValueFn , ns notifications . Service ,
2024-02-15 08:45:10 -06:00
m * metrics . Alertmanager , withAutogen bool ) ( * alertmanager , error ) {
2023-01-13 10:54:38 -06:00
workingPath := filepath . Join ( cfg . DataPath , workingDir , strconv . Itoa ( int ( orgID ) ) )
fileStore := NewFileStore ( orgID , kvStore , workingPath )
2021-03-16 04:44:52 -05:00
2023-11-29 05:49:39 -06:00
nflogFilepath , err := fileStore . FilepathFor ( ctx , NotificationLogFilename )
2021-09-09 11:25:22 -05:00
if err != nil {
return nil , err
}
2023-11-29 05:49:39 -06:00
silencesFilepath , err := fileStore . FilepathFor ( ctx , SilencesFilename )
2021-09-09 11:25:22 -05:00
if err != nil {
return nil , err
}
2021-05-17 12:06:47 -05:00
2023-01-12 10:31:38 -06:00
silencesOptions := maintenanceOptions {
2023-11-29 05:49:39 -06:00
filepath : silencesFilepath ,
2023-01-12 10:31:38 -06:00
retention : retentionNotificationsAndSilences ,
maintenanceFrequency : silenceMaintenanceInterval ,
2023-02-03 10:36:49 -06:00
maintenanceFunc : func ( state alertingNotify . State ) ( int64 , error ) {
2023-03-02 13:19:52 -06:00
// Detached context here is to make sure that when the service is shut down the persist operation is executed.
2023-11-29 05:49:39 -06:00
return fileStore . Persist ( context . Background ( ) , SilencesFilename , state )
2023-01-12 10:31:38 -06:00
} ,
}
nflogOptions := maintenanceOptions {
filepath : nflogFilepath ,
retention : retentionNotificationsAndSilences ,
maintenanceFrequency : notificationLogMaintenanceInterval ,
2023-02-03 10:36:49 -06:00
maintenanceFunc : func ( state alertingNotify . State ) ( int64 , error ) {
2023-03-02 13:19:52 -06:00
// Detached context here is to make sure that when the service is shut down the persist operation is executed.
2023-11-29 05:49:39 -06:00
return fileStore . Persist ( context . Background ( ) , NotificationLogFilename , state )
2023-01-12 10:31:38 -06:00
} ,
}
2023-02-03 10:36:49 -06:00
amcfg := & alertingNotify . GrafanaAlertmanagerConfig {
2023-04-28 09:56:59 -05:00
ExternalURL : cfg . AppURL ,
2023-01-13 10:54:38 -06:00
AlertStoreCallback : nil ,
PeerTimeout : cfg . UnifiedAlerting . HAPeerTimeout ,
Silences : silencesOptions ,
Nflog : nflogOptions ,
2021-03-19 03:26:00 -05:00
}
2023-01-13 10:54:38 -06:00
2024-01-23 09:29:38 -06:00
l := log . New ( "ngalert.notifier.alertmanager" , "org" , orgID )
2023-02-03 10:36:49 -06:00
gam , err := alertingNotify . NewGrafanaAlertmanager ( "orgID" , orgID , amcfg , peer , l , alertingNotify . NewGrafanaAlertmanagerMetrics ( m . Registerer ) )
2021-03-16 04:44:52 -05:00
if err != nil {
2023-01-13 10:54:38 -06:00
return nil , err
2021-03-16 04:44:52 -05:00
}
2023-09-06 05:59:29 -05:00
am := & alertmanager {
2023-01-13 10:54:38 -06:00
Base : gam ,
2023-06-15 03:18:01 -05:00
ConfigMetrics : m . AlertmanagerConfigMetrics ,
2023-01-13 10:54:38 -06:00
Settings : cfg ,
Store : store ,
NotificationService : ns ,
orgID : orgID ,
decryptFn : decryptFn ,
fileStore : fileStore ,
logger : l ,
2024-02-15 08:45:10 -06:00
// TODO: Preferably, logic around autogen would be outside of the specific alertmanager implementation so that remote alertmanager will get it for free.
withAutogen : withAutogen ,
2021-03-24 09:20:44 -05:00
}
2021-05-13 13:01:38 -05:00
return am , nil
2021-03-19 03:26:00 -05:00
}
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) Ready ( ) bool {
2021-07-12 08:23:01 -05:00
// We consider AM as ready only when the config has been
// applied at least once successfully. Until then, some objects
// can still be nil.
2023-01-13 10:54:38 -06:00
return am . Base . Ready ( )
2021-08-17 07:49:05 -05:00
}
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) StopAndWait ( ) {
2023-01-13 10:54:38 -06:00
am . Base . StopAndWait ( )
2021-03-19 03:26:00 -05:00
}
2021-03-16 04:44:52 -05:00
2023-02-02 11:45:17 -06:00
// SaveAndApplyDefaultConfig saves the default configuration to the database and applies it to the Alertmanager.
2023-01-23 04:30:10 -06:00
// It rolls back the save if we fail to apply the configuration.
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) SaveAndApplyDefaultConfig ( ctx context . Context ) error {
2023-01-13 10:54:38 -06:00
var outerErr error
am . Base . WithLock ( func ( ) {
cmd := & ngmodels . SaveAlertmanagerConfigurationCmd {
AlertmanagerConfiguration : am . Settings . UnifiedAlerting . DefaultConfiguration ,
Default : true ,
ConfigurationVersion : fmt . Sprintf ( "v%d" , ngmodels . AlertConfigurationVersion ) ,
OrgID : am . orgID ,
2023-02-02 11:45:17 -06:00
LastApplied : time . Now ( ) . UTC ( ) . Unix ( ) ,
2023-01-13 10:54:38 -06:00
}
2021-07-16 12:07:31 -05:00
2023-01-13 10:54:38 -06:00
cfg , err := Load ( [ ] byte ( am . Settings . UnifiedAlerting . DefaultConfiguration ) )
if err != nil {
outerErr = err
return
}
2021-07-16 12:07:31 -05:00
2023-01-13 10:54:38 -06:00
err = am . Store . SaveAlertmanagerConfigurationWithCallback ( ctx , cmd , func ( ) error {
2024-02-15 08:45:10 -06:00
if am . withAutogen {
err := AddAutogenConfig ( ctx , am . logger , am . Store , am . orgID , & cfg . AlertmanagerConfig , true )
if err != nil {
return err
}
}
_ , err = am . applyConfig ( cfg )
2023-02-02 11:45:17 -06:00
return err
2023-01-13 10:54:38 -06:00
} )
if err != nil {
2024-02-15 08:45:10 -06:00
outerErr = err
2023-01-13 10:54:38 -06:00
return
2021-07-16 12:07:31 -05:00
}
} )
2023-01-13 10:54:38 -06:00
return outerErr
2021-07-16 12:07:31 -05:00
}
2021-05-14 13:49:54 -05:00
// SaveAndApplyConfig saves the configuration the database and applies the configuration to the Alertmanager.
// It rollbacks the save if we fail to apply the configuration.
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) SaveAndApplyConfig ( ctx context . Context , cfg * apimodels . PostableUserConfig ) error {
2024-02-15 08:45:10 -06:00
// Remove autogenerated config from the user config before saving it, may not be necessary as we already remove
// the autogenerated config before provenance guard. However, this is low impact and a good safety net.
RemoveAutogenConfigIfExists ( cfg . AlertmanagerConfig . Route )
2021-04-13 07:02:44 -05:00
rawConfig , err := json . Marshal ( & cfg )
if err != nil {
2021-04-22 04:18:25 -05:00
return fmt . Errorf ( "failed to serialize to the Alertmanager configuration: %w" , err )
2021-04-13 07:02:44 -05:00
}
2023-01-13 10:54:38 -06:00
var outerErr error
am . Base . WithLock ( func ( ) {
cmd := & ngmodels . SaveAlertmanagerConfigurationCmd {
AlertmanagerConfiguration : string ( rawConfig ) ,
ConfigurationVersion : fmt . Sprintf ( "v%d" , ngmodels . AlertConfigurationVersion ) ,
OrgID : am . orgID ,
2023-02-02 11:45:17 -06:00
LastApplied : time . Now ( ) . UTC ( ) . Unix ( ) ,
2023-01-13 10:54:38 -06:00
}
2021-04-13 07:02:44 -05:00
2023-01-13 10:54:38 -06:00
err = am . Store . SaveAlertmanagerConfigurationWithCallback ( ctx , cmd , func ( ) error {
2024-02-15 08:45:10 -06:00
if am . withAutogen {
err := AddAutogenConfig ( ctx , am . logger , am . Store , am . orgID , & cfg . AlertmanagerConfig , false )
if err != nil {
return err
}
}
_ , err = am . applyConfig ( cfg )
2023-02-02 11:45:17 -06:00
return err
2023-01-13 10:54:38 -06:00
} )
if err != nil {
outerErr = err
return
2021-05-14 13:49:54 -05:00
}
} )
2021-04-13 07:02:44 -05:00
2023-01-13 10:54:38 -06:00
return outerErr
2021-03-19 03:26:00 -05:00
}
2021-03-17 11:38:33 -05:00
2021-09-21 10:01:23 -05:00
// ApplyConfig applies the configuration to the Alertmanager.
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) ApplyConfig ( ctx context . Context , dbCfg * ngmodels . AlertConfiguration ) error {
2021-09-21 10:01:23 -05:00
var err error
cfg , err := Load ( [ ] byte ( dbCfg . AlertmanagerConfiguration ) )
2021-04-13 07:02:44 -05:00
if err != nil {
2021-09-21 10:01:23 -05:00
return fmt . Errorf ( "failed to parse Alertmanager config: %w" , err )
2021-03-24 09:20:44 -05:00
}
2023-01-13 10:54:38 -06:00
var outerErr error
am . Base . WithLock ( func ( ) {
2024-02-15 08:45:10 -06:00
if am . withAutogen {
err := AddAutogenConfig ( ctx , am . logger , am . Store , am . orgID , & cfg . AlertmanagerConfig , true )
if err != nil {
outerErr = err
return
}
}
// Note: Adding the autogen config here causes alert_configuration_history to update last_applied more often.
// Since we will now update last_applied when autogen changes even if the user-created config remains the same.
// To fix this however, the local alertmanager needs to be able to tell the difference between user-created and
// autogen config, which may introduce cross-cutting complexity.
2024-01-31 13:05:30 -06:00
if err := am . applyAndMarkConfig ( ctx , dbCfg . ConfigurationHash , cfg ) ; err != nil {
2023-01-13 10:54:38 -06:00
outerErr = fmt . Errorf ( "unable to apply configuration: %w" , err )
return
}
} )
2021-04-22 04:18:25 -05:00
2023-01-13 10:54:38 -06:00
return outerErr
2021-03-19 03:26:00 -05:00
}
2023-06-15 03:18:01 -05:00
type AggregateMatchersUsage struct {
Matchers int
MatchRE int
Match int
ObjectMatchers int
}
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) updateConfigMetrics ( cfg * apimodels . PostableUserConfig ) {
2023-06-15 03:18:01 -05:00
var amu AggregateMatchersUsage
am . aggregateRouteMatchers ( cfg . AlertmanagerConfig . Route , & amu )
am . aggregateInhibitMatchers ( cfg . AlertmanagerConfig . InhibitRules , & amu )
am . ConfigMetrics . Matchers . Set ( float64 ( amu . Matchers ) )
am . ConfigMetrics . MatchRE . Set ( float64 ( amu . MatchRE ) )
am . ConfigMetrics . Match . Set ( float64 ( amu . Match ) )
am . ConfigMetrics . ObjectMatchers . Set ( float64 ( amu . ObjectMatchers ) )
2024-01-16 10:12:24 -06:00
am . ConfigMetrics . ConfigHash .
WithLabelValues ( strconv . FormatInt ( am . orgID , 10 ) ) .
Set ( hashAsMetricValue ( am . Base . ConfigHash ( ) ) )
2023-06-15 03:18:01 -05:00
}
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) aggregateRouteMatchers ( r * apimodels . Route , amu * AggregateMatchersUsage ) {
2023-06-15 03:18:01 -05:00
amu . Matchers += len ( r . Matchers )
amu . MatchRE += len ( r . MatchRE )
amu . Match += len ( r . Match )
amu . ObjectMatchers += len ( r . ObjectMatchers )
for _ , next := range r . Routes {
am . aggregateRouteMatchers ( next , amu )
}
}
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) aggregateInhibitMatchers ( rules [ ] config . InhibitRule , amu * AggregateMatchersUsage ) {
2023-06-15 03:18:01 -05:00
for _ , r := range rules {
amu . Matchers += len ( r . SourceMatchers )
amu . Matchers += len ( r . TargetMatchers )
amu . MatchRE += len ( r . SourceMatchRE )
amu . MatchRE += len ( r . TargetMatchRE )
amu . Match += len ( r . SourceMatch )
amu . Match += len ( r . TargetMatch )
}
}
2021-03-25 06:51:44 -05:00
// applyConfig applies a new configuration by re-initializing all components using the configuration provided.
2023-02-02 11:45:17 -06:00
// It returns a boolean indicating whether the user config was changed and an error.
2021-03-25 06:51:44 -05:00
// It is not safe to call concurrently.
2024-01-31 13:05:30 -06:00
func ( am * alertmanager ) applyConfig ( cfg * apimodels . PostableUserConfig ) ( bool , error ) {
2021-04-13 07:02:44 -05:00
// First, let's make sure this config is not already loaded
2024-03-04 12:12:49 -06:00
rawConfig , err := json . Marshal ( cfg )
2024-01-31 13:05:30 -06:00
if err != nil {
// In theory, this should never happen.
return false , err
2021-03-24 09:20:44 -05:00
}
2024-03-04 12:12:49 -06:00
// If configuration hasn't changed, we've got nothing to do.
configHash := md5 . Sum ( rawConfig )
if am . Base . ConfigHash ( ) == configHash {
am . logger . Debug ( "Config hasn't changed, skipping configuration sync." )
2023-02-02 11:45:17 -06:00
return false , nil
2021-04-13 07:02:44 -05:00
}
2024-03-04 12:12:49 -06:00
am . logger . Info ( "Applying new configuration to Alertmanager" , "configHash" , fmt . Sprintf ( "%x" , configHash ) )
2023-01-13 10:54:38 -06:00
err = am . Base . ApplyConfig ( AlertingConfiguration {
2023-04-28 09:56:59 -05:00
rawAlertmanagerConfig : rawConfig ,
2024-03-04 12:12:49 -06:00
configHash : configHash ,
route : cfg . AlertmanagerConfig . Route . AsAMRoute ( ) ,
inhibitRules : cfg . AlertmanagerConfig . InhibitRules ,
muteTimeIntervals : cfg . AlertmanagerConfig . MuteTimeIntervals ,
timeIntervals : cfg . AlertmanagerConfig . TimeIntervals ,
templates : ToTemplateDefinitions ( cfg ) ,
2023-04-28 09:56:59 -05:00
receivers : PostableApiAlertingConfigToApiReceivers ( cfg . AlertmanagerConfig ) ,
receiverIntegrationsFunc : am . buildReceiverIntegrations ,
2023-01-13 10:54:38 -06:00
} )
2023-02-02 11:45:17 -06:00
if err != nil {
return false , err
}
2024-01-16 10:12:24 -06:00
am . updateConfigMetrics ( cfg )
2023-02-02 11:45:17 -06:00
return true , nil
}
// applyAndMarkConfig applies a configuration and marks it as applied if no errors occur.
2024-01-31 13:05:30 -06:00
func ( am * alertmanager ) applyAndMarkConfig ( ctx context . Context , hash string , cfg * apimodels . PostableUserConfig ) error {
configChanged , err := am . applyConfig ( cfg )
2021-03-24 09:20:44 -05:00
if err != nil {
2023-01-13 10:54:38 -06:00
return err
2022-10-03 08:58:41 -05:00
}
2021-08-17 07:49:05 -05:00
2023-02-02 11:45:17 -06:00
if configChanged {
markConfigCmd := ngmodels . MarkConfigurationAsAppliedCmd {
OrgID : am . orgID ,
ConfigurationHash : hash ,
}
return am . Store . MarkConfigurationAsApplied ( ctx , & markConfigCmd )
}
2021-03-16 04:44:52 -05:00
return nil
}
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) AppURL ( ) string {
2023-04-28 09:56:59 -05:00
return am . Settings . AppURL
2021-03-24 09:20:44 -05:00
}
// buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
2023-09-06 05:59:29 -05:00
func ( am * alertmanager ) buildReceiverIntegrations ( receiver * alertingNotify . APIReceiver , tmpl * alertingTemplates . Template ) ( [ ] * alertingNotify . Integration , error ) {
2023-04-25 12:39:46 -05:00
receiverCfg , err := alertingNotify . BuildReceiverConfiguration ( context . Background ( ) , receiver , am . decryptFn )
if err != nil {
return nil , err
}
s := & sender { am . NotificationService }
2023-06-21 18:53:30 -05:00
img := newImageProvider ( am . Store , log . New ( "ngalert.notifier.image-provider" ) )
2023-04-25 12:39:46 -05:00
integrations , err := alertingNotify . BuildReceiverIntegrations (
receiverCfg ,
tmpl ,
img ,
LoggerFactory ,
func ( n receivers . Metadata ) ( receivers . WebhookSender , error ) {
return s , nil
} ,
func ( n receivers . Metadata ) ( receivers . EmailSender , error ) {
return s , nil
} ,
am . orgID ,
setting . BuildVersion ,
)
if err != nil {
return nil , err
2021-08-17 07:49:05 -05:00
}
return integrations , nil
}
2021-05-10 07:30:42 -05:00
2021-03-30 11:37:56 -05:00
// PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
2023-10-19 04:27:37 -05:00
func ( am * alertmanager ) PutAlerts ( _ context . Context , postableAlerts apimodels . PostableAlerts ) error {
2023-02-03 10:36:49 -06:00
alerts := make ( alertingNotify . PostableAlerts , 0 , len ( postableAlerts . PostableAlerts ) )
2023-01-13 10:54:38 -06:00
for _ , pa := range postableAlerts . PostableAlerts {
2023-02-03 10:36:49 -06:00
alerts = append ( alerts , & alertingNotify . PostableAlert {
2023-01-13 10:54:38 -06:00
Annotations : pa . Annotations ,
EndsAt : pa . EndsAt ,
StartsAt : pa . StartsAt ,
Alert : pa . Alert ,
} )
2021-04-22 10:12:18 -05:00
}
2023-01-13 10:54:38 -06:00
return am . Base . PutAlerts ( alerts )
2021-04-22 10:12:18 -05:00
}
2023-10-25 06:58:28 -05:00
// CleanUp removes the directory containing the alertmanager files from disk.
func ( am * alertmanager ) CleanUp ( ) {
am . fileStore . CleanUp ( )
2023-09-06 05:59:29 -05:00
}
2021-04-22 10:12:18 -05:00
// AlertValidationError is the error capturing the validation errors
// faced on the alerts.
type AlertValidationError struct {
Alerts [ ] amv2 . PostableAlert
Errors [ ] error // Errors[i] refers to Alerts[i].
}
func ( e AlertValidationError ) Error ( ) string {
errMsg := ""
if len ( e . Errors ) != 0 {
2021-07-08 07:56:09 -05:00
errMsg = e . Errors [ 0 ] . Error ( )
2021-04-22 10:12:18 -05:00
for _ , e := range e . Errors [ 1 : ] {
errMsg += ";" + e . Error ( )
}
}
return errMsg
2021-03-16 04:44:52 -05:00
}
2021-09-09 11:25:22 -05:00
type nilLimits struct { }
func ( n nilLimits ) MaxNumberOfAggregationGroups ( ) int { return 0 }
2024-01-16 10:12:24 -06:00
// This function is taken from upstream, modified to take a [16]byte instead of a []byte.
// https://github.com/prometheus/alertmanager/blob/30fa9cd44bc91c0d6adcc9985609bb08a09a127b/config/coordinator.go#L149-L156
func hashAsMetricValue ( data [ 16 ] byte ) float64 {
// We only want 48 bits as a float64 only has a 53 bit mantissa.
smallSum := data [ 0 : 6 ]
bytes := make ( [ ] byte , 8 )
copy ( bytes , smallSum )
return float64 ( binary . LittleEndian . Uint64 ( bytes ) )
}