diff --git a/docs/resources/alert_rule.md b/docs/resources/alert_rule.md index 3c5fe81..70d964d 100644 --- a/docs/resources/alert_rule.md +++ b/docs/resources/alert_rule.md @@ -44,13 +44,14 @@ resource "thousandeyes_alert_rule" "example_alert_rule" { - `notify_on_clear` (Boolean) Set to 'true' to trigger the notification when the alert clears. - `rounds_violating_mode` (String) [ANY or EXACT] Defines whether the same agent(s) must meet the EXACT same threshold in consecutive rounds or not. The default value is ANY. - `severity` (String) [INFO, MINOR, MAJOR or CRITICAL] The severity level of the alert rule. The default value is INFO. -- `test_ids` (List of Number) The valid test IDs. +- `tests` (Block List) The list of included tests. (see [below for nested schema](#nestedblock--tests)) ### Read-Only - `alert_rule_id` (Number) The unique ID of the alert rule. - `id` (String) The ID of this resource. - `rule_id` (Number) The unique ID of the alert rule. +- `test_ids` (List of Number) The valid test IDs. ### Nested Schema for `notifications` @@ -88,3 +89,12 @@ Required: - `integration_type` (String) The integration type, as a string. + + +### Nested Schema for `tests` + +Optional: + +- `test_id` (Number) The list of unique test IDs. + + diff --git a/go.mod b/go.mod index 92d3443..97dfaf7 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/hashicorp/terraform-plugin-docs v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0 github.com/stretchr/testify v1.8.4 - github.com/thousandeyes/thousandeyes-sdk-go/v2 v2.4.2 + github.com/thousandeyes/thousandeyes-sdk-go/v2 v2.4.3 ) require ( diff --git a/go.sum b/go.sum index 3d6edd8..0957629 100644 --- a/go.sum +++ b/go.sum @@ -169,8 +169,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/thousandeyes/thousandeyes-sdk-go/v2 v2.4.2 h1:j84PPcq3EBRtQxJ/zssiBxCajhPQn+unIY5JhDiBWTo= -github.com/thousandeyes/thousandeyes-sdk-go/v2 v2.4.2/go.mod h1:XQouPCy3dIp81Xzkp9bqu6vg7fmE4QQpA6REsLVTDdE= +github.com/thousandeyes/thousandeyes-sdk-go/v2 v2.4.3 h1:4gCG4SSyhgQsVPuLQl5Jdy4CwdHyyoZkktNTqnfcyJc= +github.com/thousandeyes/thousandeyes-sdk-go/v2 v2.4.3/go.mod h1:XQouPCy3dIp81Xzkp9bqu6vg7fmE4QQpA6REsLVTDdE= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/thousandeyes/acceptance_resources/alert_rule/create_test_with_alert_rule.tf b/thousandeyes/acceptance_resources/alert_rule/create_test_with_alert_rule.tf new file mode 100644 index 0000000..d18f3aa --- /dev/null +++ b/thousandeyes/acceptance_resources/alert_rule/create_test_with_alert_rule.tf @@ -0,0 +1,34 @@ +data "thousandeyes_agent" "amsterdam" { + agent_name = "Amsterdam, Netherlands" +} + +resource "thousandeyes_alert_rule" "test" { + severity = "MAJOR" + rule_name = "Agent To Server Alert Rule Test" + alert_type = "End-to-End (Server)" + expression = "((loss >= 50%) || (probDetail != \"\") || (avgLatency >= 200 ms))" + minimum_sources = 2 + rounds_violating_required = 3 + rounds_violating_out_of = 4 +} + +resource "thousandeyes_agent_to_server" "agent_to_server_test" { + test_name = "Agent To Server Test" + interval = 300 + alerts_enabled = true + server = "api.stg.thousandeyes.com" + protocol = "TCP" + port = 443 + enabled = true + bgp_measurements = true + use_public_bgp = true + mtu_measurements = true + + agents { + agent_id = data.thousandeyes_agent.amsterdam.agent_id + } + + alert_rules { + rule_id = thousandeyes_alert_rule.test.id + } +} diff --git a/thousandeyes/acceptance_resources/alert_rule/update_alert_rule.tf b/thousandeyes/acceptance_resources/alert_rule/update_alert_rule.tf new file mode 100644 index 0000000..a95c5c3 --- /dev/null +++ b/thousandeyes/acceptance_resources/alert_rule/update_alert_rule.tf @@ -0,0 +1,34 @@ +data "thousandeyes_agent" "amsterdam" { + agent_name = "Amsterdam, Netherlands" +} + +resource "thousandeyes_alert_rule" "test" { + severity = "MAJOR" + rule_name = "Agent To Server Alert Rule Test" + alert_type = "End-to-End (Server)" + expression = "((loss >= 50%) || (probDetail != \"\") || (avgLatency >= 200 ms))" + minimum_sources = 2 + rounds_violating_required = 4 + rounds_violating_out_of = 4 +} + +resource "thousandeyes_agent_to_server" "agent_to_server_test" { + test_name = "Agent To Server Test" + interval = 300 + alerts_enabled = true + server = "api.stg.thousandeyes.com" + protocol = "TCP" + port = 443 + enabled = true + bgp_measurements = true + use_public_bgp = true + mtu_measurements = true + + agents { + agent_id = data.thousandeyes_agent.amsterdam.agent_id + } + + alert_rules { + rule_id = thousandeyes_alert_rule.test.id + } +} diff --git a/thousandeyes/resource_alert_rule.go b/thousandeyes/resource_alert_rule.go index 5b84d7c..2bc1130 100644 --- a/thousandeyes/resource_alert_rule.go +++ b/thousandeyes/resource_alert_rule.go @@ -26,14 +26,20 @@ func resourceAlertRule() *schema.Resource { func resourceAlertRuleRead(d *schema.ResourceData, m interface{}) error { return GetResource(d, m, func(client *thousandeyes.Client, id int64) (interface{}, error) { - return client.GetAlertRule(id) + var alertRule, err = client.GetAlertRule(id) + if err != nil { + return nil, err + } + alertRule.TestIds = testIds(*alertRule.Tests) + alertRule.Tests = nil + return alertRule, nil }) } func resourceAlertRuleUpdate(d *schema.ResourceData, m interface{}) error { client := m.(*thousandeyes.Client) - log.Printf("[INFO] Updating ThousandEyes Test %s", d.Id()) + log.Printf("[INFO] Updating ThousandEyes Alert Rule %s", d.Id()) id, _ := strconv.ParseInt(d.Id(), 10, 64) // While most ThousandEyes updates only require updated fields and specifically // disallow some fields on update, Alert Rules actually require the full list of @@ -74,3 +80,11 @@ func resourceAlertRuleCreate(d *schema.ResourceData, m interface{}) error { func buildAlertRuleStruct(d *schema.ResourceData) *thousandeyes.AlertRule { return ResourceBuildStruct(d, &thousandeyes.AlertRule{}).(*thousandeyes.AlertRule) } + +func testIds(tests []thousandeyes.GenericTest) *[]int64 { + var testIds []int64 + for _, test := range tests { + testIds = append(testIds, *test.TestID) + } + return &testIds +} diff --git a/thousandeyes/resource_alert_rule_test.go b/thousandeyes/resource_alert_rule_test.go new file mode 100644 index 0000000..5f8a755 --- /dev/null +++ b/thousandeyes/resource_alert_rule_test.go @@ -0,0 +1,84 @@ +package thousandeyes + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccThousandEyesAlertRule(t *testing.T) { + var alertRuleResourceName = "thousandeyes_alert_rule.test" + testCases := []struct { + name string + createResourceFile string + updateResourceFile string + checkDestroyFunction func(*terraform.State) error + checkCreateFunc []resource.TestCheckFunc + checkUpdateFunc []resource.TestCheckFunc + }{ + { + name: "test_association_maintained_after_alert_rule_update", + createResourceFile: "acceptance_resources/alert_rule/create_test_with_alert_rule.tf", + updateResourceFile: "acceptance_resources/alert_rule/update_alert_rule.tf", + checkDestroyFunction: func(state *terraform.State) error { + resourceList := []ResourceType{ + { + Name: "Agent To Server Test", + ResourceName: "thousandeyes_agent_to_server", + GetResource: func(id int64) (interface{}, error) { + return testClient.GetAgentServer(id) + }}, + { + Name: "Agent To Server Alert Rule Test", + ResourceName: "thousandeyes_alert_rule", + GetResource: func(id int64) (interface{}, error) { + return testClient.GetAlertRule(id) + }}, + } + return testAccCheckResourceDestroy(resourceList, state) + }, + checkCreateFunc: []resource.TestCheckFunc{ + //alert rule is created + //with 3 required violating rounds + resource.TestCheckResourceAttr(alertRuleResourceName, "rounds_violating_required", "3"), + }, + checkUpdateFunc: []resource.TestCheckFunc{ + //alert rule is updated + //to 4 required violating rounds + resource.TestCheckResourceAttr(alertRuleResourceName, "rounds_violating_required", "4"), + //and the test association is maintained + resource.TestCheckResourceAttr(alertRuleResourceName, "test_ids.#", "1"), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: tc.checkDestroyFunction, + Steps: []resource.TestStep{ + { + Config: testAccThousandEyesAlertRuleConfig(tc.createResourceFile), + Check: resource.ComposeTestCheckFunc(tc.checkCreateFunc...), + }, + { + Config: testAccThousandEyesAlertRuleConfig(tc.updateResourceFile), + Check: resource.ComposeTestCheckFunc(tc.checkUpdateFunc...), + }, + }, + }) + }) + } +} + +func testAccThousandEyesAlertRuleConfig(testResource string) string { + content, err := os.ReadFile(testResource) + if err != nil { + panic(err) + } + return string(content) +} diff --git a/thousandeyes/schemas.go b/thousandeyes/schemas.go index 9a9d599..83c6839 100644 --- a/thousandeyes/schemas.go +++ b/thousandeyes/schemas.go @@ -918,7 +918,7 @@ var schemas = map[string]*schema.Schema{ "test_ids": { Type: schema.TypeList, Description: "The valid test IDs.", - Optional: true, + Computed: true, Elem: &schema.Schema{ Type: schema.TypeInt, },