Add opsgenie notifier
This commit is contained in:
parent
ab2a66c6b7
commit
3e7d614eb2
|
@ -163,6 +163,17 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
pdc.URL = c.Global.PagerdutyURL
|
||||
}
|
||||
}
|
||||
for _, ogc := range rcv.OpsGenieConfigs {
|
||||
if ogc.APIHost == "" {
|
||||
if c.Global.OpsGenieAPIHost == "" {
|
||||
return fmt.Errorf("no global OpsGenie URL set")
|
||||
}
|
||||
ogc.APIHost = c.Global.OpsGenieAPIHost
|
||||
}
|
||||
if !strings.HasSuffix(ogc.APIHost, "/") {
|
||||
ogc.APIHost += "/"
|
||||
}
|
||||
}
|
||||
names[rcv.Name] = struct{}{}
|
||||
}
|
||||
return checkOverflow(c.XXX, "config")
|
||||
|
@ -171,6 +182,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
// DefaultGlobalConfig provides global default values.
|
||||
var DefaultGlobalConfig = GlobalConfig{
|
||||
PagerdutyURL: "https://events.pagerduty.com/generic/2010-04-15/create_event.json",
|
||||
OpsGenieAPIHost: "https://api.opsgenie.com/",
|
||||
}
|
||||
|
||||
// GlobalConfig defines configuration parameters that are valid globally
|
||||
|
@ -180,6 +192,7 @@ type GlobalConfig struct {
|
|||
SMTPSmarthost string `yaml:"smtp_smarthost"`
|
||||
SlackURL string `yaml:"slack_url"`
|
||||
PagerdutyURL string `yaml:"pagerduty_url"`
|
||||
OpsGenieAPIHost string `yaml:"opsgenie_api_host"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
|
@ -309,6 +322,7 @@ type Receiver struct {
|
|||
PushoverConfigs []*PushoverConfig `yaml:"pushover_configs"`
|
||||
SlackConfigs []*SlackConfig `yaml:"slack_configs"`
|
||||
WebhookConfigs []*WebhookConfig `yaml:"webhook_configs"`
|
||||
OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
|
|
|
@ -47,6 +47,12 @@ var (
|
|||
Text: `{{template "slack.default.text" . }}`,
|
||||
Fallback: `{{template "slack.default.fallback" . }}`,
|
||||
}
|
||||
|
||||
// DefaultOpsGenieConfig defines default values for OpsGenie configurations.
|
||||
DefaultOpsGenieConfig = OpsGenieConfig{
|
||||
Description: `{{template "opsgenie.default.description" .}}`,
|
||||
// TODO: Add a details field with all the alerts.
|
||||
}
|
||||
)
|
||||
|
||||
// FlowdockConfig configures notifications via Flowdock.
|
||||
|
@ -280,3 +286,27 @@ func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
}
|
||||
return checkOverflow(c.XXX, "slack config")
|
||||
}
|
||||
|
||||
// OpsGenieConfig configures notifications via OpsGenie.
|
||||
type OpsGenieConfig struct {
|
||||
APIKey string `yaml:"api_key"`
|
||||
APIHost string `yaml:"api_host"`
|
||||
Description string `yaml:"description"`
|
||||
Details map[string]string `yaml:"details"`
|
||||
|
||||
// Catches all undefined fields and must be empty after parsing.
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultOpsGenieConfig
|
||||
type plain OpsGenieConfig
|
||||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.APIKey == "" {
|
||||
return fmt.Errorf("missing service key in OpsGenie config")
|
||||
}
|
||||
return checkOverflow(c.XXX, "opsgenie config")
|
||||
}
|
||||
|
|
|
@ -58,6 +58,9 @@ func Build(confs []*config.Receiver, tmpl *template.Template) map[string]Fanout
|
|||
for i, c := range nc.PagerdutyConfigs {
|
||||
add(i, NewPagerDuty(c, tmpl))
|
||||
}
|
||||
for i, c := range nc.OpsGenieConfigs {
|
||||
add(i, NewOpsGenie(c, tmpl))
|
||||
}
|
||||
|
||||
res[nc.Name] = fo
|
||||
}
|
||||
|
@ -516,3 +519,93 @@ func (n *Slack) Notify(ctx context.Context, as ...*types.Alert) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpsGenie implements a Notifier for OpsGenie notifications.
|
||||
type OpsGenie struct {
|
||||
conf *config.OpsGenieConfig
|
||||
tmpl *template.Template
|
||||
}
|
||||
|
||||
// NewOpsGenieDuty returns a new OpsGenie notifier.
|
||||
func NewOpsGenie(c *config.OpsGenieConfig, t *template.Template) *OpsGenie {
|
||||
return &OpsGenie{conf: c, tmpl: t}
|
||||
}
|
||||
|
||||
type opsGenieMessage struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Alias model.Fingerprint `json:"alias"`
|
||||
}
|
||||
|
||||
type opsGenieCreateMessage struct {
|
||||
*opsGenieMessage `json:,inline`
|
||||
Message string `json:"message"`
|
||||
Details map[string]string `json:"details"`
|
||||
}
|
||||
|
||||
type opsGenieCloseMessage struct {
|
||||
*opsGenieMessage `json:,inline`
|
||||
}
|
||||
|
||||
// Notify implements the Notifier interface.
|
||||
func (n *OpsGenie) Notify(ctx context.Context, as ...*types.Alert) error {
|
||||
data := generateTemplateData(ctx, as...)
|
||||
|
||||
key, ok := GroupKey(ctx)
|
||||
if !ok {
|
||||
return fmt.Errorf("group key missing")
|
||||
}
|
||||
|
||||
log.With("incident", key).Debugln("notifying OpsGenie")
|
||||
|
||||
var err error
|
||||
tmpl := func(name string) (s string) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s, err = n.tmpl.ExecuteTextString(name, data)
|
||||
return s
|
||||
}
|
||||
details := make(map[string]string, len(n.conf.Details))
|
||||
for k, v := range n.conf.Details {
|
||||
details[k] = tmpl(v)
|
||||
}
|
||||
|
||||
var (
|
||||
msg interface{}
|
||||
apiURL string
|
||||
)
|
||||
|
||||
apiMsg := opsGenieMessage{
|
||||
APIKey: n.conf.APIKey,
|
||||
Alias: key,
|
||||
}
|
||||
alerts := types.Alerts(as...)
|
||||
switch alerts.Status() {
|
||||
case model.AlertResolved:
|
||||
apiURL = n.conf.APIHost + "v1/json/alert/close"
|
||||
msg = &opsGenieCloseMessage{&apiMsg}
|
||||
default:
|
||||
apiURL = n.conf.APIHost + "v1/json/alert"
|
||||
msg = &opsGenieCreateMessage{
|
||||
opsGenieMessage: &apiMsg,
|
||||
Message: tmpl(n.conf.Description),
|
||||
Details: details,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return fmt.Errorf("unexpected status code %v", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
{{ define "email.default.subject" }}{{ template "__subject" . }}{{ end }}
|
||||
|
||||
{{ define "opsgenie.default.description" }}{{ template "__subject" . }}{{ end }}
|
||||
|
||||
{{ define "email.default.html" }}
|
||||
{{/*
|
||||
|
||||
|
|
Loading…
Reference in New Issue