switch to YAML config.
This commit is contained in:
parent
cb9c85ea5a
commit
2e1a01b2fa
4
Makefile
4
Makefile
|
@ -20,7 +20,3 @@ web: web/blob/files.go
|
|||
|
||||
web/blob/files.go: $(shell find web/templates/ web/static/ -type f)
|
||||
./web/blob/embed-static.sh web/static web/templates | $(GOFMT) > $@
|
||||
|
||||
.PHONY: config
|
||||
config:
|
||||
$(MAKE) -C config
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# Copyright 2013 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.
|
||||
|
||||
all: generated/config.pb.go
|
||||
|
||||
generated/config.pb.go: config.proto
|
||||
protoc --go_out=generated/ config.proto
|
565
config/config.go
565
config/config.go
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2013 Prometheus Team
|
||||
// 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
|
||||
|
@ -15,138 +15,473 @@ package config
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
pb "github.com/prometheus/alertmanager/config/generated"
|
||||
|
||||
"github.com/prometheus/alertmanager/manager"
|
||||
"github.com/prometheus/common/model"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const minimumRepeatRate = 1 * time.Minute
|
||||
var (
|
||||
DefaultAggrRule = AggrRule{
|
||||
RepeatRate: model.Duration(2 * time.Hour),
|
||||
SendResolved: false,
|
||||
}
|
||||
|
||||
// Config encapsulates the configuration of an Alert Manager instance. It wraps
|
||||
// the raw configuration protocol buffer to be able to add custom methods to
|
||||
// it.
|
||||
DefaultHipchatConfig = HipchatConfig{
|
||||
Color: "purple",
|
||||
ColorResolved: "green",
|
||||
MessageFormat: HipchatFormatHTML,
|
||||
}
|
||||
|
||||
DefaultSlackConfig = SlackConfig{
|
||||
Color: "warning",
|
||||
ColorResolved: "good",
|
||||
}
|
||||
)
|
||||
|
||||
// Load parses the YAML input s into a Config.
|
||||
func Load(s string) (*Config, error) {
|
||||
cfg := &Config{}
|
||||
err := yaml.Unmarshal([]byte(s), cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.original = s
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// LoadFile parses the given YAML file into a Config.
|
||||
func LoadFile(filename string) (*Config, error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Load(string(content))
|
||||
}
|
||||
|
||||
// Config is the top-level configuration for Alertmanager's config files.
|
||||
type Config struct {
|
||||
// The protobuf containing the actual configuration values.
|
||||
pb.AlertManagerConfig
|
||||
AggrRules []*AggrRule `yaml:"aggregation_rules,omitempty"`
|
||||
InhibitRules []*InhibitRule `yaml:"inhibit_rules,omitempty"`
|
||||
NotificationConfigs []*NotificationConfig `yaml:"notification_configs,omitempty"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
|
||||
// original is the input from which the config was parsed.
|
||||
original string
|
||||
}
|
||||
|
||||
// String returns an ASCII serialization of the loaded configuration protobuf.
|
||||
func (c Config) String() string {
|
||||
return proto.MarshalTextString(&c.AlertManagerConfig)
|
||||
}
|
||||
|
||||
// Validate checks an entire parsed Config for the validity of its fields.
|
||||
func (c Config) Validate() error {
|
||||
ncNames := map[string]bool{}
|
||||
for _, nc := range c.NotificationConfig {
|
||||
if nc.Name == nil {
|
||||
return fmt.Errorf("Missing name in notification config: %s", proto.MarshalTextString(nc))
|
||||
func checkOverflow(m map[string]interface{}, ctx string) error {
|
||||
if len(m) > 0 {
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
for _, pdc := range nc.PagerdutyConfig {
|
||||
if pdc.ServiceKey == nil {
|
||||
return fmt.Errorf("Missing service key in PagerDuty notification config: %s", proto.MarshalTextString(pdc))
|
||||
}
|
||||
}
|
||||
for _, ec := range nc.EmailConfig {
|
||||
if ec.Email == nil {
|
||||
return fmt.Errorf("Missing email address in email notification config: %s", proto.MarshalTextString(ec))
|
||||
}
|
||||
}
|
||||
for _, ec := range nc.PushoverConfig {
|
||||
if ec.Token == nil {
|
||||
return fmt.Errorf("Missing token in Pushover notification config: %s", proto.MarshalTextString(ec))
|
||||
}
|
||||
if ec.UserKey == nil {
|
||||
return fmt.Errorf("Missing user key in Pushover notification config: %s", proto.MarshalTextString(ec))
|
||||
}
|
||||
}
|
||||
for _, hcc := range nc.HipchatConfig {
|
||||
if hcc.AuthToken == nil {
|
||||
return fmt.Errorf("Missing token in HipChat config: %s", proto.MarshalTextString(hcc))
|
||||
}
|
||||
if hcc.RoomId == nil {
|
||||
return fmt.Errorf("Missing room in HipChat config: %s", proto.MarshalTextString(hcc))
|
||||
}
|
||||
}
|
||||
for _, sc := range nc.SlackConfig {
|
||||
if sc.WebhookUrl == nil {
|
||||
return fmt.Errorf("Missing webhook URL in Slack config: %s", proto.MarshalTextString(sc))
|
||||
}
|
||||
}
|
||||
for _, fc := range nc.FlowdockConfig {
|
||||
if fc.ApiToken == nil {
|
||||
return fmt.Errorf("Missing API token in Flowdock config: %s", proto.MarshalTextString(fc))
|
||||
}
|
||||
if fc.FromAddress == nil {
|
||||
return fmt.Errorf("Missing from_address Flowdock config: %s", proto.MarshalTextString(fc))
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := ncNames[nc.GetName()]; ok {
|
||||
return fmt.Errorf("Notification config name not unique: %s", nc.GetName())
|
||||
}
|
||||
|
||||
ncNames[nc.GetName()] = true
|
||||
return fmt.Errorf("unknown fields in %s: %s", ctx, strings.Join(keys, ", "))
|
||||
}
|
||||
|
||||
for _, a := range c.AggregationRule {
|
||||
for _, f := range a.Filter {
|
||||
if f.NameRe == nil {
|
||||
return fmt.Errorf("Missing name pattern (name_re) in filter definition: %s", proto.MarshalTextString(f))
|
||||
}
|
||||
if f.ValueRe == nil {
|
||||
return fmt.Errorf("Missing value pattern (value_re) in filter definition: %s", proto.MarshalTextString(f))
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := ncNames[a.GetNotificationConfigName()]; !ok {
|
||||
return fmt.Errorf("No such notification config: %s", a.GetNotificationConfigName())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func filtersFromPb(filters []*pb.Filter) manager.Filters {
|
||||
fs := make(manager.Filters, 0, len(filters))
|
||||
for _, f := range filters {
|
||||
fs = append(fs, manager.NewFilter(f.GetNameRe(), f.GetValueRe()))
|
||||
func (c Config) String() string {
|
||||
if c.original != "" {
|
||||
return c.original
|
||||
}
|
||||
return fs
|
||||
b, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<error creating config string: %s>", err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// AggregationRules returns all the AggregationRules in a Config object.
|
||||
func (c Config) AggregationRules() manager.AggregationRules {
|
||||
rules := make(manager.AggregationRules, 0, len(c.AggregationRule))
|
||||
for _, r := range c.AggregationRule {
|
||||
rate := time.Duration(r.GetRepeatRateSeconds()) * time.Second
|
||||
if rate < minimumRepeatRate {
|
||||
rate = minimumRepeatRate
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
// We want to set c to the defaults and then overwrite it with the input.
|
||||
// To make unmarshal fill the plain data struct rather than calling UnmarshalYAML
|
||||
// again, we have to hide it using a type indirection.
|
||||
type plain Config
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names := map[string]struct{}{}
|
||||
|
||||
for _, nc := range c.NotificationConfigs {
|
||||
if _, ok := names[nc.Name]; ok {
|
||||
return fmt.Errorf("notification config name %q is not unique", nc.Name)
|
||||
}
|
||||
rules = append(rules, &manager.AggregationRule{
|
||||
Filters: filtersFromPb(r.Filter),
|
||||
RepeatRate: rate,
|
||||
NotificationConfigName: r.GetNotificationConfigName(),
|
||||
})
|
||||
names[nc.Name] = struct{}{}
|
||||
}
|
||||
return rules
|
||||
return checkOverflow(c.XXX, "config")
|
||||
}
|
||||
|
||||
// InhibitRules returns all the InhibitRules in a Config object.
|
||||
func (c Config) InhibitRules() manager.InhibitRules {
|
||||
rules := make(manager.InhibitRules, 0, len(c.InhibitRule))
|
||||
for _, r := range c.InhibitRule {
|
||||
sFilters := filtersFromPb(r.SourceFilter)
|
||||
tFilters := filtersFromPb(r.TargetFilter)
|
||||
rules = append(rules, &manager.InhibitRule{
|
||||
SourceFilters: sFilters,
|
||||
TargetFilters: tFilters,
|
||||
MatchOn: r.MatchOn,
|
||||
})
|
||||
}
|
||||
return rules
|
||||
// An InhibitRule specifies that a class of (source) alerts should inhibit
|
||||
// notifications for another class of (target) alerts if all specified matching
|
||||
// labels are equal between the two alerts. This may be used to inhibit alerts
|
||||
// from sending notifications if their meaning is logically a subset of a
|
||||
// higher-level alert.
|
||||
type InhibitRule struct {
|
||||
// The set of Filters which define the group of source alerts (which inhibit
|
||||
// the target alerts).
|
||||
SourceFilters []*Filter `yaml:"source_filters,omitempty"`
|
||||
|
||||
// The set of Filters which define the group of target alerts (which are
|
||||
// inhibited by the source alerts).
|
||||
TargetFilters []*Filter `yaml:"target_filters,omitempty"`
|
||||
|
||||
// A set of label names whose label values need to be identical in source and
|
||||
// target alerts in order for the inhibition to take effect.
|
||||
MatchOn []string `yaml:"match_on,omitempty"`
|
||||
|
||||
// How many seconds to wait for a corresponding inhibit source alert to
|
||||
// appear before sending any notifications for active target alerts.
|
||||
// TODO(julius): Not supported yet. Implement this!
|
||||
// optional int32 before_allowance = 4 [default = 0];
|
||||
// How many seconds to wait after a corresponding inhibit source alert
|
||||
// disappears before sending any notifications for active target alerts.
|
||||
// TODO(julius): Not supported yet. Implement this!
|
||||
// optional int32 after_allowance = 5 [default = 0];
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (r *InhibitRule) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain InhibitRule
|
||||
if err := unmarshal((*plain)(r)); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkOverflow(r.XXX, "inhibit rule")
|
||||
}
|
||||
|
||||
// Grouping and notification setting definitions for alerts.
|
||||
type AggrRule struct {
|
||||
// Filters that define which alerts are matched by this AggregationRule.
|
||||
Filters []*Filter `yaml:"filters,omitempty"`
|
||||
|
||||
// How many seconds to wait before resending a notification for a specific alert.
|
||||
RepeatRate model.Duration `yaml:"repeat_rate"`
|
||||
|
||||
// Notification configurations to use for this AggregationRule, referenced
|
||||
// by their name.
|
||||
NotificationConfigs []string `yaml:"notification_configs"`
|
||||
|
||||
// Notify when resolved.
|
||||
SendResolved bool `yaml:"send_resolved"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (r *AggrRule) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*r = DefaultAggrRule
|
||||
type plain AggrRule
|
||||
if err := unmarshal((*plain)(r)); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(r.NotificationConfigs) == 0 {
|
||||
return fmt.Errorf("aggregation rule needs at least one notification config")
|
||||
}
|
||||
return checkOverflow(r.XXX, "aggregation rule")
|
||||
}
|
||||
|
||||
// A regex-based label filter used in aggregations.
|
||||
type Filter struct {
|
||||
Name string `yaml:"name"`
|
||||
Regex *Regexp `yaml:"regex"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (f *Filter) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain Filter
|
||||
if err := unmarshal((*plain)(f)); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkOverflow(f.XXX, "aggregation rule")
|
||||
}
|
||||
|
||||
// Notification configuration definition.
|
||||
type NotificationConfig struct {
|
||||
// Name of this NotificationConfig. Referenced from AggregationRule.
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Notify when resolved.
|
||||
SendResolved bool `yaml:"send_resolved"`
|
||||
|
||||
// Zero or more PagerDuty notification configurations.
|
||||
PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs"`
|
||||
|
||||
// Zero or more email notification configurations.
|
||||
EmailConfigs []*EmailConfig `yaml:"email_configs"`
|
||||
|
||||
// Zero or more pushover notification configurations.
|
||||
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs"`
|
||||
|
||||
// Zero or more hipchat notification configurations.
|
||||
HipchatConfigs []*HipchatConfig `yaml:"hipchat_configs"`
|
||||
|
||||
// Zero or more slack notification configurations.
|
||||
SlackConfigs []*SlackConfig `yaml:"slack_config"`
|
||||
|
||||
// Zero or more Flowdock notification configurations.
|
||||
FlowdockConfigs []*FlowdockConfig `yaml:"flowdock_config"`
|
||||
|
||||
// Zero or more generic web hook notification configurations.
|
||||
WebhookConfigs []*WebhookConfig `yaml:"webhook_config"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *NotificationConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain NotificationConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("missing name in notification config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "notification config")
|
||||
}
|
||||
|
||||
// Configuration for notification via PagerDuty.
|
||||
type PagerdutyConfig struct {
|
||||
// PagerDuty service key, see:
|
||||
// http://developer.pagerduty.com/documentation/integration/events
|
||||
ServiceKey string `yaml:"service_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 *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain PagerdutyConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.ServiceKey == "" {
|
||||
return fmt.Errorf("missing service key in PagerDuty config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "pagerduty config")
|
||||
}
|
||||
|
||||
// Configuration for notification via mail.
|
||||
type EmailConfig struct {
|
||||
// Email address to notify.
|
||||
Email string `yaml:"email"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain EmailConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Email == "" {
|
||||
return fmt.Errorf("missing email address in email config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "email config")
|
||||
}
|
||||
|
||||
// Configuration for notification via pushover.net.
|
||||
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")
|
||||
}
|
||||
|
||||
type HipchatFormat string
|
||||
|
||||
const (
|
||||
HipchatFormatHTML HipchatFormat = "html"
|
||||
HipchatFormatText HipchatFormat = "text"
|
||||
)
|
||||
|
||||
// Configuration for notification 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).
|
||||
AuthToken string `yaml:"auth_token"`
|
||||
|
||||
// HipChat room id, (https://www.hipchat.com/rooms/ids).
|
||||
RoomID int `yaml:"room_id"`
|
||||
|
||||
// Color of message when triggered.
|
||||
Color string `yaml:"color"`
|
||||
|
||||
// Color of message when resolved.
|
||||
ColorResolved string `yaml:"color_resolved"`
|
||||
|
||||
// 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.AuthToken == "" {
|
||||
return fmt.Errorf("missing auth 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")
|
||||
}
|
||||
|
||||
// Configuration for notification via Slack.
|
||||
type SlackConfig struct {
|
||||
// Slack webhook URL, (https://api.slack.com/incoming-webhooks).
|
||||
WebhookURL string `yaml:"webhook_url"`
|
||||
|
||||
// Slack channel override, (like #other-channel or @username).
|
||||
Channel string `yaml:"channel"`
|
||||
|
||||
// Color of message when triggered.
|
||||
Color string `yaml:"color"`
|
||||
|
||||
// Color of message when resolved.
|
||||
ColorResolved string `yaml:"color_resolved"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultSlackConfig
|
||||
type plain SlackConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.WebhookURL == "" {
|
||||
return fmt.Errorf("missing webhook URL in Slack config")
|
||||
}
|
||||
if c.Channel == "" {
|
||||
return fmt.Errorf("missing channel in Slack config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "slack config")
|
||||
}
|
||||
|
||||
// Configuration for notification via Flowdock.
|
||||
type FlowdockConfig struct {
|
||||
// Flowdock flow API token.
|
||||
APIToken string `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"`
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
// Configuration for notification via generic webhook.
|
||||
type WebhookConfig struct {
|
||||
// URL to send POST request to.
|
||||
URL string `yaml:"url"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain WebhookConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.URL == "" {
|
||||
return fmt.Errorf("missing URL in webhook config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "slack config")
|
||||
}
|
||||
|
||||
// Regexp encapsulates a regexp.Regexp and makes it YAML marshallable.
|
||||
type Regexp struct {
|
||||
regexp.Regexp
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var s string
|
||||
if err := unmarshal(&s); err != nil {
|
||||
return err
|
||||
}
|
||||
regex, err := regexp.Compile(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re.Regexp = *regex
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (re *Regexp) MarshalYAML() (interface{}, error) {
|
||||
if re != nil {
|
||||
return re.String(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
// Copyright 2013 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 io.prometheus.alertmanager;
|
||||
|
||||
// Configuration for notification via PagerDuty.
|
||||
message PagerDutyConfig {
|
||||
// PagerDuty service key, see:
|
||||
// http://developer.pagerduty.com/documentation/integration/events
|
||||
optional string service_key = 1;
|
||||
}
|
||||
|
||||
// Configuration for notification via mail.
|
||||
message EmailConfig {
|
||||
// Email address to notify.
|
||||
optional string email = 1;
|
||||
// Notify when resolved.
|
||||
optional bool send_resolved = 2 [default = false];
|
||||
}
|
||||
|
||||
// Configuration for notification via pushover.net.
|
||||
message PushoverConfig {
|
||||
// Pushover token.
|
||||
optional string token = 1;
|
||||
// Pushover user_key.
|
||||
optional string user_key = 2;
|
||||
// Notify when resolved.
|
||||
optional bool send_resolved = 3 [default = false];
|
||||
}
|
||||
|
||||
// Configuration for notification via HipChat.
|
||||
message HipChatConfig {
|
||||
// https://www.hipchat.com/docs/apiv2/method/send_room_notification
|
||||
|
||||
// HipChat auth token, (https://www.hipchat.com/docs/api/auth).
|
||||
optional string auth_token = 1;
|
||||
// HipChat room id, (https://www.hipchat.com/rooms/ids).
|
||||
optional int32 room_id = 2;
|
||||
// Color of message when triggered.
|
||||
optional string color = 3 [default = "purple"];
|
||||
// Color of message when resolved.
|
||||
optional string color_resolved = 5 [default = "green"];
|
||||
// Should this message notify or not.
|
||||
optional bool notify = 4 [default = false];
|
||||
// Notify when resolved.
|
||||
optional bool send_resolved = 6 [default = false];
|
||||
// Prefix to be put in front of the message (useful for @mentions, etc.).
|
||||
optional string prefix = 7 [default = ""];
|
||||
// Format the message as "html" or "text".
|
||||
enum MessageFormat {
|
||||
HTML = 0;
|
||||
TEXT = 1;
|
||||
}
|
||||
optional MessageFormat message_format = 8 [default = HTML];
|
||||
}
|
||||
|
||||
// Configuration for notification via Slack.
|
||||
message SlackConfig {
|
||||
// Slack webhook URL, (https://api.slack.com/incoming-webhooks).
|
||||
optional string webhook_url = 1;
|
||||
// Slack channel override, (like #other-channel or @username).
|
||||
optional string channel = 2;
|
||||
// Color of message when triggered.
|
||||
optional string color = 3 [default = "warning"];
|
||||
// Color of message when resolved.
|
||||
optional string color_resolved = 4 [default = "good"];
|
||||
// Notify when resolved.
|
||||
optional bool send_resolved = 5 [default = false];
|
||||
}
|
||||
|
||||
// Configuration for notification via Flowdock.
|
||||
message FlowdockConfig {
|
||||
// Flowdock flow API token.
|
||||
optional string api_token = 1;
|
||||
// Flowdock from_address.
|
||||
optional string from_address = 2;
|
||||
// Flowdock flow tags.
|
||||
repeated string tag = 3;
|
||||
// Notify when resolved.
|
||||
optional bool send_resolved = 4 [default = false];
|
||||
}
|
||||
|
||||
// Configuration for notification via generic webhook.
|
||||
message WebhookConfig {
|
||||
// URL to send POST request to.
|
||||
optional string url = 1;
|
||||
// Notify when resolved.
|
||||
optional bool send_resolved = 2 [default = false];
|
||||
}
|
||||
|
||||
// Notification configuration definition.
|
||||
message NotificationConfig {
|
||||
// Name of this NotificationConfig. Referenced from AggregationRule.
|
||||
optional string name = 1;
|
||||
// Zero or more PagerDuty notification configurations.
|
||||
repeated PagerDutyConfig pagerduty_config = 2;
|
||||
// Zero or more email notification configurations.
|
||||
repeated EmailConfig email_config = 3;
|
||||
// Zero or more pushover notification configurations.
|
||||
repeated PushoverConfig pushover_config = 4;
|
||||
// Zero or more hipchat notification configurations.
|
||||
repeated HipChatConfig hipchat_config = 5;
|
||||
// Zero or more slack notification configurations.
|
||||
repeated SlackConfig slack_config = 6;
|
||||
// Zero or more Flowdock notification configurations.
|
||||
repeated FlowdockConfig flowdock_config = 7;
|
||||
// Zero or more generic web hook notification configurations.
|
||||
repeated WebhookConfig webhook_config = 8;
|
||||
}
|
||||
|
||||
// A regex-based label filter used in aggregations.
|
||||
message Filter {
|
||||
// The regex matching the label name.
|
||||
optional string name_re = 1;
|
||||
// The regex matching the label value.
|
||||
optional string value_re = 2;
|
||||
}
|
||||
|
||||
// Grouping and notification setting definitions for alerts.
|
||||
message AggregationRule {
|
||||
// Filters that define which alerts are matched by this AggregationRule.
|
||||
repeated Filter filter = 1;
|
||||
// How many seconds to wait before resending a notification for a specific alert.
|
||||
optional int32 repeat_rate_seconds = 2 [default = 7200];
|
||||
// Notification configuration to use for this AggregationRule, referenced by
|
||||
// their name.
|
||||
optional string notification_config_name = 3;
|
||||
}
|
||||
|
||||
// An InhibitRule specifies that a class of (source) alerts should inhibit
|
||||
// notifications for another class of (target) alerts if all specified matching
|
||||
// labels are equal between the two alerts. This may be used to inhibit alerts
|
||||
// from sending notifications if their meaning is logically a subset of a
|
||||
// higher-level alert.
|
||||
//
|
||||
// For example, if an entire job is down, there is little sense in sending a
|
||||
// notification for every single instance of said job being down. This could be
|
||||
// expressed as the following inhibit rule:
|
||||
//
|
||||
// inhibit_rule {
|
||||
// # Select all source alerts that are candidates for being inhibitors. All
|
||||
// # supplied source filters have to match in order to select a source alert.
|
||||
// source_filter: {
|
||||
// name_re: "alertname"
|
||||
// value_re: "JobDown"
|
||||
// }
|
||||
// source_filter: {
|
||||
// name_re: "service"
|
||||
// value_re: "api"
|
||||
// }
|
||||
//
|
||||
// # Select all target alerts that are candidates for being inhibited. All
|
||||
// # supplied target filters have to match in order to select a target alert.
|
||||
// target_filter: {
|
||||
// name_re: "alertname"
|
||||
// value_re: "InstanceDown"
|
||||
// }
|
||||
// target_filter: {
|
||||
// name_re: "service"
|
||||
// value_re: "api"
|
||||
// }
|
||||
//
|
||||
// # A target alert only actually inhibits a source alert if they match on
|
||||
// # these labels. I.e. the alerts needs to fire for the same job in the same
|
||||
// # zone for the inhibit to take effect between them.
|
||||
// match_on: "job"
|
||||
// match_on: "zone"
|
||||
// }
|
||||
//
|
||||
// In this example, when JobDown is firing for
|
||||
//
|
||||
// JobDown{zone="aa",job="test",service="api"}
|
||||
//
|
||||
// ...it would inhibit an InstanceDown alert for
|
||||
//
|
||||
// InstanceDown{zone="aa",job="test",instance="1",service="api"}
|
||||
//
|
||||
// However, an InstanceDown alert for another zone:
|
||||
//
|
||||
// {zone="ab",job="test",instance="1",service="api"}
|
||||
//
|
||||
// ...would still fire.
|
||||
message InhibitRule {
|
||||
// The set of Filters which define the group of source alerts (which inhibit
|
||||
// the target alerts).
|
||||
repeated Filter source_filter = 1;
|
||||
// The set of Filters which define the group of target alerts (which are
|
||||
// inhibited by the source alerts).
|
||||
repeated Filter target_filter = 2;
|
||||
// A set of label names whose label values need to be identical in source and
|
||||
// target alerts in order for the inhibition to take effect.
|
||||
repeated string match_on = 3;
|
||||
// How many seconds to wait for a corresponding inhibit source alert to
|
||||
// appear before sending any notifications for active target alerts.
|
||||
// TODO(julius): Not supported yet. Implement this!
|
||||
// optional int32 before_allowance = 4 [default = 0];
|
||||
// How many seconds to wait after a corresponding inhibit source alert
|
||||
// disappears before sending any notifications for active target alerts.
|
||||
// TODO(julius): Not supported yet. Implement this!
|
||||
// optional int32 after_allowance = 5 [default = 0];
|
||||
}
|
||||
|
||||
// Global alert manager configuration.
|
||||
message AlertManagerConfig {
|
||||
// Aggregation rule definitions.
|
||||
repeated AggregationRule aggregation_rule = 1;
|
||||
// Notification configuration definitions.
|
||||
repeated NotificationConfig notification_config = 2;
|
||||
// List of alert inhibition rules.
|
||||
repeated InhibitRule inhibit_rule = 3;
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright 2013 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 config
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var fixturesPath = "fixtures"
|
||||
|
||||
type configTest struct {
|
||||
inputFile string
|
||||
shouldFail bool
|
||||
errContains string
|
||||
}
|
||||
|
||||
func (ct *configTest) test(i int, t *testing.T) {
|
||||
_, err := LoadFromFile(path.Join(fixturesPath, ct.inputFile))
|
||||
|
||||
if err != nil {
|
||||
if !ct.shouldFail {
|
||||
t.Fatalf("%d. Error parsing config %v: %v", i, ct.inputFile, err)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), ct.errContains) {
|
||||
t.Fatalf("%d. Expected error containing '%v', got: %v", i, ct.errContains, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigs(t *testing.T) {
|
||||
var configTests = []configTest{
|
||||
{
|
||||
inputFile: "empty.conf.input",
|
||||
}, {
|
||||
inputFile: "sample.conf.input",
|
||||
}, {
|
||||
inputFile: "missing_filter_name_re.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "Missing name pattern",
|
||||
}, {
|
||||
inputFile: "invalid_proto_format.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "unknown field name",
|
||||
}, {
|
||||
inputFile: "duplicate_nc_name.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "not unique",
|
||||
}, {
|
||||
inputFile: "nonexistent_nc_name.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "No such notification config",
|
||||
}, {
|
||||
inputFile: "missing_nc_name.conf.input",
|
||||
shouldFail: true,
|
||||
errContains: "Missing name",
|
||||
},
|
||||
}
|
||||
|
||||
for i, ct := range configTests {
|
||||
ct.test(i, t)
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
notification_config {
|
||||
name: "alertmanager_test"
|
||||
pagerduty_config {
|
||||
service_key: "supersecretapikey"
|
||||
}
|
||||
email_config {
|
||||
email: "test@testservice.org"
|
||||
}
|
||||
}
|
||||
|
||||
notification_config {
|
||||
name: "alertmanager_test"
|
||||
pagerduty_config {
|
||||
service_key: "supersecretapikey"
|
||||
}
|
||||
email_config {
|
||||
email: "test@testservice.org"
|
||||
}
|
||||
}
|
||||
|
||||
aggregation_rule {
|
||||
filter {
|
||||
name_re: "service"
|
||||
value_re: "test"
|
||||
}
|
||||
repeat_rate_seconds: 3600
|
||||
notification_config_name: "alertmanager_test"
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
aggregation_rule {
|
||||
filter {
|
||||
label_re: "service" // Unknown protobuf field name.
|
||||
value_re: "discovery"
|
||||
}
|
||||
filter {
|
||||
name_re: "zone"
|
||||
value_re: "aa"
|
||||
}
|
||||
repeat_rate: 3600
|
||||
}
|
||||
|
||||
aggregation_rule {
|
||||
filter {
|
||||
name_re: "service"
|
||||
value_re: "test"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
aggregation_rule {
|
||||
filter {
|
||||
value_re: "test"
|
||||
}
|
||||
repeat_rate_seconds: 3600
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
notification_config {
|
||||
pagerduty_config {
|
||||
service_key: "supersecretapikey"
|
||||
}
|
||||
email_config {
|
||||
email: "test@testservice.org"
|
||||
}
|
||||
}
|
||||
|
||||
notification_config {
|
||||
name: "alertmanager_test"
|
||||
pagerduty_config {
|
||||
service_key: "supersecretapikey"
|
||||
}
|
||||
email_config {
|
||||
email: "test@testservice.org"
|
||||
}
|
||||
}
|
||||
|
||||
aggregation_rule {
|
||||
filter {
|
||||
name_re: "service"
|
||||
value_re: "test"
|
||||
}
|
||||
repeat_rate_seconds: 3600
|
||||
notification_config_name: "alertmanager_test"
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
notification_config {
|
||||
name: "alertmanager_test"
|
||||
pagerduty_config {
|
||||
service_key: "supersecretapikey"
|
||||
}
|
||||
email_config {
|
||||
email: "test@testservice.org"
|
||||
}
|
||||
}
|
||||
|
||||
aggregation_rule {
|
||||
filter {
|
||||
name_re: "service"
|
||||
value_re: "test"
|
||||
}
|
||||
repeat_rate_seconds: 3600
|
||||
notification_config_name: "alertmanager_test2"
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
notification_config {
|
||||
name: "alertmanager_test"
|
||||
pagerduty_config {
|
||||
service_key: "supersecretapikey"
|
||||
}
|
||||
email_config {
|
||||
email: "test@testservice.org"
|
||||
}
|
||||
pushover_config {
|
||||
token: "mypushovertoken"
|
||||
user_key: "mypushoverkey"
|
||||
}
|
||||
hipchat_config {
|
||||
auth_token: "hipchatauthtoken"
|
||||
room_id: 123456
|
||||
send_resolved: true
|
||||
}
|
||||
slack_config {
|
||||
webhook_url: "webhookurl"
|
||||
send_resolved: true
|
||||
}
|
||||
flowdock_config {
|
||||
api_token: "4c7234902348234902384234234cdb59"
|
||||
from_address: "aliaswithgravatar@somehwere.com"
|
||||
tag: "monitoring"
|
||||
}
|
||||
}
|
||||
|
||||
aggregation_rule {
|
||||
filter {
|
||||
name_re: "service"
|
||||
value_re: "test"
|
||||
}
|
||||
repeat_rate_seconds: 3600
|
||||
notification_config_name: "alertmanager_test"
|
||||
}
|
|
@ -1,650 +0,0 @@
|
|||
// Code generated by protoc-gen-go.
|
||||
// source: config.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package io_prometheus_alertmanager is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
config.proto
|
||||
|
||||
It has these top-level messages:
|
||||
PagerDutyConfig
|
||||
EmailConfig
|
||||
PushoverConfig
|
||||
HipChatConfig
|
||||
SlackConfig
|
||||
FlowdockConfig
|
||||
WebhookConfig
|
||||
NotificationConfig
|
||||
Filter
|
||||
AggregationRule
|
||||
InhibitRule
|
||||
AlertManagerConfig
|
||||
*/
|
||||
package io_prometheus_alertmanager
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = math.Inf
|
||||
|
||||
// Format the message as "html" or "text".
|
||||
type HipChatConfig_MessageFormat int32
|
||||
|
||||
const (
|
||||
HipChatConfig_HTML HipChatConfig_MessageFormat = 0
|
||||
HipChatConfig_TEXT HipChatConfig_MessageFormat = 1
|
||||
)
|
||||
|
||||
var HipChatConfig_MessageFormat_name = map[int32]string{
|
||||
0: "HTML",
|
||||
1: "TEXT",
|
||||
}
|
||||
var HipChatConfig_MessageFormat_value = map[string]int32{
|
||||
"HTML": 0,
|
||||
"TEXT": 1,
|
||||
}
|
||||
|
||||
func (x HipChatConfig_MessageFormat) Enum() *HipChatConfig_MessageFormat {
|
||||
p := new(HipChatConfig_MessageFormat)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
func (x HipChatConfig_MessageFormat) String() string {
|
||||
return proto.EnumName(HipChatConfig_MessageFormat_name, int32(x))
|
||||
}
|
||||
func (x *HipChatConfig_MessageFormat) UnmarshalJSON(data []byte) error {
|
||||
value, err := proto.UnmarshalJSONEnum(HipChatConfig_MessageFormat_value, data, "HipChatConfig_MessageFormat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*x = HipChatConfig_MessageFormat(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configuration for notification via PagerDuty.
|
||||
type PagerDutyConfig struct {
|
||||
// PagerDuty service key, see:
|
||||
// http://developer.pagerduty.com/documentation/integration/events
|
||||
ServiceKey *string `protobuf:"bytes,1,opt,name=service_key" json:"service_key,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PagerDutyConfig) Reset() { *m = PagerDutyConfig{} }
|
||||
func (m *PagerDutyConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*PagerDutyConfig) ProtoMessage() {}
|
||||
|
||||
func (m *PagerDutyConfig) GetServiceKey() string {
|
||||
if m != nil && m.ServiceKey != nil {
|
||||
return *m.ServiceKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Configuration for notification via mail.
|
||||
type EmailConfig struct {
|
||||
// Email address to notify.
|
||||
Email *string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"`
|
||||
// Notify when resolved.
|
||||
SendResolved *bool `protobuf:"varint,2,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *EmailConfig) Reset() { *m = EmailConfig{} }
|
||||
func (m *EmailConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*EmailConfig) ProtoMessage() {}
|
||||
|
||||
const Default_EmailConfig_SendResolved bool = false
|
||||
|
||||
func (m *EmailConfig) GetEmail() string {
|
||||
if m != nil && m.Email != nil {
|
||||
return *m.Email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *EmailConfig) GetSendResolved() bool {
|
||||
if m != nil && m.SendResolved != nil {
|
||||
return *m.SendResolved
|
||||
}
|
||||
return Default_EmailConfig_SendResolved
|
||||
}
|
||||
|
||||
// Configuration for notification via pushover.net.
|
||||
type PushoverConfig struct {
|
||||
// Pushover token.
|
||||
Token *string `protobuf:"bytes,1,opt,name=token" json:"token,omitempty"`
|
||||
// Pushover user_key.
|
||||
UserKey *string `protobuf:"bytes,2,opt,name=user_key" json:"user_key,omitempty"`
|
||||
// Notify when resolved.
|
||||
SendResolved *bool `protobuf:"varint,3,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushoverConfig) Reset() { *m = PushoverConfig{} }
|
||||
func (m *PushoverConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushoverConfig) ProtoMessage() {}
|
||||
|
||||
const Default_PushoverConfig_SendResolved bool = false
|
||||
|
||||
func (m *PushoverConfig) GetToken() string {
|
||||
if m != nil && m.Token != nil {
|
||||
return *m.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushoverConfig) GetUserKey() string {
|
||||
if m != nil && m.UserKey != nil {
|
||||
return *m.UserKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushoverConfig) GetSendResolved() bool {
|
||||
if m != nil && m.SendResolved != nil {
|
||||
return *m.SendResolved
|
||||
}
|
||||
return Default_PushoverConfig_SendResolved
|
||||
}
|
||||
|
||||
// Configuration for notification via HipChat.
|
||||
type HipChatConfig struct {
|
||||
// HipChat auth token, (https://www.hipchat.com/docs/api/auth).
|
||||
AuthToken *string `protobuf:"bytes,1,opt,name=auth_token" json:"auth_token,omitempty"`
|
||||
// HipChat room id, (https://www.hipchat.com/rooms/ids).
|
||||
RoomId *int32 `protobuf:"varint,2,opt,name=room_id" json:"room_id,omitempty"`
|
||||
// Color of message when triggered.
|
||||
Color *string `protobuf:"bytes,3,opt,name=color,def=purple" json:"color,omitempty"`
|
||||
// Color of message when resolved.
|
||||
ColorResolved *string `protobuf:"bytes,5,opt,name=color_resolved,def=green" json:"color_resolved,omitempty"`
|
||||
// Should this message notify or not.
|
||||
Notify *bool `protobuf:"varint,4,opt,name=notify,def=0" json:"notify,omitempty"`
|
||||
// Notify when resolved.
|
||||
SendResolved *bool `protobuf:"varint,6,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
|
||||
// Prefix to be put in front of the message (useful for @mentions, etc.).
|
||||
Prefix *string `protobuf:"bytes,7,opt,name=prefix,def=" json:"prefix,omitempty"`
|
||||
MessageFormat *HipChatConfig_MessageFormat `protobuf:"varint,8,opt,name=message_format,enum=io.prometheus.alertmanager.HipChatConfig_MessageFormat,def=0" json:"message_format,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) Reset() { *m = HipChatConfig{} }
|
||||
func (m *HipChatConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*HipChatConfig) ProtoMessage() {}
|
||||
|
||||
const Default_HipChatConfig_Color string = "purple"
|
||||
const Default_HipChatConfig_ColorResolved string = "green"
|
||||
const Default_HipChatConfig_Notify bool = false
|
||||
const Default_HipChatConfig_SendResolved bool = false
|
||||
const Default_HipChatConfig_MessageFormat HipChatConfig_MessageFormat = HipChatConfig_HTML
|
||||
|
||||
func (m *HipChatConfig) GetAuthToken() string {
|
||||
if m != nil && m.AuthToken != nil {
|
||||
return *m.AuthToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) GetRoomId() int32 {
|
||||
if m != nil && m.RoomId != nil {
|
||||
return *m.RoomId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) GetColor() string {
|
||||
if m != nil && m.Color != nil {
|
||||
return *m.Color
|
||||
}
|
||||
return Default_HipChatConfig_Color
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) GetColorResolved() string {
|
||||
if m != nil && m.ColorResolved != nil {
|
||||
return *m.ColorResolved
|
||||
}
|
||||
return Default_HipChatConfig_ColorResolved
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) GetNotify() bool {
|
||||
if m != nil && m.Notify != nil {
|
||||
return *m.Notify
|
||||
}
|
||||
return Default_HipChatConfig_Notify
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) GetSendResolved() bool {
|
||||
if m != nil && m.SendResolved != nil {
|
||||
return *m.SendResolved
|
||||
}
|
||||
return Default_HipChatConfig_SendResolved
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) GetPrefix() string {
|
||||
if m != nil && m.Prefix != nil {
|
||||
return *m.Prefix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *HipChatConfig) GetMessageFormat() HipChatConfig_MessageFormat {
|
||||
if m != nil && m.MessageFormat != nil {
|
||||
return *m.MessageFormat
|
||||
}
|
||||
return Default_HipChatConfig_MessageFormat
|
||||
}
|
||||
|
||||
// Configuration for notification via Slack.
|
||||
type SlackConfig struct {
|
||||
// Slack webhook URL, (https://api.slack.com/incoming-webhooks).
|
||||
WebhookUrl *string `protobuf:"bytes,1,opt,name=webhook_url" json:"webhook_url,omitempty"`
|
||||
// Slack channel override, (like #other-channel or @username).
|
||||
Channel *string `protobuf:"bytes,2,opt,name=channel" json:"channel,omitempty"`
|
||||
// Color of message when triggered.
|
||||
Color *string `protobuf:"bytes,3,opt,name=color,def=warning" json:"color,omitempty"`
|
||||
// Color of message when resolved.
|
||||
ColorResolved *string `protobuf:"bytes,4,opt,name=color_resolved,def=good" json:"color_resolved,omitempty"`
|
||||
// Notify when resolved.
|
||||
SendResolved *bool `protobuf:"varint,5,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SlackConfig) Reset() { *m = SlackConfig{} }
|
||||
func (m *SlackConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*SlackConfig) ProtoMessage() {}
|
||||
|
||||
const Default_SlackConfig_Color string = "warning"
|
||||
const Default_SlackConfig_ColorResolved string = "good"
|
||||
const Default_SlackConfig_SendResolved bool = false
|
||||
|
||||
func (m *SlackConfig) GetWebhookUrl() string {
|
||||
if m != nil && m.WebhookUrl != nil {
|
||||
return *m.WebhookUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SlackConfig) GetChannel() string {
|
||||
if m != nil && m.Channel != nil {
|
||||
return *m.Channel
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SlackConfig) GetColor() string {
|
||||
if m != nil && m.Color != nil {
|
||||
return *m.Color
|
||||
}
|
||||
return Default_SlackConfig_Color
|
||||
}
|
||||
|
||||
func (m *SlackConfig) GetColorResolved() string {
|
||||
if m != nil && m.ColorResolved != nil {
|
||||
return *m.ColorResolved
|
||||
}
|
||||
return Default_SlackConfig_ColorResolved
|
||||
}
|
||||
|
||||
func (m *SlackConfig) GetSendResolved() bool {
|
||||
if m != nil && m.SendResolved != nil {
|
||||
return *m.SendResolved
|
||||
}
|
||||
return Default_SlackConfig_SendResolved
|
||||
}
|
||||
|
||||
// Configuration for notification via Flowdock.
|
||||
type FlowdockConfig struct {
|
||||
// Flowdock flow API token.
|
||||
ApiToken *string `protobuf:"bytes,1,opt,name=api_token" json:"api_token,omitempty"`
|
||||
// Flowdock from_address.
|
||||
FromAddress *string `protobuf:"bytes,2,opt,name=from_address" json:"from_address,omitempty"`
|
||||
// Flowdock flow tags.
|
||||
Tag []string `protobuf:"bytes,3,rep,name=tag" json:"tag,omitempty"`
|
||||
// Notify when resolved.
|
||||
SendResolved *bool `protobuf:"varint,4,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) Reset() { *m = FlowdockConfig{} }
|
||||
func (m *FlowdockConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*FlowdockConfig) ProtoMessage() {}
|
||||
|
||||
const Default_FlowdockConfig_SendResolved bool = false
|
||||
|
||||
func (m *FlowdockConfig) GetApiToken() string {
|
||||
if m != nil && m.ApiToken != nil {
|
||||
return *m.ApiToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) GetFromAddress() string {
|
||||
if m != nil && m.FromAddress != nil {
|
||||
return *m.FromAddress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) GetTag() []string {
|
||||
if m != nil {
|
||||
return m.Tag
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) GetSendResolved() bool {
|
||||
if m != nil && m.SendResolved != nil {
|
||||
return *m.SendResolved
|
||||
}
|
||||
return Default_FlowdockConfig_SendResolved
|
||||
}
|
||||
|
||||
// Configuration for notification via generic webhook.
|
||||
type WebhookConfig struct {
|
||||
// URL to send POST request to.
|
||||
Url *string `protobuf:"bytes,1,opt,name=url" json:"url,omitempty"`
|
||||
// Notify when resolved.
|
||||
SendResolved *bool `protobuf:"varint,2,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *WebhookConfig) Reset() { *m = WebhookConfig{} }
|
||||
func (m *WebhookConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*WebhookConfig) ProtoMessage() {}
|
||||
|
||||
const Default_WebhookConfig_SendResolved bool = false
|
||||
|
||||
func (m *WebhookConfig) GetUrl() string {
|
||||
if m != nil && m.Url != nil {
|
||||
return *m.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *WebhookConfig) GetSendResolved() bool {
|
||||
if m != nil && m.SendResolved != nil {
|
||||
return *m.SendResolved
|
||||
}
|
||||
return Default_WebhookConfig_SendResolved
|
||||
}
|
||||
|
||||
// Notification configuration definition.
|
||||
type NotificationConfig struct {
|
||||
// Name of this NotificationConfig. Referenced from AggregationRule.
|
||||
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
// Zero or more PagerDuty notification configurations.
|
||||
PagerdutyConfig []*PagerDutyConfig `protobuf:"bytes,2,rep,name=pagerduty_config" json:"pagerduty_config,omitempty"`
|
||||
// Zero or more email notification configurations.
|
||||
EmailConfig []*EmailConfig `protobuf:"bytes,3,rep,name=email_config" json:"email_config,omitempty"`
|
||||
// Zero or more pushover notification configurations.
|
||||
PushoverConfig []*PushoverConfig `protobuf:"bytes,4,rep,name=pushover_config" json:"pushover_config,omitempty"`
|
||||
// Zero or more hipchat notification configurations.
|
||||
HipchatConfig []*HipChatConfig `protobuf:"bytes,5,rep,name=hipchat_config" json:"hipchat_config,omitempty"`
|
||||
// Zero or more slack notification configurations.
|
||||
SlackConfig []*SlackConfig `protobuf:"bytes,6,rep,name=slack_config" json:"slack_config,omitempty"`
|
||||
// Zero or more Flowdock notification configurations.
|
||||
FlowdockConfig []*FlowdockConfig `protobuf:"bytes,7,rep,name=flowdock_config" json:"flowdock_config,omitempty"`
|
||||
// Zero or more generic web hook notification configurations.
|
||||
WebhookConfig []*WebhookConfig `protobuf:"bytes,8,rep,name=webhook_config" json:"webhook_config,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) Reset() { *m = NotificationConfig{} }
|
||||
func (m *NotificationConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*NotificationConfig) ProtoMessage() {}
|
||||
|
||||
func (m *NotificationConfig) GetName() string {
|
||||
if m != nil && m.Name != nil {
|
||||
return *m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetPagerdutyConfig() []*PagerDutyConfig {
|
||||
if m != nil {
|
||||
return m.PagerdutyConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetEmailConfig() []*EmailConfig {
|
||||
if m != nil {
|
||||
return m.EmailConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetPushoverConfig() []*PushoverConfig {
|
||||
if m != nil {
|
||||
return m.PushoverConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetHipchatConfig() []*HipChatConfig {
|
||||
if m != nil {
|
||||
return m.HipchatConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetSlackConfig() []*SlackConfig {
|
||||
if m != nil {
|
||||
return m.SlackConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetFlowdockConfig() []*FlowdockConfig {
|
||||
if m != nil {
|
||||
return m.FlowdockConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetWebhookConfig() []*WebhookConfig {
|
||||
if m != nil {
|
||||
return m.WebhookConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A regex-based label filter used in aggregations.
|
||||
type Filter struct {
|
||||
// The regex matching the label name.
|
||||
NameRe *string `protobuf:"bytes,1,opt,name=name_re" json:"name_re,omitempty"`
|
||||
// The regex matching the label value.
|
||||
ValueRe *string `protobuf:"bytes,2,opt,name=value_re" json:"value_re,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Filter) Reset() { *m = Filter{} }
|
||||
func (m *Filter) String() string { return proto.CompactTextString(m) }
|
||||
func (*Filter) ProtoMessage() {}
|
||||
|
||||
func (m *Filter) GetNameRe() string {
|
||||
if m != nil && m.NameRe != nil {
|
||||
return *m.NameRe
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Filter) GetValueRe() string {
|
||||
if m != nil && m.ValueRe != nil {
|
||||
return *m.ValueRe
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Grouping and notification setting definitions for alerts.
|
||||
type AggregationRule struct {
|
||||
// Filters that define which alerts are matched by this AggregationRule.
|
||||
Filter []*Filter `protobuf:"bytes,1,rep,name=filter" json:"filter,omitempty"`
|
||||
// How many seconds to wait before resending a notification for a specific alert.
|
||||
RepeatRateSeconds *int32 `protobuf:"varint,2,opt,name=repeat_rate_seconds,def=7200" json:"repeat_rate_seconds,omitempty"`
|
||||
// Notification configuration to use for this AggregationRule, referenced by
|
||||
// their name.
|
||||
NotificationConfigName *string `protobuf:"bytes,3,opt,name=notification_config_name" json:"notification_config_name,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *AggregationRule) Reset() { *m = AggregationRule{} }
|
||||
func (m *AggregationRule) String() string { return proto.CompactTextString(m) }
|
||||
func (*AggregationRule) ProtoMessage() {}
|
||||
|
||||
const Default_AggregationRule_RepeatRateSeconds int32 = 7200
|
||||
|
||||
func (m *AggregationRule) GetFilter() []*Filter {
|
||||
if m != nil {
|
||||
return m.Filter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AggregationRule) GetRepeatRateSeconds() int32 {
|
||||
if m != nil && m.RepeatRateSeconds != nil {
|
||||
return *m.RepeatRateSeconds
|
||||
}
|
||||
return Default_AggregationRule_RepeatRateSeconds
|
||||
}
|
||||
|
||||
func (m *AggregationRule) GetNotificationConfigName() string {
|
||||
if m != nil && m.NotificationConfigName != nil {
|
||||
return *m.NotificationConfigName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// An InhibitRule specifies that a class of (source) alerts should inhibit
|
||||
// notifications for another class of (target) alerts if all specified matching
|
||||
// labels are equal between the two alerts. This may be used to inhibit alerts
|
||||
// from sending notifications if their meaning is logically a subset of a
|
||||
// higher-level alert.
|
||||
//
|
||||
// For example, if an entire job is down, there is little sense in sending a
|
||||
// notification for every single instance of said job being down. This could be
|
||||
// expressed as the following inhibit rule:
|
||||
//
|
||||
// inhibit_rule {
|
||||
// # Select all source alerts that are candidates for being inhibitors. All
|
||||
// # supplied source filters have to match in order to select a source alert.
|
||||
// source_filter: {
|
||||
// name_re: "alertname"
|
||||
// value_re: "JobDown"
|
||||
// }
|
||||
// source_filter: {
|
||||
// name_re: "service"
|
||||
// value_re: "api"
|
||||
// }
|
||||
//
|
||||
// # Select all target alerts that are candidates for being inhibited. All
|
||||
// # supplied target filters have to match in order to select a target alert.
|
||||
// target_filter: {
|
||||
// name_re: "alertname"
|
||||
// value_re: "InstanceDown"
|
||||
// }
|
||||
// target_filter: {
|
||||
// name_re: "service"
|
||||
// value_re: "api"
|
||||
// }
|
||||
//
|
||||
// # A target alert only actually inhibits a source alert if they match on
|
||||
// # these labels. I.e. the alerts needs to fire for the same job in the same
|
||||
// # zone for the inhibit to take effect between them.
|
||||
// match_on: "job"
|
||||
// match_on: "zone"
|
||||
// }
|
||||
//
|
||||
// In this example, when JobDown is firing for
|
||||
//
|
||||
// JobDown{zone="aa",job="test",service="api"}
|
||||
//
|
||||
// ...it would inhibit an InstanceDown alert for
|
||||
//
|
||||
// InstanceDown{zone="aa",job="test",instance="1",service="api"}
|
||||
//
|
||||
// However, an InstanceDown alert for another zone:
|
||||
//
|
||||
// {zone="ab",job="test",instance="1",service="api"}
|
||||
//
|
||||
// ...would still fire.
|
||||
type InhibitRule struct {
|
||||
// The set of Filters which define the group of source alerts (which inhibit
|
||||
// the target alerts).
|
||||
SourceFilter []*Filter `protobuf:"bytes,1,rep,name=source_filter" json:"source_filter,omitempty"`
|
||||
// The set of Filters which define the group of target alerts (which are
|
||||
// inhibited by the source alerts).
|
||||
TargetFilter []*Filter `protobuf:"bytes,2,rep,name=target_filter" json:"target_filter,omitempty"`
|
||||
// A set of label names whose label values need to be identical in source and
|
||||
// target alerts in order for the inhibition to take effect.
|
||||
MatchOn []string `protobuf:"bytes,3,rep,name=match_on" json:"match_on,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *InhibitRule) Reset() { *m = InhibitRule{} }
|
||||
func (m *InhibitRule) String() string { return proto.CompactTextString(m) }
|
||||
func (*InhibitRule) ProtoMessage() {}
|
||||
|
||||
func (m *InhibitRule) GetSourceFilter() []*Filter {
|
||||
if m != nil {
|
||||
return m.SourceFilter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *InhibitRule) GetTargetFilter() []*Filter {
|
||||
if m != nil {
|
||||
return m.TargetFilter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *InhibitRule) GetMatchOn() []string {
|
||||
if m != nil {
|
||||
return m.MatchOn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Global alert manager configuration.
|
||||
type AlertManagerConfig struct {
|
||||
// Aggregation rule definitions.
|
||||
AggregationRule []*AggregationRule `protobuf:"bytes,1,rep,name=aggregation_rule" json:"aggregation_rule,omitempty"`
|
||||
// Notification configuration definitions.
|
||||
NotificationConfig []*NotificationConfig `protobuf:"bytes,2,rep,name=notification_config" json:"notification_config,omitempty"`
|
||||
// List of alert inhibition rules.
|
||||
InhibitRule []*InhibitRule `protobuf:"bytes,3,rep,name=inhibit_rule" json:"inhibit_rule,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *AlertManagerConfig) Reset() { *m = AlertManagerConfig{} }
|
||||
func (m *AlertManagerConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*AlertManagerConfig) ProtoMessage() {}
|
||||
|
||||
func (m *AlertManagerConfig) GetAggregationRule() []*AggregationRule {
|
||||
if m != nil {
|
||||
return m.AggregationRule
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AlertManagerConfig) GetNotificationConfig() []*NotificationConfig {
|
||||
if m != nil {
|
||||
return m.NotificationConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AlertManagerConfig) GetInhibitRule() []*InhibitRule {
|
||||
if m != nil {
|
||||
return m.InhibitRule
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("io.prometheus.alertmanager.HipChatConfig_MessageFormat", HipChatConfig_MessageFormat_name, HipChatConfig_MessageFormat_value)
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright 2013 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 config
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const namespace = "alertmanager"
|
||||
|
||||
var configReloads = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "config",
|
||||
Name: "reloads_total",
|
||||
Help: "The total number of configuration reloads.",
|
||||
},
|
||||
)
|
||||
|
||||
var failedConfigReloads = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "config",
|
||||
Name: "failed_reloads_total",
|
||||
Help: "The number of failed configuration reloads.",
|
||||
},
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(configReloads)
|
||||
prometheus.MustRegister(failedConfigReloads)
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2013 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 config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/log"
|
||||
|
||||
pb "github.com/prometheus/alertmanager/config/generated"
|
||||
)
|
||||
|
||||
func LoadFromString(configStr string) (Config, error) {
|
||||
configProto := pb.AlertManagerConfig{}
|
||||
if err := proto.UnmarshalText(configStr, &configProto); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
config := Config{AlertManagerConfig: configProto}
|
||||
err := config.Validate()
|
||||
|
||||
return config, err
|
||||
}
|
||||
|
||||
func LoadFromFile(fileName string) (Config, error) {
|
||||
configStr, err := ioutil.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
return LoadFromString(string(configStr))
|
||||
}
|
||||
|
||||
func MustLoadFromFile(fileName string) Config {
|
||||
conf, err := LoadFromFile(fileName)
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading configuration from %s: %s", fileName, err)
|
||||
}
|
||||
return conf
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2013 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 config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadFromFile(t *testing.T) {
|
||||
_, err := LoadFromFile("file-does-not-exist.conf")
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright 2013 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 config
|
||||
|
||||
import (
|
||||
"github.com/prometheus/log"
|
||||
"gopkg.in/fsnotify.v0"
|
||||
)
|
||||
|
||||
type ReloadCallback func(*Config)
|
||||
|
||||
type Watcher interface {
|
||||
Watch(c ReloadCallback)
|
||||
}
|
||||
|
||||
type fileWatcher struct {
|
||||
fileName string
|
||||
}
|
||||
|
||||
func NewFileWatcher(fileName string) *fileWatcher {
|
||||
return &fileWatcher{
|
||||
fileName: fileName,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *fileWatcher) Watch(cb ReloadCallback) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = watcher.WatchFlags(w.fileName, fsnotify.FSN_MODIFY)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev := <-watcher.Event:
|
||||
log.Infof("Config file changed (%s), attempting reload", ev)
|
||||
conf, err := LoadFromFile(w.fileName)
|
||||
if err != nil {
|
||||
log.Error("Error loading new config: ", err)
|
||||
failedConfigReloads.Inc()
|
||||
} else {
|
||||
cb(&conf)
|
||||
log.Info("Config reloaded successfully")
|
||||
configReloads.Inc()
|
||||
}
|
||||
// Re-add the file watcher since it can get lost on some changes. E.g.
|
||||
// saving a file with vim results in a RENAME-MODIFY-DELETE event
|
||||
// sequence, after which the newly written file is no longer watched.
|
||||
err = watcher.WatchFlags(w.fileName, fsnotify.FSN_MODIFY)
|
||||
case err := <-watcher.Error:
|
||||
log.Error("Error watching config: ", err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,471 +0,0 @@
|
|||
// 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 config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model"
|
||||
|
||||
"github.com/prometheus/prometheus/util/strutil"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultAggrRule = AggrRule{
|
||||
RepeatRate: Duration(2 * time.Hour),
|
||||
SendResolved: false,
|
||||
}
|
||||
|
||||
DefaultHipchatConfig = HipchatConfig{
|
||||
Color: "purple",
|
||||
ColorResolved: "green",
|
||||
MessageFormat: HipchatFormatHTML,
|
||||
}
|
||||
|
||||
DefaultSlackConfig = SlackConfig{
|
||||
Color: "warning",
|
||||
ColorResolved: "good",
|
||||
}
|
||||
)
|
||||
|
||||
// Load parses the YAML input s into a Config.
|
||||
func Load(s string) (*Config, error) {
|
||||
cfg := &Config{}
|
||||
err := yaml.Unmarshal([]byte(s), cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.original = s
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// LoadFromFile parses the given YAML file into a Config.
|
||||
func LoadFromFile(filename string) (*Config, error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Load(string(content))
|
||||
}
|
||||
|
||||
// Config is the top-level configuration for Alertmanager's config files.
|
||||
type Config struct {
|
||||
AggrRules []*AggrRule `yaml:"aggregation_rules,omitempty"`
|
||||
InhibitRules []*InhibitRule `yaml:"inhibit_rules,omitempty"`
|
||||
NotificationConfigs []*NotificationConfig `yaml:"notification_configs,omitempty"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
|
||||
// original is the input from which the config was parsed.
|
||||
original string
|
||||
}
|
||||
|
||||
func checkOverflow(m map[string]interface{}, ctx string) error {
|
||||
if len(m) > 0 {
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return fmt.Errorf("unknown fields in %s: %s", ctx, strings.Join(keys, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Config) String() string {
|
||||
if c.original != "" {
|
||||
return c.original
|
||||
}
|
||||
b, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<error creating config string: %s>", err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
// We want to set c to the defaults and then overwrite it with the input.
|
||||
// To make unmarshal fill the plain data struct rather than calling UnmarshalYAML
|
||||
// again, we have to hide it using a type indirection.
|
||||
type plain Config
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names := map[string]struct{}{}
|
||||
|
||||
for _, nc := range c.NotificationConfigs {
|
||||
if _, ok := names[nc.Name]; ok {
|
||||
return fmt.Errorf("notification config name %q is not unique", nc.Name)
|
||||
}
|
||||
names[nc.Name] = struct{}{}
|
||||
}
|
||||
return checkOverflow(c.XXX, "config")
|
||||
}
|
||||
|
||||
// An InhibitRule specifies that a class of (source) alerts should inhibit
|
||||
// notifications for another class of (target) alerts if all specified matching
|
||||
// labels are equal between the two alerts. This may be used to inhibit alerts
|
||||
// from sending notifications if their meaning is logically a subset of a
|
||||
// higher-level alert.
|
||||
type InhibitRule struct {
|
||||
// The set of Filters which define the group of source alerts (which inhibit
|
||||
// the target alerts).
|
||||
SourceFilters []*Filter `yaml:"source_filters,omitempty"`
|
||||
|
||||
// The set of Filters which define the group of target alerts (which are
|
||||
// inhibited by the source alerts).
|
||||
TargetFilters []*Filter `yaml:"target_filters,omitempty"`
|
||||
|
||||
// A set of label names whose label values need to be identical in source and
|
||||
// target alerts in order for the inhibition to take effect.
|
||||
MatchOn []string `yaml:"match_on,omitempty"`
|
||||
|
||||
// How many seconds to wait for a corresponding inhibit source alert to
|
||||
// appear before sending any notifications for active target alerts.
|
||||
// TODO(julius): Not supported yet. Implement this!
|
||||
// optional int32 before_allowance = 4 [default = 0];
|
||||
// How many seconds to wait after a corresponding inhibit source alert
|
||||
// disappears before sending any notifications for active target alerts.
|
||||
// TODO(julius): Not supported yet. Implement this!
|
||||
// optional int32 after_allowance = 5 [default = 0];
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (r *InhibitRule) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain InhibitRule
|
||||
if err := unmarshal((*plain)(r)); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkOverflow(r.XXX, "inhibit rule")
|
||||
}
|
||||
|
||||
// Grouping and notification setting definitions for alerts.
|
||||
type AggrRule struct {
|
||||
// Filters that define which alerts are matched by this AggregationRule.
|
||||
Filters []*Filter `yaml:"filters,omitempty"`
|
||||
|
||||
// How many seconds to wait before resending a notification for a specific alert.
|
||||
RepeatRate model.Duration `yaml:"repeat_rate"`
|
||||
|
||||
// Notification configurations to use for this AggregationRule, referenced
|
||||
// by their name.
|
||||
NotificationConfigs []string `yaml:"notification_configs"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (r *AggrRule) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*r = DefaultAggrRule
|
||||
type plain AggrRule
|
||||
if err := unmarshal((*plain)(r)); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(r.NotificationConfigs) == 0 {
|
||||
return fmt.Errorf("aggregation rule needs at least one notification config")
|
||||
}
|
||||
return checkOverflow(r.XXX, "aggregation rule")
|
||||
}
|
||||
|
||||
// A regex-based label filter used in aggregations.
|
||||
type Filter struct {
|
||||
Name string `yaml:"name"`
|
||||
Regex *Regexp `yaml:"regex"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (f *Filter) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain Filter
|
||||
if err := unmarshal((*plain)(f)); err != nil {
|
||||
return err
|
||||
}
|
||||
return checkOverflow(r.XXX, "aggregation rule")
|
||||
}
|
||||
|
||||
// Notification configuration definition.
|
||||
type NotificationConfig struct {
|
||||
// Name of this NotificationConfig. Referenced from AggregationRule.
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Notify when resolved.
|
||||
SendResolved bool `yaml:"send_resolved"`
|
||||
|
||||
// Zero or more PagerDuty notification configurations.
|
||||
PagerdutyConfigs []*PagerDutyConfig `yaml:"pagerduty_configs"`
|
||||
|
||||
// Zero or more email notification configurations.
|
||||
EmailConfigs []*EmailConfig `yaml:"email_configs"`
|
||||
|
||||
// Zero or more pushover notification configurations.
|
||||
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs"`
|
||||
|
||||
// Zero or more hipchat notification configurations.
|
||||
HipchatConfigs []*HipchatConfig `yaml:"hipchat_configs"`
|
||||
|
||||
// Zero or more slack notification configurations.
|
||||
SlackConfigs []*SlackConfig `yaml:"slack_config"`
|
||||
|
||||
// Zero or more Flowdock notification configurations.
|
||||
FlowdockConfigs []*FlowdockConfig `yaml:"flowdock_config"`
|
||||
|
||||
// Zero or more generic web hook notification configurations.
|
||||
WebhookConfig []*WebhookConfig `yaml:"webhook_config"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *NotificationConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain NotificationConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("missing name in notification config")
|
||||
}
|
||||
return checkOverflow(r.XXX, "notification config")
|
||||
}
|
||||
|
||||
// Configuration for notification via PagerDuty.
|
||||
type PagerdutyConfig struct {
|
||||
// PagerDuty service key, see:
|
||||
// http://developer.pagerduty.com/documentation/integration/events
|
||||
ServiceKey string `yaml:"service_key"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain PagerdutyConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.ServiceKey == "" {
|
||||
return fmt.Errorf("missing service key in PagerDuty config")
|
||||
}
|
||||
return checkOverflow(r.XXX, "pagerduty config")
|
||||
}
|
||||
|
||||
// Configuration for notification via mail.
|
||||
type EmailConfig struct {
|
||||
// Email address to notify.
|
||||
Email string `yaml:"email"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *EmailConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain EmailConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Email == "" {
|
||||
return fmt.Errorf("missing email address in email config")
|
||||
}
|
||||
return checkOverflow(r.XXX, "email config")
|
||||
}
|
||||
|
||||
// Configuration for notification via pushover.net.
|
||||
type PushoverConfig struct {
|
||||
// Pushover token.
|
||||
Token string `yaml:"token"`
|
||||
|
||||
// Pushover user_key.
|
||||
UserKey string `yaml:"user_key"`
|
||||
}
|
||||
|
||||
// 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(r.XXX, "pushover config")
|
||||
}
|
||||
|
||||
type HipchatFormat string
|
||||
|
||||
const (
|
||||
HipchatFormatHTML HipchatFormat = "html"
|
||||
HipchatFormatText HipchatFormat = "text"
|
||||
)
|
||||
|
||||
// Configuration for notification 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).
|
||||
AuthToken string `yaml:"auth_token"`
|
||||
|
||||
// HipChat room id, (https://www.hipchat.com/rooms/ids).
|
||||
RoomID int `yaml:"room_id"`
|
||||
|
||||
// Color of message when triggered.
|
||||
Color string `yaml:"color"`
|
||||
|
||||
// Color of message when resolved.
|
||||
ColorResolved string `yaml:"color_resolved"`
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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.AuthToken == "" {
|
||||
return fmt.Errorf("missing auth token in HipChat config")
|
||||
}
|
||||
if c.RoomID == "" {
|
||||
return fmt.Errorf("missing room ID in HipChat config")
|
||||
}
|
||||
if c.MessageFormat != HipchatFormatHTML && c.MessageFormat != HipchatFormatText {
|
||||
return fmt.Errorf("invalid message format %q", c.MessageFormat)
|
||||
}
|
||||
return checkOverflow(r.XXX, "hipchat config")
|
||||
}
|
||||
|
||||
// Configuration for notification via Slack.
|
||||
type SlackConfig struct {
|
||||
// Slack webhook URL, (https://api.slack.com/incoming-webhooks).
|
||||
WebhookURL string `yaml:"webhook_url"`
|
||||
|
||||
// Slack channel override, (like #other-channel or @username).
|
||||
Channel string `yaml:"channel"`
|
||||
|
||||
// Color of message when triggered.
|
||||
Color string `yaml:"color"`
|
||||
|
||||
// Color of message when resolved.
|
||||
ColorResolved string `yaml:"color_resolved"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *SlackConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultSlackConfig
|
||||
type plain SlackConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.WebhookURL == "" {
|
||||
return fmt.Errorf("missing webhook URL in Slack config")
|
||||
}
|
||||
if c.Channel == "" {
|
||||
return fmt.Errorf("missing channel in Slack config")
|
||||
}
|
||||
return checkOverflow(r.XXX, "slack config")
|
||||
}
|
||||
|
||||
// Configuration for notification via Flowdock.
|
||||
type FlowdockConfig struct {
|
||||
// Flowdock flow API token.
|
||||
APIToken string `yaml:"api_token"`
|
||||
|
||||
// Flowdock from_address.
|
||||
FromAddress string `yaml:"from_address"`
|
||||
|
||||
// Flowdock flow tags.
|
||||
Tags []string `yaml:"tags"`
|
||||
}
|
||||
|
||||
// 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(r.XXX, "flowdock config")
|
||||
}
|
||||
|
||||
// Configuration for notification via generic webhook.
|
||||
type WebhookConfig struct {
|
||||
// URL to send POST request to.
|
||||
URL string `yaml:"url"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type plain WebhookConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.URL == "" {
|
||||
return fmt.Errorf("missing URL in webhook config")
|
||||
}
|
||||
return checkOverflow(r.XXX, "slack config")
|
||||
}
|
||||
|
||||
// Regexp encapsulates a regexp.Regexp and makes it YAML marshallable.
|
||||
type Regexp struct {
|
||||
regexp.Regexp
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var s string
|
||||
if err := unmarshal(&s); err != nil {
|
||||
return err
|
||||
}
|
||||
regex, err := regexp.Compile(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
re.Regexp = *regex
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (re *Regexp) MarshalYAML() (interface{}, error) {
|
||||
if re != nil {
|
||||
return re.String(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
23
main.go
23
main.go
|
@ -66,12 +66,16 @@ func main() {
|
|||
|
||||
versionInfoTmpl.Execute(os.Stdout, BuildInfo)
|
||||
|
||||
conf := config.MustLoadFromFile(*configFile)
|
||||
conf, err := config.LoadFile(*configFile)
|
||||
if err != nil {
|
||||
log.Errorf("error loading config: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
silencer := manager.NewSilencer()
|
||||
defer silencer.Close()
|
||||
|
||||
err := silencer.LoadFromFile(*silencesFile)
|
||||
err = silencer.LoadFromFile(*silencesFile)
|
||||
if err != nil {
|
||||
log.Warn("Couldn't load silences, starting up with empty silence list: ", err)
|
||||
}
|
||||
|
@ -89,11 +93,11 @@ func main() {
|
|||
if err != nil {
|
||||
log.Fatalln("Error building Alertmanager URL:", err)
|
||||
}
|
||||
notifier := manager.NewNotifier(conf.NotificationConfig, amURL)
|
||||
notifier := manager.NewNotifier(conf.NotificationConfigs, amURL)
|
||||
defer notifier.Close()
|
||||
|
||||
inhibitor := new(manager.Inhibitor)
|
||||
inhibitor.SetInhibitRules(conf.InhibitRules())
|
||||
inhibitor.SetInhibitRules(conf.InhibitRules)
|
||||
|
||||
options := &manager.MemoryAlertManagerOptions{
|
||||
Inhibitor: inhibitor,
|
||||
|
@ -102,7 +106,7 @@ func main() {
|
|||
MinRefreshInterval: *minRefreshPeriod,
|
||||
}
|
||||
alertManager := manager.NewMemoryAlertManager(options)
|
||||
alertManager.SetAggregationRules(conf.AggregationRules())
|
||||
alertManager.SetAggregationRules(conf.AggrRules)
|
||||
go alertManager.Run()
|
||||
|
||||
// Web initialization.
|
||||
|
@ -139,15 +143,6 @@ func main() {
|
|||
}
|
||||
go webService.ServeForever(*listenAddress, *pathPrefix)
|
||||
|
||||
// React to configuration changes.
|
||||
watcher := config.NewFileWatcher(*configFile)
|
||||
go watcher.Watch(func(conf *config.Config) {
|
||||
inhibitor.SetInhibitRules(conf.InhibitRules())
|
||||
notifier.SetNotificationConfigs(conf.NotificationConfig)
|
||||
alertManager.SetAggregationRules(conf.AggregationRules())
|
||||
statusHandler.UpdateConfig(conf.String())
|
||||
})
|
||||
|
||||
log.Info("Running notification dispatcher...")
|
||||
notifier.Dispatch()
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ import (
|
|||
"fmt"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const AlertNameLabel = "alertname"
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
type AlertFingerprint uint64
|
||||
|
||||
|
|
|
@ -16,36 +16,35 @@ package manager
|
|||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"regexp"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
)
|
||||
|
||||
type Filters []*Filter
|
||||
|
||||
type Filter struct {
|
||||
Name *regexp.Regexp
|
||||
Value *regexp.Regexp
|
||||
NamePattern string
|
||||
Name string
|
||||
Value *config.Regexp
|
||||
ValuePattern string
|
||||
|
||||
fingerprint uint64
|
||||
}
|
||||
|
||||
func NewFilter(namePattern string, valuePattern string) *Filter {
|
||||
func NewFilter(name string, value *config.Regexp) *Filter {
|
||||
summer := fnv.New64a()
|
||||
fmt.Fprintf(summer, namePattern, valuePattern)
|
||||
fmt.Fprintf(summer, name, value.String())
|
||||
|
||||
return &Filter{
|
||||
Name: regexp.MustCompile("^" + namePattern + "$"),
|
||||
Value: regexp.MustCompile("^" + valuePattern + "$"),
|
||||
NamePattern: namePattern,
|
||||
ValuePattern: valuePattern,
|
||||
Name: name,
|
||||
Value: value,
|
||||
ValuePattern: value.String(),
|
||||
fingerprint: summer.Sum64(),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Filter) Handles(l AlertLabelSet) bool {
|
||||
for k, v := range l {
|
||||
if f.Name.MatchString(k) && f.Value.MatchString(v) {
|
||||
if f.Name == k && f.Value.MatchString(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ package manager
|
|||
import (
|
||||
"sync"
|
||||
|
||||
_ "github.com/prometheus/alertmanager/config/generated"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
)
|
||||
|
||||
type InhibitRules []*InhibitRule
|
||||
|
@ -53,16 +53,30 @@ func (i *InhibitRule) Filter(s AlertLabelSets, t AlertLabelSets) AlertLabelSets
|
|||
// emits uninhibited alert labelsets.
|
||||
type Inhibitor struct {
|
||||
mu sync.Mutex
|
||||
inhibitRules InhibitRules
|
||||
inhibitRules []*InhibitRule
|
||||
dirty bool
|
||||
}
|
||||
|
||||
// Replaces the current InhibitRules with a new set.
|
||||
func (i *Inhibitor) SetInhibitRules(r InhibitRules) {
|
||||
func (i *Inhibitor) SetInhibitRules(r []*config.InhibitRule) {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
i.inhibitRules = r
|
||||
i.inhibitRules = i.inhibitRules[:0]
|
||||
|
||||
for _, ih := range r {
|
||||
ihr := &InhibitRule{
|
||||
MatchOn: ih.MatchOn,
|
||||
}
|
||||
for _, f := range ih.SourceFilters {
|
||||
ihr.SourceFilters = append(ihr.SourceFilters, NewFilter(f.Name, f.Regex))
|
||||
}
|
||||
for _, f := range ih.TargetFilters {
|
||||
ihr.TargetFilters = append(ihr.TargetFilters, NewFilter(f.Name, f.Regex))
|
||||
}
|
||||
|
||||
i.inhibitRules = append(i.inhibitRules, ihr)
|
||||
}
|
||||
i.dirty = true
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/log"
|
||||
)
|
||||
|
||||
|
@ -30,7 +31,7 @@ type AlertManager interface {
|
|||
// Retrieves all alerts from the store that match the provided Filters.
|
||||
GetAll(Filters) AlertAggregates
|
||||
// Sets the AggregationRules to associate with alerts.
|
||||
SetAggregationRules(AggregationRules)
|
||||
SetAggregationRules([]*config.AggrRule)
|
||||
// Runs the AlertManager dispatcher loop.
|
||||
Run()
|
||||
}
|
||||
|
@ -39,9 +40,9 @@ type AggregationRules []*AggregationRule
|
|||
|
||||
// AggregationRule creates and manages the scope for received events.
|
||||
type AggregationRule struct {
|
||||
Filters Filters
|
||||
RepeatRate time.Duration
|
||||
NotificationConfigName string
|
||||
Filters Filters
|
||||
RepeatRate time.Duration
|
||||
NotificationConfigs []string
|
||||
}
|
||||
|
||||
// Returns whether a given AggregationRule matches an Alert.
|
||||
|
@ -239,10 +240,23 @@ func (s memoryAlertManager) GetAll(f Filters) AlertAggregates {
|
|||
}
|
||||
|
||||
// Replace the current set of loaded AggregationRules by another.
|
||||
func (s *memoryAlertManager) SetAggregationRules(rules AggregationRules) {
|
||||
func (s *memoryAlertManager) SetAggregationRules(crs []*config.AggrRule) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var rules AggregationRules
|
||||
for _, ar := range crs {
|
||||
var filters Filters
|
||||
for _, f := range ar.Filters {
|
||||
filters = append(filters, NewFilter(f.Name, f.Regex))
|
||||
}
|
||||
rules = append(rules, &AggregationRule{
|
||||
Filters: filters,
|
||||
RepeatRate: time.Duration(ar.RepeatRate),
|
||||
NotificationConfigs: ar.NotificationConfigs,
|
||||
})
|
||||
}
|
||||
|
||||
log.Infof("Replacing aggregator rules (old: %d, new: %d)...", len(s.rules), len(rules))
|
||||
s.rules = rules
|
||||
|
||||
|
@ -279,7 +293,7 @@ func (s *memoryAlertManager) removeExpiredAggregates() {
|
|||
|
||||
if time.Since(agg.LastRefreshed) > s.minRefreshInterval {
|
||||
delete(s.aggregates, agg.Alert.Fingerprint())
|
||||
s.notifier.QueueNotification(agg.Alert, notificationOpResolve, agg.Rule.NotificationConfigName)
|
||||
s.notifier.QueueNotification(agg.Alert, notificationOpResolve, agg.Rule.NotificationConfigs)
|
||||
s.needsNotificationRefresh = true
|
||||
} else {
|
||||
heap.Push(&s.aggregatesByLastRefreshed, agg)
|
||||
|
@ -343,7 +357,7 @@ func (s *memoryAlertManager) refreshNotifications() {
|
|||
continue
|
||||
}
|
||||
if agg.Rule != nil {
|
||||
s.notifier.QueueNotification(agg.Alert, notificationOpTrigger, agg.Rule.NotificationConfigName)
|
||||
s.notifier.QueueNotification(agg.Alert, notificationOpTrigger, agg.Rule.NotificationConfigs)
|
||||
agg.LastNotification = time.Now()
|
||||
agg.NextNotification = agg.LastNotification.Add(agg.Rule.RepeatRate)
|
||||
numSent++
|
||||
|
|
|
@ -34,7 +34,7 @@ import (
|
|||
"github.com/prometheus/log"
|
||||
"github.com/thorduri/pushover"
|
||||
|
||||
pb "github.com/prometheus/alertmanager/config/generated"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -77,10 +77,10 @@ type notificationOp int
|
|||
// a provided notification configuration.
|
||||
type Notifier interface {
|
||||
// Queue a notification for asynchronous dispatching.
|
||||
QueueNotification(a *Alert, op notificationOp, configName string) error
|
||||
QueueNotification(a *Alert, op notificationOp, configs []string) error
|
||||
// Replace current notification configs. Already enqueued messages will remain
|
||||
// unaffected.
|
||||
SetNotificationConfigs([]*pb.NotificationConfig)
|
||||
SetNotificationConfigs([]*config.NotificationConfig)
|
||||
// Start alert notification dispatch loop.
|
||||
Dispatch()
|
||||
// Stop the alert notification dispatch loop.
|
||||
|
@ -90,7 +90,7 @@ type Notifier interface {
|
|||
// Request for sending a notification.
|
||||
type notificationReq struct {
|
||||
alert *Alert
|
||||
notificationConfig *pb.NotificationConfig
|
||||
notificationConfig *config.NotificationConfig
|
||||
op notificationOp
|
||||
}
|
||||
|
||||
|
@ -104,11 +104,11 @@ type notifier struct {
|
|||
// Mutex to protect the fields below.
|
||||
mu sync.Mutex
|
||||
// Map of notification configs by name.
|
||||
notificationConfigs map[string]*pb.NotificationConfig
|
||||
notificationConfigs map[string]*config.NotificationConfig
|
||||
}
|
||||
|
||||
// NewNotifier construct a new notifier.
|
||||
func NewNotifier(configs []*pb.NotificationConfig, amURL string) *notifier {
|
||||
func NewNotifier(configs []*config.NotificationConfig, amURL string) *notifier {
|
||||
notifier := ¬ifier{
|
||||
pendingNotifications: make(chan *notificationReq, *notificationBufferSize),
|
||||
alertmanagerURL: amURL,
|
||||
|
@ -117,32 +117,34 @@ func NewNotifier(configs []*pb.NotificationConfig, amURL string) *notifier {
|
|||
return notifier
|
||||
}
|
||||
|
||||
func (n *notifier) SetNotificationConfigs(configs []*pb.NotificationConfig) {
|
||||
func (n *notifier) SetNotificationConfigs(configs []*config.NotificationConfig) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
n.notificationConfigs = map[string]*pb.NotificationConfig{}
|
||||
n.notificationConfigs = map[string]*config.NotificationConfig{}
|
||||
for _, c := range configs {
|
||||
n.notificationConfigs[c.GetName()] = c
|
||||
n.notificationConfigs[c.Name] = c
|
||||
}
|
||||
}
|
||||
|
||||
func (n *notifier) QueueNotification(a *Alert, op notificationOp, configName string) error {
|
||||
n.mu.Lock()
|
||||
nc, ok := n.notificationConfigs[configName]
|
||||
n.mu.Unlock()
|
||||
func (n *notifier) QueueNotification(a *Alert, op notificationOp, configs []string) error {
|
||||
for _, cname := range configs {
|
||||
n.mu.Lock()
|
||||
nc, ok := n.notificationConfigs[cname]
|
||||
n.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("No such notification configuration %s", configName)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("No such notification configuration %s", cname)
|
||||
}
|
||||
|
||||
// We need to save a reference to the notification config in the
|
||||
// notificationReq since the config might be replaced or gone at the time the
|
||||
// message gets dispatched.
|
||||
n.pendingNotifications <- ¬ificationReq{
|
||||
alert: a,
|
||||
notificationConfig: nc,
|
||||
op: op,
|
||||
// We need to save a reference to the notification config in the
|
||||
// notificationReq since the config might be replaced or gone at the time the
|
||||
// message gets dispatched.
|
||||
n.pendingNotifications <- ¬ificationReq{
|
||||
alert: a,
|
||||
notificationConfig: nc,
|
||||
op: op,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -195,32 +197,34 @@ func (n *notifier) sendPagerDutyNotification(serviceKey string, op notificationO
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *notifier) sendHipChatNotification(op notificationOp, config *pb.HipChatConfig, a *Alert) error {
|
||||
func (n *notifier) sendHipChatNotification(op notificationOp, conf *config.HipchatConfig, a *Alert) error {
|
||||
// https://www.hipchat.com/docs/apiv2/method/send_room_notification
|
||||
incidentKey := a.Fingerprint()
|
||||
color := ""
|
||||
status := ""
|
||||
message := ""
|
||||
messageFormat := ""
|
||||
var (
|
||||
color string
|
||||
status string
|
||||
message string
|
||||
messageFormat config.HipchatFormat
|
||||
)
|
||||
switch op {
|
||||
case notificationOpTrigger:
|
||||
color = config.GetColor()
|
||||
color = conf.Color
|
||||
status = "firing"
|
||||
case notificationOpResolve:
|
||||
color = config.GetColorResolved()
|
||||
color = conf.ColorResolved
|
||||
status = "resolved"
|
||||
}
|
||||
if config.GetMessageFormat() == pb.HipChatConfig_TEXT {
|
||||
message = fmt.Sprintf("%s%s %s: %s", config.GetPrefix(), a.Labels["alertname"], status, a.Summary)
|
||||
if conf.MessageFormat == config.HipchatFormatText {
|
||||
message = fmt.Sprintf("%s%s %s: %s", conf.Prefix, a.Labels["alertname"], status, a.Summary)
|
||||
messageFormat = "text"
|
||||
} else {
|
||||
message = fmt.Sprintf("%s<b>%s %s</b>: %s (<a href='%s'>view</a>)", config.GetPrefix(), html.EscapeString(a.Labels["alertname"]), status, html.EscapeString(a.Summary), a.Payload["generatorURL"])
|
||||
message = fmt.Sprintf("%s<b>%s %s</b>: %s (<a href='%s'>view</a>)", conf.Prefix, html.EscapeString(a.Labels["alertname"]), status, html.EscapeString(a.Summary), a.Payload["generatorURL"])
|
||||
messageFormat = "html"
|
||||
}
|
||||
buf, err := json.Marshal(map[string]interface{}{
|
||||
"color": color,
|
||||
"message": message,
|
||||
"notify": config.GetNotify(),
|
||||
"notify": conf.Notify,
|
||||
"message_format": messageFormat,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -232,7 +236,7 @@ func (n *notifier) sendHipChatNotification(op notificationOp, config *pb.HipChat
|
|||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Post(
|
||||
fmt.Sprintf("%s/room/%d/notification?auth_token=%s", *hipchatURL, config.GetRoomId(), config.GetAuthToken()),
|
||||
fmt.Sprintf("%s/room/%d/notification?auth_token=%s", *hipchatURL, conf.RoomID, conf.AuthToken),
|
||||
contentTypeJSON,
|
||||
bytes.NewBuffer(buf),
|
||||
)
|
||||
|
@ -276,17 +280,17 @@ type slackAttachmentField struct {
|
|||
Short bool `json:"short,omitempty"`
|
||||
}
|
||||
|
||||
func (n *notifier) sendSlackNotification(op notificationOp, config *pb.SlackConfig, a *Alert) error {
|
||||
func (n *notifier) sendSlackNotification(op notificationOp, conf *config.SlackConfig, a *Alert) error {
|
||||
// https://api.slack.com/incoming-webhooks
|
||||
incidentKey := a.Fingerprint()
|
||||
color := ""
|
||||
status := ""
|
||||
switch op {
|
||||
case notificationOpTrigger:
|
||||
color = config.GetColor()
|
||||
color = conf.Color
|
||||
status = "firing"
|
||||
case notificationOpResolve:
|
||||
color = config.GetColorResolved()
|
||||
color = conf.ColorResolved
|
||||
status = "resolved"
|
||||
}
|
||||
|
||||
|
@ -310,7 +314,7 @@ func (n *notifier) sendSlackNotification(op notificationOp, config *pb.SlackConf
|
|||
}
|
||||
|
||||
req := &slackReq{
|
||||
Channel: config.GetChannel(),
|
||||
Channel: conf.Channel,
|
||||
Attachments: []slackAttachment{
|
||||
*attachment,
|
||||
},
|
||||
|
@ -325,11 +329,7 @@ func (n *notifier) sendSlackNotification(op notificationOp, config *pb.SlackConf
|
|||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Post(
|
||||
config.GetWebhookUrl(),
|
||||
contentTypeJSON,
|
||||
bytes.NewBuffer(buf),
|
||||
)
|
||||
resp, err := client.Post(conf.WebhookURL, contentTypeJSON, bytes.NewBuffer(buf))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -355,9 +355,10 @@ type flowdockMessage struct {
|
|||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
func (n *notifier) sendFlowdockNotification(op notificationOp, config *pb.FlowdockConfig, a *Alert) error {
|
||||
flowdockMessage := newFlowdockMessage(op, config, a)
|
||||
url := strings.TrimRight(*flowdockURL, "/") + "/" + config.GetApiToken()
|
||||
func (n *notifier) sendFlowdockNotification(op notificationOp, conf *config.FlowdockConfig, a *Alert) error {
|
||||
flowdockMessage := newFlowdockMessage(op, conf, a)
|
||||
url := strings.TrimRight(*flowdockURL, "/") + "/" + conf.APIToken
|
||||
|
||||
jsonMessage, err := json.Marshal(flowdockMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -372,7 +373,7 @@ func (n *notifier) sendFlowdockNotification(op notificationOp, config *pb.Flowdo
|
|||
return nil
|
||||
}
|
||||
|
||||
func newFlowdockMessage(op notificationOp, config *pb.FlowdockConfig, a *Alert) *flowdockMessage {
|
||||
func newFlowdockMessage(op notificationOp, conf *config.FlowdockConfig, a *Alert) *flowdockMessage {
|
||||
status := ""
|
||||
switch op {
|
||||
case notificationOpTrigger:
|
||||
|
@ -383,12 +384,12 @@ func newFlowdockMessage(op notificationOp, config *pb.FlowdockConfig, a *Alert)
|
|||
|
||||
msg := &flowdockMessage{
|
||||
Source: "Prometheus",
|
||||
FromAddress: config.GetFromAddress(),
|
||||
FromAddress: conf.FromAddress,
|
||||
Subject: html.EscapeString(a.Summary),
|
||||
Format: "html",
|
||||
Content: fmt.Sprintf("*%s %s*: %s (<%s|view>)", html.EscapeString(a.Labels["alertname"]), status, html.EscapeString(a.Summary), a.Payload["generatorURL"]),
|
||||
Link: a.Payload["generatorURL"],
|
||||
Tags: append(config.GetTag(), status),
|
||||
Tags: append(conf.Tags, status),
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
@ -399,7 +400,7 @@ type webhookMessage struct {
|
|||
Alerts []Alert `json:"alert"`
|
||||
}
|
||||
|
||||
func (n *notifier) sendWebhookNotification(op notificationOp, config *pb.WebhookConfig, a *Alert) error {
|
||||
func (n *notifier) sendWebhookNotification(op notificationOp, conf *config.WebhookConfig, a *Alert) error {
|
||||
status := ""
|
||||
switch op {
|
||||
case notificationOpTrigger:
|
||||
|
@ -417,7 +418,7 @@ func (n *notifier) sendWebhookNotification(op notificationOp, config *pb.Webhook
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpResponse, err := postJSON(jsonMessage, config.GetUrl())
|
||||
httpResponse, err := postJSON(jsonMessage, conf.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -587,61 +588,47 @@ func processResponse(r *http.Response, targetName string, a *Alert) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *notifier) handleNotification(a *Alert, op notificationOp, config *pb.NotificationConfig) {
|
||||
for _, pdConfig := range config.PagerdutyConfig {
|
||||
if err := n.sendPagerDutyNotification(pdConfig.GetServiceKey(), op, a); err != nil {
|
||||
func (n *notifier) handleNotification(a *Alert, op notificationOp, conf *config.NotificationConfig) {
|
||||
if op == notificationOpResolve && !conf.SendResolved {
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range conf.PagerdutyConfigs {
|
||||
if err := n.sendPagerDutyNotification(c.ServiceKey, op, a); err != nil {
|
||||
log.Errorln("Error sending PagerDuty notification:", err)
|
||||
}
|
||||
}
|
||||
for _, emailConfig := range config.EmailConfig {
|
||||
if op == notificationOpResolve && !emailConfig.GetSendResolved() {
|
||||
continue
|
||||
}
|
||||
for _, c := range conf.EmailConfigs {
|
||||
if *smtpSmartHost == "" {
|
||||
log.Warn("No SMTP smarthost configured, not sending email notification.")
|
||||
continue
|
||||
}
|
||||
if err := n.sendEmailNotification(emailConfig.GetEmail(), op, a); err != nil {
|
||||
if err := n.sendEmailNotification(c.Email, op, a); err != nil {
|
||||
log.Errorln("Error sending email notification:", err)
|
||||
}
|
||||
}
|
||||
for _, poConfig := range config.PushoverConfig {
|
||||
if op == notificationOpResolve && !poConfig.GetSendResolved() {
|
||||
continue
|
||||
}
|
||||
if err := n.sendPushoverNotification(poConfig.GetToken(), op, poConfig.GetUserKey(), a); err != nil {
|
||||
for _, c := range conf.PushoverConfigs {
|
||||
if err := n.sendPushoverNotification(c.Token, op, c.UserKey, a); err != nil {
|
||||
log.Errorln("Error sending Pushover notification:", err)
|
||||
}
|
||||
}
|
||||
for _, hcConfig := range config.HipchatConfig {
|
||||
if op == notificationOpResolve && !hcConfig.GetSendResolved() {
|
||||
continue
|
||||
}
|
||||
if err := n.sendHipChatNotification(op, hcConfig, a); err != nil {
|
||||
for _, c := range conf.HipchatConfigs {
|
||||
if err := n.sendHipChatNotification(op, c, a); err != nil {
|
||||
log.Errorln("Error sending HipChat notification:", err)
|
||||
}
|
||||
}
|
||||
for _, scConfig := range config.SlackConfig {
|
||||
if op == notificationOpResolve && !scConfig.GetSendResolved() {
|
||||
continue
|
||||
}
|
||||
if err := n.sendSlackNotification(op, scConfig, a); err != nil {
|
||||
for _, c := range conf.SlackConfigs {
|
||||
if err := n.sendSlackNotification(op, c, a); err != nil {
|
||||
log.Errorln("Error sending Slack notification:", err)
|
||||
}
|
||||
}
|
||||
for _, fdConfig := range config.FlowdockConfig {
|
||||
if op == notificationOpResolve && !fdConfig.GetSendResolved() {
|
||||
continue
|
||||
}
|
||||
if err := n.sendFlowdockNotification(op, fdConfig, a); err != nil {
|
||||
for _, c := range conf.FlowdockConfigs {
|
||||
if err := n.sendFlowdockNotification(op, c, a); err != nil {
|
||||
log.Errorln("Error sending Flowdock notification:", err)
|
||||
}
|
||||
}
|
||||
for _, whConfig := range config.WebhookConfig {
|
||||
if op == notificationOpResolve && !whConfig.GetSendResolved() {
|
||||
continue
|
||||
}
|
||||
if err := n.sendWebhookNotification(op, whConfig, a); err != nil {
|
||||
for _, c := range conf.WebhookConfigs {
|
||||
if err := n.sendWebhookNotification(op, c, a); err != nil {
|
||||
log.Errorln("Error sending Webhook notification:", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
pb "github.com/prometheus/alertmanager/config/generated"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
)
|
||||
|
||||
func TestWriteEmailBody(t *testing.T) {
|
||||
|
@ -192,8 +192,8 @@ func TestSendWebhookNotification(t *testing.T) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
config := &pb.WebhookConfig{
|
||||
Url: &ts.URL,
|
||||
conf := &config.WebhookConfig{
|
||||
URL: ts.URL,
|
||||
}
|
||||
alert := &Alert{
|
||||
Summary: "Testsummary",
|
||||
|
@ -206,7 +206,7 @@ func TestSendWebhookNotification(t *testing.T) {
|
|||
},
|
||||
}
|
||||
n := ¬ifier{}
|
||||
err := n.sendWebhookNotification(notificationOpTrigger, config, alert)
|
||||
err := n.sendWebhookNotification(notificationOpTrigger, conf, alert)
|
||||
if err != nil {
|
||||
t.Errorf("error sending webhook notification: %s", err)
|
||||
}
|
||||
|
|
|
@ -17,10 +17,13 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/log"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
)
|
||||
|
||||
type SilenceID uint
|
||||
|
@ -56,8 +59,8 @@ type ApiSilence struct {
|
|||
func (s *Silence) MarshalJSON() ([]byte, error) {
|
||||
filters := map[string]string{}
|
||||
for _, f := range s.Filters {
|
||||
name := f.Name.String()[1 : len(f.Name.String())-1]
|
||||
value := f.Value.String()[1 : len(f.Value.String())-1]
|
||||
name := f.Name
|
||||
value := f.ValuePattern
|
||||
filters[name] = value
|
||||
}
|
||||
|
||||
|
@ -76,8 +79,13 @@ func (s *Silence) UnmarshalJSON(data []byte) error {
|
|||
json.Unmarshal(data, sc)
|
||||
|
||||
filters := make(Filters, 0, len(sc.Filters))
|
||||
|
||||
for label, value := range sc.Filters {
|
||||
filters = append(filters, NewFilter(label, value))
|
||||
re, err := regexp.Compile(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filters = append(filters, NewFilter(label, &config.Regexp{*re}))
|
||||
}
|
||||
|
||||
if sc.CreatedAtSeconds == 0 {
|
||||
|
|
Loading…
Reference in New Issue