Skip to content

Commit 7dafddf

Browse files
Config Generation: Add tests for alerting resources (#1598)
They deal with an alertmanager that needs to be provisioned for each org so it's good to add a test for this I also investigated why contact points can't be generated. I think it's fixable. To be seen
1 parent 6879e52 commit 7dafddf

File tree

8 files changed

+395
-18
lines changed

8 files changed

+395
-18
lines changed

internal/resources/grafana/resource_alerting_contact_point.go

+25-12
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ This resource requires Grafana 9.1.0 or later.
105105
)
106106
}
107107

108-
// TODO: Fix lister
109-
// .WithLister(listerFunction(listContactPoints))
108+
// TODO: Fix contact points lister. Terraform doesn't read any of the sensitive fields (or their container)
109+
// It outputs an empty `email {}` block for example, which is not valid.
110110
// func listContactPoints(ctx context.Context, client *goapi.GrafanaHTTPAPI, data *ListerData) ([]string, error) {
111111
// orgIDs, err := data.OrgIDs(client)
112112
// if err != nil {
@@ -117,14 +117,24 @@ This resource requires Grafana 9.1.0 or later.
117117
// for _, orgID := range orgIDs {
118118
// client = client.Clone().WithOrgID(orgID)
119119

120-
// resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams())
121-
// if err != nil {
120+
// // Retry if the API returns 500 because it may be that the alertmanager is not ready in the org yet.
121+
// // The alertmanager is provisioned asynchronously when the org is created.
122+
// if err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError {
123+
// resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams())
124+
// if err != nil {
125+
// if orgID > 1 && (err.(*runtime.APIError).IsCode(500) || err.(*runtime.APIError).IsCode(403)) {
126+
// return retry.RetryableError(err)
127+
// }
128+
// return retry.NonRetryableError(err)
129+
// }
130+
131+
// for _, contactPoint := range resp.Payload {
132+
// idMap[MakeOrgResourceID(orgID, contactPoint.Name)] = true
133+
// }
134+
// return nil
135+
// }); err != nil {
122136
// return nil, err
123137
// }
124-
125-
// for _, contactPoint := range resp.Payload {
126-
// idMap[MakeOrgResourceID(orgID, contactPoint.Name)] = true
127-
// }
128138
// }
129139

130140
// var ids []string
@@ -138,13 +148,16 @@ This resource requires Grafana 9.1.0 or later.
138148
func readContactPoint(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
139149
client, orgID, name := OAPIClientFromExistingOrgResource(meta, data.Id())
140150

141-
// First, try to fetch the contact point by name.
142-
// If that fails, try to fetch it by the UID of its notifiers.
143-
resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams().WithName(&name))
151+
resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams())
144152
if err != nil {
145153
return diag.FromErr(err)
146154
}
147-
points := resp.Payload
155+
var points []*models.EmbeddedContactPoint
156+
for _, p := range resp.Payload {
157+
if p.Name == name {
158+
points = append(points, p)
159+
}
160+
}
148161
if len(points) == 0 {
149162
return common.WarnMissing("contact point", data)
150163
}

internal/resources/grafana/resource_alerting_notification_policy.go

+18
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,24 @@ func listNotificationPolicies(ctx context.Context, client *goapi.GrafanaHTTPAPI,
198198

199199
var ids []string
200200
for _, orgID := range orgIDs {
201+
client = client.Clone().WithOrgID(orgID)
202+
203+
// Retry if the API returns 500 because it may be that the alertmanager is not ready in the org yet.
204+
// The alertmanager is provisioned asynchronously when the org is created.
205+
if err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError {
206+
_, err := client.Provisioning.GetPolicyTree()
207+
if err != nil {
208+
if orgID > 1 && (err.(*runtime.APIError).IsCode(500) || err.(*runtime.APIError).IsCode(403)) {
209+
return retry.RetryableError(err)
210+
}
211+
return retry.NonRetryableError(err)
212+
}
213+
214+
return nil
215+
}); err != nil {
216+
return nil, err
217+
}
218+
201219
ids = append(ids, MakeOrgResourceID(orgID, PolicySingletonID))
202220
}
203221

internal/resources/grafana/resource_alerting_rule_group.go

+18-6
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import (
99
"strings"
1010
"time"
1111

12+
"github.com/go-openapi/runtime"
1213
"github.com/go-openapi/strfmt"
1314
goapi "github.com/grafana/grafana-openapi-client-go/client"
1415
"github.com/grafana/grafana-openapi-client-go/client/provisioning"
1516
"github.com/grafana/grafana-openapi-client-go/models"
1617
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
18+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
1719
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1820
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1921

@@ -264,13 +266,23 @@ func listRuleGroups(ctx context.Context, client *goapi.GrafanaHTTPAPI, data *Lis
264266
for _, orgID := range orgIDs {
265267
client = client.Clone().WithOrgID(orgID)
266268

267-
resp, err := client.Provisioning.GetAlertRules()
268-
if err != nil {
269-
return nil, err
270-
}
269+
// Retry if the API returns 500 because it may be that the alertmanager is not ready in the org yet.
270+
// The alertmanager is provisioned asynchronously when the org is created.
271+
if err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError {
272+
resp, err := client.Provisioning.GetAlertRules()
273+
if err != nil {
274+
if orgID > 1 && (err.(*runtime.APIError).IsCode(500) || err.(*runtime.APIError).IsCode(403)) {
275+
return retry.RetryableError(err)
276+
}
277+
return retry.NonRetryableError(err)
278+
}
271279

272-
for _, rule := range resp.Payload {
273-
idMap[MakeOrgResourceID(orgID, resourceRuleGroupID.Make(rule.FolderUID, rule.RuleGroup))] = true
280+
for _, rule := range resp.Payload {
281+
idMap[resourceRuleGroupID.Make(orgID, rule.FolderUID, rule.RuleGroup)] = true
282+
}
283+
return nil
284+
}); err != nil {
285+
return nil, err
274286
}
275287
}
276288

pkg/generate/generate_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ func TestAccGenerate(t *testing.T) {
9090
})
9191
},
9292
},
93+
{
94+
name: "alerting-in-org",
95+
config: func() string {
96+
content, err := os.ReadFile("testdata/generate/alerting-in-org.tf")
97+
require.NoError(t, err)
98+
return string(content)
99+
}(),
100+
check: func(t *testing.T, tempDir string) {
101+
assertFiles(t, tempDir, "testdata/generate/alerting-in-org", []string{
102+
".terraform",
103+
".terraform.lock.hcl",
104+
})
105+
},
106+
},
93107
}
94108

95109
for _, tc := range cases {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
resource "grafana_organization" "test" {
2+
name = "alerting-org"
3+
}
4+
5+
resource "grafana_contact_point" "test" {
6+
org_id = grafana_organization.test.id
7+
name = "my-contact-point"
8+
email {
9+
addresses = ["[email protected]"]
10+
}
11+
}
12+
13+
resource "grafana_notification_policy" "test" {
14+
org_id = grafana_organization.test.id
15+
contact_point = grafana_contact_point.test.name
16+
group_by = ["..."]
17+
}
18+
19+
resource "grafana_mute_timing" "my_mute_timing" {
20+
org_id = grafana_organization.test.id
21+
name = "My Mute Timing"
22+
23+
intervals {
24+
times {
25+
start = "04:56"
26+
end = "14:17"
27+
}
28+
weekdays = ["monday", "tuesday:thursday"]
29+
days_of_month = ["1:7", "-1"]
30+
months = ["1:3", "december"]
31+
years = ["2030", "2025:2026"]
32+
location = "America/New_York"
33+
}
34+
}
35+
36+
resource "grafana_message_template" "my_template" {
37+
org_id = grafana_organization.test.id
38+
name = "My Reusable Template"
39+
template = "{{define \"My Reusable Template\" }}\n template content\n{{ end }}"
40+
}
41+
42+
resource "grafana_folder" "rule_folder" {
43+
org_id = grafana_organization.test.id
44+
title = "My Alert Rule Folder"
45+
uid = "alert-rule-folder"
46+
}
47+
48+
resource "grafana_rule_group" "my_alert_rule" {
49+
org_id = grafana_organization.test.id
50+
name = "My Rule Group"
51+
folder_uid = grafana_folder.rule_folder.uid
52+
interval_seconds = 240
53+
rule {
54+
name = "My Alert Rule 1"
55+
for = "2m"
56+
condition = "B"
57+
no_data_state = "NoData"
58+
exec_err_state = "Alerting"
59+
annotations = {
60+
"a" = "b"
61+
"c" = "d"
62+
}
63+
labels = {
64+
"e" = "f"
65+
"g" = "h"
66+
}
67+
is_paused = false
68+
data {
69+
ref_id = "A"
70+
query_type = ""
71+
relative_time_range {
72+
from = 600
73+
to = 0
74+
}
75+
datasource_uid = "PD8C576611E62080A"
76+
model = jsonencode({
77+
hide = false
78+
intervalMs = 1000
79+
maxDataPoints = 43200
80+
refId = "A"
81+
})
82+
}
83+
data {
84+
ref_id = "B"
85+
query_type = ""
86+
relative_time_range {
87+
from = 0
88+
to = 0
89+
}
90+
datasource_uid = "-100"
91+
model = <<EOT
92+
{
93+
"conditions": [
94+
{
95+
"evaluator": {
96+
"params": [
97+
3
98+
],
99+
"type": "gt"
100+
},
101+
"operator": {
102+
"type": "and"
103+
},
104+
"query": {
105+
"params": [
106+
"A"
107+
]
108+
},
109+
"reducer": {
110+
"params": [],
111+
"type": "last"
112+
},
113+
"type": "query"
114+
}
115+
],
116+
"datasource": {
117+
"type": "__expr__",
118+
"uid": "-100"
119+
},
120+
"hide": false,
121+
"intervalMs": 1000,
122+
"maxDataPoints": 43200,
123+
"refId": "B",
124+
"type": "classic_conditions"
125+
}
126+
EOT
127+
}
128+
}
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {
2+
to = grafana_folder._2_alert-rule-folder
3+
id = "2:alert-rule-folder"
4+
}
5+
6+
import {
7+
to = grafana_message_template._2_My_Reusable_Template
8+
id = "2:My Reusable Template"
9+
}
10+
11+
import {
12+
to = grafana_mute_timing._2_My_Mute_Timing
13+
id = "2:My Mute Timing"
14+
}
15+
16+
import {
17+
to = grafana_notification_policy._2_policy
18+
id = "2:policy"
19+
}
20+
21+
import {
22+
to = grafana_notification_policy._1_policy
23+
id = "1:policy"
24+
}
25+
26+
import {
27+
to = grafana_organization._2
28+
id = "2"
29+
}
30+
31+
import {
32+
to = grafana_organization_preferences._2
33+
id = "2"
34+
}
35+
36+
import {
37+
to = grafana_organization_preferences._1
38+
id = "1"
39+
}
40+
41+
import {
42+
to = grafana_rule_group._2_alert-rule-folder_My_Rule_Group
43+
id = "2:alert-rule-folder:My Rule Group"
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
terraform {
2+
required_providers {
3+
grafana = {
4+
source = "grafana/grafana"
5+
version = "3.0.0"
6+
}
7+
}
8+
}
9+
10+
provider "grafana" {
11+
url = "http://localhost:3000"
12+
auth = "admin:admin"
13+
}

0 commit comments

Comments
 (0)