github.com/sequix/cortex@v1.1.6/pkg/ruler/notifier.go (about) 1 package ruler 2 3 import ( 4 "context" 5 "crypto/md5" 6 "encoding/json" 7 "fmt" 8 "strings" 9 "sync" 10 11 gklog "github.com/go-kit/kit/log" 12 "github.com/go-kit/kit/log/level" 13 config_util "github.com/prometheus/common/config" 14 "github.com/prometheus/common/model" 15 "github.com/prometheus/prometheus/config" 16 "github.com/prometheus/prometheus/discovery" 17 sd_config "github.com/prometheus/prometheus/discovery/config" 18 "github.com/prometheus/prometheus/discovery/dns" 19 "github.com/prometheus/prometheus/discovery/targetgroup" 20 "github.com/prometheus/prometheus/notifier" 21 ) 22 23 // rulerNotifier bundles a notifier.Manager together with an associated 24 // Alertmanager service discovery manager and handles the lifecycle 25 // of both actors. 26 type rulerNotifier struct { 27 notifier *notifier.Manager 28 sdCancel context.CancelFunc 29 sdManager *discovery.Manager 30 wg sync.WaitGroup 31 logger gklog.Logger 32 } 33 34 func newRulerNotifier(o *notifier.Options, l gklog.Logger) *rulerNotifier { 35 sdCtx, sdCancel := context.WithCancel(context.Background()) 36 return &rulerNotifier{ 37 notifier: notifier.NewManager(o, l), 38 sdCancel: sdCancel, 39 sdManager: discovery.NewManager(sdCtx, l), 40 logger: l, 41 } 42 } 43 44 func (rn *rulerNotifier) run() { 45 rn.wg.Add(2) 46 go func() { 47 if err := rn.sdManager.Run(); err != nil { 48 level.Error(rn.logger).Log("msg", "error starting notifier discovery manager", "err", err) 49 } 50 rn.wg.Done() 51 }() 52 go func() { 53 rn.notifier.Run(rn.sdManager.SyncCh()) 54 rn.wg.Done() 55 }() 56 } 57 58 func (rn *rulerNotifier) applyConfig(cfg *config.Config) error { 59 if err := rn.notifier.ApplyConfig(cfg); err != nil { 60 return err 61 } 62 63 sdCfgs := make(map[string]sd_config.ServiceDiscoveryConfig) 64 for _, v := range cfg.AlertingConfig.AlertmanagerConfigs { 65 // AlertmanagerConfigs doesn't hold an unique identifier so we use the config hash as the identifier. 66 b, err := json.Marshal(v) 67 if err != nil { 68 return err 69 } 70 // This hash needs to be identical to the one computed in the notifier in 71 // https://github.com/prometheus/prometheus/blob/719c579f7b917b384c3d629752dea026513317dc/notifier/notifier.go#L265 72 // This kind of sucks, but it's done in Prometheus in main.go in the same way. 73 sdCfgs[fmt.Sprintf("%x", md5.Sum(b))] = v.ServiceDiscoveryConfig 74 } 75 return rn.sdManager.ApplyConfig(sdCfgs) 76 } 77 78 func (rn *rulerNotifier) stop() { 79 rn.sdCancel() 80 rn.notifier.Stop() 81 rn.wg.Wait() 82 } 83 84 // Builds a Prometheus config.Config from a ruler.Config with just the required 85 // options to configure notifications to Alertmanager. 86 func buildNotifierConfig(rulerConfig *Config) (*config.Config, error) { 87 if rulerConfig.AlertmanagerURL.URL == nil { 88 return &config.Config{}, nil 89 } 90 91 u := rulerConfig.AlertmanagerURL 92 var sdConfig sd_config.ServiceDiscoveryConfig 93 if rulerConfig.AlertmanagerDiscovery { 94 if !strings.Contains(u.Host, "_tcp.") { 95 return nil, fmt.Errorf("When alertmanager-discovery is on, host name must be of the form _portname._tcp.service.fqdn (is %q)", u.Host) 96 } 97 dnsSDConfig := dns.SDConfig{ 98 Names: []string{u.Host}, 99 RefreshInterval: model.Duration(rulerConfig.AlertmanagerRefreshInterval), 100 Type: "SRV", 101 Port: 0, // Ignored, because of SRV. 102 } 103 sdConfig = sd_config.ServiceDiscoveryConfig{ 104 DNSSDConfigs: []*dns.SDConfig{&dnsSDConfig}, 105 } 106 } else { 107 sdConfig = sd_config.ServiceDiscoveryConfig{ 108 StaticConfigs: []*targetgroup.Group{ 109 { 110 Targets: []model.LabelSet{ 111 { 112 model.AddressLabel: model.LabelValue(u.Host), 113 }, 114 }, 115 }, 116 }, 117 } 118 } 119 amConfig := &config.AlertmanagerConfig{ 120 APIVersion: config.AlertmanagerAPIVersionV1, 121 Scheme: u.Scheme, 122 PathPrefix: u.Path, 123 Timeout: model.Duration(rulerConfig.NotificationTimeout), 124 ServiceDiscoveryConfig: sdConfig, 125 } 126 127 promConfig := &config.Config{ 128 AlertingConfig: config.AlertingConfig{ 129 AlertmanagerConfigs: []*config.AlertmanagerConfig{amConfig}, 130 }, 131 } 132 133 if u.User != nil { 134 amConfig.HTTPClientConfig = config_util.HTTPClientConfig{ 135 BasicAuth: &config_util.BasicAuth{ 136 Username: u.User.Username(), 137 }, 138 } 139 140 if password, isSet := u.User.Password(); isSet { 141 amConfig.HTTPClientConfig.BasicAuth.Password = config_util.Secret(password) 142 } 143 } 144 145 return promConfig, nil 146 }