@ -2,7 +2,6 @@ package base
import (
"context"
"flag"
"fmt"
"net/url"
"regexp"
@ -11,29 +10,18 @@ import (
gklog "github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/grafana/dskit/crypto/tls "
"github.com/imdario/mergo "
config_util "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/discovery"
"github.com/prometheus/prometheus/discovery/dns"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/notifier"
"github.com/grafana/loki/pkg/uti l"
ruler_config "github.com/grafana/loki/pkg/r uler/config "
)
type NotifierConfig struct {
TLS tls . ClientConfig ` yaml:",inline" `
BasicAuth util . BasicAuth ` yaml:",inline" `
HeaderAuth util . HeaderAuth ` yaml:",inline" `
}
func ( cfg * NotifierConfig ) RegisterFlags ( f * flag . FlagSet ) {
cfg . TLS . RegisterFlagsWithPrefix ( "ruler.alertmanager-client" , f )
cfg . BasicAuth . RegisterFlagsWithPrefix ( "ruler.alertmanager-client." , f )
cfg . HeaderAuth . RegisterFlagsWithPrefix ( "ruler.alertmanager-client." , f )
}
// rulerNotifier bundles a notifier.Manager together with an associated
// Alertmanager service discovery manager and handles the lifecycle
// of both actors.
@ -88,10 +76,46 @@ func (rn *rulerNotifier) stop() {
rn . wg . Wait ( )
}
func getAlertmanagerTenantConfig ( amConfig ruler_config . AlertManagerConfig , amOverrides ruler_config . AlertManagerConfig ) ( ruler_config . AlertManagerConfig , error ) {
if amOverrides . AlertmanagerURL != "" {
amConfig . AlertmanagerURL = amOverrides . AlertmanagerURL
}
if len ( amOverrides . AlertRelabelConfigs ) > 0 {
amConfig . AlertRelabelConfigs = amOverrides . AlertRelabelConfigs
}
if amOverrides . AlertmanagerDiscovery {
amConfig . AlertmanagerDiscovery = amOverrides . AlertmanagerDiscovery
}
if amOverrides . AlertmanangerEnableV2API {
amConfig . AlertmanangerEnableV2API = amOverrides . AlertmanangerEnableV2API
}
if amOverrides . AlertmanagerRefreshInterval > 0 {
amConfig . AlertmanagerRefreshInterval = amOverrides . AlertmanagerRefreshInterval
}
if amOverrides . NotificationQueueCapacity > 0 {
amConfig . NotificationQueueCapacity = amOverrides . NotificationQueueCapacity
}
if amOverrides . NotificationTimeout > 0 {
amConfig . NotificationTimeout = amOverrides . NotificationTimeout
}
if err := mergo . Merge ( & amConfig . Notifier , amOverrides . Notifier , mergo . WithOverride ) ; err != nil {
return amConfig , fmt . Errorf ( "failed to apply alertmanager notifier limits config: %w" , err )
}
return amConfig , nil
}
// Builds a Prometheus config.Config from a ruler.Config with just the required
// options to configure notifications to Alertmanager.
func buildNotifierConfig ( rulerConfig * Config ) ( * config . Config , error ) {
amURLs := strings . Split ( rulerConfig . AlertmanagerURL , "," )
func buildNotifierConfig ( amConfig * ruler_config . AlertManagerConfig , externalLabels labels . Labels ) ( * config . Config , error ) {
amURLs := strings . Split ( am Config. AlertmanagerURL , "," )
validURLs := make ( [ ] * url . URL , 0 , len ( amURLs ) )
srvDNSregexp := regexp . MustCompile ( ` ^_.+._.+ ` )
@ -108,7 +132,7 @@ func buildNotifierConfig(rulerConfig *Config) (*config.Config, error) {
// Given we only support SRV lookups as part of service discovery, we need to ensure
// hosts provided follow this specification: _service._proto.name
// e.g. _http._tcp.alertmanager.com
if ruler Config. AlertmanagerDiscovery && ! srvDNSregexp . MatchString ( url . Host ) {
if am Config. AlertmanagerDiscovery && ! srvDNSregexp . MatchString ( url . Host ) {
return nil , fmt . Errorf ( "when alertmanager-discovery is on, host name must be of the form _portname._tcp.service.fqdn (is %q)" , url . Host )
}
@ -120,21 +144,21 @@ func buildNotifierConfig(rulerConfig *Config) (*config.Config, error) {
}
apiVersion := config . AlertmanagerAPIVersionV1
if ruler Config. AlertmanangerEnableV2API {
if am Config. AlertmanangerEnableV2API {
apiVersion = config . AlertmanagerAPIVersionV2
}
amConfigs := make ( [ ] * config . AlertmanagerConfig , 0 , len ( validURLs ) )
for _ , url := range validURLs {
amConfigs = append ( amConfigs , amConfigFromURL ( ruler Config, url , apiVersion ) )
amConfigs = append ( amConfigs , amConfigFromURL ( am Config, url , apiVersion ) )
}
promConfig := & config . Config {
GlobalConfig : config . GlobalConfig {
ExternalLabels : rul erConfig . E xternalLabels,
ExternalLabels : externalLabels ,
} ,
AlertingConfig : config . AlertingConfig {
AlertRelabelConfigs : ruler Config. AlertRelabelConfigs ,
AlertRelabelConfigs : am Config. AlertRelabelConfigs ,
AlertmanagerConfigs : amConfigs ,
} ,
}
@ -142,13 +166,13 @@ func buildNotifierConfig(rulerConfig *Config) (*config.Config, error) {
return promConfig , nil
}
func amConfigFromURL ( rulerConfig * Config , url * url . URL , apiVersion config . AlertmanagerAPIVersion ) * config . AlertmanagerConfig {
func amConfigFromURL ( cfg * ruler_config . AlertManager Config, url * url . URL , apiVersion config . AlertmanagerAPIVersion ) * config . AlertmanagerConfig {
var sdConfig discovery . Configs
if rulerConfi g. AlertmanagerDiscovery {
if cf g. AlertmanagerDiscovery {
sdConfig = discovery . Configs {
& dns . SDConfig {
Names : [ ] string { url . Host } ,
RefreshInterval : model . Duration ( rulerConfi g. AlertmanagerRefreshInterval ) ,
RefreshInterval : model . Duration ( cf g. AlertmanagerRefreshInterval ) ,
Type : "SRV" ,
Port : 0 , // Ignored, because of SRV.
} ,
@ -168,15 +192,15 @@ func amConfigFromURL(rulerConfig *Config, url *url.URL, apiVersion config.Alertm
APIVersion : apiVersion ,
Scheme : url . Scheme ,
PathPrefix : url . Path ,
Timeout : model . Duration ( rulerConfi g. NotificationTimeout ) ,
Timeout : model . Duration ( cf g. NotificationTimeout ) ,
ServiceDiscoveryConfigs : sdConfig ,
HTTPClientConfig : config_util . HTTPClientConfig {
TLSConfig : config_util . TLSConfig {
CAFile : rulerConfi g. Notifier . TLS . CAPath ,
CertFile : rulerConfi g. Notifier . TLS . CertPath ,
KeyFile : rulerConfi g. Notifier . TLS . KeyPath ,
InsecureSkipVerify : rulerConfi g. Notifier . TLS . InsecureSkipVerify ,
ServerName : rulerConfi g. Notifier . TLS . ServerName ,
CAFile : cf g. Notifier . TLS . CAPath ,
CertFile : cf g. Notifier . TLS . CertPath ,
KeyFile : cf g. Notifier . TLS . KeyPath ,
InsecureSkipVerify : cf g. Notifier . TLS . InsecureSkipVerify ,
ServerName : cf g. Notifier . TLS . ServerName ,
} ,
} ,
}
@ -193,23 +217,23 @@ func amConfigFromURL(rulerConfig *Config, url *url.URL, apiVersion config.Alertm
}
// Override URL basic authentication configs with hard coded config values if present
if rulerConfi g. Notifier . BasicAuth . IsEnabled ( ) {
if cf g. Notifier . BasicAuth . IsEnabled ( ) {
amConfig . HTTPClientConfig . BasicAuth = & config_util . BasicAuth {
Username : rulerConfi g. Notifier . BasicAuth . Username ,
Password : config_util . Secret ( rulerConfi g. Notifier . BasicAuth . Password ) ,
Username : cf g. Notifier . BasicAuth . Username ,
Password : config_util . Secret ( cf g. Notifier . BasicAuth . Password ) ,
}
}
if rulerConfi g. Notifier . HeaderAuth . IsEnabled ( ) {
if rulerConfi g. Notifier . HeaderAuth . Credentials != "" {
if cf g. Notifier . HeaderAuth . IsEnabled ( ) {
if cf g. Notifier . HeaderAuth . Credentials != "" {
amConfig . HTTPClientConfig . Authorization = & config_util . Authorization {
Type : rulerConfi g. Notifier . HeaderAuth . Type ,
Credentials : config_util . Secret ( rulerConfi g. Notifier . HeaderAuth . Credentials ) ,
Type : cf g. Notifier . HeaderAuth . Type ,
Credentials : config_util . Secret ( cf g. Notifier . HeaderAuth . Credentials ) ,
}
} else if rulerConfi g. Notifier . HeaderAuth . CredentialsFile != "" {
} else if cf g. Notifier . HeaderAuth . CredentialsFile != "" {
amConfig . HTTPClientConfig . Authorization = & config_util . Authorization {
Type : rulerConfi g. Notifier . HeaderAuth . Type ,
CredentialsFile : rulerConfi g. Notifier . HeaderAuth . CredentialsFile ,
Type : cf g. Notifier . HeaderAuth . Type ,
CredentialsFile : cf g. Notifier . HeaderAuth . CredentialsFile ,
}
}