Refactor, read configuration from file

This commit is contained in:
Timothée Floure 2020-05-03 09:57:05 +02:00
parent a89b9ff694
commit 560f0db15b
3 changed files with 102 additions and 67 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
matrix-alertmanager-receiver
config.toml

5
config.toml.sample Normal file
View file

@ -0,0 +1,5 @@
Homeserver = "https://staging.matrix.ungleich.cloud"
TargetRoomID = "!jHFKHemgIAaDJekoxN:matrix-staging.ungleich.ch"
MXID = "@fnux:matrix-staging.ungleich.ch"
MXToken = "secret"
HTTPPort = 9088

163
main.go
View file

@ -5,98 +5,127 @@ import (
"flag"
"fmt"
"log"
"io/ioutil"
"net/http"
"encoding/json"
"github.com/prometheus/alertmanager/template"
"github.com/BurntSushi/toml"
"github.com/matrix-org/gomatrix"
)
var logger *log.Logger
type Configuration struct {
Homeserver string
TargetRoomID string
MXID string
MXToken string
HTTPPort int
HTTPToken string
}
func generateMatrixMessageBody(alert template.Alert) string {
return alert.Status + " // " + alert.Annotations["summary"]
}
func main() {
// Initialize logger.
var logger *log.Logger = log.New(os.Stdout, "", log.Flags())
// Handle command-line arguments.
var homeserver = flag.String("homeserver", "https://matrix.org", "Address of Matrix homeserver")
var user = flag.String("user", "", "Full MXID (e.g. @example.domain.tld) of Matrix user")
var token = flag.String("token", "", "Access Token of Matrix user")
var target = flag.String("target-room", "", "Matrix room to be notified of alerts.")
var port = flag.Int("port", 9088, "HTTP port to listen on (incoming alertmanager webhooks)")
flag.Parse()
if *user == "" {
logger.Fatal("Matrix user is required. See --help for usage.")
}
if *token == "" {
logger.Fatal("Matrix access token is required. See --help for usage.")
}
if *target== "" {
logger.Fatal("Matrix target room is required. See --help for usage.")
}
// Initialize Matrix client.
matrixClient, err := gomatrix.NewClient(*homeserver, *user, *token)
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 (%v): %v", *homeserver, err)
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 joined rooms: %v", err)
logger.Fatalf("Could not fetch Matrix rooms: %v", err)
}
alreadyJoinedTarget := false
for _, roomID := range joinedRooms.JoinedRooms {
// FIXME: will only work if target is a roomID, not an alias.
if *target == roomID {
if targetRoomID == roomID {
alreadyJoinedTarget = true
}
}
if !alreadyJoinedTarget {
logger.Printf("Trying to join %v...", *target)
_, err := matrixClient.JoinRoom(*target, "", nil)
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", *target, err)
logger.Fatalf("Failed to join %v: %v", targetRoomID, err)
}
}
// Initialize HTTP serve (= listen for incoming requests).
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `Hi! I receive prometheus-alertmanager webhooks on /alert and forward them to Matrix.
You will find more details on: http://git.sr.ht/~fnux/matrix-prometheus-alertmanager`)
})
http.HandleFunc("/alert", func(w http.ResponseWriter, r *http.Request) {
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 {
body := generateMatrixMessageBody(alert)
logger.Printf("> %v", body)
_, err := matrixClient.SendText(*target, body)
if err != nil {
logger.Fatalf("Could not forward to Matrix: %v", err)
}
}
w.WriteHeader(http.StatusOK)
})
var listenAddr = fmt.Sprintf(":%v", *port)
logger.Printf("Listening for HTTP requests (webhooks) on %v", listenAddr)
log.Fatal(http.ListenAndServe(listenAddr, nil))
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 {
body := generateMatrixMessageBody(alert)
logger.Printf("> %v", body)
_, err := matrixClient.SendText(targetRoomID, body)
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)
raw, err := ioutil.ReadFile(*configPath)
if err != nil {
logger.Fatalf("Could not read configuration file (%v): %v", *configPath, err)
}
var config Configuration
md, err := toml.Decode(string(raw), &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", config.HTTPPort)
logger.Printf("Listening for HTTP requests (webhooks) on %v", listenAddr)
logger.Fatal(http.ListenAndServe(listenAddr, nil))
}