mirror of
https://github.com/google/alertmanager-irc-relay.git
synced 2024-11-16 16:19:21 +01:00
Refactor message formatting functionality
This commit is contained in:
parent
b62dde73d4
commit
767cb9fb8e
73
format.go
Normal file
73
format.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"text/template"
|
||||
|
||||
promtmpl "github.com/prometheus/alertmanager/template"
|
||||
)
|
||||
|
||||
type Formatter struct {
|
||||
MsgTemplate *template.Template
|
||||
MsgOnce bool
|
||||
}
|
||||
|
||||
func NewFormatter(config *Config) (*Formatter, error) {
|
||||
tmpl, err := template.New("msg").Parse(config.MsgTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Formatter{
|
||||
MsgTemplate: tmpl,
|
||||
MsgOnce: config.MsgOnce,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *Formatter) FormatMsg(ircChannel string, data interface{}) string {
|
||||
output := bytes.Buffer{}
|
||||
var msg string
|
||||
if err := f.MsgTemplate.Execute(&output, data); err != nil {
|
||||
msg_bytes, _ := json.Marshal(data)
|
||||
msg = string(msg_bytes)
|
||||
log.Printf("Could not apply msg template on alert (%s): %s",
|
||||
err, msg)
|
||||
log.Printf("Sending raw alert")
|
||||
alertHandlingErrors.WithLabelValues(ircChannel, "format_msg").Inc()
|
||||
} else {
|
||||
msg = output.String()
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func (f *Formatter) GetMsgsFromAlertMessage(ircChannel string,
|
||||
data *promtmpl.Data) []AlertMsg {
|
||||
msgs := []AlertMsg{}
|
||||
if f.MsgOnce {
|
||||
msg := f.FormatMsg(ircChannel, data)
|
||||
msgs = append(msgs,
|
||||
AlertMsg{Channel: ircChannel, Alert: msg})
|
||||
} else {
|
||||
for _, alert := range data.Alerts {
|
||||
msg := f.FormatMsg(ircChannel, alert)
|
||||
msgs = append(msgs,
|
||||
AlertMsg{Channel: ircChannel, Alert: msg})
|
||||
}
|
||||
}
|
||||
return msgs
|
||||
}
|
84
format_test.go
Normal file
84
format_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
promtmpl "github.com/prometheus/alertmanager/template"
|
||||
)
|
||||
|
||||
func TestTemplateErrorsCreateRawAlertMsg(t *testing.T) {
|
||||
testingConfig := Config{MsgTemplate: "Bogus template {{ nil }}"}
|
||||
formatter, _ := NewFormatter(&testingConfig)
|
||||
|
||||
expectedAlertMsgs := []AlertMsg{
|
||||
AlertMsg{
|
||||
Channel: "#somechannel",
|
||||
Alert: `{"status":"resolved","labels":{"alertname":"airDown","instance":"instance1:3456","job":"air","service":"prometheus","severity":"ticket","zone":"global"},"annotations":{"DESCRIPTION":"service /prometheus has irc gateway down on instance1","SUMMARY":"service /prometheus air down on instance1"},"startsAt":"2017-05-15T13:49:37.834Z","endsAt":"2017-05-15T13:50:37.835Z","generatorURL":"https://prometheus.example.com/prometheus/...","fingerprint":"66214a361160fb6f"}`,
|
||||
},
|
||||
AlertMsg{
|
||||
Channel: "#somechannel",
|
||||
Alert: `{"status":"resolved","labels":{"alertname":"airDown","instance":"instance2:7890","job":"air","service":"prometheus","severity":"ticket","zone":"global"},"annotations":{"DESCRIPTION":"service /prometheus has irc gateway down on instance2","SUMMARY":"service /prometheus air down on instance2"},"startsAt":"2017-05-15T11:47:37.834Z","endsAt":"2017-05-15T11:48:37.834Z","generatorURL":"https://prometheus.example.com/prometheus/...","fingerprint":"25a874c99325d1ce"}`,
|
||||
},
|
||||
}
|
||||
|
||||
var alertMessage = promtmpl.Data{}
|
||||
if err := json.Unmarshal([]byte(testdataSimpleAlertJson), &alertMessage); err != nil {
|
||||
t.Fatal(fmt.Sprintf("Could not unmarshal %s", testdataSimpleAlertJson))
|
||||
}
|
||||
|
||||
alertMsgs := formatter.GetMsgsFromAlertMessage("#somechannel", &alertMessage)
|
||||
|
||||
if !reflect.DeepEqual(expectedAlertMsgs, alertMsgs) {
|
||||
t.Error(fmt.Sprintf(
|
||||
"Unexpected alert msg.\nExpected: %s\nActual: %s",
|
||||
expectedAlertMsgs, alertMsgs))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAlertsDispatchedOnce(t *testing.T) {
|
||||
testingConfig := Config{
|
||||
MsgTemplate: "Alert {{ .GroupLabels.alertname }} is {{ .Status }}",
|
||||
MsgOnce: true,
|
||||
}
|
||||
formatter, _ := NewFormatter(&testingConfig)
|
||||
|
||||
expectedAlertMsgs := []AlertMsg{
|
||||
AlertMsg{
|
||||
Channel: "#somechannel",
|
||||
Alert: "Alert airDown is resolved",
|
||||
},
|
||||
}
|
||||
|
||||
var alertMessage = promtmpl.Data{}
|
||||
if err := json.Unmarshal([]byte(testdataSimpleAlertJson), &alertMessage); err != nil {
|
||||
t.Fatal(fmt.Sprintf("Could not unmarshal %s", testdataSimpleAlertJson))
|
||||
}
|
||||
|
||||
alertMsgs := formatter.GetMsgsFromAlertMessage("#somechannel", &alertMessage)
|
||||
|
||||
if !reflect.DeepEqual(expectedAlertMsgs, alertMsgs) {
|
||||
t.Error(fmt.Sprintf(
|
||||
"Unexpected alert msg.\nExpected: %s\nActual: %s",
|
||||
expectedAlertMsgs, alertMsgs))
|
||||
|
||||
}
|
||||
}
|
45
http.go
45
http.go
@ -15,7 +15,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -23,7 +22,6 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
promtmpl "github.com/prometheus/alertmanager/template"
|
||||
@ -56,8 +54,7 @@ type HTTPServer struct {
|
||||
StoppedRunning chan bool
|
||||
Addr string
|
||||
Port int
|
||||
MsgTemplate *template.Template
|
||||
MsgOnce bool
|
||||
formatter *Formatter
|
||||
AlertMsgs chan AlertMsg
|
||||
httpListener HTTPListener
|
||||
}
|
||||
@ -69,7 +66,7 @@ func NewHTTPServer(config *Config, alertMsgs chan AlertMsg) (
|
||||
|
||||
func NewHTTPServerForTesting(config *Config, alertMsgs chan AlertMsg,
|
||||
httpListener HTTPListener) (*HTTPServer, error) {
|
||||
tmpl, err := template.New("msg").Parse(config.MsgTemplate)
|
||||
formatter, err := NewFormatter(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -77,8 +74,7 @@ func NewHTTPServerForTesting(config *Config, alertMsgs chan AlertMsg,
|
||||
StoppedRunning: make(chan bool),
|
||||
Addr: config.HTTPHost,
|
||||
Port: config.HTTPPort,
|
||||
MsgTemplate: tmpl,
|
||||
MsgOnce: config.MsgOnce,
|
||||
formatter: formatter,
|
||||
AlertMsgs: alertMsgs,
|
||||
httpListener: httpListener,
|
||||
}
|
||||
@ -86,39 +82,6 @@ func NewHTTPServerForTesting(config *Config, alertMsgs chan AlertMsg,
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func (server *HTTPServer) FormatMsg(ircChannel string, data interface{}) string {
|
||||
output := bytes.Buffer{}
|
||||
var msg string
|
||||
if err := server.MsgTemplate.Execute(&output, data); err != nil {
|
||||
msg_bytes, _ := json.Marshal(data)
|
||||
msg = string(msg_bytes)
|
||||
log.Printf("Could not apply msg template on alert (%s): %s",
|
||||
err, msg)
|
||||
log.Printf("Sending raw alert")
|
||||
alertHandlingErrors.WithLabelValues(ircChannel, "format_msg").Inc()
|
||||
} else {
|
||||
msg = output.String()
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func (server *HTTPServer) GetMsgsFromAlertMessage(ircChannel string,
|
||||
data *promtmpl.Data) []AlertMsg {
|
||||
msgs := []AlertMsg{}
|
||||
if server.MsgOnce {
|
||||
msg := server.FormatMsg(ircChannel, data)
|
||||
msgs = append(msgs,
|
||||
AlertMsg{Channel: ircChannel, Alert: msg})
|
||||
} else {
|
||||
for _, alert := range data.Alerts {
|
||||
msg := server.FormatMsg(ircChannel, alert)
|
||||
msgs = append(msgs,
|
||||
AlertMsg{Channel: ircChannel, Alert: msg})
|
||||
}
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
||||
func (server *HTTPServer) RelayAlert(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
ircChannel := "#" + vars["IRCChannel"]
|
||||
@ -143,7 +106,7 @@ func (server *HTTPServer) RelayAlert(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
handledAlertGroups.WithLabelValues(ircChannel).Inc()
|
||||
for _, alertMsg := range server.GetMsgsFromAlertMessage(
|
||||
for _, alertMsg := range server.formatter.GetMsgsFromAlertMessage(
|
||||
ircChannel, &alertMessage) {
|
||||
select {
|
||||
case server.AlertMsgs <- alertMsg:
|
||||
|
69
http_test.go
69
http_test.go
@ -116,39 +116,6 @@ func TestAlertsDispatched(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertsDispatchedOnce(t *testing.T) {
|
||||
listener := NewFakeHTTPListener()
|
||||
testingConfig := MakeHTTPTestingConfig()
|
||||
testingConfig.MsgOnce = true
|
||||
testingConfig.MsgTemplate = "Alert {{ .GroupLabels.alertname }} is {{ .Status }}"
|
||||
|
||||
expectedAlertMsgs := []AlertMsg{
|
||||
AlertMsg{
|
||||
Channel: "#somechannel",
|
||||
Alert: "Alert airDown is resolved",
|
||||
},
|
||||
}
|
||||
expectedStatusCode := 200
|
||||
|
||||
response := RunHTTPTest(
|
||||
t, testdataSimpleAlertJson, "/somechannel",
|
||||
testingConfig, listener)
|
||||
|
||||
if expectedStatusCode != response.StatusCode {
|
||||
t.Error(fmt.Sprintf("Expected %d status in response, got %d",
|
||||
expectedStatusCode, response.StatusCode))
|
||||
}
|
||||
|
||||
for _, expectedAlertMsg := range expectedAlertMsgs {
|
||||
alertMsg := <-listener.AlertMsgs
|
||||
if !reflect.DeepEqual(expectedAlertMsg, alertMsg) {
|
||||
t.Error(fmt.Sprintf(
|
||||
"Unexpected alert msg.\nExpected: %s\nActual: %s",
|
||||
expectedAlertMsg, alertMsg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootReturnsError(t *testing.T) {
|
||||
listener := NewFakeHTTPListener()
|
||||
testingConfig := MakeHTTPTestingConfig()
|
||||
@ -180,39 +147,3 @@ func TestInvalidDataReturnsError(t *testing.T) {
|
||||
expectedStatusCode, response.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateErrorsCreateRawAlertMsg(t *testing.T) {
|
||||
listener := NewFakeHTTPListener()
|
||||
testingConfig := MakeHTTPTestingConfig()
|
||||
testingConfig.MsgTemplate = "Bogus template {{ nil }}"
|
||||
|
||||
expectedAlertMsgs := []AlertMsg{
|
||||
AlertMsg{
|
||||
Channel: "#somechannel",
|
||||
Alert: `{"status":"resolved","labels":{"alertname":"airDown","instance":"instance1:3456","job":"air","service":"prometheus","severity":"ticket","zone":"global"},"annotations":{"DESCRIPTION":"service /prometheus has irc gateway down on instance1","SUMMARY":"service /prometheus air down on instance1"},"startsAt":"2017-05-15T13:49:37.834Z","endsAt":"2017-05-15T13:50:37.835Z","generatorURL":"https://prometheus.example.com/prometheus/...","fingerprint":"66214a361160fb6f"}`,
|
||||
},
|
||||
AlertMsg{
|
||||
Channel: "#somechannel",
|
||||
Alert: `{"status":"resolved","labels":{"alertname":"airDown","instance":"instance2:7890","job":"air","service":"prometheus","severity":"ticket","zone":"global"},"annotations":{"DESCRIPTION":"service /prometheus has irc gateway down on instance2","SUMMARY":"service /prometheus air down on instance2"},"startsAt":"2017-05-15T11:47:37.834Z","endsAt":"2017-05-15T11:48:37.834Z","generatorURL":"https://prometheus.example.com/prometheus/...","fingerprint":"25a874c99325d1ce"}`,
|
||||
},
|
||||
}
|
||||
expectedStatusCode := 200
|
||||
|
||||
response := RunHTTPTest(
|
||||
t, testdataSimpleAlertJson, "/somechannel",
|
||||
testingConfig, listener)
|
||||
|
||||
if expectedStatusCode != response.StatusCode {
|
||||
t.Error(fmt.Sprintf("Expected %d status in response, got %d",
|
||||
expectedStatusCode, response.StatusCode))
|
||||
}
|
||||
|
||||
for _, expectedAlertMsg := range expectedAlertMsgs {
|
||||
alertMsg := <-listener.AlertMsgs
|
||||
if !reflect.DeepEqual(expectedAlertMsg, alertMsg) {
|
||||
t.Error(fmt.Sprintf(
|
||||
"Unexpected alert msg.\nExpected: %s\nActual: %s",
|
||||
expectedAlertMsg, alertMsg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user