opsgenie: Add visible_to
Add visible_to with the same support for templating and fields as the existing responders field. Opsgenie documentation: Teams and users that the alert will become visible to without sending any notification. type field is mandatory for each item, where possible values are team and user. In addition to the type field, either id or name should be given for teams and either id or username should be given for users. Please note: that alert will be visible to the teams that are specified within responders field by default, so there is no need to re-specify them within visibleTo field. You can refer below for example values. The functionality has been tested with unit tests, and with the following configuration: route: receiver: og group_wait: 30s group_interval: 5m repeat_interval: 12h receivers: - name: og opsgenie_configs: - send_resolved: true api_key: ...top secret... api_url: https://api.eu.opsgenie.com/ message: 'Lab message' description: 'Static description' source: lab details: foo: bar priority: P4 visible_to: - type: user username: foo@example.com Fixes #3953 Signed-off-by: Carl Henrik Lunde <chlunde@ifi.uio.no>
This commit is contained in:
parent
b233fe1816
commit
db0f43f6e9
|
@ -584,6 +584,7 @@ type OpsGenieConfig struct {
|
|||
Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"`
|
||||
Entity string `yaml:"entity,omitempty" json:"entity,omitempty"`
|
||||
Responders []OpsGenieConfigResponder `yaml:"responders,omitempty" json:"responders,omitempty"`
|
||||
VisibleTo []OpsGenieConfigVisibleTo `yaml:"visible_to,omitempty" json:"visible_to,omitempty"`
|
||||
Actions string `yaml:"actions,omitempty" json:"actions,omitempty"`
|
||||
Tags string `yaml:"tags,omitempty" json:"tags,omitempty"`
|
||||
Note string `yaml:"note,omitempty" json:"note,omitempty"`
|
||||
|
@ -625,6 +626,24 @@ func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
|
|||
}
|
||||
}
|
||||
|
||||
for _, v := range c.VisibleTo {
|
||||
if v.ID == "" && v.Username == "" && v.Name == "" {
|
||||
return fmt.Errorf("opsGenieConfig visible_to %v has to have at least one of id, username or name specified", v)
|
||||
}
|
||||
|
||||
if strings.Contains(v.Type, "{{") {
|
||||
_, err := template.New("").Parse(v.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opsGenieConfig visible_to %v type is not a valid template: %w", v, err)
|
||||
}
|
||||
} else {
|
||||
v.Type = strings.ToLower(v.Type)
|
||||
if !opsgenieTypeMatcher.MatchString(v.Type) {
|
||||
return fmt.Errorf("opsGenieConfig visible_to %v type does not match valid options %s", v, opsgenieValidTypesRe)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -638,6 +657,16 @@ type OpsGenieConfigResponder struct {
|
|||
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type OpsGenieConfigVisibleTo struct {
|
||||
// One of those 3 should be filled.
|
||||
ID string `yaml:"id,omitempty" json:"id,omitempty"`
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
Username string `yaml:"username,omitempty" json:"username,omitempty"`
|
||||
|
||||
// team, user
|
||||
Type string `yaml:"type,omitempty" json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// VictorOpsConfig configures notifications via VictorOps.
|
||||
type VictorOpsConfig struct {
|
||||
NotifierConfig `yaml:",inline" json:",inline"`
|
||||
|
|
|
@ -1075,6 +1075,10 @@ OpsGenie notifications are sent via the [OpsGenie API](https://docs.opsgenie.com
|
|||
responders:
|
||||
[ - <responder> ... ]
|
||||
|
||||
# List of teams and users the alert will become visible to without sending any notification.
|
||||
visible_to:
|
||||
[ - <visible_to> ... ]
|
||||
|
||||
# Comma separated list of tags attached to the notifications.
|
||||
[ tags: <tmpl_string> ]
|
||||
|
||||
|
@ -1114,6 +1118,22 @@ responders:
|
|||
type: <tmpl_string>
|
||||
```
|
||||
|
||||
#### `<visible_to>`
|
||||
|
||||
```yaml
|
||||
# Exactly one of these fields should be defined.
|
||||
[ id: <tmpl_string> ]
|
||||
[ name: <tmpl_string> ]
|
||||
[ username: <tmpl_string> ]
|
||||
|
||||
# One of `team`, `teams` or `user`.
|
||||
#
|
||||
# The `teams` responder is configured using the `name` field above.
|
||||
# This field can contain a comma-separated list of team names.
|
||||
# If the list is empty, no additional visibility will be configured.
|
||||
type: <tmpl_string>
|
||||
```
|
||||
|
||||
### `<pagerduty_config>`
|
||||
|
||||
PagerDuty notifications are sent via the [PagerDuty API](https://developer.pagerduty.com/documentation/integration/events).
|
||||
|
|
|
@ -67,6 +67,7 @@ type opsGenieCreateMessage struct {
|
|||
Details map[string]string `json:"details"`
|
||||
Source string `json:"source"`
|
||||
Responders []opsGenieCreateMessageResponder `json:"responders,omitempty"`
|
||||
VisibleTo []opsGenieCreateMessageVisibleTo `json:"visibleTo,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Priority string `json:"priority,omitempty"`
|
||||
|
@ -81,6 +82,13 @@ type opsGenieCreateMessageResponder struct {
|
|||
Type string `json:"type"` // team, user, escalation, schedule etc.
|
||||
}
|
||||
|
||||
type opsGenieCreateMessageVisibleTo struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Type string `json:"type"` // team, user
|
||||
}
|
||||
|
||||
type opsGenieCloseMessage struct {
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
@ -211,6 +219,36 @@ func (n *Notifier) createRequests(ctx context.Context, as ...*types.Alert) ([]*h
|
|||
responders = append(responders, responder)
|
||||
}
|
||||
|
||||
var visibleTos []opsGenieCreateMessageVisibleTo
|
||||
for _, v := range n.conf.VisibleTo {
|
||||
visibleTo := opsGenieCreateMessageVisibleTo{
|
||||
ID: tmpl(v.ID),
|
||||
Name: tmpl(v.Name),
|
||||
Username: tmpl(v.Username),
|
||||
Type: tmpl(v.Type),
|
||||
}
|
||||
|
||||
if visibleTo == (opsGenieCreateMessageVisibleTo{}) {
|
||||
// Filter out empty responders. This is useful if you want to fill
|
||||
// responders dynamically from alert's common labels.
|
||||
continue
|
||||
}
|
||||
|
||||
if visibleTo.Type == "teams" {
|
||||
teams := safeSplit(visibleTo.Name, ",")
|
||||
for _, team := range teams {
|
||||
newVisibleTo := opsGenieCreateMessageVisibleTo{
|
||||
Name: tmpl(team),
|
||||
Type: tmpl("team"),
|
||||
}
|
||||
visibleTos = append(visibleTos, newVisibleTo)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
visibleTos = append(visibleTos, visibleTo)
|
||||
}
|
||||
|
||||
msg := &opsGenieCreateMessage{
|
||||
Alias: alias,
|
||||
Message: message,
|
||||
|
@ -218,6 +256,7 @@ func (n *Notifier) createRequests(ctx context.Context, as ...*types.Alert) ([]*h
|
|||
Details: details,
|
||||
Source: tmpl(n.conf.Source),
|
||||
Responders: responders,
|
||||
VisibleTo: visibleTos,
|
||||
Tags: safeSplit(tmpl(n.conf.Tags), ","),
|
||||
Note: tmpl(n.conf.Note),
|
||||
Priority: tmpl(n.conf.Priority),
|
||||
|
|
|
@ -140,7 +140,7 @@ func TestOpsGenie(t *testing.T) {
|
|||
},
|
||||
expectedEmptyAlertBody: `{"alias":"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b","message":"","details":{},"source":""}
|
||||
`,
|
||||
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"]}
|
||||
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","VisibleToName1":"TeamA","VisibleToName2":"user1","VisibleToType1":"team","VisibleToType2":"user"},"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"]}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -176,7 +176,7 @@ func TestOpsGenie(t *testing.T) {
|
|||
},
|
||||
expectedEmptyAlertBody: `{"alias":"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b","message":"","details":{"Description":"adjusted "},"source":""}
|
||||
`,
|
||||
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"]}
|
||||
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","VisibleToName1":"TeamA","VisibleToName2":"user1","VisibleToType1":"team","VisibleToType2":"user"},"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"]}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -206,7 +206,50 @@ func TestOpsGenie(t *testing.T) {
|
|||
},
|
||||
expectedEmptyAlertBody: `{"alias":"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b","message":"","details":{"Description":"adjusted "},"source":""}
|
||||
`,
|
||||
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"}
|
||||
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","VisibleToName1":"TeamA","VisibleToName2":"user1","VisibleToType1":"team","VisibleToType2":"user"},"source":"http://prometheus","responders":[{"name":"TeamA","type":"team"},{"name":"TeamB","type":"team"}],"tags":["tag1","tag2"],"note":"this is a note","priority":"P1"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
title: "config with visible_to",
|
||||
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 }}`,
|
||||
},
|
||||
},
|
||||
VisibleTo: []config.OpsGenieConfigVisibleTo{
|
||||
{
|
||||
Name: `{{ .CommonLabels.VisibleToName1 }}`,
|
||||
Type: `{{ .CommonLabels.VisibleToType1 }}`,
|
||||
},
|
||||
{
|
||||
Name: `{{ .CommonLabels.VisibleToName2 }}`,
|
||||
Type: `{{ .CommonLabels.VisibleToType2 }}`,
|
||||
},
|
||||
},
|
||||
Tags: `{{ .CommonLabels.Tags }}`,
|
||||
Note: `{{ .CommonLabels.Note }}`,
|
||||
Priority: `{{ .CommonLabels.Priority }}`,
|
||||
Entity: `{{ .CommonLabels.Entity }}`,
|
||||
Actions: `{{ .CommonLabels.Actions }}`,
|
||||
APIKey: `{{ .ExternalURL }}`,
|
||||
APIURL: &config.URL{URL: u},
|
||||
HTTPConfig: &commoncfg.HTTPClientConfig{},
|
||||
},
|
||||
expectedEmptyAlertBody: `{"alias":"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b","message":"","details":{},"source":""}
|
||||
`,
|
||||
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","VisibleToName1":"TeamA","VisibleToName2":"user1","VisibleToType1":"team","VisibleToType2":"user"},"source":"http://prometheus","responders":[{"name":"TeamA","type":"team"},{"name":"EscalationA","type":"escalation"}],"visibleTo":[{"name":"TeamA","type":"team"},{"name":"user1","type":"user"}],"tags":["tag1","tag2"],"note":"this is a note","priority":"P1","entity":"test-domain","actions":["doThis","doThat"]}
|
||||
`,
|
||||
},
|
||||
} {
|
||||
|
@ -248,6 +291,10 @@ func TestOpsGenie(t *testing.T) {
|
|||
"ResponderType2": "escalation",
|
||||
"ResponderName3": "TeamA,TeamB",
|
||||
"ResponderType3": "teams",
|
||||
"VisibleToName1": "TeamA",
|
||||
"VisibleToType1": "team",
|
||||
"VisibleToName2": "user1",
|
||||
"VisibleToType2": "user",
|
||||
"Tags": "tag1,tag2",
|
||||
"Note": "this is a note",
|
||||
"Priority": "P1",
|
||||
|
|
Loading…
Reference in New Issue