mirror of https://github.com/matrix-org/go-neb.git
Jan Christian Grünhage
6 years ago
3 changed files with 191 additions and 0 deletions
-
3README.md
-
1src/github.com/matrix-org/go-neb/goneb.go
-
187src/github.com/matrix-org/go-neb/services/alertmanager/alertmanager.go
@ -0,0 +1,187 @@ |
|||
// Package alertmanager implements a Service capable of processing webhooks from prometheus alertmanager.
|
|||
package alertmanager |
|||
|
|||
import ( |
|||
"bytes" |
|||
"encoding/json" |
|||
"fmt" |
|||
log "github.com/Sirupsen/logrus" |
|||
"github.com/matrix-org/go-neb/database" |
|||
"github.com/matrix-org/go-neb/types" |
|||
"github.com/matrix-org/gomatrix" |
|||
html "html/template" |
|||
"net/http" |
|||
text "text/template" |
|||
) |
|||
|
|||
// ServiceType of the Alertmanager service.
|
|||
const ServiceType = "alertmanager" |
|||
|
|||
// Service contains the Config fields for the Alertmanager service.
|
|||
//
|
|||
// This service will send notifications into a Matrix room when Alertmanager sends
|
|||
// webhook events to it. It requires a public domain which Alertmanager can reach.
|
|||
// Notices will be sent as the service user ID.
|
|||
//
|
|||
// For the template strings, take a look at https://golang.org/pkg/text/template/
|
|||
// and the html variant https://golang.org/pkg/html/template/.
|
|||
// The data they get is a webhookNotification
|
|||
//
|
|||
// You can set msg_type to either m.text or m.notice
|
|||
//
|
|||
// Example JSON request:
|
|||
// {
|
|||
// rooms: {
|
|||
// "!ewfug483gsfe:localhost": {
|
|||
// "text_template": "your plain text template goes here",
|
|||
// "html_template": "your html template goes here",
|
|||
// "msg_type": "m.text"
|
|||
// },
|
|||
// }
|
|||
// }
|
|||
type Service struct { |
|||
types.DefaultService |
|||
webhookEndpointURL string |
|||
// The URL which should be added to alertmanagers config - Populated by Go-NEB after Service registration.
|
|||
WebhookURL string `json:"webhook_url"` |
|||
// A map of matrix rooms to templates
|
|||
Rooms map[string]struct { |
|||
TextTemplate string `json:"text_template"` |
|||
HTMLTemplate string `json:"html_template"` |
|||
MsgType string `json:"msg_type"` |
|||
} `json:"rooms"` |
|||
} |
|||
|
|||
// The payload from Alertmanager
|
|||
type WebhookNotification struct { |
|||
Version string `json:"version"` |
|||
GroupKey string `json:"groupKey"` |
|||
Status string `json:"status"` |
|||
Receiver string `json:"receiver"` |
|||
GroupLabels map[string]string `json:"groupLabels"` |
|||
CommonLabels map[string]string `json:"commonLabels"` |
|||
CommonAnnotations map[string]string `json:"commonAnnotations"` |
|||
ExternalUrl string `json:"externalURL"` |
|||
Alerts []struct { |
|||
Status string `json:"status"` |
|||
Labels map[string]string `json:"labels"` |
|||
Annotations map[string]string `json:"annotations"` |
|||
StartsAt string `json:"startsAt"` |
|||
EndsAt string `json:"endsAt"` |
|||
GeneratorUrl string `json:"generatorURL"` |
|||
} `json:"alerts"` |
|||
} |
|||
|
|||
// OnReceiveWebhook receives requests from Alertmanager and sends requests to Matrix as a result.
|
|||
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) { |
|||
decoder := json.NewDecoder(req.Body) |
|||
var notif WebhookNotification |
|||
if err := decoder.Decode(¬if); err != nil { |
|||
log.WithError(err).Error("Alertmanager webhook received an invalid JSON payload") |
|||
w.WriteHeader(400) |
|||
return |
|||
} |
|||
|
|||
for roomID, templates := range s.Rooms { |
|||
var msg interface{} |
|||
// we don't check whether the templates parse because we already did when storing them in the db
|
|||
textTemplate, _ := text.New("textTemplate").Parse(templates.TextTemplate) |
|||
var bodyBuffer bytes.Buffer |
|||
textTemplate.Execute(&bodyBuffer, notif) |
|||
if templates.HTMLTemplate != "" { |
|||
// we don't check whether the templates parse because we already did when storing them in the db
|
|||
htmlTemplate, _ := html.New("htmlTemplate").Parse(templates.HTMLTemplate) |
|||
var formattedBodyBuffer bytes.Buffer |
|||
htmlTemplate.Execute(&formattedBodyBuffer, notif) |
|||
msg = gomatrix.HTMLMessage{ |
|||
Body: bodyBuffer.String(), |
|||
MsgType: templates.MsgType, |
|||
Format: "org.matrix.custom.html", |
|||
FormattedBody: formattedBodyBuffer.String(), |
|||
} |
|||
} else { |
|||
msg = gomatrix.TextMessage{ |
|||
Body: bodyBuffer.String(), |
|||
MsgType: templates.MsgType, |
|||
} |
|||
} |
|||
|
|||
log.WithFields(log.Fields{ |
|||
"message": msg, |
|||
"room_id": roomID, |
|||
}).Print("Sending Alertmanager notification to room") |
|||
if _, e := cli.SendMessageEvent(roomID, "m.room.message", msg); e != nil { |
|||
log.WithError(e).WithField("room_id", roomID).Print( |
|||
"Failed to send Alertmanager notification to room.") |
|||
} |
|||
} |
|||
w.WriteHeader(200) |
|||
} |
|||
|
|||
// Register makes sure the Config information supplied is valid.
|
|||
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error { |
|||
s.WebhookURL = s.webhookEndpointURL |
|||
for _, templates := range s.Rooms { |
|||
// validate that we have at least a plain text template
|
|||
if templates.TextTemplate == "" { |
|||
return fmt.Errorf("plain text template missing") |
|||
} else { |
|||
// validate the plain text template is valid
|
|||
_, err := text.New("textTemplate").Parse(templates.TextTemplate) |
|||
if err != nil { |
|||
return fmt.Errorf("plain text template is invalid") |
|||
} |
|||
} |
|||
if templates.HTMLTemplate != "" { |
|||
// validate that the html template is valid
|
|||
_, err := html.New("htmlTemplate").Parse(templates.HTMLTemplate) |
|||
if err != nil { |
|||
return fmt.Errorf("html template is invalid") |
|||
} |
|||
} |
|||
// validate that the msgtype is either m.notice or m.text
|
|||
if templates.MsgType != "m.notice" && templates.MsgType != "m.text" { |
|||
return fmt.Errorf("msg_type is neither 'm.notice' nor 'm.text'") |
|||
} |
|||
} |
|||
s.joinRooms(client) |
|||
return nil |
|||
} |
|||
|
|||
// PostRegister deletes this service if there are no registered repos.
|
|||
func (s *Service) PostRegister(oldService types.Service) { |
|||
// At least one room still active
|
|||
if len(s.Rooms) > 0 { |
|||
return |
|||
} |
|||
// Delete this service since no repos are configured
|
|||
logger := log.WithFields(log.Fields{ |
|||
"service_type": s.ServiceType(), |
|||
"service_id": s.ServiceID(), |
|||
}) |
|||
logger.Info("Removing service as no repositories are registered.") |
|||
if err := database.GetServiceDB().DeleteService(s.ServiceID()); err != nil { |
|||
logger.WithError(err).Error("Failed to delete service") |
|||
} |
|||
} |
|||
|
|||
func (s *Service) joinRooms(client *gomatrix.Client) { |
|||
for roomID := range s.Rooms { |
|||
if _, err := client.JoinRoom(roomID, "", nil); err != nil { |
|||
log.WithFields(log.Fields{ |
|||
log.ErrorKey: err, |
|||
"room_id": roomID, |
|||
"user_id": client.UserID, |
|||
}).Error("Failed to join room") |
|||
} |
|||
} |
|||
} |
|||
|
|||
func init() { |
|||
types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service { |
|||
return &Service{ |
|||
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType), |
|||
webhookEndpointURL: webhookEndpointURL, |
|||
} |
|||
}) |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue