Refactor, read configuration from file
This commit is contained in:
parent
a89b9ff694
commit
560f0db15b
3 changed files with 102 additions and 67 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
matrix-alertmanager-receiver
|
||||
config.toml
|
||||
|
|
5
config.toml.sample
Normal file
5
config.toml.sample
Normal 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
163
main.go
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue