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  }