2017-11-11 14:45:11 +00:00
package notify
import (
"fmt"
"net/http"
"testing"
2018-02-06 12:45:59 +00:00
"time"
2017-11-11 14:45:11 +00:00
2018-02-06 12:45:59 +00:00
"github.com/go-kit/kit/log"
2017-11-11 14:45:11 +00:00
"github.com/stretchr/testify/require"
2018-02-06 12:45:59 +00:00
"golang.org/x/net/context"
2018-02-11 19:09:47 +00:00
"io/ioutil"
"net/url"
2018-02-06 12:45:59 +00:00
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/template"
2018-02-11 19:09:47 +00:00
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
2017-11-11 14:45:11 +00:00
)
func TestWebhookRetry ( t * testing . T ) {
notifier := new ( Webhook )
for statusCode , expected := range retryTests ( defaultRetryCodes ( ) ) {
actual , _ := notifier . retry ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "error on status %d" , statusCode ) )
}
}
func TestPagerDutyRetryV1 ( t * testing . T ) {
notifier := new ( PagerDuty )
retryCodes := append ( defaultRetryCodes ( ) , http . StatusForbidden )
for statusCode , expected := range retryTests ( retryCodes ) {
actual , _ := notifier . retryV1 ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "retryv1 - error on status %d" , statusCode ) )
}
}
func TestPagerDutyRetryV2 ( t * testing . T ) {
notifier := new ( PagerDuty )
retryCodes := append ( defaultRetryCodes ( ) , http . StatusTooManyRequests )
for statusCode , expected := range retryTests ( retryCodes ) {
actual , _ := notifier . retryV2 ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "retryv2 - error on status %d" , statusCode ) )
}
}
func TestSlackRetry ( t * testing . T ) {
notifier := new ( Slack )
for statusCode , expected := range retryTests ( defaultRetryCodes ( ) ) {
actual , _ := notifier . retry ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "error on status %d" , statusCode ) )
}
}
func TestHipchatRetry ( t * testing . T ) {
notifier := new ( Hipchat )
retryCodes := append ( defaultRetryCodes ( ) , http . StatusTooManyRequests )
for statusCode , expected := range retryTests ( retryCodes ) {
actual , _ := notifier . retry ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "error on status %d" , statusCode ) )
}
}
func TestOpsGenieRetry ( t * testing . T ) {
notifier := new ( OpsGenie )
retryCodes := append ( defaultRetryCodes ( ) , http . StatusTooManyRequests )
for statusCode , expected := range retryTests ( retryCodes ) {
actual , _ := notifier . retry ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "error on status %d" , statusCode ) )
}
}
func TestVictorOpsRetry ( t * testing . T ) {
notifier := new ( VictorOps )
for statusCode , expected := range retryTests ( defaultRetryCodes ( ) ) {
actual , _ := notifier . retry ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "error on status %d" , statusCode ) )
}
}
func TestPushoverRetry ( t * testing . T ) {
notifier := new ( Pushover )
for statusCode , expected := range retryTests ( defaultRetryCodes ( ) ) {
actual , _ := notifier . retry ( statusCode )
require . Equal ( t , expected , actual , fmt . Sprintf ( "error on status %d" , statusCode ) )
}
}
func retryTests ( retryCodes [ ] int ) map [ int ] bool {
tests := map [ int ] bool {
// 1xx
http . StatusContinue : false ,
http . StatusSwitchingProtocols : false ,
http . StatusProcessing : false ,
// 2xx
http . StatusOK : false ,
http . StatusCreated : false ,
http . StatusAccepted : false ,
http . StatusNonAuthoritativeInfo : false ,
http . StatusNoContent : false ,
http . StatusResetContent : false ,
http . StatusPartialContent : false ,
http . StatusMultiStatus : false ,
http . StatusAlreadyReported : false ,
http . StatusIMUsed : false ,
// 3xx
http . StatusMultipleChoices : false ,
http . StatusMovedPermanently : false ,
http . StatusFound : false ,
http . StatusSeeOther : false ,
http . StatusNotModified : false ,
http . StatusUseProxy : false ,
http . StatusTemporaryRedirect : false ,
http . StatusPermanentRedirect : false ,
// 4xx
http . StatusBadRequest : false ,
http . StatusUnauthorized : false ,
http . StatusPaymentRequired : false ,
http . StatusForbidden : false ,
http . StatusNotFound : false ,
http . StatusMethodNotAllowed : false ,
http . StatusNotAcceptable : false ,
http . StatusProxyAuthRequired : false ,
http . StatusRequestTimeout : false ,
http . StatusConflict : false ,
http . StatusGone : false ,
http . StatusLengthRequired : false ,
http . StatusPreconditionFailed : false ,
http . StatusRequestEntityTooLarge : false ,
http . StatusRequestURITooLong : false ,
http . StatusUnsupportedMediaType : false ,
http . StatusRequestedRangeNotSatisfiable : false ,
http . StatusExpectationFailed : false ,
http . StatusTeapot : false ,
http . StatusUnprocessableEntity : false ,
http . StatusLocked : false ,
http . StatusFailedDependency : false ,
http . StatusUpgradeRequired : false ,
http . StatusPreconditionRequired : false ,
http . StatusTooManyRequests : false ,
http . StatusRequestHeaderFieldsTooLarge : false ,
http . StatusUnavailableForLegalReasons : false ,
// 5xx
http . StatusInternalServerError : false ,
http . StatusNotImplemented : false ,
http . StatusBadGateway : false ,
http . StatusServiceUnavailable : false ,
http . StatusGatewayTimeout : false ,
http . StatusHTTPVersionNotSupported : false ,
http . StatusVariantAlsoNegotiates : false ,
http . StatusInsufficientStorage : false ,
http . StatusLoopDetected : false ,
http . StatusNotExtended : false ,
http . StatusNetworkAuthenticationRequired : false ,
}
for _ , statusCode := range retryCodes {
tests [ statusCode ] = true
}
return tests
}
func defaultRetryCodes ( ) [ ] int {
return [ ] int {
http . StatusInternalServerError ,
http . StatusNotImplemented ,
http . StatusBadGateway ,
http . StatusServiceUnavailable ,
http . StatusGatewayTimeout ,
http . StatusHTTPVersionNotSupported ,
http . StatusVariantAlsoNegotiates ,
http . StatusInsufficientStorage ,
http . StatusLoopDetected ,
http . StatusNotExtended ,
http . StatusNetworkAuthenticationRequired ,
}
}
2018-02-06 12:45:59 +00:00
func createTmpl ( t * testing . T ) * template . Template {
tmpl , err := template . FromGlobs ( )
require . NoError ( t , err )
tmpl . ExternalURL , _ = url . Parse ( "http://am" )
return tmpl
}
2018-02-11 19:09:47 +00:00
func readBody ( t * testing . T , r * http . Request ) string {
2018-02-06 12:45:59 +00:00
body , err := ioutil . ReadAll ( r . Body )
require . NoError ( t , err )
return string ( body )
}
func TestOpsGenie ( t * testing . T ) {
logger := log . NewNopLogger ( )
tmpl := createTmpl ( t )
conf := & config . OpsGenieConfig {
NotifierConfig : config . NotifierConfig {
VSendResolved : true ,
} ,
Message : ` {{ .CommonLabels .Message }} ` ,
Description : ` {{ .CommonLabels .Description }} ` ,
Source : ` {{ .CommonLabels .Source }} ` ,
2018-02-11 19:09:47 +00:00
Teams : ` {{ .CommonLabels .Teams }} ` ,
2018-02-06 12:45:59 +00:00
Tags : ` {{ .CommonLabels .Tags }} ` ,
Note : ` {{ .CommonLabels .Note }} ` ,
Priority : ` {{ .CommonLabels .Priority }} ` ,
APIKey : ` s3cr3t ` ,
APIURL : ` https://opsgenie/api ` ,
}
notifier := NewOpsGenie ( conf , tmpl , logger )
ctx := context . Background ( )
ctx = WithGroupKey ( ctx , "1" )
expectedUrl , _ := url . Parse ( "https://opsgenie/apiv2/alerts" )
// Empty alert.
2018-02-11 19:09:47 +00:00
alert1 := & types . Alert {
Alert : model . Alert {
StartsAt : time . Now ( ) ,
EndsAt : time . Now ( ) . Add ( time . Hour ) ,
} ,
2018-02-06 12:45:59 +00:00
}
expectedBody := ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "" , "details" : { } , "source" : "" }
`
req , retry , err := notifier . createRequest ( ctx , alert1 )
require . NoError ( t , err )
require . Equal ( t , true , retry )
require . Equal ( t , expectedUrl , req . URL )
require . Equal ( t , "GenieKey s3cr3t" , req . Header . Get ( "Authorization" ) )
require . Equal ( t , expectedBody , readBody ( t , req ) )
// Fully defined alert.
2018-02-11 19:09:47 +00:00
alert2 := & types . Alert {
2018-02-06 12:45:59 +00:00
Alert : model . Alert {
Labels : model . LabelSet {
2018-02-11 19:09:47 +00:00
"Message" : "message" ,
2018-02-06 12:45:59 +00:00
"Description" : "description" ,
2018-02-11 19:09:47 +00:00
"Source" : "http://prometheus" ,
"Teams" : "TeamA,TeamB," ,
"Tags" : "tag1,tag2" ,
"Note" : "this is a note" ,
"Priotity" : "P1" ,
} ,
2018-02-06 12:45:59 +00:00
StartsAt : time . Now ( ) ,
2018-02-11 19:09:47 +00:00
EndsAt : time . Now ( ) . Add ( time . Hour ) ,
2018-02-06 12:45:59 +00:00
} ,
}
expectedBody = ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "message" , "description" : "description" , "details" : { } , "source" : "http://prometheus" , "teams" : [ { "name" : "TeamA" } , { "name" : "TeamB" } ] , "tags" : [ "tag1" , "tag2" ] , "note" : "this is a note" }
`
req , retry , err = notifier . createRequest ( ctx , alert2 )
require . Equal ( t , expectedBody , readBody ( t , req ) )
2018-02-11 19:09:47 +00:00
}
func TestWechat ( t * testing . T ) {
logger := log . NewNopLogger ( )
tmpl := createTmpl ( t )
conf := & config . WechatConfig {
NotifierConfig : config . NotifierConfig {
VSendResolved : true ,
} ,
Message : ` {{ template "wechat.default.message" . }} ` ,
APIURL : config . DefaultGlobalConfig . WeChatAPIURL ,
APISecret : "invalidSecret" ,
CorpID : "invalidCorpID" ,
AgentID : "1" ,
ToUser : "admin" ,
}
notifier := NewWechat ( conf , tmpl , logger )
ctx := context . Background ( )
alert := & types . Alert {
Alert : model . Alert {
Labels : model . LabelSet {
"Message" : "message" ,
"Description" : "description" ,
"Source" : "http://prometheus" ,
"Teams" : "TeamA,TeamB," ,
"Tags" : "tag1,tag2" ,
"Note" : "this is a note" ,
"Priotity" : "P1" ,
} ,
StartsAt : time . Now ( ) ,
EndsAt : time . Now ( ) . Add ( time . Hour ) ,
} ,
}
// miss group key
retry , err := notifier . Notify ( ctx , alert )
require . False ( t , retry )
require . Error ( t , err )
ctx = WithGroupKey ( ctx , "2" )
// invalid secret
retry , err = notifier . Notify ( ctx , alert )
require . False ( t , retry )
require . Error ( t , err )
}