Skip to content

Commit

Permalink
INTMDB-272: Validate using interval_min for PagerDuty, VictorOps, Gen…
Browse files Browse the repository at this point in the history
…ieOps (#624)

* added envvvar for pager duty service secret

* refactor: validate if it uses or not with interval min for certain services name in notifications

* made changes suggested by tony

* docs: updated docs
  • Loading branch information
coderGo93 authored Dec 6, 2021
1 parent 055a118 commit 30bcded
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 6 deletions.
1 change: 1 addition & 0 deletions .github/workflows/automated-test-acceptances.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,5 @@ jobs:
CA_CERT: ${{ secrets.CA_CERT }}
MONGODB_ATLAS_ENABLE_BETA: ${{ secrets.MONGODB_ATLAS_ENABLE_BETA }}
ACCTEST_TIMEOUT: ${{ secrets.ACCTEST_TIMEOUT }}
PAGER_DUTY_SERVICE_KEY: ${{ secrets.PAGER_DUTY_SERVICE_KEY }}
run: make testacc
46 changes: 46 additions & 0 deletions mongodbatlas/data_source_mongodbatlas_alert_configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ func TestAccDataSourceMongoDBAtlaAlertConfiguration_withThreshold(t *testing.T)
})
}

func TestAccDataSourceMongoDBAtlaAlertConfiguration_withPagerDuty(t *testing.T) {
SkipTestExtCred(t) // Will skip because requires external credentials aka api key
var (
alert = &matlas.AlertConfiguration{}
dataSourceName = "data.mongodbatlas_alert_configuration.test"
projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID")
serviceKey = os.Getenv("PAGER_DUTY_SERVICE_KEY")
)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckMongoDBAtlasAlertConfigurationDestroy,
Steps: []resource.TestStep{
{
Config: testAccDSMongoDBAtlasAlertConfigurationConfigWithPagerDuty(projectID, serviceKey, true),
Check: resource.ComposeTestCheckFunc(
testAccCheckMongoDBAtlasAlertConfigurationExists(dataSourceName, alert),
resource.TestCheckResourceAttrSet(dataSourceName, "project_id"),
),
},
},
})
}

func testAccDSMongoDBAtlasAlertConfiguration(projectID string) string {
return fmt.Sprintf(`
resource "mongodbatlas_alert_configuration" "test" {
Expand Down Expand Up @@ -127,3 +152,24 @@ func testAccDSMongoDBAtlasAlertConfigurationConfigWithThreshold(projectID string
}
`, projectID, enabled, threshold)
}

func testAccDSMongoDBAtlasAlertConfigurationConfigWithPagerDuty(projectID, serviceKey string, enabled bool) string {
return fmt.Sprintf(`
resource "mongodbatlas_alert_configuration" "test" {
project_id = %[1]q
event_type = "NO_PRIMARY"
enabled = "%[3]t"
notification {
type_name = "PAGER_DUTY"
service_key = %[2]q
delay_min = 0
}
}
data "mongodbatlas_alert_configuration" "test" {
project_id = "${mongodbatlas_alert_configuration.test.project_id}"
alert_configuration_id = "${mongodbatlas_alert_configuration.test.id}"
}
`, projectID, serviceKey, enabled)
}
32 changes: 27 additions & 5 deletions mongodbatlas/resource_mongodbatlas_alert_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ import (
const (
errorCreateAlertConf = "error creating Alert Configuration information: %s"
errorReadAlertConf = "error getting Alert Configuration information: %s"
errorUpdateAlertConf = "error updating Alert Configuration information: %s"
errorDeleteAlertConf = "error deleting Alert Configuration information: %s"
errorAlertConfSetting = "error setting `%s` for Alert Configuration (%s): %s"
errorImportAlertConf = "couldn't import Alert Configuration (%s) in project %s, error: %s"
pagerDuty = "PAGER_DUTY"
opsGenie = "OPS_GENIE"
victorOps = "VICTOR_OPS"
)

func resourceMongoDBAtlasAlertConfiguration() *schema.Resource {
Expand Down Expand Up @@ -269,6 +273,9 @@ func resourceMongoDBAtlasAlertConfiguration() *schema.Resource {
"type_name": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"EMAIL", "SMS", pagerDuty, "SLACK",
"FLOWDOCK", "DATADOG", opsGenie, victorOps,
"WEBHOOK", "USER", "TEAM", "GROUP", "ORG"}, false),
},
"username": {
Type: schema.TypeString,
Expand Down Expand Up @@ -309,9 +316,14 @@ func resourceMongoDBAtlasAlertConfigurationCreate(ctx context.Context, d *schema
Matchers: expandAlertConfigurationMatchers(d),
MetricThreshold: expandAlertConfigurationMetricThresholdConfig(d),
Threshold: expandAlertConfigurationThresholdConfig(d),
Notifications: expandAlertConfigurationNotification(d),
}

notifications, err := expandAlertConfigurationNotification(d)
if err != nil {
return diag.FromErr(fmt.Errorf(errorCreateAlertConf, err))
}
req.Notifications = notifications

resp, _, err := conn.AlertConfigurations.Create(ctx, projectID, req)
if err != nil {
return diag.FromErr(fmt.Errorf(errorCreateAlertConf, err))
Expand Down Expand Up @@ -412,7 +424,11 @@ func resourceMongoDBAtlasAlertConfigurationUpdate(ctx context.Context, d *schema
}

if d.HasChange("notification") {
req.Notifications = expandAlertConfigurationNotification(d)
notifications, err := expandAlertConfigurationNotification(d)
if err != nil {
return diag.FromErr(fmt.Errorf(errorUpdateAlertConf, err))
}
req.Notifications = notifications
}

// Cannot enable/disable ONLY via update (if only send enable as changed field server returns a 500 error) so have to use different method to change enabled.
Expand All @@ -424,7 +440,7 @@ func resourceMongoDBAtlasAlertConfigurationUpdate(ctx context.Context, d *schema
}

if err != nil {
return diag.FromErr(fmt.Errorf(errorReadAlertConf, err))
return diag.FromErr(fmt.Errorf(errorUpdateAlertConf, err))
}

return resourceMongoDBAtlasAlertConfigurationRead(ctx, d, meta)
Expand Down Expand Up @@ -670,11 +686,17 @@ func flattenAlertConfigurationThresholdConfig(m *matlas.Threshold) []interface{}
return []interface{}{}
}

func expandAlertConfigurationNotification(d *schema.ResourceData) []matlas.Notification {
func expandAlertConfigurationNotification(d *schema.ResourceData) ([]matlas.Notification, error) {
notifications := make([]matlas.Notification, len(d.Get("notification").([]interface{})))

for i, value := range d.Get("notification").([]interface{}) {
v := value.(map[string]interface{})
if v1, ok := v["interval_min"]; ok && v1.(int) > 0 {
typeName := v["type_name"].(string)
if strings.EqualFold(typeName, pagerDuty) || strings.EqualFold(typeName, opsGenie) || strings.EqualFold(typeName, victorOps) {
return nil, fmt.Errorf(`'interval_min' doesn't need to be set if type_name is 'PAGER_DUTY', 'OPS_GENIE' or 'VICTOR_OPS'`)
}
}
notifications[i] = matlas.Notification{
APIToken: cast.ToString(v["api_token"]),
ChannelName: cast.ToString(v["channel_name"]),
Expand All @@ -701,7 +723,7 @@ func expandAlertConfigurationNotification(d *schema.ResourceData) []matlas.Notif
}
}

return notifications
return notifications, nil
}

func flattenAlertConfigurationNotifications(notifications []matlas.Notification) []map[string]interface{} {
Expand Down
125 changes: 125 additions & 0 deletions mongodbatlas/resource_mongodbatlas_alert_configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,81 @@ func TestAccResourceMongoDBAtlasAlertConfiguration_DataDog(t *testing.T) {
})
}

func TestAccResourceMongoDBAtlasAlertConfiguration_PagerDuty(t *testing.T) {
SkipTestExtCred(t) // Will skip because requires external credentials aka api key
var (
resourceName = "mongodbatlas_alert_configuration.test"
projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID")
serviceKey = os.Getenv("PAGER_DUTY_SERVICE_KEY")
alert = &matlas.AlertConfiguration{}
)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckMongoDBAtlasAlertConfigurationDestroy,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasAlertConfigurationPagerDutyConfig(projectID, serviceKey, true),
Check: resource.ComposeTestCheckFunc(
testAccCheckMongoDBAtlasAlertConfigurationExists(resourceName, alert),
resource.TestCheckResourceAttrSet(resourceName, "project_id"),
),
},
},
})
}

func TestAccResourceMongoDBAtlasAlertConfiguration_OpsGenie(t *testing.T) {
SkipTestExtCred(t) // Will skip because requires external credentials aka api key
var (
resourceName = "mongodbatlas_alert_configuration.test"
projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID")
apiKey = os.Getenv("OPS_GENIE_API_KEY")
alert = &matlas.AlertConfiguration{}
)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckMongoDBAtlasAlertConfigurationDestroy,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasAlertConfigurationOpsGenieConfig(projectID, apiKey, true),
Check: resource.ComposeTestCheckFunc(
testAccCheckMongoDBAtlasAlertConfigurationExists(resourceName, alert),
resource.TestCheckResourceAttrSet(resourceName, "project_id"),
),
},
},
})
}

func TestAccResourceMongoDBAtlasAlertConfiguration_VictorOps(t *testing.T) {
SkipTestExtCred(t) // Will skip because requires external credentials aka api key
var (
resourceName = "mongodbatlas_alert_configuration.test"
projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID")
apiKey = os.Getenv("VICTOR_OPS_API_KEY")
alert = &matlas.AlertConfiguration{}
)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckMongoDBAtlasAlertConfigurationDestroy,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasAlertConfigurationVictorOpsConfig(projectID, apiKey, true),
Check: resource.ComposeTestCheckFunc(
testAccCheckMongoDBAtlasAlertConfigurationExists(resourceName, alert),
resource.TestCheckResourceAttrSet(resourceName, "project_id"),
),
},
},
})
}

func testAccCheckMongoDBAtlasAlertConfigurationExists(resourceName string, alert *matlas.AlertConfiguration) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*MongoDBClient).Atlas
Expand Down Expand Up @@ -578,3 +653,53 @@ resource "mongodbatlas_alert_configuration" "test" {
}
`, projectID, enabled, dataDogAPIKey, dataDogRegion)
}

func testAccMongoDBAtlasAlertConfigurationPagerDutyConfig(projectID, serviceKey string, enabled bool) string {
return fmt.Sprintf(`
resource "mongodbatlas_alert_configuration" "test" {
project_id = %[1]q
event_type = "NO_PRIMARY"
enabled = "%[3]t"
notification {
type_name = "PAGER_DUTY"
service_key = %[2]q
delay_min = 0
}
}
`, projectID, serviceKey, enabled)
}

func testAccMongoDBAtlasAlertConfigurationOpsGenieConfig(projectID, apiKey string, enabled bool) string {
return fmt.Sprintf(`
resource "mongodbatlas_alert_configuration" "test" {
project_id = %[1]q
event_type = "NO_PRIMARY"
enabled = "%[3]t"
notification {
type_name = "OPS_GENIE"
ops_genie_api_key = %[2]q
ops_genie_region = "US"
delay_min = 0
}
}
`, projectID, apiKey, enabled)
}

func testAccMongoDBAtlasAlertConfigurationVictorOpsConfig(projectID, apiKey string, enabled bool) string {
return fmt.Sprintf(`
resource "mongodbatlas_alert_configuration" "test" {
project_id = %[1]q
event_type = "NO_PRIMARY"
enabled = "%[3]t"
notification {
type_name = "VICTOR_OPS"
victor_ops_api_key = %[2]q
victor_ops_routing_key = "testing"
delay_min = 0
}
}
`, projectID, apiKey, enabled)
}
2 changes: 1 addition & 1 deletion website/docs/r/alert_configuration.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ Notifications to send when an alert condition is detected.
* `email_enabled` - Flag indicating if email notifications should be sent. Configurable for `ORG`, `GROUP`, and `USER` notifications types.
* `flowdock_api_token` - The Flowdock personal API token. Required for the `FLOWDOCK` notifications type. If the token later becomes invalid, Atlas sends an email to the project owner and eventually removes the token.
* `flow_name` - Flowdock flow name in lower-case letters. Required for the `FLOWDOCK` notifications type
* `interval_min` - Number of minutes to wait between successive notifications for unacknowledged alerts that are not resolved. The minimum value is 5. **CONDITIONAL** PAGER_DUTY manages the interval value, please do not set it in case of PAGER_DUTY
* `interval_min` - Number of minutes to wait between successive notifications for unacknowledged alerts that are not resolved. The minimum value is 5. **NOTE** `PAGER_DUTY`, `VICTOR_OPS`, and `OPS_GENIE` notifications do not return this value. The notification interval must be configured and managed within each external service.
* `mobile_number` - Mobile number to which alert notifications are sent. Required for the SMS notifications type.
* `ops_genie_api_key` - Opsgenie API Key. Required for the `OPS_GENIE` notifications type. If the key later becomes invalid, Atlas sends an email to the project owner and eventually removes the token.
* `ops_genie_region` - Region that indicates which API URL to use. Accepted regions are: `US` ,`EU`. The default Opsgenie region is US.
Expand Down

0 comments on commit 30bcded

Please sign in to comment.