Skip to content

Commit

Permalink
Alerting: Introduce support of responders for OpsGenie integration (#…
Browse files Browse the repository at this point in the history
…1100)

* introduce support of responders for OpsGenie integration

* Run tests in cloud instance

---------

Co-authored-by: Julien Duchesne <julien.duchesne@grafana.com>
  • Loading branch information
yuri-tceretian and julienduchesne authored Nov 1, 2023
1 parent 4386b97 commit 4e8aa22
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 0 deletions.
15 changes: 15 additions & 0 deletions docs/resources/contact_point.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ Optional:
- `disable_resolve_message` (Boolean) Whether to disable sending resolve messages. Defaults to `false`.
- `message` (String) The templated content of the message.
- `override_priority` (Boolean) Whether to allow the alert priority to be configured via the value of the `og_priority` annotation on the alert.
- `responders` (Block List) Teams, users, escalations and schedules that the alert will be routed to send notifications. If the API Key belongs to a team integration, this field will be overwritten with the owner team. This feature is available from Grafana 10.3+. (see [below for nested schema](#nestedblock--opsgenie--responders))
- `send_tags_as` (String) Whether to send annotations to OpsGenie as Tags, Details, or both. Supported values are `tags`, `details`, `both`, or empty to use the default behavior of Tags.
- `settings` (Map of String, Sensitive) Additional custom properties to attach to the notifier. Defaults to `map[]`.
- `url` (String) Allows customization of the OpsGenie API URL.
Expand All @@ -256,6 +257,20 @@ Read-Only:

- `uid` (String) The UID of the contact point.

<a id="nestedblock--opsgenie--responders"></a>
### Nested Schema for `opsgenie.responders`

Required:

- `type` (String) Type of the responder. Supported: team, teams, user, escalation, schedule or a template that is expanded to one of these values.

Optional:

- `id` (String) ID of the responder. Must be specified if name and username are empty.
- `name` (String) Name of the responder. Must be specified if username and id are empty.
- `username` (String) User name of the responder. Must be specified if name and id are empty.



<a id="nestedblock--pagerduty"></a>
### Nested Schema for `pagerduty`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "grafana_contact_point" "receiver_types" {
name = "Receiver Types since v10.3"

opsgenie {
url = "http://opsgenie-api"
api_key = "token"
message = "message"
description = "description"
auto_close = true
override_priority = true
send_tags_as = "both"
responders {
type = "user"
id = "803f87e1a7f848b0a0779810bee5d1d3"
}
responders {
type = "team"
name = "Test team"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,36 @@ func (o opsGenieNotifier) schema() *schema.Resource {
ValidateFunc: validation.StringInSlice([]string{"tags", "details", "both"}, false),
Description: "Whether to send annotations to OpsGenie as Tags, Details, or both. Supported values are `tags`, `details`, `both`, or empty to use the default behavior of Tags.",
}
r.Schema["responders"] = &schema.Schema{
Type: schema.TypeList,
Optional: true,
Description: "Teams, users, escalations and schedules that the alert will be routed to send notifications. If the API Key belongs to a team integration, this field will be overwritten with the owner team. This feature is available from Grafana 10.3+.",
Elem: &schema.Resource{
Description: "Defines a responder. Either id, name or username must be specified",
Schema: map[string]*schema.Schema{
"type": {
Type: schema.TypeString,
Required: true,
Description: "Type of the responder. Supported: team, teams, user, escalation, schedule or a template that is expanded to one of these values.",
},
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of the responder. Must be specified if username and id are empty.",
},
"username": {
Type: schema.TypeString,
Optional: true,
Description: "User name of the responder. Must be specified if name and id are empty.",
},
"id": {
Type: schema.TypeString,
Optional: true,
Description: "ID of the responder. Must be specified if name and username are empty.",
},
},
},
}
return r
}

Expand Down Expand Up @@ -830,6 +860,21 @@ func (o opsGenieNotifier) pack(p gapi.ContactPoint, data *schema.ResourceData) (
notifier["send_tags_as"] = v.(string)
delete(p.Settings, "sendTagsAs")
}
if v, ok := p.Settings["responders"]; ok && v != nil {
items := v.([]any)
responders := make([]map[string]interface{}, 0, len(items))
for _, item := range items {
itemMap := item.(map[string]interface{})
responder := make(map[string]interface{}, 4)
packNotifierStringField(&itemMap, &responder, "type", "type")
packNotifierStringField(&itemMap, &responder, "id", "id")
packNotifierStringField(&itemMap, &responder, "name", "name")
packNotifierStringField(&itemMap, &responder, "username", "username")
responders = append(responders, responder)
}
notifier["responders"] = responders
delete(p.Settings, "responders")
}

packSecureFields(notifier, getNotifierConfigFromStateWithUID(data, o, p.UID), o.meta().secureFields)

Expand Down Expand Up @@ -862,6 +907,20 @@ func (o opsGenieNotifier) unpack(raw interface{}, name string) gapi.ContactPoint
if v, ok := json["send_tags_as"]; ok && v != nil {
settings["sendTagsAs"] = v.(string)
}
if v, ok := json["responders"]; ok && v != nil {
items := v.([]any)
responders := make([]map[string]interface{}, 0, len(items))
for _, item := range items {
tfResponder := item.(map[string]interface{})
responder := make(map[string]interface{}, 4)
unpackNotifierStringField(&tfResponder, &responder, "type", "type")
unpackNotifierStringField(&tfResponder, &responder, "id", "id")
unpackNotifierStringField(&tfResponder, &responder, "name", "name")
unpackNotifierStringField(&tfResponder, &responder, "username", "username")
responders = append(responders, responder)
}
settings["responders"] = responders
}
return gapi.ContactPoint{
UID: uid,
Name: name,
Expand Down
34 changes: 34 additions & 0 deletions internal/resources/grafana/resource_alerting_contact_point_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,40 @@ func TestAccContactPoint_notifiers10_2(t *testing.T) {
})
}

func TestAccContactPoint_notifiers10_3(t *testing.T) {
testutils.CheckCloudInstanceTestsEnabled(t) // TODO: Switch to `testutils.CheckOSSTestsEnabled(t, ">=10.3.0")` once 10.3 is released.

var points []gapi.ContactPoint

resource.ParallelTest(t, resource.TestCase{
ProviderFactories: testutils.ProviderFactories,
// Implicitly tests deletion.
CheckDestroy: testContactPointCheckDestroy(points),
Steps: []resource.TestStep{
// Test creation.
{
Config: testutils.TestAccExample(t, "resources/grafana_contact_point/_acc_receiver_types_10_3.tf"),
Check: resource.ComposeTestCheckFunc(
testContactPointCheckExists("grafana_contact_point.receiver_types", &points, 1),
// opsgenie
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.#", "1"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.url", "http://opsgenie-api"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.api_key", "token"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.message", "message"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.description", "description"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.auto_close", "true"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.override_priority", "true"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.send_tags_as", "both"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.responders.0.type", "user"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.responders.0.id", "803f87e1a7f848b0a0779810bee5d1d3"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.responders.1.type", "team"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "opsgenie.0.responders.1.name", "Test team"),
),
},
},
})
}

func TestAccContactPoint_empty(t *testing.T) {
testutils.CheckOSSTestsEnabled(t, ">=9.1.0")

Expand Down

0 comments on commit 4e8aa22

Please sign in to comment.