Skip to content

Commit

Permalink
OpsGenie notification backend (#280)
Browse files Browse the repository at this point in the history
* basic notification to opsgenie alert call

Signed-off-by: Raphaël Berbain <[email protected]>

* review: change error message for opsgenie

Signed-off-by: Romain Beuque <[email protected]>

* lint

Signed-off-by: Raphaël Berbain <[email protected]>

* make timeout configurable

Signed-off-by: Raphaël Berbain <[email protected]>

Co-authored-by: Raphaël Berbain <[email protected]>
Co-authored-by: Romain Beuque <[email protected]>
  • Loading branch information
3 people committed Jun 30, 2021
1 parent e3af415 commit 90d475a
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 11 deletions.
12 changes: 12 additions & 0 deletions config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ postgres://user:pass@db/utask?sslmode=disable
"completed_task_expiration": "720h", // default == 720h == 30 days
// notify_config contains a map of named notification configurations, composed of a type and config data,
// implemented notifiers include:
// - opsgenie (https://www.atlassian.com/software/opsgenie); available zones are: global, eu, sandbox
// - tat (github.com/ovh/tat)
// - slack webhook (https://api.slack.com/messaging/webhooks)
// - generic webhook (custom URL, with HTTP POST method)
Expand All @@ -53,6 +54,17 @@ postgres://user:pass@db/utask?sslmode=disable
// - default_notification_strategy is the strategy that will apply, if none matched above
// available strategies are: always, failure_only, silent
"notify_config": {
"opsgenie-eu": {
"type": "opsgenie",
"config": {
"zone": "eu",
"api_key": "very-secret",
"timeout": "30s"
},
"default_notification_strategy": {
"task_state_update": "failure_only"
}
},
"tat-internal": {
"type": "tat",
"config": {
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/gin-gonic/gin v1.6.3
github.com/go-gorp/gorp v2.2.0+incompatible
github.com/go-ping/ping v0.0.0-20210506233800-ff8be3320020
github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/gofrs/uuid v3.3.0+incompatible
github.com/golang/protobuf v1.4.0 // indirect
Expand All @@ -31,6 +32,7 @@ require (
github.com/maxatome/go-testdeep v1.8.0
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 // indirect
github.com/mitchellh/reflectwalk v1.0.1 // indirect
github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.8
github.com/ovh/configstore v0.3.2
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
github.com/ovh/symmecrypt v0.4.3
Expand All @@ -45,7 +47,6 @@ require (
github.com/santhosh-tekuri/jsonschema v1.2.4
github.com/sirupsen/logrus v1.6.0
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.6
github.com/spf13/jwalterweatherman v1.1.0 // indirect
Expand All @@ -58,9 +59,8 @@ require (
github.com/ybriffa/go-http-digest-auth-client v0.6.3
github.com/ziutek/mymysql v1.5.4 // indirect
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20200904194848-62affa334b73
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/ini.v1 v1.46.0 // indirect
Expand Down
23 changes: 17 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ping/ping v0.0.0-20210506233800-ff8be3320020 h1:mdi6AbCEoKCA1xKCmp7UtRB5fvGFlP92PvlhxgdvXEw=
github.com/go-ping/ping v0.0.0-20210506233800-ff8be3320020/go.mod h1:KmHOjTUmJh/l04ukqPoBWPEZr9jwN05h5NXQl5C+DyY=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
Expand Down Expand Up @@ -127,6 +129,10 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-retryablehttp v0.5.1 h1:Vsx5XKPqPs3M6sM4U4GWyUqFS8aBiL9U5gkgvpkg4SE=
github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
Expand Down Expand Up @@ -234,6 +240,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/nelsam/hel/v2 v2.3.2 h1:tXRsJBqRxj4ISSPCrXhbqF8sT+BXA/UaIvjhYjP5Bhk=
github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.8 h1:qF/rRi8GSU2mjBXfJIyMj9GGmjedsV3Gm1uYbiGlCRk=
github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.8/go.mod h1:4OjcxgwdXzezqytxN534MooNmrxRD50geWZxTD7845s=
github.com/ovh/configstore v0.3.2 h1:/kr1B27JVzW4Eiz20muZSnQ5UyizFjLy5+2CVfp/mKs=
github.com/ovh/configstore v0.3.2/go.mod h1:bBc7U++7HXgf9lrtmmJb31DK3Tp+Zv8GaIn0Bjolv/o=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 h1:37VE5TYj2m/FLA9SNr4z0+A0JefvTmR60Zwf8XSEV7c=
Expand Down Expand Up @@ -291,8 +299,6 @@ github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbd
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c h1:gqEdF4VwBu3lTKGHS9rXE9x1/pEaSwCXRLOZRF6qtlw=
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c/go.mod h1:eMyUVp6f/5jnzM+3zahzl7q6UXLbgSc3MKg/+ow9QW0=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
Expand Down Expand Up @@ -368,14 +374,16 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -391,11 +399,14 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
16 changes: 16 additions & 0 deletions pkg/notify/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ovh/configstore"
"github.com/ovh/utask"
"github.com/ovh/utask/pkg/notify"
"github.com/ovh/utask/pkg/notify/opsgenie"
"github.com/ovh/utask/pkg/notify/slack"
"github.com/ovh/utask/pkg/notify/tat"
"github.com/ovh/utask/pkg/notify/webhook"
Expand All @@ -34,6 +35,21 @@ func Init(store *configstore.Store) error {
ncfg.DefaultNotificationStrategy = newncfg.DefaultNotificationStrategy

switch ncfg.Type {
case opsgenie.Type:
f := utask.NotifyBackendOpsGenie{}
if err := json.Unmarshal(ncfg.Config, &f); err != nil {
return fmt.Errorf("%s: %s, %s: %s", errRetrieveCfg, ncfg.Type, name, err)
}
ogns, err := opsgenie.NewOpsGenieNotificationSender(
f.Zone,
f.APIKey,
f.Timeout,
)
if err != nil {
return fmt.Errorf("Failed to instantiate tat notification sender: %s", err)
}
notify.RegisterSender(name, ogns, ncfg.DefaultNotificationStrategy, ncfg.TemplateNotificationStrategies)

case tat.Type:
f := utask.NotifyBackendTat{}
if err := json.Unmarshal(ncfg.Config, &f); err != nil {
Expand Down
81 changes: 81 additions & 0 deletions pkg/notify/opsgenie/opsgenie.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package opsgenie

import (
"context"
"time"

"github.com/juju/errors"
"github.com/opsgenie/opsgenie-go-sdk-v2/alert"
"github.com/opsgenie/opsgenie-go-sdk-v2/client"
"github.com/ovh/utask/pkg/notify"
)

const (
// Type represents OpsGenie as notify backend
Type = "opsgenie"

// Zone are opsgenie api zones
ZoneSandbox = "sandbox"
ZoneDefault = "global"
ZoneEU = "eu"
)

// NotificationSender is a notify.NotificationSender implementation
// capable of sending formatted notifications over OpsGenie (https://www.atlassian.com/software/opsgenie)
type NotificationSender struct {
opsGenieZone string
opsGenieAPIKey string
opsGenieTimeout time.Duration
client *alert.Client
}

// NewOpsGenieNotificationSender instantiates a NotificationSender
func NewOpsGenieNotificationSender(zone, apikey, timeout string) (*NotificationSender, error) {
zonesToAPIUrls := map[string]client.ApiUrl{
ZoneDefault: client.API_URL,
ZoneEU: client.API_URL_EU,
ZoneSandbox: client.API_URL_SANDBOX,
}
apiURL, present := zonesToAPIUrls[zone]
if !present {
return nil, errors.NotFoundf("opsgenie zone %q", zone)
}
client, err := alert.NewClient(&client.Config{
ApiKey: apikey,
OpsGenieAPIURL: apiURL,
})
if err != nil {
return nil, err
}
timeoutDuration := 30 * time.Second
if timeout != "" {
timeoutDuration, err = time.ParseDuration(timeout)
if err != nil {
return nil, err
}
}
return &NotificationSender{
opsGenieZone: zone,
opsGenieAPIKey: apikey,
opsGenieTimeout: timeoutDuration,
client: client,
}, nil
}

// Send dispatches a notify.Message to OpsGenie
func (ns *NotificationSender) Send(m *notify.Message, name string) {
req := &alert.CreateAlertRequest{
Message: m.MainMessage,
Description: m.MainMessage,
Details: m.Fields,
}

ctx, cancel := context.WithTimeout(context.Background(), ns.opsGenieTimeout)
defer cancel()

_, err := ns.client.Create(ctx, req)
if err != nil {
notify.WrappedSendError(Type, err.Error())
return
}
}
2 changes: 1 addition & 1 deletion pkg/plugins/builtin/ping/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strconv"
"time"

ping "github.com/sparrc/go-ping"
ping "github.com/go-ping/ping"

"github.com/ovh/utask/pkg/plugins/taskplugin"
)
Expand Down
7 changes: 7 additions & 0 deletions utask.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ type TemplateNotificationStrategy struct {
NotificationStrategy string `json:"notification_strategy"` // value can be `always`, `failure_only`, `silent`
}

// NotifyBackendOpsGenie holds configuration for instantiating an OPsGenie notify client
type NotifyBackendOpsGenie struct {
Zone string `json:"zone"`
APIKey string `json:"api_key"`
Timeout string `json:"timeout"`
}

// NotifyBackendTat holds configuration for instantiating a Tat notify client
type NotifyBackendTat struct {
Username string `json:"username"`
Expand Down

0 comments on commit 90d475a

Please sign in to comment.