diff --git a/docs/data-sources/slos.md b/docs/data-sources/slos.md index 6c927052c..2cbab00e3 100644 --- a/docs/data-sources/slos.md +++ b/docs/data-sources/slos.md @@ -94,11 +94,20 @@ Read-Only: Read-Only: +- `advanced_options` (List of Object) (see [below for nested schema](#nestedobjatt--slos--alerting--advanced_options)) - `annotation` (List of Object) (see [below for nested schema](#nestedobjatt--slos--alerting--annotation)) - `fastburn` (List of Object) (see [below for nested schema](#nestedobjatt--slos--alerting--fastburn)) - `label` (List of Object) (see [below for nested schema](#nestedobjatt--slos--alerting--label)) - `slowburn` (List of Object) (see [below for nested schema](#nestedobjatt--slos--alerting--slowburn)) + +### Nested Schema for `slos.alerting.advanced_options` + +Read-Only: + +- `min_failures` (Number) + + ### Nested Schema for `slos.alerting.annotation` diff --git a/docs/resources/slo.md b/docs/resources/slo.md index 3a728a462..cb6c8f35c 100644 --- a/docs/resources/slo.md +++ b/docs/resources/slo.md @@ -198,11 +198,20 @@ Optional: Optional: +- `advanced_options` (Block List, Max: 1) Advanced Options for Alert Rules (see [below for nested schema](#nestedblock--alerting--advanced_options)) - `annotation` (Block List) Annotations will be attached to all alerts generated by any of these rules. (see [below for nested schema](#nestedblock--alerting--annotation)) - `fastburn` (Block List, Max: 1) Alerting Rules generated for Fast Burn alerts (see [below for nested schema](#nestedblock--alerting--fastburn)) - `label` (Block List) Labels will be attached to all alerts generated by any of these rules. (see [below for nested schema](#nestedblock--alerting--label)) - `slowburn` (Block List, Max: 1) Alerting Rules generated for Slow Burn alerts (see [below for nested schema](#nestedblock--alerting--slowburn)) + +### Nested Schema for `alerting.advanced_options` + +Optional: + +- `min_failures` (Number) Minimum number of failed events to trigger an alert + + ### Nested Schema for `alerting.annotation` diff --git a/examples/resources/grafana_slo/resource_ratio_advanced_options.tf b/examples/resources/grafana_slo/resource_ratio_advanced_options.tf new file mode 100644 index 000000000..8a10b8075 --- /dev/null +++ b/examples/resources/grafana_slo/resource_ratio_advanced_options.tf @@ -0,0 +1,49 @@ +resource "grafana_slo" "ratio_options" { + name = "Terraform Testing - Ratio Query" + description = "Terraform Description - Ratio Query" + query { + ratio { + success_metric = "kubelet_http_requests_total{status!~\"5..\"}" + total_metric = "kubelet_http_requests_total" + group_by_labels = ["job", "instance"] + } + type = "ratio" + } + objectives { + value = 0.995 + window = "30d" + } + destination_datasource { + uid = "grafanacloud-prom" + } + label { + key = "slo" + value = "terraform" + } + alerting { + fastburn { + annotation { + key = "name" + value = "SLO Burn Rate Very High" + } + annotation { + key = "description" + value = "Error budget is burning too fast" + } + } + + slowburn { + annotation { + key = "name" + value = "SLO Burn Rate High" + } + annotation { + key = "description" + value = "Error budget is burning too fast" + } + } + advanced_options { + min_failures = 10 + } + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index a432df54f..1538f9a10 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/grafana/grafana-com-public-clients/go/gcom v0.0.0-20240322153219-42c6a1d2bcab github.com/grafana/grafana-openapi-client-go v0.0.0-20240523010106-657d101fcbd9 github.com/grafana/machine-learning-go-client v0.7.0 - github.com/grafana/slo-openapi-client/go v0.0.0-20240507015908-bf9e85638f2f + github.com/grafana/slo-openapi-client/go v0.0.0-20240626093634-e6741482b090 github.com/grafana/synthetic-monitoring-agent v0.24.3 github.com/grafana/synthetic-monitoring-api-go-client v0.8.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 diff --git a/go.sum b/go.sum index a066eae60..24a4edfbf 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.7 h1:C11j63y7gymiW8VugJ9ZW0pWfxTZugdSJyC48olk5KY= github.com/grafana/pyroscope-go/godeltaprof v0.1.7/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= -github.com/grafana/slo-openapi-client/go v0.0.0-20240507015908-bf9e85638f2f h1:aUDhr6LmO2W08tEP0Xe694DRfYZjlGp9kUnbeAF+194= -github.com/grafana/slo-openapi-client/go v0.0.0-20240507015908-bf9e85638f2f/go.mod h1:HgbbeH2gFfCk2XZCrCly39DB13WkwWyQ+Ww+HTxePCs= +github.com/grafana/slo-openapi-client/go v0.0.0-20240626093634-e6741482b090 h1:gDkJPpTL84zx+UkSY6a1pPlUm9aDEVBzPlVOkUbXmgM= +github.com/grafana/slo-openapi-client/go v0.0.0-20240626093634-e6741482b090/go.mod h1:HgbbeH2gFfCk2XZCrCly39DB13WkwWyQ+Ww+HTxePCs= github.com/grafana/synthetic-monitoring-agent v0.24.3 h1:+xscAsGZtWTNTNDxdYqqcz4w1tG6QPaOIgCONsVMoO8= github.com/grafana/synthetic-monitoring-agent v0.24.3/go.mod h1:CJQmPtKRcJMjb/sDe6fDA4vyS2qFPElu0szI33nKlzk= github.com/grafana/synthetic-monitoring-api-go-client v0.8.0 h1:Tm4MtwwYmPNInGfnj66l6j6KOshMkNV4emIVKJdlXMg= diff --git a/internal/resources/slo/data_source_slo.go b/internal/resources/slo/data_source_slo.go index 1a6880ed8..438cdc98b 100644 --- a/internal/resources/slo/data_source_slo.go +++ b/internal/resources/slo/data_source_slo.go @@ -190,6 +190,10 @@ func unpackAlerting(alertData *slo.SloV00Alerting) []map[string]interface{} { alertObject["slowburn"] = unpackAlertingMetadata(*alertData.SlowBurn) } + if alertData.AdvancedOptions != nil { + alertObject["advanced_options"] = unpackAdvancedOptions(*alertData.AdvancedOptions) + } + retAlertData = append(retAlertData, alertObject) return retAlertData } @@ -212,6 +216,18 @@ func unpackAlertingMetadata(metaData slo.SloV00AlertingMetadata) []map[string]in return retAlertMetaData } +func unpackAdvancedOptions(options slo.SloV00AdvancedOptions) []map[string]interface{} { + retAdvancedOptions := []map[string]interface{}{} + minFailuresStruct := make(map[string]interface{}) + + if options.MinFailures != nil { + minFailuresStruct["min_failures"] = int(*options.MinFailures) + } + + retAdvancedOptions = append(retAdvancedOptions, minFailuresStruct) + return retAdvancedOptions +} + func unpackDestinationDatasource(destinationDatasource *slo.SloV00DestinationDatasource) []map[string]interface{} { retDestinationDatasources := []map[string]interface{}{} diff --git a/internal/resources/slo/resource_slo.go b/internal/resources/slo/resource_slo.go index 7365022c1..75678fdc3 100644 --- a/internal/resources/slo/resource_slo.go +++ b/internal/resources/slo/resource_slo.go @@ -221,6 +221,22 @@ Resource manages Grafana SLOs. }, }, }, + "advanced_options": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "Advanced Options for Alert Rules", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "min_failures": { + Type: schema.TypeInt, + Optional: true, + Description: "Minimum number of failed events to trigger an alert", + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, }, }, }, @@ -393,7 +409,6 @@ func packSloResource(d *schema.ResourceData) (slo.SloV00Slo, error) { tfalerting = packAlerting(alert) } } - req.Alerting = &tfalerting } @@ -524,6 +539,7 @@ func packAlerting(tfAlerting map[string]interface{}) slo.SloV00Alerting { var tfLabels []slo.SloV00Label var tfFastBurn slo.SloV00AlertingMetadata var tfSlowBurn slo.SloV00AlertingMetadata + var tfAdvancedOptions slo.SloV00AdvancedOptions annots, ok := tfAlerting["annotation"].([]interface{}) if ok { @@ -552,6 +568,22 @@ func packAlerting(tfAlerting map[string]interface{}) slo.SloV00Alerting { SlowBurn: &tfSlowBurn, } + // All options in advanced options will be optional + // Adding a second feature will need to make a better way of checking what is there + if failures := tfAlerting["advanced_options"]; failures != nil { + lf, ok := failures.([]interface{}) + if ok && len(lf) > 0 { + lf2, ok := lf[0].(map[string]interface{}) + if ok { + i64 := int64(lf2["min_failures"].(int)) + tfAdvancedOptions = slo.SloV00AdvancedOptions{ + MinFailures: &i64, + } + alerting.SetAdvancedOptions(tfAdvancedOptions) + } + } + } + return alerting } diff --git a/internal/resources/slo/resource_slo_test.go b/internal/resources/slo/resource_slo_test.go index df97e8663..73697e4ae 100644 --- a/internal/resources/slo/resource_slo_test.go +++ b/internal/resources/slo/resource_slo_test.go @@ -94,6 +94,16 @@ func TestAccResourceSlo(t *testing.T) { resource.TestCheckResourceAttr("grafana_slo.ratio", "query.0.ratio.0.group_by_labels.1", "instance"), ), }, + { + // Tests Advanced Options + Config: testutils.TestAccExample(t, "resources/grafana_slo/resource_ratio_advanced_options.tf"), + Check: resource.ComposeTestCheckFunc( + testAccSloCheckExists("grafana_slo.ratio_options", &slo), + testAlertingExists(true, "grafana_slo.ratio_options", &slo), + testAdvancedOptionsExists(true, "grafana_slo.ratio_options", &slo), + resource.TestCheckResourceAttr("grafana_slo.ratio_options", "alerting.0.advanced_options.0.min_failures", "10"), + ), + }, }, }) } @@ -150,6 +160,30 @@ func testAlertingExists(expectation bool, rn string, slo *slo.SloV00Slo) resourc } } +func testAdvancedOptionsExists(expectation bool, rn string, slo *slo.SloV00Slo) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs := s.RootModule().Resources[rn] + client := testutils.Provider.Meta().(*common.Client).SLOClient + req := client.DefaultAPI.V1SloIdGet(context.Background(), rs.Primary.ID) + gotSlo, _, err := req.Execute() + + if err != nil { + return fmt.Errorf("error getting SLO: %s", err) + } + *slo = *gotSlo + + if slo.Alerting.AdvancedOptions == nil && expectation == false { + return nil + } + + if slo.Alerting.AdvancedOptions != nil && expectation == true { + return nil + } + + return fmt.Errorf("SLO Advanced Options expectation mismatch") + } +} + func testAccSloCheckDestroy(slo *slo.SloV00Slo) resource.TestCheckFunc { return func(s *terraform.State) error { client := testutils.Provider.Meta().(*common.Client).SLOClient