...

Source file src/github.com/prometheus/alertmanager/config/coordinator.go

Documentation: github.com/prometheus/alertmanager/config

     1  // Copyright 2019 Prometheus Team
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package config
    15  
    16  import (
    17  	"crypto/md5"
    18  	"encoding/binary"
    19  	"sync"
    20  
    21  	"github.com/go-kit/log"
    22  	"github.com/go-kit/log/level"
    23  	"github.com/prometheus/client_golang/prometheus"
    24  )
    25  
    26  // Coordinator coordinates Alertmanager configurations beyond the lifetime of a
    27  // single configuration.
    28  type Coordinator struct {
    29  	configFilePath string
    30  	logger         log.Logger
    31  
    32  	// Protects config and subscribers
    33  	mutex       sync.Mutex
    34  	config      *Config
    35  	subscribers []func(*Config) error
    36  
    37  	configHashMetric        prometheus.Gauge
    38  	configSuccessMetric     prometheus.Gauge
    39  	configSuccessTimeMetric prometheus.Gauge
    40  }
    41  
    42  // NewCoordinator returns a new coordinator with the given configuration file
    43  // path. It does not yet load the configuration from file. This is done in
    44  // `Reload()`.
    45  func NewCoordinator(configFilePath string, r prometheus.Registerer, l log.Logger) *Coordinator {
    46  	c := &Coordinator{
    47  		configFilePath: configFilePath,
    48  		logger:         l,
    49  	}
    50  
    51  	c.registerMetrics(r)
    52  
    53  	return c
    54  }
    55  
    56  func (c *Coordinator) registerMetrics(r prometheus.Registerer) {
    57  	configHash := prometheus.NewGauge(prometheus.GaugeOpts{
    58  		Name: "alertmanager_config_hash",
    59  		Help: "Hash of the currently loaded alertmanager configuration.",
    60  	})
    61  	configSuccess := prometheus.NewGauge(prometheus.GaugeOpts{
    62  		Name: "alertmanager_config_last_reload_successful",
    63  		Help: "Whether the last configuration reload attempt was successful.",
    64  	})
    65  	configSuccessTime := prometheus.NewGauge(prometheus.GaugeOpts{
    66  		Name: "alertmanager_config_last_reload_success_timestamp_seconds",
    67  		Help: "Timestamp of the last successful configuration reload.",
    68  	})
    69  
    70  	r.MustRegister(configHash, configSuccess, configSuccessTime)
    71  
    72  	c.configHashMetric = configHash
    73  	c.configSuccessMetric = configSuccess
    74  	c.configSuccessTimeMetric = configSuccessTime
    75  }
    76  
    77  // Subscribe subscribes the given Subscribers to configuration changes.
    78  func (c *Coordinator) Subscribe(ss ...func(*Config) error) {
    79  	c.mutex.Lock()
    80  	defer c.mutex.Unlock()
    81  
    82  	c.subscribers = append(c.subscribers, ss...)
    83  }
    84  
    85  func (c *Coordinator) notifySubscribers() error {
    86  	for _, s := range c.subscribers {
    87  		if err := s(c.config); err != nil {
    88  			return err
    89  		}
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // loadFromFile triggers a configuration load, discarding the old configuration.
    96  func (c *Coordinator) loadFromFile() error {
    97  	conf, err := LoadFile(c.configFilePath)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	c.config = conf
   103  
   104  	return nil
   105  }
   106  
   107  // Reload triggers a configuration reload from file and notifies all
   108  // configuration change subscribers.
   109  func (c *Coordinator) Reload() error {
   110  	c.mutex.Lock()
   111  	defer c.mutex.Unlock()
   112  
   113  	level.Info(c.logger).Log(
   114  		"msg", "Loading configuration file",
   115  		"file", c.configFilePath,
   116  	)
   117  	if err := c.loadFromFile(); err != nil {
   118  		level.Error(c.logger).Log(
   119  			"msg", "Loading configuration file failed",
   120  			"file", c.configFilePath,
   121  			"err", err,
   122  		)
   123  		c.configSuccessMetric.Set(0)
   124  		return err
   125  	}
   126  	level.Info(c.logger).Log(
   127  		"msg", "Completed loading of configuration file",
   128  		"file", c.configFilePath,
   129  	)
   130  
   131  	if err := c.notifySubscribers(); err != nil {
   132  		c.logger.Log(
   133  			"msg", "one or more config change subscribers failed to apply new config",
   134  			"file", c.configFilePath,
   135  			"err", err,
   136  		)
   137  		c.configSuccessMetric.Set(0)
   138  		return err
   139  	}
   140  
   141  	c.configSuccessMetric.Set(1)
   142  	c.configSuccessTimeMetric.SetToCurrentTime()
   143  	hash := md5HashAsMetricValue([]byte(c.config.original))
   144  	c.configHashMetric.Set(hash)
   145  
   146  	return nil
   147  }
   148  
   149  func md5HashAsMetricValue(data []byte) float64 {
   150  	sum := md5.Sum(data)
   151  	// We only want 48 bits as a float64 only has a 53 bit mantissa.
   152  	smallSum := sum[0:6]
   153  	bytes := make([]byte, 8)
   154  	copy(bytes, smallSum)
   155  	return float64(binary.LittleEndian.Uint64(bytes))
   156  }
   157  

View as plain text