Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(alerts): Add incident title template support #2662

Merged
merged 9 commits into from
Aug 12, 2024
5 changes: 5 additions & 0 deletions newrelic/resource_newrelic_nrql_alert_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ func resourceNewRelicNrqlAlertCondition() *schema.Resource {
Optional: true,
Description: "The description of the NRQL alert condition.",
},
"incident_title_template": {
Type: schema.TypeString,
Optional: true,
Description: "This field allows you to create a custom incident title to be used when incidents are opened by the condition. Setting this field will override the default incident title. Must be Handlebars format.",
},
"violation_time_limit": {
Type: schema.TypeString,
Deprecated: "use `violation_time_limit_seconds` attribute instead",
Expand Down
174 changes: 142 additions & 32 deletions newrelic/resource_newrelic_nrql_alert_condition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,39 @@ func TestAccNewRelicNrqlAlertCondition_StaticConditionEvaluationDelay(t *testing
})
}

func TestAccNewRelicNrqlAlertCondition_IncidentTitleTemplate(t *testing.T) {
resourceName := "newrelic_nrql_alert_condition.foo"
rName := acctest.RandString(5)
incidentTitleTemplate := "Incident Title {{Template}}"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheckEnvVars(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNewRelicNrqlAlertConditionDestroy,
Steps: []resource.TestStep{
// Test: Create condition with an incident title template
{
Config: testAccNewRelicNrqlAlertConditionWithIncidentTitleTemplate(
rName,
incidentTitleTemplate,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckNewRelicNrqlAlertConditionExists(resourceName),
),
},
// Test: Incident title template is nullable
{
Config: testAccNewRelicNrqlAlertConditionNullIncidentTitleTemplate(
rName,
),
Check: resource.ComposeTestCheckFunc(
testAccCheckNewRelicNrqlAlertConditionExists(resourceName),
),
},
},
})
}

func testAccCheckNewRelicNrqlAlertConditionDestroy(s *terraform.State) error {
providerConfig := testAccProvider.Meta().(*ProviderConfig)
client := providerConfig.NewClient
Expand Down Expand Up @@ -874,10 +907,10 @@ resource "newrelic_nrql_alert_condition" "foo" {
violation_time_limit_seconds = 86400

critical {
operator = "above"
threshold = 80
operator = "above"
threshold = 80
threshold_duration = 5 * 60
threshold_occurrences = "all"
threshold_occurrences = "all"
}

nrql {
Expand All @@ -900,33 +933,33 @@ func testAccNewRelicNrqlAlertConditionSinceValue(

resource "newrelic_nrql_alert_condition" "since_value" {
policy_id = newrelic_alert_policy.foo.id
type = "static"
type = "static"
name = "tf-test-%[1]s"
description = "Test desc"
runbook_url = "REDACTED"
enabled = true
violation_time_limit_seconds = 86400
expiration_duration = 120
close_violations_on_expiration = true

nrql {
description = "Test desc"
runbook_url = "REDACTED"
enabled = true
violation_time_limit_seconds = 86400
expiration_duration = 120
close_violations_on_expiration = true

nrql {
query = <<-EOT
SELECT count(*) FROM TestEvent
EOT
since_value = 1
}
critical {
}
critical {
operator = "above"
threshold = 50
threshold_duration = 300
time_function = "any"
}
warning {
}
warning {
operator = "above"
threshold = 10
threshold_duration = 300
time_function = "any"
}
}
}
`, name)
}
Expand All @@ -941,34 +974,34 @@ func testAccNewRelicNrqlAlertConditionSinceValueUpdateWithStreamingMethods(

resource "newrelic_nrql_alert_condition" "since_value" {
policy_id = newrelic_alert_policy.foo.id
type = "static"
type = "static"
name = "tf-test-%[1]s"
description = "Test desc"
runbook_url = "REDACTED"
enabled = true
violation_time_limit_seconds = 86400
expiration_duration = 120
close_violations_on_expiration = true
description = "Test desc"
runbook_url = "REDACTED"
enabled = true
violation_time_limit_seconds = 86400
expiration_duration = 120
close_violations_on_expiration = true
aggregation_method = "event_timer"
aggregation_timer = 1200

nrql {
nrql {
query = <<-EOT
SELECT count(*) FROM TestEvent
EOT
}
critical {
}
critical {
operator = "above"
threshold = 50
threshold_duration = 300
time_function = "any"
}
warning {
}
warning {
operator = "above"
threshold = 10
threshold_duration = 300
time_function = "any"
}
}
}
`, name)
}
Expand Down Expand Up @@ -1081,7 +1114,7 @@ resource "newrelic_nrql_alert_condition" "foo" {
close_violations_on_expiration = true
open_violation_on_expiration = true
expiration_duration = 120
aggregation_delay = 120
aggregation_delay = 120
aggregation_method = "event_flow"
slide_by = 30

Expand Down Expand Up @@ -1119,7 +1152,7 @@ resource "newrelic_nrql_alert_condition" "foo" {
close_violations_on_expiration = true
open_violation_on_expiration = true
expiration_duration = 120
aggregation_delay = 120
aggregation_delay = 120
aggregation_method = "event_flow"
slide_by = 30
evaluation_delay = 60
Expand All @@ -1137,3 +1170,80 @@ resource "newrelic_nrql_alert_condition" "foo" {
}
`, name)
}

func testAccNewRelicNrqlAlertConditionWithIncidentTitleTemplate(
name string,
incidentTitleTemplate string,
) string {
return fmt.Sprintf(`
resource "newrelic_alert_policy" "foo" {
name = "tf-test-%[1]s"
}

resource "newrelic_nrql_alert_condition" "foo" {
policy_id = newrelic_alert_policy.foo.id

name = "tf-test-%[1]s"
type = "static"
runbook_url = "https://foo.example.com"
enabled = false
description = "test description"
incident_title_template = "%[2]s"
violation_time_limit_seconds = 3600
close_violations_on_expiration = true
open_violation_on_expiration = true
expiration_duration = 120
aggregation_delay = 120
aggregation_method = "event_flow"

nrql {
query = "SELECT uniqueCount(hostname) FROM ComputeSample"
}

critical {
operator = "above"
threshold = 0
threshold_duration = 120
threshold_occurrences = "ALL"
}
}
`, name, incidentTitleTemplate)
}

func testAccNewRelicNrqlAlertConditionNullIncidentTitleTemplate(
name string,
) string {
return fmt.Sprintf(`
resource "newrelic_alert_policy" "foo" {
name = "tf-test-%[1]s"
}

resource "newrelic_nrql_alert_condition" "foo" {
policy_id = newrelic_alert_policy.foo.id

name = "tf-test-%[1]s"
type = "static"
runbook_url = "https://foo.example.com"
enabled = false
description = "test description"
incident_title_template = null
violation_time_limit_seconds = 3600
close_violations_on_expiration = true
open_violation_on_expiration = true
expiration_duration = 120
aggregation_delay = 120
aggregation_method = "event_flow"

nrql {
query = "SELECT uniqueCount(hostname) FROM ComputeSample"
}

critical {
operator = "above"
threshold = 0
threshold_duration = 120
threshold_occurrences = "ALL"
}
}
`, name)
}
11 changes: 11 additions & 0 deletions newrelic/structures_newrelic_nrql_alert_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
input.RunbookURL = runbookURL.(string)
}

if incidentTitleTemplate, ok := d.GetOk("incident_title_template"); ok {
incidentTitle := incidentTitleTemplate.(string)
input.IncidentTitleTemplate = &incidentTitle

Check failure on line 81 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / compile (1.21.x, ubuntu-latest)

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionCreateInput has no field or method IncidentTitleTemplate)

Check failure on line 81 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / test-unit

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionCreateInput has no field or method IncidentTitleTemplate)

Check failure on line 81 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / test-unit

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionCreateInput has no field or method IncidentTitleTemplate)

Check failure on line 81 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / compile (1.21.x, macos-latest)

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionCreateInput has no field or method IncidentTitleTemplate)

Check failure on line 81 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / lint

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionCreateInput has no field or method IncidentTitleTemplate)

Check failure on line 81 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / lint

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionCreateInput has no field or method IncidentTitleTemplate)
}

if violationTimeLimitSec, ok := d.GetOk("violation_time_limit_seconds"); ok {
input.ViolationTimeLimitSeconds = violationTimeLimitSec.(int)
} else if violationTimeLimit, ok := d.GetOk("violation_time_limit"); ok {
Expand Down Expand Up @@ -131,6 +136,11 @@
input.RunbookURL = runbookURL.(string)
}

if incidentTitleTemplate, ok := d.GetOk("incident_title_template"); ok {
incidentTitle := incidentTitleTemplate.(string)
input.IncidentTitleTemplate = &incidentTitle

Check failure on line 141 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / compile (1.21.x, ubuntu-latest)

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionUpdateInput has no field or method IncidentTitleTemplate)

Check failure on line 141 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / test-unit

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionUpdateInput has no field or method IncidentTitleTemplate)

Check failure on line 141 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / test-unit

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionUpdateInput has no field or method IncidentTitleTemplate)

Check failure on line 141 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / compile (1.21.x, macos-latest)

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionUpdateInput has no field or method IncidentTitleTemplate)

Check failure on line 141 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / lint

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionUpdateInput has no field or method IncidentTitleTemplate)

Check failure on line 141 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / lint

input.IncidentTitleTemplate undefined (type alerts.NrqlConditionUpdateInput has no field or method IncidentTitleTemplate)
}

if violationTimeLimitSec, ok := d.GetOk("violation_time_limit_seconds"); ok {
input.ViolationTimeLimitSeconds = violationTimeLimitSec.(int)
} else if violationTimeLimit, ok := d.GetOk("violation_time_limit"); ok {
Expand Down Expand Up @@ -499,6 +509,7 @@
_ = d.Set("policy_id", policyID)
_ = d.Set("name", condition.Name)
_ = d.Set("runbook_url", condition.RunbookURL)
_ = d.Set("incident_title_template", condition.IncidentTitleTemplate)

Check failure on line 512 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / compile (1.21.x, ubuntu-latest)

condition.IncidentTitleTemplate undefined (type *alerts.NrqlAlertCondition has no field or method IncidentTitleTemplate)

Check failure on line 512 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / test-unit

condition.IncidentTitleTemplate undefined (type *alerts.NrqlAlertCondition has no field or method IncidentTitleTemplate)

Check failure on line 512 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / test-unit

condition.IncidentTitleTemplate undefined (type *alerts.NrqlAlertCondition has no field or method IncidentTitleTemplate)

Check failure on line 512 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / compile (1.21.x, macos-latest)

condition.IncidentTitleTemplate undefined (type *alerts.NrqlAlertCondition has no field or method IncidentTitleTemplate)

Check failure on line 512 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / lint

condition.IncidentTitleTemplate undefined (type *alerts.NrqlAlertCondition has no field or method IncidentTitleTemplate)) (typecheck)

Check failure on line 512 in newrelic/structures_newrelic_nrql_alert_condition.go

View workflow job for this annotation

GitHub Actions / lint

condition.IncidentTitleTemplate undefined (type *alerts.NrqlAlertCondition has no field or method IncidentTitleTemplate) (typecheck)
_ = d.Set("enabled", condition.Enabled)
_ = d.Set("entity_guid", condition.EntityGUID)

Expand Down
33 changes: 30 additions & 3 deletions newrelic/structures_newrelic_nrql_alert_condition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func TestExpandNrqlAlertConditionInput(t *testing.T) {
evalOffset := nrql["evaluation_offset"].(int)
expectedNrql.Nrql.EvaluationOffset = &evalOffset

incidentTitleTemplate := "Incident title {{template}}"

cases := map[string]struct {
Data map[string]interface{}
ExpectErr bool
Expand Down Expand Up @@ -356,6 +358,26 @@ func TestExpandNrqlAlertConditionInput(t *testing.T) {
},
},
},
"incident title template nil": {
Data: map[string]interface{}{
"nrql": []interface{}{nrql},
"incident_title_template": nil,
},
Expanded: &alerts.NrqlConditionCreateInput{
NrqlConditionCreateBase: alerts.NrqlConditionCreateBase{},
},
},
"incident title template not nill": {
Data: map[string]interface{}{
"nrql": []interface{}{nrql},
"incident_title_template": "Incident title {{template}}",
},
Expanded: &alerts.NrqlConditionCreateInput{
NrqlConditionCreateBase: alerts.NrqlConditionCreateBase{
IncidentTitleTemplate: &incidentTitleTemplate,
},
},
},
}

r := resourceNewRelicNrqlAlertCondition()
Expand Down Expand Up @@ -423,14 +445,16 @@ func TestExpandNrqlAlertConditionInput(t *testing.T) {
func TestFlattenNrqlAlertCondition(t *testing.T) {
r := resourceNewRelicNrqlAlertCondition()
evalOffset := 3
incidentTitleTemplate := "Incident title {{template}}"

nrqlCondition := alerts.NrqlAlertCondition{
ID: "1234567",
PolicyID: "7654321",
NrqlConditionBase: alerts.NrqlConditionBase{
Description: "description test",
Enabled: true,
Name: "name-test",
Description: "description test",
IncidentTitleTemplate: &incidentTitleTemplate,
Enabled: true,
Name: "name-test",
Nrql: alerts.NrqlConditionQuery{
Query: "SELECT average(duration) from Transaction where appName='Dummy App'",
EvaluationOffset: &evalOffset,
Expand Down Expand Up @@ -541,6 +565,9 @@ func TestFlattenNrqlAlertCondition(t *testing.T) {
assert.Equal(t, 660, warningTerms[0].(map[string]interface{})["threshold_duration"])
assert.Equal(t, "below", warningTerms[0].(map[string]interface{})["operator"])

incidentTitleTemplate := d.Get("incident_title_template").(string)
assert.Equal(t, "Incident title {{template}}", incidentTitleTemplate)

switch condition.Type {
case alerts.NrqlConditionTypes.Baseline:
require.Equal(t, string(alerts.NrqlBaselineDirections.LowerOnly), d.Get("baseline_direction").(string))
Expand Down
Loading