alertmanager/route.go
2015-11-05 10:49:32 +01:00

241 lines
6.0 KiB
Go

// Copyright 2015 Prometheus Team
// 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 main
import (
"encoding/json"
"fmt"
"time"
"github.com/prometheus/common/model"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/types"
)
// DefaultRouteOpts are the defaulting routing options which apply
// to the root route of a routing tree.
var DefaultRouteOpts = RouteOpts{
GroupWait: 30 * time.Second,
GroupInterval: 5 * time.Minute,
RepeatInterval: 4 * time.Hour,
SendResolved: true,
GroupBy: map[model.LabelName]struct{}{
model.AlertNameLabel: struct{}{},
},
}
// A Route is a node that contains definitions of how to handle alerts.
type Route struct {
parent *Route
// The configuration parameters for matches of this route.
RouteOpts RouteOpts
// Equality or regex matchers an alert has to fulfill to match
// this route.
Matchers types.Matchers
// If true, an alert matches further routes on the same level.
Continue bool
// Children routes of this route.
Routes []*Route
}
// NewRoute returns a new route.
func NewRoute(cr *config.Route, parent *Route) *Route {
// Create default and overwrite with configured settings.
opts := DefaultRouteOpts
if parent != nil {
opts = parent.RouteOpts
}
if cr.SendTo != "" {
opts.SendTo = cr.SendTo
}
if cr.GroupBy != nil {
opts.GroupBy = map[model.LabelName]struct{}{}
for _, ln := range cr.GroupBy {
opts.GroupBy[ln] = struct{}{}
}
}
if cr.GroupWait != nil {
opts.GroupWait = time.Duration(*cr.GroupWait)
}
if cr.GroupInterval != nil {
opts.GroupInterval = time.Duration(*cr.GroupInterval)
}
if cr.RepeatInterval != nil {
opts.RepeatInterval = time.Duration(*cr.RepeatInterval)
}
if cr.SendResolved != nil {
opts.SendResolved = *cr.SendResolved
}
// Build matchers.
var matchers types.Matchers
for ln, lv := range cr.Match {
matchers = append(matchers, types.NewMatcher(model.LabelName(ln), lv))
}
for ln, lv := range cr.MatchRE {
m, err := types.NewRegexMatcher(model.LabelName(ln), lv.String())
if err != nil {
// Must have been sanitized during config validation.
panic(err)
}
matchers = append(matchers, m)
}
route := &Route{
parent: parent,
RouteOpts: opts,
Matchers: matchers,
Continue: cr.Continue,
}
route.Routes = NewRoutes(cr.Routes, route)
return route
}
// NewRoutes returns a slice of routes.
func NewRoutes(croutes []*config.Route, parent *Route) []*Route {
res := []*Route{}
for _, cr := range croutes {
res = append(res, NewRoute(cr, parent))
}
return res
}
// UIRoute returns the API data representation of a the route.
func (r *Route) UIRoute() *UIRoute {
var subs []*UIRoute
for _, sr := range r.Routes {
subs = append(subs, sr.UIRoute())
}
uir := &UIRoute{
RouteOpts: &r.RouteOpts,
Matchers: r.Matchers,
Routes: subs,
}
return uir
}
// Match does a depth-first left-to-right search through the route tree
// and returns the flattened configuration for the reached node.
func (r *Route) Match(lset model.LabelSet) []*RouteOpts {
if !r.Matchers.Match(lset) {
return nil
}
var all []*RouteOpts
for _, cr := range r.Routes {
matches := cr.Match(lset)
all = append(all, matches...)
if matches != nil && !cr.Continue {
break
}
}
// If no child nodes were matches, the current node itself is
// a match.
if len(all) == 0 {
all = append(all, &r.RouteOpts)
}
return all
}
// SquashMatchers returns the total set of matchers on the path of the tree
// that have to apply to reach the route.
func (r *Route) SquashMatchers() types.Matchers {
var res types.Matchers
res = append(res, r.Matchers...)
if r.parent == nil {
return res
}
pm := r.parent.SquashMatchers()
res = append(pm, res...)
return res
}
// Fingerprint returns a hash of the Route based on its grouping labels,
// routing options and the total set of matchers necessary to reach this route.
func (r *Route) Fingerprint() model.Fingerprint {
lset := make(model.LabelSet, len(r.RouteOpts.GroupBy))
for ln := range r.RouteOpts.GroupBy {
lset[ln] = ""
}
return r.SquashMatchers().Fingerprint() ^ lset.Fingerprint()
}
// RouteOpts holds various routing options necessary for processing alerts
// that match a given route.
type RouteOpts struct {
// The identifier of the associated notification configuration
SendTo string
SendResolved bool
// What labels to group alerts by for notifications.
GroupBy map[model.LabelName]struct{}
// How long to wait to group matching alerts before sending
// a notificaiton
GroupWait time.Duration
GroupInterval time.Duration
RepeatInterval time.Duration
}
func (ro *RouteOpts) String() string {
var labels []model.LabelName
for ln := range ro.GroupBy {
labels = append(labels, ln)
}
return fmt.Sprintf("<RouteOpts send_to:%q group_by:%q timers:%q|%q>", ro.SendTo, labels, ro.GroupWait, ro.GroupInterval)
}
// MarshalJSON returns a JSON representation of the routing options.
func (ro *RouteOpts) MarshalJSON() ([]byte, error) {
v := struct {
SendTo string `json:"sendTo"`
SendResolved bool `json:"sendResolved"`
GroupBy model.LabelNames `json:"groupBy"`
GroupWait time.Duration `json:"groupWait"`
GroupInterval time.Duration `json:"groupInterval"`
RepeatInterval time.Duration `json:"repeatInterval"`
}{
SendTo: ro.SendTo,
SendResolved: ro.SendResolved,
GroupWait: ro.GroupWait,
GroupInterval: ro.GroupInterval,
RepeatInterval: ro.RepeatInterval,
}
for ln := range ro.GroupBy {
v.GroupBy = append(v.GroupBy, ln)
}
return json.Marshal(&v)
}