mirror of
https://github.com/prometheus/alertmanager
synced 2024-12-25 07:32:19 +00:00
Add VictorOps Notifier
Add default VictorOpsAPIURL Add VictorOps default config Add VictorOpsConfig struct in notifiers Add new template tags for victorops Add notifications logic for victorops Compiled template tags with make assets Remove common labels from entity_id template Set messageType default value to CRITICAL Recovery messageType is not configurable anymore. Firing state only allows specific keys Make assets Using log.Debugf EntityID should not be configureable Remove entity_id from template Use GroupKey(ctx) as entity_id Improve debug logging Fix type of entity_id
This commit is contained in:
parent
9a2132a45b
commit
8e0f405e67
@ -217,6 +217,17 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
ogc.APIHost += "/"
|
||||
}
|
||||
}
|
||||
for _, voc := range rcv.VictorOpsConfigs {
|
||||
if voc.APIURL == "" {
|
||||
if c.Global.VictorOpsAPIURL == "" {
|
||||
return fmt.Errorf("no global VictorOps URL set")
|
||||
}
|
||||
voc.APIURL = c.Global.VictorOpsAPIURL
|
||||
}
|
||||
if !strings.HasSuffix(voc.APIURL, "/") {
|
||||
voc.APIURL += "/"
|
||||
}
|
||||
}
|
||||
names[rcv.Name] = struct{}{}
|
||||
}
|
||||
|
||||
@ -264,6 +275,7 @@ var DefaultGlobalConfig = GlobalConfig{
|
||||
PagerdutyURL: "https://events.pagerduty.com/generic/2010-04-15/create_event.json",
|
||||
HipchatURL: "https://api.hipchat.com/",
|
||||
OpsGenieAPIHost: "https://api.opsgenie.com/",
|
||||
VictorOpsAPIURL: "https://alert.victorops.com/integrations/generic/20131114/alert/",
|
||||
}
|
||||
|
||||
// GlobalConfig defines configuration parameters that are valid globally
|
||||
@ -284,6 +296,7 @@ type GlobalConfig struct {
|
||||
HipchatURL string `yaml:"hipchat_url"`
|
||||
HipchatAuthToken Secret `yaml:"hipchat_auth_token"`
|
||||
OpsGenieAPIHost string `yaml:"opsgenie_api_host"`
|
||||
VictorOpsAPIURL string `yaml:"victorops_api_url"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
@ -412,6 +425,7 @@ type Receiver struct {
|
||||
WebhookConfigs []*WebhookConfig `yaml:"webhook_configs,omitempty"`
|
||||
OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty"`
|
||||
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty"`
|
||||
VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
|
@ -93,6 +93,16 @@ var (
|
||||
// TODO: Add a details field with all the alerts.
|
||||
}
|
||||
|
||||
// DefaultVictorOpsConfig defines default values for VictorOps configurations.
|
||||
DefaultVictorOpsConfig = VictorOpsConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: true,
|
||||
},
|
||||
MessageType: `CRITICAL`,
|
||||
StateMessage: `{{ template "victorops.default.message" . }}`,
|
||||
From: `{{ template "victorops.default.from" . }}`,
|
||||
}
|
||||
|
||||
// DefaultPushoverConfig defines default values for Pushover configurations.
|
||||
DefaultPushoverConfig = PushoverConfig{
|
||||
NotifierConfig: NotifierConfig{
|
||||
@ -309,6 +319,36 @@ func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
|
||||
return checkOverflow(c.XXX, "opsgenie config")
|
||||
}
|
||||
|
||||
// VictorOpsConfig configures notifications via VictorOps.
|
||||
type VictorOpsConfig struct {
|
||||
NotifierConfig `yaml:",inline"`
|
||||
|
||||
APIKey Secret `yaml:"api_key"`
|
||||
APIURL string `yaml:"api_url"`
|
||||
RoutingKey string `yaml:"routing_key"`
|
||||
MessageType string `yaml:"message_type"`
|
||||
StateMessage string `yaml:"message"`
|
||||
From string `yaml:"from"`
|
||||
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *VictorOpsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultVictorOpsConfig
|
||||
type plain VictorOpsConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.APIKey == "" {
|
||||
return fmt.Errorf("missing API key in VictorOps config")
|
||||
}
|
||||
if c.RoutingKey == "" {
|
||||
return fmt.Errorf("missing Routing key in VictorOps config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "victorops config")
|
||||
}
|
||||
|
||||
type duration time.Duration
|
||||
|
||||
func (d *duration) UnmarshalText(text []byte) error {
|
||||
|
106
notify/impl.go
106
notify/impl.go
@ -136,6 +136,10 @@ func Build(confs []*config.Receiver, tmpl *template.Template) map[string]Fanout
|
||||
n := NewHipchat(c, tmpl)
|
||||
add(i, n, filter(n, c))
|
||||
}
|
||||
for i, c := range nc.VictorOpsConfigs {
|
||||
n := NewVictorOps(c, tmpl)
|
||||
add(i, n, filter(n, c))
|
||||
}
|
||||
for i, c := range nc.PushoverConfigs {
|
||||
n := NewPushover(c, tmpl)
|
||||
add(i, n, filter(n, c))
|
||||
@ -730,6 +734,108 @@ func (n *OpsGenie) Notify(ctx context.Context, as ...*types.Alert) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// VictorOps implements a Notifier for VictorOps notifications.
|
||||
type VictorOps struct {
|
||||
conf *config.VictorOpsConfig
|
||||
tmpl *template.Template
|
||||
}
|
||||
|
||||
// NewVictorOps returns a new VictorOps notifier.
|
||||
func NewVictorOps(c *config.VictorOpsConfig, t *template.Template) *VictorOps {
|
||||
return &VictorOps{
|
||||
conf: c,
|
||||
tmpl: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (*VictorOps) name() string { return "victorops" }
|
||||
|
||||
const (
|
||||
victorOpsEventTrigger = "CRITICAL"
|
||||
victorOpsEventResolve = "RECOVERY"
|
||||
)
|
||||
|
||||
type victorOpsMessage struct {
|
||||
MessageType string `json:"message_type"`
|
||||
EntityID model.Fingerprint `json:"entity_id"`
|
||||
StateMessage string `json:"state_message"`
|
||||
From string `json:"monitoring_tool"`
|
||||
}
|
||||
|
||||
type victorOpsErrorResponse struct {
|
||||
Result string `json:"result"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Notify implements the Notifier interface.
|
||||
func (n *VictorOps) Notify(ctx context.Context, as ...*types.Alert) error {
|
||||
victorOpsAllowedEvents := map[string]bool{
|
||||
"INFO": true,
|
||||
"WARNING": true,
|
||||
"CRITICAL": true,
|
||||
}
|
||||
|
||||
key, ok := GroupKey(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("group key missing")
|
||||
}
|
||||
|
||||
var err error
|
||||
var (
|
||||
alerts = types.Alerts(as...)
|
||||
data = n.tmpl.Data(receiver(ctx), groupLabels(ctx), as...)
|
||||
tmpl = tmplText(n.tmpl, data, &err)
|
||||
apiURL = fmt.Sprintf("%s%s/%s", n.conf.APIURL, n.conf.APIKey, n.conf.RoutingKey)
|
||||
messageType = n.conf.MessageType
|
||||
)
|
||||
|
||||
if alerts.Status() == model.AlertFiring && !victorOpsAllowedEvents[messageType] {
|
||||
messageType = victorOpsEventTrigger
|
||||
}
|
||||
|
||||
if alerts.Status() == model.AlertResolved {
|
||||
messageType = victorOpsEventResolve
|
||||
}
|
||||
|
||||
msg := &victorOpsMessage{
|
||||
MessageType: messageType,
|
||||
EntityID: key,
|
||||
StateMessage: tmpl(n.conf.StateMessage),
|
||||
From: tmpl(n.conf.From),
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("templating error: %s", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := ctxhttp.Post(ctx, http.DefaultClient, apiURL, contentTypeJSON, &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
|
||||
var responseMessage victorOpsErrorResponse
|
||||
if err := json.Unmarshal(body, &responseMessage); err != nil {
|
||||
return fmt.Errorf("could not parse error response %q", body)
|
||||
}
|
||||
|
||||
log.With("incident", key).Debugf("unexpected VictorOps response from %s (POSTed %s), %s: %s", apiURL, msg, resp.Status, body)
|
||||
|
||||
return fmt.Errorf("error when posting alert: result %q, message %q",
|
||||
responseMessage.Result, responseMessage.Message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pushover implements a Notifier for Pushover notifications.
|
||||
type Pushover struct {
|
||||
conf *config.PushoverConfig
|
||||
|
@ -45,6 +45,10 @@ Alerts Resolved:
|
||||
{{ define "opsgenie.default.source" }}{{ template "__alertmanagerURL" . }}{{ end }}
|
||||
|
||||
|
||||
{{ define "victorops.default.message" }}{{ template "__subject" . }} | {{ template "__alertmanagerURL" . }}{{ end }}
|
||||
{{ define "victorops.default.from" }}{{ template "__alertmanager" . }}{{ end }}
|
||||
|
||||
|
||||
{{ define "email.default.subject" }}{{ template "__subject" . }}{{ end }}
|
||||
{{ define "email.default.html" }}
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
@ -93,7 +97,7 @@ SOFTWARE.
|
||||
<tr style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
|
||||
<td style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #E6522C; margin: 0; padding: 20px;" align="center" bgcolor="#E6522C" valign="top">
|
||||
{{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }}
|
||||
{{ .Name }}={{ .Value }}
|
||||
{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
|
File diff suppressed because one or more lines are too long
120
ui/bindata.go
120
ui/bindata.go
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user