matrix-alertmanager-receiver/main.go
2020-05-04 08:58:49 +02:00

135 lines
3.7 KiB
Go

package main
import (
"os"
"fmt"
"log"
"flag"
"net/http"
"encoding/json"
"github.com/BurntSushi/toml"
"github.com/matrix-org/gomatrix"
"github.com/prometheus/alertmanager/template"
)
var logger *log.Logger
var config Configuration
type Configuration struct {
Homeserver string
TargetRoomID string
MXID string
MXToken string
HTTPPort int
HTTPAddress string
}
func getMatrixMessageFor(alert template.Alert) gomatrix.HTMLMessage {
var prefix string
switch alert.Status {
case "firing":
prefix = "<strong><font color=\"#ff0000\">FIRING</font></strong> "
case "resolved":
prefix = "<strong><font color=\"#33cc33\">RESOLVED</font></strong> "
default:
prefix = fmt.Sprintf("<strong>%v</strong> ", alert.Status)
}
return gomatrix.GetHTMLMessage("m.text", prefix + alert.Labels["name"] + " >> " + alert.Annotations["summary"])
}
func getMatrixClient(homeserver string, user string, token string, targetRoomID string) *gomatrix.Client {
logger.Printf("Connecting to Matrix Homserver %v as %v.", homeserver, user)
matrixClient, err := gomatrix.NewClient(homeserver, user, token)
if err != nil {
logger.Fatalf("Could not log in to Matrix Homeserver (%v): %v", homeserver, err)
}
joinedRooms, err := matrixClient.JoinedRooms()
if err != nil {
logger.Fatalf("Could not fetch Matrix rooms: %v", err)
}
alreadyJoinedTarget := false
for _, roomID := range joinedRooms.JoinedRooms {
if targetRoomID == roomID {
alreadyJoinedTarget = true
}
}
if alreadyJoinedTarget {
logger.Printf("%v is already part of %v.", user, targetRoomID,)
} else {
logger.Printf("Joining %v.", targetRoomID)
_, err := matrixClient.JoinRoom(targetRoomID, "", nil)
if err != nil {
logger.Fatalf("Failed to join %v: %v", targetRoomID, err)
}
}
return matrixClient
}
func handleIncomingHooks( w http.ResponseWriter, r *http.Request,
matrixClient *gomatrix.Client, targetRoomID string) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
payload := template.Data{}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
w.WriteHeader(http.StatusBadRequest)
}
logger.Printf("Received valid hook from %v", r.RemoteAddr)
for _, alert := range payload.Alerts {
msg := getMatrixMessageFor(alert)
logger.Printf("> %v", msg.Body)
_, err := matrixClient.SendMessageEvent(targetRoomID, "m.room.message", msg)
if err != nil {
logger.Printf(">> Could not forward to Matrix: %v", err)
}
}
w.WriteHeader(http.StatusOK)
}
func main() {
// Initialize logger.
logger = log.New(os.Stdout, "", log.Flags())
// We use a configuration file since we need to specify secrets, and read
// everything else from it to keep things simple.
var configPath = flag.String("config", "/etc/matrix-alertmanager-receiver.toml", "Path to configuration file")
flag.Parse()
logger.Printf("Reading configuration from %v.", *configPath)
md, err := toml.DecodeFile(*configPath, &config)
if err != nil {
logger.Fatalf("Could not parse configuration file (%v): %v", *configPath, err)
}
for _, field := range []string{"Homeserver", "MXID", "MXToken", "TargetRoomID", "HTTPPort"} {
if ! md.IsDefined(field) {
logger.Fatalf("Field %v is not set in config. Exiting.", field)
}
}
// Initialize Matrix client.
matrixClient := getMatrixClient(
config.Homeserver, config.MXID, config.MXToken, config.TargetRoomID)
// Initialize HTTP server.
http.HandleFunc("/alert", func(w http.ResponseWriter, r *http.Request) {
handleIncomingHooks(w, r, matrixClient, config.TargetRoomID)
})
var listenAddr = fmt.Sprintf("%v:%v", config.HTTPAddress, config.HTTPPort)
logger.Printf("Listening for HTTP requests (webhooks) on %v", listenAddr)
logger.Fatal(http.ListenAndServe(listenAddr, nil))
}