commit 3e30ee275f8d050d6deb3b1866ebfefe52d3ac8d
parent edced6e71a9ca83fd275e2b6dd0af64cf146cd5f
Author: Thomas Maier <contact@thomas-maier.net>
Date: Tue, 26 Sep 2017 14:39:19 +0200
refactoring
Diffstat:
D | grafana.go | | | 50 | -------------------------------------------------- |
A | grafana.json | | | 17 | +++++++++++++++++ |
A | handler.go | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | main.go | | | 112 | ++++++++++++++++++++++++++++--------------------------------------------------- |
4 files changed, 120 insertions(+), 122 deletions(-)
diff --git a/grafana.go b/grafana.go
@@ -1,50 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net/http"
-)
-
-/*
-{
- "title": "My alert",
- "ruleId": 1,
- "ruleName": "Load peaking!",
- "ruleUrl": "http://url.to.grafana/db/dashboard/my_dashboard?panelId=2",
- "state": "alerting",
- "imageUrl": "http://s3.image.url",
- "message": "Load is peaking. Make sure the traffic is real and spin up more webfronts",
- "evalMatches": [
- {
- "metric": "requests",
- "tags": {},
- "value": 122
- }
- ]
-}
-*/
-
-type GrafanaAlert struct {
- Title string `json:"title"`
- RuleName string `json:"ruleName"`
- RuleUrl string `json:"ruleUrl"`
- State string `json:"state"`
- Message string `json:"message"`
-}
-
-func makeGrafanaHandler(messages chan<- string) func(http.ResponseWriter, *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- body, err := ioutil.ReadAll(r.Body)
- fmt.Printf("%v", string(body))
- if err == nil {
- var alert GrafanaAlert
- err = json.Unmarshal(body, &alert)
- if err == nil {
- message := alert.State + ": " + alert.Title + "/" + alert.Message + "(" + alert.RuleUrl + ")"
- messages <- message
- }
- }
- }
-}
diff --git a/grafana.json b/grafana.json
@@ -0,0 +1,17 @@
+{
+ "title": "My alert",
+ "ruleId": 1,
+ "ruleName": "Load peaking!",
+ "ruleUrl": "http://url.to.grafana/db/dashboard/my_dashboard?panelId=2",
+ "state": "alerting",
+ "imageUrl": "http://s3.image.url",
+ "message": "Load is peaking. Make sure the traffic is real and spin up more webfronts",
+ "evalMatches": [
+ {
+ "metric": "requests",
+ "tags": {},
+ "value": 122
+ }
+ ]
+}
+
diff --git a/handler.go b/handler.go
@@ -0,0 +1,63 @@
+package main
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "net/http"
+)
+
+// interface for parser functions (grafana, prometheus, ...)
+type parserFunc func(*http.Request) (string, error)
+
+type messageHandler struct {
+ messages chan<- string // chan to xmpp client
+ parserFunc parserFunc
+}
+
+// http request handler
+func (h *messageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // parse/generate message from http request
+ m, err := h.parserFunc(r)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+ // send message to xmpp client
+ h.messages <- m
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// returns new handler with a given parser function
+func newMessageHandler(m chan<- string, f parserFunc) *messageHandler {
+ return &messageHandler{
+ messages: m,
+ parserFunc: f,
+ }
+}
+
+/*************
+GRAFANA PARSER
+*************/
+func grafanaParserFunc(r *http.Request) (string, error) {
+ // get alert data from request
+ body, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ return "", err
+ }
+
+ // grafana alert struct
+ alert := &struct {
+ Title string `json:"title"`
+ RuleURL string `json:"ruleUrl"`
+ State string `json:"state"`
+ Message string `json:"message"`
+ }{}
+
+ // parse body into the alert struct
+ err = json.Unmarshal(body, &alert)
+ if err != nil {
+ return "", err
+ }
+
+ // contruct and return alert message
+ return alert.State + ": " + alert.Title + "/" + alert.Message + "(" + alert.RuleURL + ")", nil
+}
diff --git a/main.go b/main.go
@@ -5,23 +5,10 @@ import (
"net/http"
"os"
"strings"
- "time"
"github.com/emgee/go-xmpp/src/xmpp"
)
-const (
- envXMPPID = "XMPP_ID"
- envXMPPPASS = "XMPP_PASS"
- envXMPPReceivers = "XMPP_RECEIVERS"
- errWrongArgs = "XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set"
- xmppBotAnswer = "im a dumb bot"
- xmppConnErr = "failed to connect "
- xmppOfflineErr = "not connected to XMPP server, dropped message"
- xmppFailedPause = 30
- webHookAddr = ":4321"
-)
-
// starts xmpp session and returns the xmpp client
func xmppLogin(id string, pass string) (*xmpp.XMPP, error) {
// parse jid structure
@@ -51,84 +38,65 @@ func xmppLogin(id string, pass string) (*xmpp.XMPP, error) {
return client, nil
}
-// creates MessageBody slice suitable for xmpp.Message
-func xmppBodyCreate(message string) []xmpp.MessageBody {
- return []xmpp.MessageBody{
- xmpp.MessageBody{
- Value: message,
- },
- }
-}
-
-// handles incoming stanzas
-func handleXMPPStanza(in <-chan interface{}, out chan<- interface{}) {
- for stanza := range in {
- // check if stanza is a message
- message, ok := stanza.(*xmpp.Message)
- if ok && len(message.Body) > 0 {
- // send constant as answer
- out <- xmpp.Message{
- To: message.From,
- Body: xmppBodyCreate(xmppBotAnswer),
- }
- }
- }
- // func returns when in chan is closed (server terminated stream)
-}
-
func main() {
- // get xmpp credentials from ENV
- xi := os.Getenv(envXMPPID)
- xp := os.Getenv(envXMPPPASS)
- xr := os.Getenv(envXMPPReceivers)
+ // get xmpp credentials and message receivers from env
+ xi := os.Getenv("XMPP_ID")
+ xp := os.Getenv("XMPP_PASS")
+ xr := os.Getenv("XMPP_RECEIVERS")
// check if xmpp credentials and receiver list are supplied
if len(xi) < 1 || len(xp) < 1 || len(xr) < 1 {
- log.Fatal(errWrongArgs)
+ log.Fatal("XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set")
}
- // connect xmpp client and observe connection - reconnect if needed
- var xc *xmpp.XMPP
+ // connect to xmpp server
+ xc, err := xmppLogin(xi, xp)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // announce initial presence
+ xc.Out <- xmpp.Presence{}
+
+ // listen for incoming xmpp stanzas
go func() {
- for {
- // try to connect to xmpp server
- var err error
- xc, err = xmppLogin(xi, xp)
- if err != nil {
- // report failure and wait
- log.Print(xmppConnErr, err)
- time.Sleep(time.Second * time.Duration(xmppFailedPause))
- } else {
- // send initial presence and dispatch channels to handler for incoming messages
- xc.Out <- xmpp.Presence{}
- handleXMPPStanza(xc.In, xc.Out)
+ for stanza := range xc.In {
+ // check if stanza is a message
+ m, ok := stanza.(*xmpp.Message)
+ if ok && len(m.Body) > 0 {
+ // echo the message
+ xc.Out <- xmpp.Message{
+ To: m.From,
+ Body: m.Body,
+ }
}
}
+ // xc.In is closed when the server closes the stream
+ log.Fatal("connection lost")
}()
- // create channel for alerts (Webhook -> XMPP)
- alertChan := make(chan string)
+ // create chan for messages (webhooks -> xmpp)
+ messages := make(chan string)
- // create handler for outgoing XMPP messages
+ // wait for messages from the webhooks and send them to all receivers
go func() {
- for message := range alertChan {
+ for m := range messages {
for _, r := range strings.Split(xr, ",") {
- if xc != nil {
- xc.Out <- xmpp.Message{
- To: r,
- Body: xmppBodyCreate(message),
- }
- } else {
- log.Print(xmppOfflineErr)
+ xc.Out <- xmpp.Message{
+ To: r,
+ Body: []xmpp.MessageBody{
+ xmpp.MessageBody{
+ Value: m,
+ },
+ },
}
}
}
}()
- // initialize HTTP handlers with chan for alerts
- grafanaHandler := makeGrafanaHandler(alertChan)
- http.HandleFunc("/grafana", grafanaHandler)
+ // initialize handler for grafana alerts
+ http.Handle("/grafana", newMessageHandler(messages, grafanaParserFunc))
// listen for requests
- http.ListenAndServe(webHookAddr, nil)
+ http.ListenAndServe(":4321", nil)
}