mirror of
https://github.com/prometheus/alertmanager
synced 2025-01-31 18:51:53 +00:00
Merge pull request #196 from prometheus/resolvconf
Move send_resolved parameter to notifier configuration
This commit is contained in:
commit
68aa792c0c
@ -235,7 +235,6 @@ type Route struct {
|
||||
GroupWait *model.Duration `yaml:"group_wait,omitempty"`
|
||||
GroupInterval *model.Duration `yaml:"group_interval,omitempty"`
|
||||
RepeatInterval *model.Duration `yaml:"repeat_interval,omitempty"`
|
||||
SendResolved *bool `yaml:"send_resolved,omitempty"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
@ -336,10 +335,7 @@ type Receiver struct {
|
||||
Name string `yaml:"name"`
|
||||
|
||||
EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty"`
|
||||
FlowdockConfigs []*FlowdockConfig `yaml:"flowdock_configs,omitempty"`
|
||||
HipchatConfigs []*HipchatConfig `yaml:"hipchat_configs,omitempty"`
|
||||
PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty"`
|
||||
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty"`
|
||||
SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty"`
|
||||
WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty"`
|
||||
OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty"`
|
||||
|
@ -19,22 +19,29 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultWebhookConfig defines default values for Webhook configurations.
|
||||
DefaultWebhookConfig = WebhookConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: true,
|
||||
},
|
||||
}
|
||||
|
||||
// DefaultEmailConfig defines default values for Email configurations.
|
||||
DefaultEmailConfig = EmailConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: false,
|
||||
},
|
||||
HTML: `{{ template "email.default.html" . }}`,
|
||||
}
|
||||
|
||||
// DefaultEmailSubject defines the default Subject header of an Email.
|
||||
DefaultEmailSubject = `{{ template "email.default.subject" . }}`
|
||||
|
||||
// DefaultHipchatConfig defines default values for Hipchat configurations.
|
||||
DefaultHipchatConfig = HipchatConfig{
|
||||
Color: `{{ if eq .Status "firing" }}purple{{ else }}green{{ end }}`,
|
||||
MessageFormat: HipchatFormatHTML,
|
||||
}
|
||||
|
||||
// DefaultPagerdutyConfig defines default values for PagerDuty configurations.
|
||||
DefaultPagerdutyConfig = PagerdutyConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: true,
|
||||
},
|
||||
Description: `{{ template "pagerduty.default.description" .}}`,
|
||||
Client: `{{ template "pagerduty.default.client" . }}`,
|
||||
ClientURL: `{{ template "pagerduty.default.clientURL" . }}`,
|
||||
@ -48,6 +55,9 @@ var (
|
||||
|
||||
// DefaultSlackConfig defines default values for Slack configurations.
|
||||
DefaultSlackConfig = SlackConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: true,
|
||||
},
|
||||
Color: `{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}`,
|
||||
Username: `{{ template "slack.default.username" . }}`,
|
||||
Title: `{{ template "slack.default.title" . }}`,
|
||||
@ -59,44 +69,28 @@ var (
|
||||
|
||||
// DefaultOpsGenieConfig defines default values for OpsGenie configurations.
|
||||
DefaultOpsGenieConfig = OpsGenieConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: true,
|
||||
},
|
||||
Description: `{{ template "opsgenie.default.description" . }}`,
|
||||
Source: `{{ template "opsgenie.default.source" . }}`,
|
||||
// TODO: Add a details field with all the alerts.
|
||||
}
|
||||
)
|
||||
|
||||
// FlowdockConfig configures notifications via Flowdock.
|
||||
type FlowdockConfig struct {
|
||||
// Flowdock flow API token.
|
||||
APIToken Secret `yaml:"api_token"`
|
||||
|
||||
// Flowdock from_address.
|
||||
FromAddress string `yaml:"from_address"`
|
||||
|
||||
// Flowdock flow tags.
|
||||
Tags []string `yaml:"tags"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
// NotifierConfig contains base options common across all notifier configurations.
|
||||
type NotifierConfig struct {
|
||||
VSendResolved bool `yaml:"send_resolved"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *FlowdockConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain FlowdockConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.APIToken == "" {
|
||||
return fmt.Errorf("missing API token in Flowdock config")
|
||||
}
|
||||
if c.FromAddress == "" {
|
||||
return fmt.Errorf("missing from address in Flowdock config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "flowdock config")
|
||||
func (nc *NotifierConfig) SendResolved() bool {
|
||||
return nc.VSendResolved
|
||||
}
|
||||
|
||||
// EmailConfig configures notifications via mail.
|
||||
type EmailConfig struct {
|
||||
NotifierConfig
|
||||
|
||||
// Email address to notify.
|
||||
To string `yaml:"to"`
|
||||
From string `yaml:"from"`
|
||||
@ -132,58 +126,10 @@ func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
return checkOverflow(c.XXX, "email config")
|
||||
}
|
||||
|
||||
// HipchatFormat defines text formats for Hipchat.
|
||||
type HipchatFormat string
|
||||
|
||||
// Possible values of HipchatFormat.
|
||||
const (
|
||||
HipchatFormatHTML HipchatFormat = "html"
|
||||
HipchatFormatText HipchatFormat = "text"
|
||||
)
|
||||
|
||||
// HipchatConfig configures notifications via Hipchat.
|
||||
// https://www.hipchat.com/docs/apiv2/method/send_room_notification
|
||||
type HipchatConfig struct {
|
||||
// HipChat auth token, (https://www.hipchat.com/docs/api/auth).
|
||||
APIToken Secret `yaml:"api_token"`
|
||||
|
||||
// HipChat room id, (https://www.hipchat.com/rooms/ids).
|
||||
RoomID int `yaml:"room_id"`
|
||||
|
||||
// The message color.
|
||||
Color string `yaml:"color"`
|
||||
|
||||
// Should this message notify or not.
|
||||
Notify bool `yaml:"notify"`
|
||||
|
||||
// Prefix to be put in front of the message (useful for @mentions, etc.).
|
||||
Prefix string `yaml:"prefix"`
|
||||
|
||||
// Format the message as "html" or "text".
|
||||
MessageFormat HipchatFormat `yaml:"message_format"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *HipchatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultHipchatConfig
|
||||
type plain HipchatConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.APIToken == "" {
|
||||
return fmt.Errorf("missing API token in HipChat config")
|
||||
}
|
||||
if c.MessageFormat != HipchatFormatHTML && c.MessageFormat != HipchatFormatText {
|
||||
return fmt.Errorf("invalid message format %q", c.MessageFormat)
|
||||
}
|
||||
return checkOverflow(c.XXX, "hipchat config")
|
||||
}
|
||||
|
||||
// PagerdutyConfig configures notifications via PagerDuty.
|
||||
type PagerdutyConfig struct {
|
||||
NotifierConfig
|
||||
|
||||
ServiceKey Secret `yaml:"service_key"`
|
||||
URL string `yaml:"url"`
|
||||
Client string `yaml:"client"`
|
||||
@ -208,35 +154,10 @@ func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
|
||||
return checkOverflow(c.XXX, "pagerduty config")
|
||||
}
|
||||
|
||||
// PushoverConfig configures notifications via PushOver.
|
||||
type PushoverConfig struct {
|
||||
// Pushover token.
|
||||
Token string `yaml:"token"`
|
||||
|
||||
// Pushover user_key.
|
||||
UserKey string `yaml:"user_key"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *PushoverConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain PushoverConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Token == "" {
|
||||
return fmt.Errorf("missing token in Pushover config")
|
||||
}
|
||||
if c.UserKey == "" {
|
||||
return fmt.Errorf("missing user key in Pushover config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "pushover config")
|
||||
}
|
||||
|
||||
// SlackConfig configures notifications via Slack.
|
||||
type SlackConfig struct {
|
||||
NotifierConfig
|
||||
|
||||
APIURL Secret `yaml:"api_url"`
|
||||
|
||||
// Slack channel override, (like #other-channel or @username).
|
||||
@ -269,6 +190,8 @@ func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
|
||||
// WebhookConfig configures notifications via a generic webhook.
|
||||
type WebhookConfig struct {
|
||||
NotifierConfig
|
||||
|
||||
// URL to send POST request to.
|
||||
URL string `yaml:"url"`
|
||||
|
||||
@ -278,6 +201,7 @@ type WebhookConfig struct {
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultWebhookConfig
|
||||
type plain WebhookConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
@ -290,6 +214,8 @@ func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
|
||||
// OpsGenieConfig configures notifications via OpsGenie.
|
||||
type OpsGenieConfig struct {
|
||||
NotifierConfig
|
||||
|
||||
APIKey Secret `yaml:"api_key"`
|
||||
APIHost string `yaml:"api_host"`
|
||||
Description string `yaml:"description"`
|
||||
|
@ -326,7 +326,6 @@ func (ag *aggrGroup) run(nf notifyFunc) {
|
||||
ctx = notify.WithGroupLabels(ctx, ag.labels)
|
||||
ctx = notify.WithReceiver(ctx, ag.opts.Receiver)
|
||||
ctx = notify.WithRepeatInterval(ctx, ag.opts.RepeatInterval)
|
||||
ctx = notify.WithSendResolved(ctx, ag.opts.SendResolved)
|
||||
|
||||
// Wait the configured interval before calling flush again.
|
||||
ag.mtx.Lock()
|
||||
|
@ -20,7 +20,6 @@ func TestAggrGroup(t *testing.T) {
|
||||
}
|
||||
opts := &RouteOpts{
|
||||
Receiver: "n1",
|
||||
SendResolved: true,
|
||||
GroupBy: map[model.LabelName]struct{}{},
|
||||
GroupWait: 1 * time.Second,
|
||||
GroupInterval: 300 * time.Millisecond,
|
||||
@ -89,9 +88,6 @@ func TestAggrGroup(t *testing.T) {
|
||||
if ri, ok := notify.RepeatInterval(ctx); !ok || ri != opts.RepeatInterval {
|
||||
t.Errorf("wrong repeat interval: %q", ri)
|
||||
}
|
||||
if sr, ok := notify.SendResolved(ctx); !ok || sr != opts.SendResolved {
|
||||
t.Errorf("wrong send_resolved: %v", sr)
|
||||
}
|
||||
|
||||
last = current
|
||||
current = time.Now()
|
||||
|
@ -38,10 +38,40 @@ import (
|
||||
"github.com/prometheus/alertmanager/types"
|
||||
)
|
||||
|
||||
type notifierConfig interface {
|
||||
SendResolved() bool
|
||||
}
|
||||
|
||||
type NotifierFunc func(context.Context, ...*types.Alert) error
|
||||
|
||||
func (f NotifierFunc) Notify(ctx context.Context, alerts ...*types.Alert) error {
|
||||
return f(ctx, alerts...)
|
||||
}
|
||||
|
||||
// Build creates a fanout notifier for each receiver.
|
||||
func Build(confs []*config.Receiver, tmpl *template.Template) map[string]Fanout {
|
||||
res := map[string]Fanout{}
|
||||
|
||||
filter := func(n Notifier, c notifierConfig) Notifier {
|
||||
return NotifierFunc(func(ctx context.Context, alerts ...*types.Alert) error {
|
||||
var res []*types.Alert
|
||||
|
||||
if c.SendResolved() {
|
||||
res = alerts
|
||||
} else {
|
||||
for _, a := range alerts {
|
||||
if a.Status() != model.AlertResolved {
|
||||
res = append(res, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return nil
|
||||
}
|
||||
return n.Notify(ctx, res...)
|
||||
})
|
||||
}
|
||||
|
||||
for _, nc := range confs {
|
||||
var (
|
||||
fo = Fanout{}
|
||||
@ -49,19 +79,19 @@ func Build(confs []*config.Receiver, tmpl *template.Template) map[string]Fanout
|
||||
)
|
||||
|
||||
for i, c := range nc.WebhookConfigs {
|
||||
add(i, NewWebhook(c))
|
||||
add(i, filter(NewWebhook(c), c))
|
||||
}
|
||||
for i, c := range nc.EmailConfigs {
|
||||
add(i, NewEmail(c, tmpl))
|
||||
add(i, filter(NewEmail(c, tmpl), c))
|
||||
}
|
||||
for i, c := range nc.PagerdutyConfigs {
|
||||
add(i, NewPagerDuty(c, tmpl))
|
||||
add(i, filter(NewPagerDuty(c, tmpl), c))
|
||||
}
|
||||
for i, c := range nc.OpsGenieConfigs {
|
||||
add(i, NewOpsGenie(c, tmpl))
|
||||
add(i, filter(NewOpsGenie(c, tmpl), c))
|
||||
}
|
||||
for i, c := range nc.SlackConfigs {
|
||||
add(i, NewSlack(c, tmpl))
|
||||
add(i, filter(NewSlack(c, tmpl), c))
|
||||
}
|
||||
|
||||
res[nc.Name] = fo
|
||||
|
@ -38,7 +38,6 @@ type notifyKey int
|
||||
const (
|
||||
keyReceiver notifyKey = iota
|
||||
keyRepeatInterval
|
||||
keySendResolved
|
||||
keyGroupLabels
|
||||
keyGroupKey
|
||||
keyNow
|
||||
@ -54,11 +53,6 @@ func WithRepeatInterval(ctx context.Context, t time.Duration) context.Context {
|
||||
return context.WithValue(ctx, keyRepeatInterval, t)
|
||||
}
|
||||
|
||||
// WithSendResolved populates a context with a send resolved boolean.
|
||||
func WithSendResolved(ctx context.Context, b bool) context.Context {
|
||||
return context.WithValue(ctx, keySendResolved, b)
|
||||
}
|
||||
|
||||
// WithGroupKey populates a context with a group key.
|
||||
func WithGroupKey(ctx context.Context, fp model.Fingerprint) context.Context {
|
||||
return context.WithValue(ctx, keyGroupKey, fp)
|
||||
@ -96,13 +90,6 @@ func RepeatInterval(ctx context.Context) (time.Duration, bool) {
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// SendResolved extracts a send resolved boolean from the context.
|
||||
// Iff none exists, the second argument is false.
|
||||
func SendResolved(ctx context.Context) (bool, bool) {
|
||||
v, ok := ctx.Value(keySendResolved).(bool)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// GroupKey extracts a group key from the context. Iff none exists, the
|
||||
// second argument is false.
|
||||
func GroupKey(ctx context.Context) (model.Fingerprint, bool) {
|
||||
@ -238,11 +225,6 @@ func (n *DedupingNotifier) Notify(ctx context.Context, alerts ...*types.Alert) e
|
||||
return fmt.Errorf("repeat interval missing")
|
||||
}
|
||||
|
||||
sendResolved, ok := SendResolved(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("send resolved missing")
|
||||
}
|
||||
|
||||
now, ok := Now(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("now time missing")
|
||||
@ -268,7 +250,7 @@ func (n *DedupingNotifier) Notify(ctx context.Context, alerts ...*types.Alert) e
|
||||
|
||||
if last != nil {
|
||||
if a.Resolved() {
|
||||
if !sendResolved || last.Resolved {
|
||||
if last.Resolved {
|
||||
continue
|
||||
}
|
||||
} else if !last.Resolved {
|
||||
@ -301,12 +283,6 @@ func (n *DedupingNotifier) Notify(ctx context.Context, alerts ...*types.Alert) e
|
||||
filtered = append(filtered, resendQueue...)
|
||||
}
|
||||
|
||||
// The deduping notifier is the last one before actually sending notifications.
|
||||
// Thus, this is the place where we abort if after all filtering, nothing is left.
|
||||
if len(filtered) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var newNotifies []*types.NotifyInfo
|
||||
|
||||
for _, a := range filtered {
|
||||
|
@ -54,7 +54,6 @@ func TestDedupingNotifier(t *testing.T) {
|
||||
|
||||
ctx = WithReceiver(ctx, "name")
|
||||
ctx = WithRepeatInterval(ctx, time.Duration(100*time.Minute))
|
||||
ctx = WithSendResolved(ctx, true)
|
||||
ctx = WithNow(ctx, now)
|
||||
|
||||
alerts := []*types.Alert{
|
||||
|
9
route.go
9
route.go
@ -30,7 +30,6 @@ 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{}{},
|
||||
},
|
||||
@ -80,9 +79,6 @@ func NewRoute(cr *config.Route, parent *Route) *Route {
|
||||
if cr.RepeatInterval != nil {
|
||||
opts.RepeatInterval = time.Duration(*cr.RepeatInterval)
|
||||
}
|
||||
if cr.SendResolved != nil {
|
||||
opts.SendResolved = *cr.SendResolved
|
||||
}
|
||||
|
||||
// Build matchers.
|
||||
var matchers types.Matchers
|
||||
@ -175,8 +171,7 @@ func (r *Route) Fingerprint() model.Fingerprint {
|
||||
// that match a given route.
|
||||
type RouteOpts struct {
|
||||
// The identifier of the associated notification configuration
|
||||
Receiver string
|
||||
SendResolved bool
|
||||
Receiver string
|
||||
|
||||
// What labels to group alerts by for notifications.
|
||||
GroupBy map[model.LabelName]struct{}
|
||||
@ -200,14 +195,12 @@ func (ro *RouteOpts) String() string {
|
||||
func (ro *RouteOpts) MarshalJSON() ([]byte, error) {
|
||||
v := struct {
|
||||
Receiver string `json:"receiver"`
|
||||
SendResolved bool `json:"sendResolved"`
|
||||
GroupBy model.LabelNames `json:"groupBy"`
|
||||
GroupWait time.Duration `json:"groupWait"`
|
||||
GroupInterval time.Duration `json:"groupInterval"`
|
||||
RepeatInterval time.Duration `json:"repeatInterval"`
|
||||
}{
|
||||
Receiver: ro.Receiver,
|
||||
SendResolved: ro.SendResolved,
|
||||
GroupWait: ro.GroupWait,
|
||||
GroupInterval: ro.GroupInterval,
|
||||
RepeatInterval: ro.RepeatInterval,
|
||||
|
@ -64,7 +64,6 @@ routes:
|
||||
|
||||
group_by: ['foo', 'bar']
|
||||
group_wait: 2m
|
||||
send_resolved: false
|
||||
receiver: 'notify-BC'
|
||||
`
|
||||
|
||||
@ -99,7 +98,6 @@ routes:
|
||||
GroupWait: def.GroupWait,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
SendResolved: def.SendResolved,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -115,7 +113,6 @@ routes:
|
||||
GroupWait: def.GroupWait,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
SendResolved: def.SendResolved,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -130,7 +127,6 @@ routes:
|
||||
GroupWait: 2 * time.Minute,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
SendResolved: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -146,7 +142,6 @@ routes:
|
||||
GroupWait: def.GroupWait,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
SendResolved: def.SendResolved,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -162,7 +157,6 @@ routes:
|
||||
GroupWait: 1 * time.Minute,
|
||||
GroupInterval: def.GroupInterval,
|
||||
RepeatInterval: def.RepeatInterval,
|
||||
SendResolved: def.SendResolved,
|
||||
},
|
||||
{
|
||||
Receiver: "notify-productionB",
|
||||
@ -170,7 +164,6 @@ routes:
|
||||
GroupWait: 30 * time.Second,
|
||||
GroupInterval: 5 * time.Minute,
|
||||
RepeatInterval: 1 * time.Hour,
|
||||
SendResolved: def.SendResolved,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user