2019-06-18 13:34:46 +00:00
// Copyright 2019 Prometheus Team
// 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
//
// http://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 opsgenie
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"testing"
"time"
2021-07-30 08:11:43 +00:00
"github.com/go-kit/log"
2021-08-10 17:01:46 +00:00
commoncfg "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
2019-06-18 13:34:46 +00:00
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/notify/test"
"github.com/prometheus/alertmanager/types"
)
func TestOpsGenieRetry ( t * testing . T ) {
2019-08-02 14:17:40 +00:00
notifier , err := New (
& config . OpsGenieConfig {
HTTPConfig : & commoncfg . HTTPClientConfig { } ,
} ,
test . CreateTmpl ( t ) ,
log . NewNopLogger ( ) ,
)
require . NoError ( t , err )
2019-06-18 13:34:46 +00:00
retryCodes := append ( test . DefaultRetryCodes ( ) , http . StatusTooManyRequests )
for statusCode , expected := range test . RetryTests ( retryCodes ) {
2019-08-02 14:17:40 +00:00
actual , _ := notifier . retrier . Check ( statusCode , nil )
2019-06-18 13:34:46 +00:00
require . Equal ( t , expected , actual , fmt . Sprintf ( "error on status %d" , statusCode ) )
}
}
func TestOpsGenieRedactedURL ( t * testing . T ) {
ctx , u , fn := test . GetContextWithCancelingURL ( )
defer fn ( )
key := "key"
notifier , err := New (
& config . OpsGenieConfig {
APIURL : & config . URL { URL : u } ,
APIKey : config . Secret ( key ) ,
HTTPConfig : & commoncfg . HTTPClientConfig { } ,
} ,
test . CreateTmpl ( t ) ,
log . NewNopLogger ( ) ,
)
require . NoError ( t , err )
test . AssertNotifyLeaksNoSecret ( t , ctx , notifier , key )
}
2021-10-21 07:29:03 +00:00
func TestGettingOpsGegineApikeyFromFile ( t * testing . T ) {
ctx , u , fn := test . GetContextWithCancelingURL ( )
defer fn ( )
key := "key"
f , err := ioutil . TempFile ( "" , "opsgenie_test" )
require . NoError ( t , err , "creating temp file failed" )
_ , err = f . WriteString ( key )
require . NoError ( t , err , "writing to temp file failed" )
notifier , err := New (
& config . OpsGenieConfig {
APIURL : & config . URL { URL : u } ,
APIKeyFile : f . Name ( ) ,
HTTPConfig : & commoncfg . HTTPClientConfig { } ,
} ,
test . CreateTmpl ( t ) ,
log . NewNopLogger ( ) ,
)
require . NoError ( t , err )
test . AssertNotifyLeaksNoSecret ( t , ctx , notifier , key )
}
2019-06-18 13:34:46 +00:00
func TestOpsGenie ( t * testing . T ) {
u , err := url . Parse ( "https://opsgenie/api" )
if err != nil {
t . Fatalf ( "failed to parse URL: %v" , err )
}
logger := log . NewNopLogger ( )
tmpl := test . CreateTmpl ( t )
2020-06-09 07:00:52 +00:00
for _ , tc := range [ ] struct {
title string
cfg * config . OpsGenieConfig
expectedEmptyAlertBody string
expectedBody string
} {
{
title : "config without details" ,
cfg : & config . OpsGenieConfig {
NotifierConfig : config . NotifierConfig {
VSendResolved : true ,
} ,
Message : ` {{ .CommonLabels .Message }} ` ,
Description : ` {{ .CommonLabels .Description }} ` ,
Source : ` {{ .CommonLabels .Source }} ` ,
Responders : [ ] config . OpsGenieConfigResponder {
{
Name : ` {{ .CommonLabels .ResponderName1 }} ` ,
Type : ` {{ .CommonLabels .ResponderType1 }} ` ,
} ,
{
Name : ` {{ .CommonLabels .ResponderName2 }} ` ,
Type : ` {{ .CommonLabels .ResponderType2 }} ` ,
} ,
} ,
Tags : ` {{ .CommonLabels .Tags }} ` ,
Note : ` {{ .CommonLabels .Note }} ` ,
Priority : ` {{ .CommonLabels .Priority }} ` ,
2021-11-13 06:37:59 +00:00
Entity : ` {{ .CommonLabels .Entity }} ` ,
Actions : ` {{ .CommonLabels .Actions }} ` ,
2020-06-09 07:00:52 +00:00
APIKey : ` {{ .ExternalURL }} ` ,
APIURL : & config . URL { URL : u } ,
HTTPConfig : & commoncfg . HTTPClientConfig { } ,
} ,
expectedEmptyAlertBody : ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "" , "details" : { } , "source" : "" }
` ,
2021-11-13 06:37:59 +00:00
expectedBody : ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "message" , "description" : "description" , "details" : { "Actions" : "doThis,doThat" , "Description" : "description" , "Entity" : "test-domain" , "Message" : "message" , "Note" : "this is a note" , "Priority" : "P1" , "ResponderName1" : "TeamA" , "ResponderName2" : "EscalationA" , "ResponderName3" : "TeamA,TeamB" , "ResponderType1" : "team" , "ResponderType2" : "escalation" , "ResponderType3" : "teams" , "Source" : "http://prometheus" , "Tags" : "tag1,tag2" } , "source" : "http://prometheus" , "responders" : [ { "name" : "TeamA" , "type" : "team" } , { "name" : "EscalationA" , "type" : "escalation" } ] , "tags" : [ "tag1" , "tag2" ] , "note" : "this is a note" , "priority" : "P1" , "entity" : "test-domain" , "actions" : [ "doThis" , "doThat" ] }
2020-06-09 07:00:52 +00:00
` ,
2019-06-18 13:34:46 +00:00
} ,
2020-06-09 07:00:52 +00:00
{
2020-06-09 11:51:46 +00:00
title : "config with details" ,
2020-06-09 07:00:52 +00:00
cfg : & config . OpsGenieConfig {
NotifierConfig : config . NotifierConfig {
VSendResolved : true ,
} ,
Message : ` {{ .CommonLabels .Message }} ` ,
Description : ` {{ .CommonLabels .Description }} ` ,
Source : ` {{ .CommonLabels .Source }} ` ,
Details : map [ string ] string {
2020-06-09 11:51:46 +00:00
"Description" : ` adjusted {{ .CommonLabels .Description }} ` ,
2020-06-09 07:00:52 +00:00
} ,
Responders : [ ] config . OpsGenieConfigResponder {
{
Name : ` {{ .CommonLabels .ResponderName1 }} ` ,
Type : ` {{ .CommonLabels .ResponderType1 }} ` ,
} ,
{
Name : ` {{ .CommonLabels .ResponderName2 }} ` ,
Type : ` {{ .CommonLabels .ResponderType2 }} ` ,
} ,
} ,
Tags : ` {{ .CommonLabels .Tags }} ` ,
Note : ` {{ .CommonLabels .Note }} ` ,
Priority : ` {{ .CommonLabels .Priority }} ` ,
2021-11-13 06:37:59 +00:00
Entity : ` {{ .CommonLabels .Entity }} ` ,
Actions : ` {{ .CommonLabels .Actions }} ` ,
2020-06-09 07:00:52 +00:00
APIKey : ` {{ .ExternalURL }} ` ,
APIURL : & config . URL { URL : u } ,
HTTPConfig : & commoncfg . HTTPClientConfig { } ,
} ,
2020-06-09 11:51:46 +00:00
expectedEmptyAlertBody : ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "" , "details" : { "Description" : "adjusted " } , "source" : "" }
2020-06-09 07:00:52 +00:00
` ,
2021-11-13 06:37:59 +00:00
expectedBody : ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "message" , "description" : "description" , "details" : { "Actions" : "doThis,doThat" , "Description" : "adjusted description" , "Entity" : "test-domain" , "Message" : "message" , "Note" : "this is a note" , "Priority" : "P1" , "ResponderName1" : "TeamA" , "ResponderName2" : "EscalationA" , "ResponderName3" : "TeamA,TeamB" , "ResponderType1" : "team" , "ResponderType2" : "escalation" , "ResponderType3" : "teams" , "Source" : "http://prometheus" , "Tags" : "tag1,tag2" } , "source" : "http://prometheus" , "responders" : [ { "name" : "TeamA" , "type" : "team" } , { "name" : "EscalationA" , "type" : "escalation" } ] , "tags" : [ "tag1" , "tag2" ] , "note" : "this is a note" , "priority" : "P1" , "entity" : "test-domain" , "actions" : [ "doThis" , "doThat" ] }
2021-11-04 21:32:20 +00:00
` ,
} ,
{
title : "config with multiple teams" ,
cfg : & config . OpsGenieConfig {
NotifierConfig : config . NotifierConfig {
VSendResolved : true ,
} ,
Message : ` {{ .CommonLabels .Message }} ` ,
Description : ` {{ .CommonLabels .Description }} ` ,
Source : ` {{ .CommonLabels .Source }} ` ,
Details : map [ string ] string {
"Description" : ` adjusted {{ .CommonLabels .Description }} ` ,
} ,
Responders : [ ] config . OpsGenieConfigResponder {
{
Name : ` {{ .CommonLabels .ResponderName3 }} ` ,
Type : ` {{ .CommonLabels .ResponderType3 }} ` ,
} ,
} ,
Tags : ` {{ .CommonLabels .Tags }} ` ,
Note : ` {{ .CommonLabels .Note }} ` ,
Priority : ` {{ .CommonLabels .Priority }} ` ,
APIKey : ` {{ .ExternalURL }} ` ,
APIURL : & config . URL { URL : u } ,
HTTPConfig : & commoncfg . HTTPClientConfig { } ,
} ,
expectedEmptyAlertBody : ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "" , "details" : { "Description" : "adjusted " } , "source" : "" }
` ,
2021-11-13 06:37:59 +00:00
expectedBody : ` { "alias" : "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b" , "message" : "message" , "description" : "description" , "details" : { "Actions" : "doThis,doThat" , "Description" : "adjusted description" , "Entity" : "test-domain" , "Message" : "message" , "Note" : "this is a note" , "Priority" : "P1" , "ResponderName1" : "TeamA" , "ResponderName2" : "EscalationA" , "ResponderName3" : "TeamA,TeamB" , "ResponderType1" : "team" , "ResponderType2" : "escalation" , "ResponderType3" : "teams" , "Source" : "http://prometheus" , "Tags" : "tag1,tag2" } , "source" : "http://prometheus" , "responders" : [ { "name" : "TeamA" , "type" : "team" } , { "name" : "TeamB" , "type" : "team" } ] , "tags" : [ "tag1" , "tag2" ] , "note" : "this is a note" , "priority" : "P1" }
2020-06-09 07:00:52 +00:00
` ,
} ,
} {
t . Run ( tc . title , func ( t * testing . T ) {
notifier , err := New ( tc . cfg , tmpl , logger )
require . NoError ( t , err )
2019-06-18 13:34:46 +00:00
2020-06-09 07:00:52 +00:00
ctx := context . Background ( )
ctx = notify . WithGroupKey ( ctx , "1" )
2019-06-18 13:34:46 +00:00
2020-06-09 07:00:52 +00:00
expectedURL , _ := url . Parse ( "https://opsgenie/apiv2/alerts" )
2019-06-18 13:34:46 +00:00
2020-06-09 07:00:52 +00:00
// Empty alert.
alert1 := & types . Alert {
Alert : model . Alert {
StartsAt : time . Now ( ) ,
EndsAt : time . Now ( ) . Add ( time . Hour ) ,
} ,
}
2021-03-19 20:13:50 +00:00
req , retry , err := notifier . createRequests ( ctx , alert1 )
2020-06-09 07:00:52 +00:00
require . NoError ( t , err )
2021-03-19 20:13:50 +00:00
require . Len ( t , req , 1 )
2020-06-09 07:00:52 +00:00
require . Equal ( t , true , retry )
2021-03-19 20:13:50 +00:00
require . Equal ( t , expectedURL , req [ 0 ] . URL )
require . Equal ( t , "GenieKey http://am" , req [ 0 ] . Header . Get ( "Authorization" ) )
require . Equal ( t , tc . expectedEmptyAlertBody , readBody ( t , req [ 0 ] ) )
2020-06-09 07:00:52 +00:00
// Fully defined alert.
alert2 := & types . Alert {
Alert : model . Alert {
Labels : model . LabelSet {
"Message" : "message" ,
"Description" : "description" ,
"Source" : "http://prometheus" ,
"ResponderName1" : "TeamA" ,
"ResponderType1" : "team" ,
"ResponderName2" : "EscalationA" ,
"ResponderType2" : "escalation" ,
2021-11-04 21:32:20 +00:00
"ResponderName3" : "TeamA,TeamB" ,
"ResponderType3" : "teams" ,
2020-06-09 07:00:52 +00:00
"Tags" : "tag1,tag2" ,
"Note" : "this is a note" ,
"Priority" : "P1" ,
2021-11-13 06:37:59 +00:00
"Entity" : "test-domain" ,
"Actions" : "doThis,doThat" ,
2020-06-09 07:00:52 +00:00
} ,
StartsAt : time . Now ( ) ,
EndsAt : time . Now ( ) . Add ( time . Hour ) ,
} ,
}
2021-03-19 20:13:50 +00:00
req , retry , err = notifier . createRequests ( ctx , alert2 )
2020-06-09 07:00:52 +00:00
require . NoError ( t , err )
require . Equal ( t , true , retry )
2021-03-19 20:13:50 +00:00
require . Len ( t , req , 1 )
require . Equal ( t , tc . expectedBody , readBody ( t , req [ 0 ] ) )
2020-06-09 07:00:52 +00:00
// Broken API Key Template.
tc . cfg . APIKey = "{{ kaput "
2021-03-19 20:13:50 +00:00
_ , _ , err = notifier . createRequests ( ctx , alert2 )
2020-06-09 07:00:52 +00:00
require . Error ( t , err )
require . Equal ( t , err . Error ( ) , "templating error: template: :1: function \"kaput\" not defined" )
} )
2019-06-18 13:34:46 +00:00
}
}
2021-03-19 20:13:50 +00:00
func TestOpsGenieWithUpdate ( t * testing . T ) {
u , err := url . Parse ( "https://test-opsgenie-url" )
require . NoError ( t , err )
tmpl := test . CreateTmpl ( t )
ctx := context . Background ( )
ctx = notify . WithGroupKey ( ctx , "1" )
opsGenieConfigWithUpdate := config . OpsGenieConfig {
2021-08-10 16:26:48 +00:00
Message : ` {{ .CommonLabels .Message }} ` ,
Description : ` {{ .CommonLabels .Description }} ` ,
UpdateAlerts : true ,
APIKey : "test-api-key" ,
APIURL : & config . URL { URL : u } ,
HTTPConfig : & commoncfg . HTTPClientConfig { } ,
2021-03-19 20:13:50 +00:00
}
notifierWithUpdate , err := New ( & opsGenieConfigWithUpdate , tmpl , log . NewNopLogger ( ) )
alert := & types . Alert {
Alert : model . Alert {
StartsAt : time . Now ( ) ,
EndsAt : time . Now ( ) . Add ( time . Hour ) ,
Labels : model . LabelSet {
"Message" : "new message" ,
"Description" : "new description" ,
} ,
} ,
}
require . NoError ( t , err )
requests , retry , err := notifierWithUpdate . createRequests ( ctx , alert )
require . NoError ( t , err )
require . True ( t , retry )
require . Len ( t , requests , 3 )
body0 := readBody ( t , requests [ 0 ] )
body1 := readBody ( t , requests [ 1 ] )
body2 := readBody ( t , requests [ 2 ] )
key , _ := notify . ExtractGroupKey ( ctx )
alias := key . Hash ( )
require . Equal ( t , requests [ 0 ] . URL . String ( ) , "https://test-opsgenie-url/v2/alerts" )
require . NotEmpty ( t , body0 )
require . Equal ( t , requests [ 1 ] . URL . String ( ) , fmt . Sprintf ( "https://test-opsgenie-url/v2/alerts/%s/message?identifierType=alias" , alias ) )
require . Equal ( t , body1 , ` { "message" : "new message" }
` )
require . Equal ( t , requests [ 2 ] . URL . String ( ) , fmt . Sprintf ( "https://test-opsgenie-url/v2/alerts/%s/description?identifierType=alias" , alias ) )
require . Equal ( t , body2 , ` { "description" : "new description" }
` )
}
2019-06-18 13:34:46 +00:00
func readBody ( t * testing . T , r * http . Request ) string {
t . Helper ( )
body , err := ioutil . ReadAll ( r . Body )
require . NoError ( t , err )
return string ( body )
}