// Copyright 2013 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package notifier import ( "crypto/md5" "encoding/hex" "log/slog" "net/http" "sync" config_util "github.com/prometheus/common/config" "github.com/prometheus/sigv4" "gopkg.in/yaml.v2" "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/discovery/targetgroup" ) // alertmanagerSet contains a set of Alertmanagers discovered via a group of service // discovery definitions that have a common configuration on how alerts should be sent. type alertmanagerSet struct { cfg *config.AlertmanagerConfig client *http.Client metrics *alertMetrics mtx sync.RWMutex ams []alertmanager droppedAms []alertmanager logger *slog.Logger } func newAlertmanagerSet(cfg *config.AlertmanagerConfig, logger *slog.Logger, metrics *alertMetrics) (*alertmanagerSet, error) { client, err := config_util.NewClientFromConfig(cfg.HTTPClientConfig, "alertmanager") if err != nil { return nil, err } t := client.Transport if cfg.SigV4Config != nil { t, err = sigv4.NewSigV4RoundTripper(cfg.SigV4Config, client.Transport) if err != nil { return nil, err } } client.Transport = t s := &alertmanagerSet{ client: client, cfg: cfg, logger: logger, metrics: metrics, } return s, nil } // sync extracts a deduplicated set of Alertmanager endpoints from a list // of target groups definitions. func (s *alertmanagerSet) sync(tgs []*targetgroup.Group) { allAms := []alertmanager{} allDroppedAms := []alertmanager{} for _, tg := range tgs { ams, droppedAms, err := AlertmanagerFromGroup(tg, s.cfg) if err != nil { s.logger.Error("Creating discovered Alertmanagers failed", "err", err) continue } allAms = append(allAms, ams...) allDroppedAms = append(allDroppedAms, droppedAms...) } s.mtx.Lock() defer s.mtx.Unlock() previousAms := s.ams // Set new Alertmanagers and deduplicate them along their unique URL. s.ams = []alertmanager{} s.droppedAms = []alertmanager{} s.droppedAms = append(s.droppedAms, allDroppedAms...) seen := map[string]struct{}{} for _, am := range allAms { us := am.url().String() if _, ok := seen[us]; ok { continue } // This will initialize the Counters for the AM to 0. s.metrics.sent.WithLabelValues(us) s.metrics.errors.WithLabelValues(us) seen[us] = struct{}{} s.ams = append(s.ams, am) } // Now remove counters for any removed Alertmanagers. for _, am := range previousAms { us := am.url().String() if _, ok := seen[us]; ok { continue } s.metrics.latency.DeleteLabelValues(us) s.metrics.sent.DeleteLabelValues(us) s.metrics.errors.DeleteLabelValues(us) seen[us] = struct{}{} } } func (s *alertmanagerSet) configHash() (string, error) { b, err := yaml.Marshal(s.cfg) if err != nil { return "", err } hash := md5.Sum(b) return hex.EncodeToString(hash[:]), nil }