Skip to content

Commit 5ab97ee

Browse files
authored
Rule group detect rule name conflicts (#1610)
* grafana_rule_group: Detect rule name conflicts * update grafana_rule_group unit test * revert alert group name check * Fix undeclared variables * fix resource_alerting_rule_group unit test
1 parent 8c78dfc commit 5ab97ee

File tree

2 files changed

+106
-17
lines changed

2 files changed

+106
-17
lines changed

internal/resources/grafana/resource_alerting_rule_group.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -340,18 +340,19 @@ func readAlertRuleGroup(ctx context.Context, data *schema.ResourceData, meta int
340340
func putAlertRuleGroup(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
341341
client, orgID := OAPIClientFromNewOrgResource(meta, data)
342342

343+
respAlertRules, err := client.Provisioning.GetAlertRules()
344+
if err != nil {
345+
return diag.FromErr(err)
346+
}
347+
343348
if data.IsNewResource() {
344349
// Check if a rule group with the same name already exists. The API either:
345-
// - returns a 500 error if it exists in a different folder, which is not very helpful.
346350
// - overwrites the existing rule group if it exists in the same folder, which is not expected of a TF provider.
347-
resp, err := client.Provisioning.GetAlertRules()
348-
if err != nil {
349-
return diag.FromErr(err)
350-
}
351351

352-
for _, rule := range resp.Payload {
352+
for _, rule := range respAlertRules.Payload {
353353
name := data.Get("name").(string)
354-
if *rule.RuleGroup == name {
354+
folder := data.Get("folder_uid").(string)
355+
if *rule.RuleGroup == name && *rule.FolderUID == folder {
355356
return diag.Errorf("rule group with name %q already exists", name)
356357
}
357358
}
@@ -363,11 +364,26 @@ func putAlertRuleGroup(ctx context.Context, data *schema.ResourceData, meta inte
363364

364365
packedRules := data.Get("rule").([]interface{})
365366
rules := make([]*models.ProvisionedAlertRule, 0, len(packedRules))
367+
366368
for i := range packedRules {
367369
rule, err := unpackAlertRule(packedRules[i], group, folder, orgID)
368370
if err != nil {
369371
return diag.FromErr(err)
370372
}
373+
374+
// Check if a rule with the same name already exists.
375+
for _, r := range rules {
376+
if *r.Title == *rule.Title {
377+
return diag.Errorf("rule with name %q is defined more than once", *rule.Title)
378+
}
379+
}
380+
381+
for _, r := range respAlertRules.Payload {
382+
if r.UID != rule.UID && *r.Title == *rule.Title && *r.FolderUID == *rule.FolderUID {
383+
return diag.Errorf("rule with name %q already exists in the alert group", *rule.Title)
384+
}
385+
}
386+
371387
rules = append(rules, rule)
372388
}
373389

internal/resources/grafana/resource_alerting_rule_group_test.go

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -307,15 +307,15 @@ resource "grafana_organization" "test" {
307307
name = "%[1]s"
308308
}
309309
310-
resource "grafana_folder" "first" {
310+
resource "grafana_folder" "test" {
311311
org_id = grafana_organization.test.id
312-
title = "%[1]s-first"
312+
title = "%[1]s-test"
313313
}
314314
315315
resource "grafana_rule_group" "first" {
316316
org_id = grafana_organization.test.id
317317
name = "%[1]s"
318-
folder_uid = grafana_folder.first.uid
318+
folder_uid = grafana_folder.test.uid
319319
interval_seconds = 60
320320
rule {
321321
name = "My Alert Rule first"
@@ -342,16 +342,11 @@ resource "grafana_rule_group" "first" {
342342
}
343343
}
344344
345-
resource "grafana_folder" "second" {
346-
depends_on = [grafana_rule_group.first]
347-
org_id = grafana_organization.test.id
348-
title = "%[1]s-second"
349-
}
350-
351345
resource "grafana_rule_group" "second" {
346+
depends_on = [ grafana_rule_group.first ]
352347
org_id = grafana_organization.test.id
353348
name = "%[1]s"
354-
folder_uid = grafana_folder.second.uid
349+
folder_uid = grafana_folder.test.uid
355350
interval_seconds = 60
356351
rule {
357352
name = "My Alert Rule second"
@@ -384,6 +379,84 @@ resource "grafana_rule_group" "second" {
384379
})
385380
}
386381

382+
func TestAccAlertRule_ruleNameConflict(t *testing.T) {
383+
testutils.CheckOSSTestsEnabled(t, ">=9.1.0")
384+
385+
name := acctest.RandString(10)
386+
387+
resource.ParallelTest(t, resource.TestCase{
388+
ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
389+
Steps: []resource.TestStep{
390+
{
391+
Config: fmt.Sprintf(`
392+
resource "grafana_organization" "test" {
393+
name = "%[1]s"
394+
}
395+
396+
resource "grafana_folder" "first" {
397+
org_id = grafana_organization.test.id
398+
title = "%[1]s-first"
399+
}
400+
401+
resource "grafana_rule_group" "first" {
402+
org_id = grafana_organization.test.id
403+
name = "alert rule group"
404+
folder_uid = grafana_folder.first.uid
405+
interval_seconds = 60
406+
rule {
407+
name = "My Alert Rule"
408+
for = "2m"
409+
condition = "B"
410+
no_data_state = "NoData"
411+
exec_err_state = "Alerting"
412+
is_paused = false
413+
data {
414+
ref_id = "A"
415+
query_type = ""
416+
relative_time_range {
417+
from = 600
418+
to = 0
419+
}
420+
datasource_uid = "PD8C576611E62080A"
421+
model = jsonencode({
422+
hide = false
423+
intervalMs = 1000
424+
maxDataPoints = 43200
425+
refId = "A"
426+
})
427+
}
428+
}
429+
rule {
430+
name = "My Alert Rule"
431+
for = "2m"
432+
condition = "B"
433+
no_data_state = "NoData"
434+
exec_err_state = "Alerting"
435+
is_paused = false
436+
data {
437+
ref_id = "A"
438+
query_type = ""
439+
relative_time_range {
440+
from = 600
441+
to = 0
442+
}
443+
datasource_uid = "PD8C576611E62080A"
444+
model = jsonencode({
445+
hide = false
446+
intervalMs = 1000
447+
maxDataPoints = 43200
448+
refId = "A"
449+
})
450+
}
451+
}
452+
}
453+
`, name),
454+
ExpectError: regexp.MustCompile(`rule with name "My Alert Rule" is defined more than once`),
455+
},
456+
},
457+
})
458+
}
459+
387460
func TestAccAlertRule_disableProvenance(t *testing.T) {
388461
testutils.CheckOSSTestsEnabled(t, ">=9.1.0")
389462

0 commit comments

Comments
 (0)