Skip to content

Config Generation: Add tests for alerting resources #1598

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

Merged
merged 1 commit into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions internal/resources/grafana/resource_alerting_contact_point.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ This resource requires Grafana 9.1.0 or later.
)
}

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

// resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams())
// if err != nil {
// // Retry if the API returns 500 because it may be that the alertmanager is not ready in the org yet.
// // The alertmanager is provisioned asynchronously when the org is created.
// if err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError {
// resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams())
// if err != nil {
// if orgID > 1 && (err.(*runtime.APIError).IsCode(500) || err.(*runtime.APIError).IsCode(403)) {
// return retry.RetryableError(err)
// }
// return retry.NonRetryableError(err)
// }

// for _, contactPoint := range resp.Payload {
// idMap[MakeOrgResourceID(orgID, contactPoint.Name)] = true
// }
// return nil
// }); err != nil {
// return nil, err
// }

// for _, contactPoint := range resp.Payload {
// idMap[MakeOrgResourceID(orgID, contactPoint.Name)] = true
// }
// }

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

// First, try to fetch the contact point by name.
// If that fails, try to fetch it by the UID of its notifiers.
resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams().WithName(&name))
resp, err := client.Provisioning.GetContactpoints(provisioning.NewGetContactpointsParams())
if err != nil {
return diag.FromErr(err)
}
points := resp.Payload
var points []*models.EmbeddedContactPoint
for _, p := range resp.Payload {
if p.Name == name {
points = append(points, p)
}
}
if len(points) == 0 {
return common.WarnMissing("contact point", data)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,24 @@ func listNotificationPolicies(ctx context.Context, client *goapi.GrafanaHTTPAPI,

var ids []string
for _, orgID := range orgIDs {
client = client.Clone().WithOrgID(orgID)

// Retry if the API returns 500 because it may be that the alertmanager is not ready in the org yet.
// The alertmanager is provisioned asynchronously when the org is created.
if err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError {
_, err := client.Provisioning.GetPolicyTree()
if err != nil {
if orgID > 1 && (err.(*runtime.APIError).IsCode(500) || err.(*runtime.APIError).IsCode(403)) {
return retry.RetryableError(err)
}
return retry.NonRetryableError(err)
}

return nil
}); err != nil {
return nil, err
}

ids = append(ids, MakeOrgResourceID(orgID, PolicySingletonID))
}

Expand Down
24 changes: 18 additions & 6 deletions internal/resources/grafana/resource_alerting_rule_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import (
"strings"
"time"

"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
goapi "github.com/grafana/grafana-openapi-client-go/client"
"github.com/grafana/grafana-openapi-client-go/client/provisioning"
"github.com/grafana/grafana-openapi-client-go/models"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"

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

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

for _, rule := range resp.Payload {
idMap[MakeOrgResourceID(orgID, resourceRuleGroupID.Make(rule.FolderUID, rule.RuleGroup))] = true
for _, rule := range resp.Payload {
idMap[resourceRuleGroupID.Make(orgID, rule.FolderUID, rule.RuleGroup)] = true
}
return nil
}); err != nil {
return nil, err
}
}

Expand Down
14 changes: 14 additions & 0 deletions pkg/generate/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ func TestAccGenerate(t *testing.T) {
})
},
},
{
name: "alerting-in-org",
config: func() string {
content, err := os.ReadFile("testdata/generate/alerting-in-org.tf")
require.NoError(t, err)
return string(content)
}(),
check: func(t *testing.T, tempDir string) {
assertFiles(t, tempDir, "testdata/generate/alerting-in-org", []string{
".terraform",
".terraform.lock.hcl",
})
},
},
}

for _, tc := range cases {
Expand Down
129 changes: 129 additions & 0 deletions pkg/generate/testdata/generate/alerting-in-org.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
resource "grafana_organization" "test" {
name = "alerting-org"
}

resource "grafana_contact_point" "test" {
org_id = grafana_organization.test.id
name = "my-contact-point"
email {
addresses = ["[email protected]"]
}
}

resource "grafana_notification_policy" "test" {
org_id = grafana_organization.test.id
contact_point = grafana_contact_point.test.name
group_by = ["..."]
}

resource "grafana_mute_timing" "my_mute_timing" {
org_id = grafana_organization.test.id
name = "My Mute Timing"

intervals {
times {
start = "04:56"
end = "14:17"
}
weekdays = ["monday", "tuesday:thursday"]
days_of_month = ["1:7", "-1"]
months = ["1:3", "december"]
years = ["2030", "2025:2026"]
location = "America/New_York"
}
}

resource "grafana_message_template" "my_template" {
org_id = grafana_organization.test.id
name = "My Reusable Template"
template = "{{define \"My Reusable Template\" }}\n template content\n{{ end }}"
}

resource "grafana_folder" "rule_folder" {
org_id = grafana_organization.test.id
title = "My Alert Rule Folder"
uid = "alert-rule-folder"
}

resource "grafana_rule_group" "my_alert_rule" {
org_id = grafana_organization.test.id
name = "My Rule Group"
folder_uid = grafana_folder.rule_folder.uid
interval_seconds = 240
rule {
name = "My Alert Rule 1"
for = "2m"
condition = "B"
no_data_state = "NoData"
exec_err_state = "Alerting"
annotations = {
"a" = "b"
"c" = "d"
}
labels = {
"e" = "f"
"g" = "h"
}
is_paused = false
data {
ref_id = "A"
query_type = ""
relative_time_range {
from = 600
to = 0
}
datasource_uid = "PD8C576611E62080A"
model = jsonencode({
hide = false
intervalMs = 1000
maxDataPoints = 43200
refId = "A"
})
}
data {
ref_id = "B"
query_type = ""
relative_time_range {
from = 0
to = 0
}
datasource_uid = "-100"
model = <<EOT
{
"conditions": [
{
"evaluator": {
"params": [
3
],
"type": "gt"
},
"operator": {
"type": "and"
},
"query": {
"params": [
"A"
]
},
"reducer": {
"params": [],
"type": "last"
},
"type": "query"
}
],
"datasource": {
"type": "__expr__",
"uid": "-100"
},
"hide": false,
"intervalMs": 1000,
"maxDataPoints": 43200,
"refId": "B",
"type": "classic_conditions"
}
EOT
}
}
}
44 changes: 44 additions & 0 deletions pkg/generate/testdata/generate/alerting-in-org/imports.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
to = grafana_folder._2_alert-rule-folder
id = "2:alert-rule-folder"
}

import {
to = grafana_message_template._2_My_Reusable_Template
id = "2:My Reusable Template"
}

import {
to = grafana_mute_timing._2_My_Mute_Timing
id = "2:My Mute Timing"
}

import {
to = grafana_notification_policy._2_policy
id = "2:policy"
}

import {
to = grafana_notification_policy._1_policy
id = "1:policy"
}

import {
to = grafana_organization._2
id = "2"
}

import {
to = grafana_organization_preferences._2
id = "2"
}

import {
to = grafana_organization_preferences._1
id = "1"
}

import {
to = grafana_rule_group._2_alert-rule-folder_My_Rule_Group
id = "2:alert-rule-folder:My Rule Group"
}
13 changes: 13 additions & 0 deletions pkg/generate/testdata/generate/alerting-in-org/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_providers {
grafana = {
source = "grafana/grafana"
version = "3.0.0"
}
}
}

provider "grafana" {
url = "http://localhost:3000"
auth = "admin:admin"
}
Loading
Loading