2020-06-28 13:26:22 +02:00
package notifier
import (
2022-02-02 13:11:41 +01:00
"flag"
2020-06-28 13:26:22 +02:00
"fmt"
2022-05-14 11:38:44 +02:00
"net/url"
2022-02-02 15:18:40 +01:00
"strings"
2022-02-02 13:11:41 +01:00
"time"
2020-06-28 13:26:22 +02:00
2022-05-14 11:38:44 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/templates"
2020-06-29 21:21:03 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
2022-02-02 13:11:41 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
2022-11-30 06:22:12 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
2020-06-28 13:26:22 +02:00
)
var (
2022-02-02 13:11:41 +01:00
configPath = flag . String ( "notifier.config" , "" , "Path to configuration file for notifiers" )
suppressDuplicateTargetErrors = flag . Bool ( "notifier.suppressDuplicateTargetErrors" , false , "Whether to suppress 'duplicate target' errors during discovery" )
2022-12-30 09:54:03 +01:00
addrs = flagutil . NewArrayString ( "notifier.url" , "Prometheus Alertmanager URL, e.g. http://127.0.0.1:9093. " +
"List all Alertmanager URLs if it runs in the cluster mode to ensure high availability." )
2023-10-10 11:40:27 +02:00
showNotifierURL = flag . Bool ( "notifier.showURL" , false , "Whether to avoid stripping sensitive information such as passwords from URL in log messages or UI for -notifier.url. " +
"It is hidden by default, since it can contain sensitive info such as auth key" )
2023-07-18 15:53:37 +02:00
blackHole = flag . Bool ( "notifier.blackhole" , false , "Whether to blackhole alerting notifications. " +
"Enable this flag if you want vmalert to evaluate alerting rules without sending any notifications to external receivers (eg. alertmanager). " +
2023-11-22 19:06:11 +01:00
"-notifier.url, -notifier.config and -notifier.blackhole are mutually exclusive." )
2022-03-10 12:09:12 +01:00
2022-10-01 17:26:05 +02:00
basicAuthUsername = flagutil . NewArrayString ( "notifier.basicAuth.username" , "Optional basic auth username for -notifier.url" )
basicAuthPassword = flagutil . NewArrayString ( "notifier.basicAuth.password" , "Optional basic auth password for -notifier.url" )
basicAuthPasswordFile = flagutil . NewArrayString ( "notifier.basicAuth.passwordFile" , "Optional path to basic auth password file for -notifier.url" )
2020-06-29 21:21:03 +02:00
2022-10-01 17:26:05 +02:00
bearerToken = flagutil . NewArrayString ( "notifier.bearerToken" , "Optional bearer token for -notifier.url" )
bearerTokenFile = flagutil . NewArrayString ( "notifier.bearerTokenFile" , "Optional path to bearer token file for -notifier.url" )
2022-03-10 12:09:12 +01:00
2020-12-22 21:23:04 +01:00
tlsInsecureSkipVerify = flagutil . NewArrayBool ( "notifier.tlsInsecureSkipVerify" , "Whether to skip tls verification when connecting to -notifier.url" )
2022-10-01 17:26:05 +02:00
tlsCertFile = flagutil . NewArrayString ( "notifier.tlsCertFile" , "Optional path to client-side TLS certificate file to use when connecting to -notifier.url" )
tlsKeyFile = flagutil . NewArrayString ( "notifier.tlsKeyFile" , "Optional path to client-side TLS certificate key to use when connecting to -notifier.url" )
tlsCAFile = flagutil . NewArrayString ( "notifier.tlsCAFile" , "Optional path to TLS CA file to use for verifying connections to -notifier.url. " +
2023-05-10 09:50:41 +02:00
"By default, system CA is used" )
2022-10-01 17:26:05 +02:00
tlsServerName = flagutil . NewArrayString ( "notifier.tlsServerName" , "Optional TLS server name to use for connections to -notifier.url. " +
2023-05-10 09:50:41 +02:00
"By default, the server name from -notifier.url is used" )
2022-03-10 12:09:12 +01:00
2022-10-01 17:26:05 +02:00
oauth2ClientID = flagutil . NewArrayString ( "notifier.oauth2.clientID" , "Optional OAuth2 clientID to use for -notifier.url. " +
2022-03-10 12:09:12 +01:00
"If multiple args are set, then they are applied independently for the corresponding -notifier.url" )
2022-10-01 17:26:05 +02:00
oauth2ClientSecret = flagutil . NewArrayString ( "notifier.oauth2.clientSecret" , "Optional OAuth2 clientSecret to use for -notifier.url. " +
2022-03-10 12:09:12 +01:00
"If multiple args are set, then they are applied independently for the corresponding -notifier.url" )
2022-10-01 17:26:05 +02:00
oauth2ClientSecretFile = flagutil . NewArrayString ( "notifier.oauth2.clientSecretFile" , "Optional OAuth2 clientSecretFile to use for -notifier.url. " +
2022-03-10 12:09:12 +01:00
"If multiple args are set, then they are applied independently for the corresponding -notifier.url" )
2023-12-20 20:35:16 +01:00
oauth2EndpointParams = flagutil . NewArrayString ( "notifier.oauth2.endpointParams" , "Optional OAuth2 endpoint parameters to use for the corresponding -notifier.url . " +
` The endpoint parameters must be set in JSON format: { "param1":"value1",...,"paramN":"valueN"} ` )
2022-10-01 17:26:05 +02:00
oauth2TokenURL = flagutil . NewArrayString ( "notifier.oauth2.tokenUrl" , "Optional OAuth2 tokenURL to use for -notifier.url. " +
2022-03-10 12:09:12 +01:00
"If multiple args are set, then they are applied independently for the corresponding -notifier.url" )
2022-10-01 17:26:05 +02:00
oauth2Scopes = flagutil . NewArrayString ( "notifier.oauth2.scopes" , "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. " +
2022-03-10 12:09:12 +01:00
"If multiple args are set, then they are applied independently for the corresponding -notifier.url" )
2020-06-28 13:26:22 +02:00
)
2022-02-02 13:11:41 +01:00
// cw holds a configWatcher for configPath configuration file
// configWatcher provides a list of Notifier objects discovered
// from static config or via service discovery.
// cw is not nil only if configPath is provided.
var cw * configWatcher
// Reload checks the changes in configPath configuration file
// and applies changes if any.
func Reload ( ) error {
if cw == nil {
return nil
}
return cw . reload ( * configPath )
}
var staticNotifiersFn func ( ) [ ] Notifier
2022-02-15 14:59:45 +01:00
var (
// externalLabels is a global variable for holding external labels configured via flags
// It is supposed to be inited via Init function only.
externalLabels map [ string ] string
// externalURL is a global variable for holding external URL value configured via flag
// It is supposed to be inited via Init function only.
externalURL string
)
2022-02-02 13:11:41 +01:00
// Init returns a function for retrieving actual list of Notifier objects.
// Init works in two mods:
2022-07-11 18:21:59 +02:00
// - configuration via flags (for backward compatibility). Is always static
2022-02-02 13:11:41 +01:00
// and don't support live reloads.
2022-07-11 18:21:59 +02:00
// - configuration via file. Supports live reloads and service discovery.
//
2022-02-02 13:11:41 +01:00
// Init returns an error if both mods are used.
2022-02-15 14:59:45 +01:00
func Init ( gen AlertURLGenerator , extLabels map [ string ] string , extURL string ) ( func ( ) [ ] Notifier , error ) {
externalURL = extURL
externalLabels = extLabels
2022-05-14 11:38:44 +02:00
eu , err := url . Parse ( externalURL )
if err != nil {
2023-10-25 21:24:01 +02:00
return nil , fmt . Errorf ( "failed to parse external URL: %w" , err )
2022-05-14 11:38:44 +02:00
}
templates . UpdateWithFuncs ( templates . FuncsWithExternalURL ( eu ) )
2022-02-15 14:59:45 +01:00
2023-07-18 15:06:19 +02:00
if * blackHole {
if len ( * addrs ) > 0 || * configPath != "" {
return nil , fmt . Errorf ( "only one of -notifier.blackhole, -notifier.url and -notifier.config flags must be specified" )
}
staticNotifiersFn = func ( ) [ ] Notifier {
2023-07-18 15:53:37 +02:00
return [ ] Notifier { newBlackHoleNotifier ( ) }
2023-07-18 15:06:19 +02:00
}
return staticNotifiersFn , nil
}
2022-02-02 13:11:41 +01:00
if * configPath == "" && len ( * addrs ) == 0 {
return nil , nil
}
if * configPath != "" && len ( * addrs ) > 0 {
return nil , fmt . Errorf ( "only one of -notifier.config or -notifier.url flags must be specified" )
}
if len ( * addrs ) > 0 {
notifiers , err := notifiersFromFlags ( gen )
if err != nil {
2023-10-25 21:24:01 +02:00
return nil , fmt . Errorf ( "failed to create notifier from flag values: %w" , err )
2022-02-02 13:11:41 +01:00
}
staticNotifiersFn = func ( ) [ ] Notifier {
return notifiers
}
return staticNotifiersFn , nil
}
cw , err = newWatcher ( * configPath , gen )
if err != nil {
2023-10-25 21:24:01 +02:00
return nil , fmt . Errorf ( "failed to init config watcher: %w" , err )
2022-02-02 13:11:41 +01:00
}
return cw . notifiers , nil
}
2023-10-10 11:40:27 +02:00
// InitSecretFlags must be called after flag.Parse and before any logging
func InitSecretFlags ( ) {
if ! * showNotifierURL {
flagutil . RegisterSecretFlag ( "notifier.url" )
}
}
2022-02-02 13:11:41 +01:00
func notifiersFromFlags ( gen AlertURLGenerator ) ( [ ] Notifier , error ) {
2020-06-29 21:21:03 +02:00
var notifiers [ ] Notifier
for i , addr := range * addrs {
2023-12-20 20:35:16 +01:00
endpointParamsJSON := oauth2EndpointParams . GetOptionalArg ( i )
endpointParams , err := flagutil . ParseJSONMap ( endpointParamsJSON )
if err != nil {
return nil , fmt . Errorf ( "cannot parse JSON for -notifier.oauth2.endpointParams=%s: %w" , endpointParamsJSON , err )
}
2022-02-02 13:11:41 +01:00
authCfg := promauth . HTTPClientConfig {
TLSConfig : & promauth . TLSConfig {
CAFile : tlsCAFile . GetOptionalArg ( i ) ,
CertFile : tlsCertFile . GetOptionalArg ( i ) ,
KeyFile : tlsKeyFile . GetOptionalArg ( i ) ,
ServerName : tlsServerName . GetOptionalArg ( i ) ,
InsecureSkipVerify : tlsInsecureSkipVerify . GetOptionalArg ( i ) ,
} ,
BasicAuth : & promauth . BasicAuthConfig {
2022-02-02 16:56:14 +01:00
Username : basicAuthUsername . GetOptionalArg ( i ) ,
Password : promauth . NewSecret ( basicAuthPassword . GetOptionalArg ( i ) ) ,
PasswordFile : basicAuthPasswordFile . GetOptionalArg ( i ) ,
2022-02-02 13:11:41 +01:00
} ,
2022-03-10 12:09:12 +01:00
BearerToken : promauth . NewSecret ( bearerToken . GetOptionalArg ( i ) ) ,
BearerTokenFile : bearerTokenFile . GetOptionalArg ( i ) ,
OAuth2 : & promauth . OAuth2Config {
ClientID : oauth2ClientID . GetOptionalArg ( i ) ,
ClientSecret : promauth . NewSecret ( oauth2ClientSecret . GetOptionalArg ( i ) ) ,
ClientSecretFile : oauth2ClientSecretFile . GetOptionalArg ( i ) ,
2023-12-20 20:35:16 +01:00
EndpointParams : endpointParams ,
2022-03-10 12:09:12 +01:00
Scopes : strings . Split ( oauth2Scopes . GetOptionalArg ( i ) , ";" ) ,
TokenURL : oauth2TokenURL . GetOptionalArg ( i ) ,
} ,
2022-02-02 13:11:41 +01:00
}
2022-03-10 12:09:12 +01:00
2022-02-02 15:18:40 +01:00
addr = strings . TrimSuffix ( addr , "/" )
2022-06-18 09:11:37 +02:00
am , err := NewAlertManager ( addr + alertManagerPath , gen , authCfg , nil , time . Second * 10 )
2020-06-29 21:21:03 +02:00
if err != nil {
2022-02-02 13:11:41 +01:00
return nil , err
2020-06-29 21:21:03 +02:00
}
notifiers = append ( notifiers , am )
2020-06-28 13:26:22 +02:00
}
2020-06-29 21:21:03 +02:00
return notifiers , nil
2020-06-28 13:26:22 +02:00
}
2022-02-02 13:11:41 +01:00
// Target represents a Notifier and optional
// list of labels added during discovery.
type Target struct {
Notifier
2022-11-30 06:22:12 +01:00
Labels * promutils . Labels
2022-02-02 13:11:41 +01:00
}
// TargetType defines how the Target was discovered
type TargetType string
const (
// TargetStatic is for targets configured statically
TargetStatic TargetType = "static"
// TargetConsul is for targets discovered via Consul
TargetConsul TargetType = "consulSD"
2022-04-13 10:50:26 +02:00
// TargetDNS is for targets discovered via DNS
TargetDNS TargetType = "DNSSD"
2022-02-02 13:11:41 +01:00
)
// GetTargets returns list of static or discovered targets
// via notifier configuration.
func GetTargets ( ) map [ TargetType ] [ ] Target {
var targets = make ( map [ TargetType ] [ ] Target )
if staticNotifiersFn != nil {
for _ , ns := range staticNotifiersFn ( ) {
targets [ TargetStatic ] = append ( targets [ TargetStatic ] , Target {
Notifier : ns ,
} )
}
}
if cw != nil {
cw . targetsMu . RLock ( )
for key , ns := range cw . targets {
targets [ key ] = append ( targets [ key ] , ns ... )
}
cw . targetsMu . RUnlock ( )
}
return targets
}