Add dropped alertmanagers to alertmanagers API (#3865)

This commit is contained in:
Conor Broderick 2018-02-21 09:00:07 +00:00 committed by Brian Brazil
parent 93a63ac5fd
commit 1fd20fc954
5 changed files with 132 additions and 30 deletions

View file

@ -356,9 +356,6 @@ $ curl http://localhost:9090/api/v1/targets
## Alertmanagers ## Alertmanagers
> This API is experimental as it is intended to be extended with Alertmanagers
> dropped due to relabelling in the future.
The following endpoint returns an overview of the current state of the The following endpoint returns an overview of the current state of the
Prometheus alertmanager discovery: Prometheus alertmanager discovery:
@ -366,7 +363,7 @@ Prometheus alertmanager discovery:
GET /api/v1/alertmanagers GET /api/v1/alertmanagers
``` ```
Currently only the active Alertmanagers are part of the response. Both the active and dropped Alertmanagers are part of the response.
```json ```json
$ curl http://localhost:9090/api/v1/alertmanagers $ curl http://localhost:9090/api/v1/alertmanagers
@ -377,6 +374,11 @@ $ curl http://localhost:9090/api/v1/alertmanagers
{ {
"url": "http://127.0.0.1:9090/api/v1/alerts" "url": "http://127.0.0.1:9090/api/v1/alerts"
} }
],
"droppedAlertmanagers": [
{
"url": "http://127.0.0.1:9093/api/v1/alerts"
}
] ]
} }
} }

View file

@ -419,6 +419,25 @@ func (n *Manager) Alertmanagers() []*url.URL {
return res return res
} }
// DroppedAlertmanagers returns a slice of Alertmanager URLs.
func (n *Manager) DroppedAlertmanagers() []*url.URL {
n.mtx.RLock()
amSets := n.alertmanagers
n.mtx.RUnlock()
var res []*url.URL
for _, ams := range amSets {
ams.mtx.RLock()
for _, dam := range ams.droppedAms {
res = append(res, dam.url())
}
ams.mtx.RUnlock()
}
return res
}
// sendAll sends the alerts to all configured Alertmanagers concurrently. // sendAll sends the alerts to all configured Alertmanagers concurrently.
// It returns true if the alerts could be sent successfully to at least one Alertmanager. // It returns true if the alerts could be sent successfully to at least one Alertmanager.
func (n *Manager) sendAll(alerts ...*Alert) bool { func (n *Manager) sendAll(alerts ...*Alert) bool {
@ -521,6 +540,7 @@ type alertmanagerSet struct {
mtx sync.RWMutex mtx sync.RWMutex
ams []alertmanager ams []alertmanager
droppedAms []alertmanager
logger log.Logger logger log.Logger
} }
@ -540,24 +560,28 @@ func newAlertmanagerSet(cfg *config.AlertmanagerConfig, logger log.Logger) (*ale
// sync extracts a deduplicated set of Alertmanager endpoints from a list // sync extracts a deduplicated set of Alertmanager endpoints from a list
// of target groups definitions. // of target groups definitions.
func (s *alertmanagerSet) sync(tgs []*targetgroup.Group) { func (s *alertmanagerSet) sync(tgs []*targetgroup.Group) {
all := []alertmanager{} allAms := []alertmanager{}
allDroppedAms := []alertmanager{}
for _, tg := range tgs { for _, tg := range tgs {
ams, err := alertmanagerFromGroup(tg, s.cfg) ams, droppedAms, err := alertmanagerFromGroup(tg, s.cfg)
if err != nil { if err != nil {
level.Error(s.logger).Log("msg", "Creating discovered Alertmanagers failed", "err", err) level.Error(s.logger).Log("msg", "Creating discovered Alertmanagers failed", "err", err)
continue continue
} }
all = append(all, ams...) allAms = append(allAms, ams...)
allDroppedAms = append(allDroppedAms, droppedAms...)
} }
s.mtx.Lock() s.mtx.Lock()
defer s.mtx.Unlock() defer s.mtx.Unlock()
// Set new Alertmanagers and deduplicate them along their unique URL. // Set new Alertmanagers and deduplicate them along their unique URL.
s.ams = []alertmanager{} s.ams = []alertmanager{}
s.droppedAms = []alertmanager{}
s.droppedAms = append(s.droppedAms, allDroppedAms...)
seen := map[string]struct{}{} seen := map[string]struct{}{}
for _, am := range all { for _, am := range allAms {
us := am.url().String() us := am.url().String()
if _, ok := seen[us]; ok { if _, ok := seen[us]; ok {
continue continue
@ -578,8 +602,9 @@ func postPath(pre string) string {
// alertmanagersFromGroup extracts a list of alertmanagers from a target group and an associcated // alertmanagersFromGroup extracts a list of alertmanagers from a target group and an associcated
// AlertmanagerConfig. // AlertmanagerConfig.
func alertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig) ([]alertmanager, error) { func alertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig) ([]alertmanager, []alertmanager, error) {
var res []alertmanager var res []alertmanager
var droppedAlertManagers []alertmanager
for _, tlset := range tg.Targets { for _, tlset := range tg.Targets {
lbls := make([]labels.Label, 0, len(tlset)+2+len(tg.Labels)) lbls := make([]labels.Label, 0, len(tlset)+2+len(tg.Labels))
@ -600,6 +625,7 @@ func alertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig
lset := relabel.Process(labels.New(lbls...), cfg.RelabelConfigs...) lset := relabel.Process(labels.New(lbls...), cfg.RelabelConfigs...)
if lset == nil { if lset == nil {
droppedAlertManagers = append(droppedAlertManagers, alertmanagerLabels{lbls})
continue continue
} }
@ -627,13 +653,13 @@ func alertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig
case "https": case "https":
addr = addr + ":443" addr = addr + ":443"
default: default:
return nil, fmt.Errorf("invalid scheme: %q", cfg.Scheme) return nil, nil, fmt.Errorf("invalid scheme: %q", cfg.Scheme)
} }
lb.Set(model.AddressLabel, addr) lb.Set(model.AddressLabel, addr)
} }
if err := config.CheckTargetAddress(model.LabelValue(addr)); err != nil { if err := config.CheckTargetAddress(model.LabelValue(addr)); err != nil {
return nil, err return nil, nil, err
} }
// Meta labels are deleted after relabelling. Other internal labels propagate to // Meta labels are deleted after relabelling. Other internal labels propagate to
@ -646,5 +672,5 @@ func alertmanagerFromGroup(tg *targetgroup.Group, cfg *config.AlertmanagerConfig
res = append(res, alertmanagerLabels{lset}) res = append(res, alertmanagerLabels{lset})
} }
return res, nil return res, droppedAlertManagers, nil
} }

View file

@ -442,7 +442,7 @@ func (a alertmanagerMock) url() *url.URL {
func TestLabelSetNotReused(t *testing.T) { func TestLabelSetNotReused(t *testing.T) {
tg := makeInputTargetGroup() tg := makeInputTargetGroup()
_, err := alertmanagerFromGroup(tg, &config.AlertmanagerConfig{}) _, _, err := alertmanagerFromGroup(tg, &config.AlertmanagerConfig{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -503,6 +503,61 @@ alerting:
} }
func TestDroppedAlertmanagers(t *testing.T) {
var tests = []struct {
in *targetgroup.Group
out string
}{
{
in: &targetgroup.Group{
Targets: []model.LabelSet{
{
"__address__": "alertmanager:9093",
},
},
},
out: "http://alertmanager:9093/api/v1/alerts",
},
}
n := NewManager(&Options{}, nil)
cfg := &config.Config{}
s := `
alerting:
alertmanagers:
- static_configs:
relabel_configs:
- source_labels: ['__address__']
regex: 'alertmanager:9093'
action: drop
`
if err := yaml.Unmarshal([]byte(s), cfg); err != nil {
t.Fatalf("Unable to load YAML config: %s", err)
}
if err := n.ApplyConfig(cfg); err != nil {
t.Fatalf("Error Applying the config:%v", err)
}
tgs := make(map[string][]*targetgroup.Group)
for _, tt := range tests {
b, err := json.Marshal(cfg.AlertingConfig.AlertmanagerConfigs[0])
if err != nil {
t.Fatalf("Error creating config hash:%v", err)
}
tgs[fmt.Sprintf("%x", md5.Sum(b))] = []*targetgroup.Group{
tt.in,
}
n.reload(tgs)
res := n.DroppedAlertmanagers()[0].String()
testutil.Equals(t, res, tt.out)
}
}
func makeInputTargetGroup() *targetgroup.Group { func makeInputTargetGroup() *targetgroup.Group {
return &targetgroup.Group{ return &targetgroup.Group{
Targets: []model.LabelSet{ Targets: []model.LabelSet{

View file

@ -87,6 +87,7 @@ type targetRetriever interface {
type alertmanagerRetriever interface { type alertmanagerRetriever interface {
Alertmanagers() []*url.URL Alertmanagers() []*url.URL
DroppedAlertmanagers() []*url.URL
} }
type response struct { type response struct {
@ -469,6 +470,7 @@ func (api *API) targets(r *http.Request) (interface{}, *apiError) {
// AlertmanagerDiscovery has all the active Alertmanagers. // AlertmanagerDiscovery has all the active Alertmanagers.
type AlertmanagerDiscovery struct { type AlertmanagerDiscovery struct {
ActiveAlertmanagers []*AlertmanagerTarget `json:"activeAlertmanagers"` ActiveAlertmanagers []*AlertmanagerTarget `json:"activeAlertmanagers"`
DroppedAlertmanagers []*AlertmanagerTarget `json:"droppedAlertmanagers"`
} }
// AlertmanagerTarget has info on one AM. // AlertmanagerTarget has info on one AM.
@ -478,12 +480,14 @@ type AlertmanagerTarget struct {
func (api *API) alertmanagers(r *http.Request) (interface{}, *apiError) { func (api *API) alertmanagers(r *http.Request) (interface{}, *apiError) {
urls := api.alertmanagerRetriever.Alertmanagers() urls := api.alertmanagerRetriever.Alertmanagers()
ams := &AlertmanagerDiscovery{ActiveAlertmanagers: make([]*AlertmanagerTarget, len(urls))} droppedURLS := api.alertmanagerRetriever.DroppedAlertmanagers()
ams := &AlertmanagerDiscovery{ActiveAlertmanagers: make([]*AlertmanagerTarget, len(urls)), DroppedAlertmanagers: make([]*AlertmanagerTarget, len(droppedURLS))}
for i, url := range urls { for i, url := range urls {
ams.ActiveAlertmanagers[i] = &AlertmanagerTarget{URL: url.String()} ams.ActiveAlertmanagers[i] = &AlertmanagerTarget{URL: url.String()}
} }
for i, url := range droppedURLS {
ams.DroppedAlertmanagers[i] = &AlertmanagerTarget{URL: url.String()}
}
return ams, nil return ams, nil
} }

View file

@ -48,10 +48,26 @@ func (f targetRetrieverFunc) Targets() []*scrape.Target {
return f() return f()
} }
type alertmanagerRetrieverFunc func() []*url.URL type testAlertmanagerRetriever struct{}
func (f alertmanagerRetrieverFunc) Alertmanagers() []*url.URL { func (t testAlertmanagerRetriever) Alertmanagers() []*url.URL {
return f() return []*url.URL{
{
Scheme: "http",
Host: "alertmanager.example.com:8080",
Path: "/api/v1/alerts",
},
}
}
func (t testAlertmanagerRetriever) DroppedAlertmanagers() []*url.URL {
return []*url.URL{
{
Scheme: "http",
Host: "dropped.alertmanager.example.com:8080",
Path: "/api/v1/alerts",
},
}
} }
var samplePrometheusCfg = config.Config{ var samplePrometheusCfg = config.Config{
@ -100,13 +116,7 @@ func TestEndpoints(t *testing.T) {
} }
}) })
ar := alertmanagerRetrieverFunc(func() []*url.URL { var ar testAlertmanagerRetriever
return []*url.URL{{
Scheme: "http",
Host: "alertmanager.example.com:8080",
Path: "/api/v1/alerts",
}}
})
api := &API{ api := &API{
Queryable: suite.Storage(), Queryable: suite.Storage(),
@ -447,6 +457,11 @@ func TestEndpoints(t *testing.T) {
URL: "http://alertmanager.example.com:8080/api/v1/alerts", URL: "http://alertmanager.example.com:8080/api/v1/alerts",
}, },
}, },
DroppedAlertmanagers: []*AlertmanagerTarget{
{
URL: "http://dropped.alertmanager.example.com:8080/api/v1/alerts",
},
},
}, },
}, },
{ {