1// Copyright 2015 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 14package dispatch 15 16import ( 17 "encoding/json" 18 "fmt" 19 "sort" 20 "strings" 21 "time" 22 23 "github.com/prometheus/common/model" 24 25 "github.com/prometheus/alertmanager/config" 26 "github.com/prometheus/alertmanager/pkg/labels" 27) 28 29// DefaultRouteOpts are the defaulting routing options which apply 30// to the root route of a routing tree. 31var DefaultRouteOpts = RouteOpts{ 32 GroupWait: 30 * time.Second, 33 GroupInterval: 5 * time.Minute, 34 RepeatInterval: 4 * time.Hour, 35 GroupBy: map[model.LabelName]struct{}{}, 36 GroupByAll: false, 37 MuteTimeIntervals: []string{}, 38} 39 40// A Route is a node that contains definitions of how to handle alerts. 41type Route struct { 42 parent *Route 43 44 // The configuration parameters for matches of this route. 45 RouteOpts RouteOpts 46 47 // Matchers an alert has to fulfill to match 48 // this route. 49 Matchers labels.Matchers 50 51 // If true, an alert matches further routes on the same level. 52 Continue bool 53 54 // Children routes of this route. 55 Routes []*Route 56} 57 58// NewRoute returns a new route. 59func NewRoute(cr *config.Route, parent *Route) *Route { 60 // Create default and overwrite with configured settings. 61 opts := DefaultRouteOpts 62 if parent != nil { 63 opts = parent.RouteOpts 64 } 65 66 if cr.Receiver != "" { 67 opts.Receiver = cr.Receiver 68 } 69 70 if cr.GroupBy != nil { 71 opts.GroupBy = map[model.LabelName]struct{}{} 72 for _, ln := range cr.GroupBy { 73 opts.GroupBy[ln] = struct{}{} 74 } 75 opts.GroupByAll = false 76 } else { 77 if cr.GroupByAll { 78 opts.GroupByAll = cr.GroupByAll 79 } 80 } 81 82 if cr.GroupWait != nil { 83 opts.GroupWait = time.Duration(*cr.GroupWait) 84 } 85 if cr.GroupInterval != nil { 86 opts.GroupInterval = time.Duration(*cr.GroupInterval) 87 } 88 if cr.RepeatInterval != nil { 89 opts.RepeatInterval = time.Duration(*cr.RepeatInterval) 90 } 91 92 // Build matchers. 93 var matchers labels.Matchers 94 95 // cr.Match will be deprecated. This for loop appends matchers. 96 for ln, lv := range cr.Match { 97 matcher, err := labels.NewMatcher(labels.MatchEqual, ln, lv) 98 if err != nil { 99 // This error must not happen because the config already validates the yaml. 100 panic(err) 101 } 102 matchers = append(matchers, matcher) 103 } 104 105 // cr.MatchRE will be deprecated. This for loop appends regex matchers. 106 for ln, lv := range cr.MatchRE { 107 matcher, err := labels.NewMatcher(labels.MatchRegexp, ln, lv.String()) 108 if err != nil { 109 // This error must not happen because the config already validates the yaml. 110 panic(err) 111 } 112 matchers = append(matchers, matcher) 113 } 114 115 // We append the new-style matchers. This can be simplified once the deprecated matcher syntax is removed. 116 matchers = append(matchers, cr.Matchers...) 117 118 sort.Sort(matchers) 119 120 opts.MuteTimeIntervals = cr.MuteTimeIntervals 121 122 route := &Route{ 123 parent: parent, 124 RouteOpts: opts, 125 Matchers: matchers, 126 Continue: cr.Continue, 127 } 128 129 route.Routes = NewRoutes(cr.Routes, route) 130 131 return route 132} 133 134// NewRoutes returns a slice of routes. 135func NewRoutes(croutes []*config.Route, parent *Route) []*Route { 136 res := []*Route{} 137 for _, cr := range croutes { 138 res = append(res, NewRoute(cr, parent)) 139 } 140 return res 141} 142 143// Match does a depth-first left-to-right search through the route tree 144// and returns the matching routing nodes. 145func (r *Route) Match(lset model.LabelSet) []*Route { 146 if !r.Matchers.Matches(lset) { 147 return nil 148 } 149 150 var all []*Route 151 152 for _, cr := range r.Routes { 153 matches := cr.Match(lset) 154 155 all = append(all, matches...) 156 157 if matches != nil && !cr.Continue { 158 break 159 } 160 } 161 162 // If no child nodes were matches, the current node itself is a match. 163 if len(all) == 0 { 164 all = append(all, r) 165 } 166 167 return all 168} 169 170// Key returns a key for the route. It does not uniquely identify the route in general. 171func (r *Route) Key() string { 172 b := strings.Builder{} 173 174 if r.parent != nil { 175 b.WriteString(r.parent.Key()) 176 b.WriteRune('/') 177 } 178 b.WriteString(r.Matchers.String()) 179 return b.String() 180} 181 182// Walk traverses the route tree in depth-first order. 183func (r *Route) Walk(visit func(*Route)) { 184 visit(r) 185 if r.Routes == nil { 186 return 187 } 188 for i := range r.Routes { 189 r.Routes[i].Walk(visit) 190 } 191} 192 193// RouteOpts holds various routing options necessary for processing alerts 194// that match a given route. 195type RouteOpts struct { 196 // The identifier of the associated notification configuration. 197 Receiver string 198 199 // What labels to group alerts by for notifications. 200 GroupBy map[model.LabelName]struct{} 201 202 // Use all alert labels to group. 203 GroupByAll bool 204 205 // How long to wait to group matching alerts before sending 206 // a notification. 207 GroupWait time.Duration 208 GroupInterval time.Duration 209 RepeatInterval time.Duration 210 211 // A list of time intervals for which the route is muted. 212 MuteTimeIntervals []string 213} 214 215func (ro *RouteOpts) String() string { 216 var labels []model.LabelName 217 for ln := range ro.GroupBy { 218 labels = append(labels, ln) 219 } 220 return fmt.Sprintf("<RouteOpts send_to:%q group_by:%q group_by_all:%t timers:%q|%q>", 221 ro.Receiver, labels, ro.GroupByAll, ro.GroupWait, ro.GroupInterval) 222} 223 224// MarshalJSON returns a JSON representation of the routing options. 225func (ro *RouteOpts) MarshalJSON() ([]byte, error) { 226 v := struct { 227 Receiver string `json:"receiver"` 228 GroupBy model.LabelNames `json:"groupBy"` 229 GroupByAll bool `json:"groupByAll"` 230 GroupWait time.Duration `json:"groupWait"` 231 GroupInterval time.Duration `json:"groupInterval"` 232 RepeatInterval time.Duration `json:"repeatInterval"` 233 }{ 234 Receiver: ro.Receiver, 235 GroupByAll: ro.GroupByAll, 236 GroupWait: ro.GroupWait, 237 GroupInterval: ro.GroupInterval, 238 RepeatInterval: ro.RepeatInterval, 239 } 240 for ln := range ro.GroupBy { 241 v.GroupBy = append(v.GroupBy, ln) 242 } 243 244 return json.Marshal(&v) 245} 246