Merge pull request #196 from prometheus/resolvconf

Move send_resolved parameter to notifier configuration
This commit is contained in:
Fabian Reinartz 2015-12-17 13:50:11 +01:00
commit 68aa792c0c
9 changed files with 72 additions and 164 deletions

View File

@ -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"`

View File

@ -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"`

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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 {

View File

@ -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{

View File

@ -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,

View File

@ -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,
},
},
},