Skip to content

Commit

Permalink
Merge pull request prometheus#1929 from simonpasquier/refactor-notify
Browse files Browse the repository at this point in the history
Split the notify package into sub packages
  • Loading branch information
stuartnelson3 authored Jun 19, 2019
2 parents 96d1b33 + 5f881d6 commit bef850a
Show file tree
Hide file tree
Showing 30 changed files with 2,756 additions and 2,197 deletions.
6 changes: 5 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ jobs:
username: user
password: pass
EOF
- run: make
- run:
command: make
environment:
# Run garbage collection more aggresively to avoid getting OOMed.
GOGC: "20"
- run:
command: |
curl -s -L https://github.com/protocolbuffers/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_64.zip > /tmp/protoc.zip
Expand Down
59 changes: 58 additions & 1 deletion cmd/alertmanager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ import (
"github.com/prometheus/alertmanager/inhibit"
"github.com/prometheus/alertmanager/nflog"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/notify/email"
"github.com/prometheus/alertmanager/notify/hipchat"
"github.com/prometheus/alertmanager/notify/opsgenie"
"github.com/prometheus/alertmanager/notify/pagerduty"
"github.com/prometheus/alertmanager/notify/pushover"
"github.com/prometheus/alertmanager/notify/slack"
"github.com/prometheus/alertmanager/notify/victorops"
"github.com/prometheus/alertmanager/notify/webhook"
"github.com/prometheus/alertmanager/notify/wechat"
"github.com/prometheus/alertmanager/provider/mem"
"github.com/prometheus/alertmanager/silence"
"github.com/prometheus/alertmanager/template"
Expand Down Expand Up @@ -92,6 +101,54 @@ func instrumentHandler(handlerName string, handler http.HandlerFunc) http.Handle

const defaultClusterAddr = "0.0.0.0:9094"

// buildReceiverIntegrations builds a list of integration notifiers off of a
// receiver config.
func buildReceiverIntegrations(nc *config.Receiver, tmpl *template.Template, logger log.Logger) ([]notify.Integration, error) {
var (
errs types.MultiError
integrations []notify.Integration
add = func(name string, i int, rs notify.ResolvedSender, f func(l log.Logger) (notify.Notifier, error)) {
n, err := f(log.With(logger, "integration", name))
if err != nil {
errs.Add(err)
return
}
integrations = append(integrations, notify.NewIntegration(n, rs, name, i))
}
)

for i, c := range nc.WebhookConfigs {
add("webhook", i, c, func(l log.Logger) (notify.Notifier, error) { return webhook.New(c, tmpl, l) })
}
for i, c := range nc.EmailConfigs {
add("email", i, c, func(l log.Logger) (notify.Notifier, error) { return email.New(c, tmpl, l), nil })
}
for i, c := range nc.PagerdutyConfigs {
add("pagerduty", i, c, func(l log.Logger) (notify.Notifier, error) { return pagerduty.New(c, tmpl, l) })
}
for i, c := range nc.OpsGenieConfigs {
add("opsgenie", i, c, func(l log.Logger) (notify.Notifier, error) { return opsgenie.New(c, tmpl, l) })
}
for i, c := range nc.WechatConfigs {
add("wechat", i, c, func(l log.Logger) (notify.Notifier, error) { return wechat.New(c, tmpl, l) })
}
for i, c := range nc.SlackConfigs {
add("slack", i, c, func(l log.Logger) (notify.Notifier, error) { return slack.New(c, tmpl, l) })
}
for i, c := range nc.HipchatConfigs {
add("hipchat", i, c, func(l log.Logger) (notify.Notifier, error) { return hipchat.New(c, tmpl, l) })
}
for i, c := range nc.VictorOpsConfigs {
add("victorops", i, c, func(l log.Logger) (notify.Notifier, error) { return victorops.New(c, tmpl, l) })
}
for i, c := range nc.PushoverConfigs {
add("pushover", i, c, func(l log.Logger) (notify.Notifier, error) { return pushover.New(c, tmpl, l) })
}
if errs.Len() > 0 {
return nil, &errs
}
return integrations, nil
}
func main() {
os.Exit(run())
}
Expand Down Expand Up @@ -307,7 +364,7 @@ func run() int {
// Build the map of receiver to integrations.
receivers := make(map[string][]notify.Integration, len(conf.Receivers))
for _, rcv := range conf.Receivers {
integrations, err := notify.BuildReceiverIntegrations(rcv, tmpl, logger)
integrations, err := buildReceiverIntegrations(rcv, tmpl, logger)
if err != nil {
return err
}
Expand Down
88 changes: 88 additions & 0 deletions cmd/alertmanager/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2019 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 main

import (
"testing"

commoncfg "github.com/prometheus/common/config"
"github.com/stretchr/testify/require"

"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify"
)

type sendResolved bool

func (s sendResolved) SendResolved() bool { return bool(s) }

func TestBuildReceiverIntegrations(t *testing.T) {
for _, tc := range []struct {
receiver *config.Receiver
err bool
exp []notify.Integration
}{
{
receiver: &config.Receiver{
Name: "foo",
WebhookConfigs: []*config.WebhookConfig{
&config.WebhookConfig{
HTTPConfig: &commoncfg.HTTPClientConfig{},
},
&config.WebhookConfig{
HTTPConfig: &commoncfg.HTTPClientConfig{},
NotifierConfig: config.NotifierConfig{
VSendResolved: true,
},
},
},
},
exp: []notify.Integration{
notify.NewIntegration(nil, sendResolved(false), "webhook", 0),
notify.NewIntegration(nil, sendResolved(true), "webhook", 1),
},
},
{
receiver: &config.Receiver{
Name: "foo",
WebhookConfigs: []*config.WebhookConfig{
&config.WebhookConfig{
HTTPConfig: &commoncfg.HTTPClientConfig{
TLSConfig: commoncfg.TLSConfig{
CAFile: "not_existing",
},
},
},
},
},
err: true,
},
} {
tc := tc
t.Run("", func(t *testing.T) {
integrations, err := buildReceiverIntegrations(tc.receiver, nil, nil)
if tc.err {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Len(t, integrations, len(tc.exp))
for i := range tc.exp {
require.Equal(t, tc.exp[i].SendResolved(), integrations[i].SendResolved())
require.Equal(t, tc.exp[i].Name(), integrations[i].Name())
require.Equal(t, tc.exp[i].Index(), integrations[i].Index())
}
})
}
}
11 changes: 6 additions & 5 deletions notify/email.go → notify/email/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package notify
package email

import (
"bytes"
Expand All @@ -34,6 +34,7 @@ import (
commoncfg "github.com/prometheus/common/config"

"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
)
Expand All @@ -45,8 +46,8 @@ type Email struct {
logger log.Logger
}

// NewEmail returns a new Email notifier.
func NewEmail(c *config.EmailConfig, t *template.Template, l log.Logger) *Email {
// New returns a new Email notifier.
func New(c *config.EmailConfig, t *template.Template, l log.Logger) *Email {
if _, ok := c.Headers["Subject"]; !ok {
c.Headers["Subject"] = config.DefaultEmailSubject
}
Expand Down Expand Up @@ -188,8 +189,8 @@ func (n *Email) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {

var (
tmplErr error
data = n.tmpl.Data(receiverName(ctx, n.logger), groupLabels(ctx, n.logger), as...)
tmpl = tmplText(n.tmpl, data, &tmplErr)
data = notify.GetTemplateData(ctx, n.tmpl, as, n.logger)
tmpl = notify.TmplText(n.tmpl, data, &tmplErr)
from = tmpl(n.conf.From)
to = tmpl(n.conf.To)
)
Expand Down
4 changes: 2 additions & 2 deletions notify/email_test.go → notify/email/email_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
// $ EMAIL_NO_AUTH_CONFIG=testdata/email_noauth.yml EMAIL_AUTH_CONFIG=testdata/email_auth.yml make
//
// See also https://github.com/djfarrelly/MailDev for more details.
package notify
package email

import (
"context"
Expand Down Expand Up @@ -184,7 +184,7 @@ func notifyEmail(cfg *config.EmailConfig, server *mailDev) (*email, bool, error)
return nil, false, err
}
tmpl.ExternalURL, _ = url.Parse("http://am")
email := NewEmail(cfg, tmpl, log.NewNopLogger())
email := New(cfg, tmpl, log.NewNopLogger())

ctx := context.Background()
retry, err := email.Notify(ctx, firingAlert)
Expand Down
118 changes: 118 additions & 0 deletions notify/hipchat/hipchat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2019 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 hipchat

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/go-kit/kit/log"
commoncfg "github.com/prometheus/common/config"

"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
)

// Notifier implements a Notifier for Hipchat notifications.
type Notifier struct {
conf *config.HipchatConfig
tmpl *template.Template
logger log.Logger
client *http.Client
}

// New returns a new Hipchat notification handler.
func New(c *config.HipchatConfig, t *template.Template, l log.Logger) (*Notifier, error) {
client, err := commoncfg.NewClientFromConfig(*c.HTTPConfig, "hipchat")
if err != nil {
return nil, err
}
return &Notifier{
conf: c,
tmpl: t,
logger: l,
client: client,
}, nil
}

type hipchatReq struct {
From string `json:"from"`
Notify bool `json:"notify"`
Message string `json:"message"`
MessageFormat string `json:"message_format"`
Color string `json:"color"`
}

// Notify implements the Notifier interface.
func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
var err error
var msg string
var (
data = notify.GetTemplateData(ctx, n.tmpl, as, n.logger)
tmplText = notify.TmplText(n.tmpl, data, &err)
tmplHTML = notify.TmplHTML(n.tmpl, data, &err)
roomid = tmplText(n.conf.RoomID)
apiURL = n.conf.APIURL.Copy()
)
apiURL.Path += fmt.Sprintf("v2/room/%s/notification", roomid)
q := apiURL.Query()
q.Set("auth_token", string(n.conf.AuthToken))
apiURL.RawQuery = q.Encode()

if n.conf.MessageFormat == "html" {
msg = tmplHTML(n.conf.Message)
} else {
msg = tmplText(n.conf.Message)
}

req := &hipchatReq{
From: tmplText(n.conf.From),
Notify: n.conf.Notify,
Message: msg,
MessageFormat: n.conf.MessageFormat,
Color: tmplText(n.conf.Color),
}
if err != nil {
return false, err
}

var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(req); err != nil {
return false, err
}

resp, err := notify.PostJSON(ctx, n.client, apiURL.String(), &buf)
if err != nil {
return true, notify.RedactURL(err)
}
defer notify.Drain(resp)

return n.retry(resp.StatusCode)
}

func (n *Notifier) retry(statusCode int) (bool, error) {
// Response codes 429 (rate limiting) and 5xx can potentially recover.
// 2xx response codes indicate successful requests.
// https://developer.atlassian.com/hipchat/guide/hipchat-rest-api/api-response-codes
if statusCode/100 != 2 {
return (statusCode == 429 || statusCode/100 == 5), fmt.Errorf("unexpected status code %v", statusCode)
}

return false, nil
}
Loading

0 comments on commit bef850a

Please sign in to comment.