diff --git a/CHANGELOG.md b/CHANGELOG.md index 96e11af8a..58b1c2f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Use the auto-generated OAS schema from elastic/kibana for the Fleet API. ([#834](https://github.com/elastic/terraform-provider-elasticstack/issues/834)) - Support description in `elasticstack_elasticsearch_security_role` data sources. ([#884](https://github.com/elastic/terraform-provider-elasticstack/pull/884)) - Prevent spurious recreation of `elasticstack_fleet_agent_policy` resources due to 'changing' policy ids ([#885](https://github.com/elastic/terraform-provider-elasticstack/pull/885)) +- Support `elasticstack_kibana_alerting_rule` resources with only one of `kql` or `timeframe` attributes set ([#886](https://github.com/elastic/terraform-provider-elasticstack/pull/886)) ## [0.11.11] - 2024-10-25 diff --git a/internal/clients/kibana/alerting.go b/internal/clients/kibana/alerting.go index 0eea03d07..2fa54c65c 100644 --- a/internal/clients/kibana/alerting.go +++ b/internal/clients/kibana/alerting.go @@ -39,16 +39,21 @@ func ruleResponseToModel(spaceID string, res *alerting.RuleResponseProperties) * if !alerting.IsNil(action.AlertsFilter) { filter := unwrapOptionalField(action.AlertsFilter) - timeframe := unwrapOptionalField(filter.Timeframe) - a.AlertsFilter = &models.ActionAlertsFilter{ - Kql: *filter.Query.Kql, - Timeframe: models.AlertsFilterTimeframe{ + a.AlertsFilter = &models.ActionAlertsFilter{} + + if filter.Query != nil { + a.AlertsFilter.Kql = filter.Query.Kql + } + + if filter.Timeframe != nil { + timeframe := unwrapOptionalField(filter.Timeframe) + a.AlertsFilter.Timeframe = &models.AlertsFilterTimeframe{ Days: timeframe.Days, Timezone: *timeframe.Timezone, HoursStart: *timeframe.Hours.Start, HoursEnd: *timeframe.Hours.End, - }, + } } } @@ -116,21 +121,25 @@ func ruleActionsToActionsInner(ruleActions []models.AlertingRuleAction) []alerti } if !alerting.IsNil(action.AlertsFilter) { - timeframe := action.AlertsFilter.Timeframe + filter := alerting.ActionsInnerAlertsFilter{} - filter := alerting.ActionsInnerAlertsFilter{ - Query: &alerting.ActionsInnerAlertsFilterQuery{ - Kql: &action.AlertsFilter.Kql, + if action.AlertsFilter.Kql != nil { + filter.Query = &alerting.ActionsInnerAlertsFilterQuery{ + Kql: action.AlertsFilter.Kql, Filters: []alerting.Filter{}, - }, - Timeframe: &alerting.ActionsInnerAlertsFilterTimeframe{ + } + } + + if action.AlertsFilter.Timeframe != nil { + timeframe := action.AlertsFilter.Timeframe + filter.Timeframe = &alerting.ActionsInnerAlertsFilterTimeframe{ Timezone: &timeframe.Timezone, Days: timeframe.Days, Hours: &alerting.ActionsInnerAlertsFilterTimeframeHours{ Start: &timeframe.HoursStart, End: &timeframe.HoursEnd, }, - }, + } } actionToAppend.AlertsFilter = &filter diff --git a/internal/clients/kibana/alerting_test.go b/internal/clients/kibana/alerting_test.go index f970b4a51..4e5cd9fd8 100644 --- a/internal/clients/kibana/alerting_test.go +++ b/internal/clients/kibana/alerting_test.go @@ -147,8 +147,8 @@ func Test_ruleResponseToModel(t *testing.T) { Throttle: utils.Pointer("10s"), }, AlertsFilter: &models.ActionAlertsFilter{ - Kql: "foobar", - Timeframe: models.AlertsFilterTimeframe{ + Kql: utils.Pointer("foobar"), + Timeframe: &models.AlertsFilterTimeframe{ Days: []int32{3, 5, 7}, Timezone: "UTC+1", HoursStart: "00:00", diff --git a/internal/kibana/alerting.go b/internal/kibana/alerting.go index 06e4639a2..96d9536eb 100644 --- a/internal/kibana/alerting.go +++ b/internal/kibana/alerting.go @@ -367,24 +367,29 @@ func getActionsFromResourceData(d *schema.ResourceData, serverVersion *version.V return []models.AlertingRuleAction{}, diag.Errorf("actions.alerts_filter is only supported for Elasticsearch v8.9 or higher") } - resourceDays := d.Get(currentAction + ".alerts_filter.0.timeframe.0.days").([]interface{}) - days := []int32{} + filter := models.ActionAlertsFilter{} - for _, a := range resourceDays { - day := int32(a.(int)) - days = append(days, day) - } + if _, ok := d.GetOk(currentAction + ".alerts_filter.0.timeframe"); ok { + + resourceDays := d.Get(currentAction + ".alerts_filter.0.timeframe.0.days").([]interface{}) + days := []int32{} - timeframe := models.AlertsFilterTimeframe{ - Days: days, - Timezone: d.Get(currentAction + ".alerts_filter.0.timeframe.0.timezone").(string), - HoursStart: d.Get(currentAction + ".alerts_filter.0.timeframe.0.hours_start").(string), - HoursEnd: d.Get(currentAction + ".alerts_filter.0.timeframe.0.hours_end").(string), + for _, a := range resourceDays { + day := int32(a.(int)) + days = append(days, day) + } + + timeframe := models.AlertsFilterTimeframe{ + Days: days, + Timezone: d.Get(currentAction + ".alerts_filter.0.timeframe.0.timezone").(string), + HoursStart: d.Get(currentAction + ".alerts_filter.0.timeframe.0.hours_start").(string), + HoursEnd: d.Get(currentAction + ".alerts_filter.0.timeframe.0.hours_end").(string), + } + filter.Timeframe = &timeframe } - filter := models.ActionAlertsFilter{ - Kql: d.Get(currentAction + ".alerts_filter.0.kql").(string), - Timeframe: timeframe, + if kql, ok := d.GetOk(currentAction + ".alerts_filter.0.kql"); ok { + filter.Kql = utils.Pointer(kql.(string)) } a.AlertsFilter = &filter @@ -547,18 +552,24 @@ func resourceRuleRead(ctx context.Context, d *schema.ResourceData, meta interfac alerts_filter := []interface{}{} if action.AlertsFilter != nil { - timeframe := []interface{}{} - timeframe = append(timeframe, map[string]interface{}{ - "days": action.AlertsFilter.Timeframe.Days, - "timezone": action.AlertsFilter.Timeframe.Timezone, - "hours_start": action.AlertsFilter.Timeframe.HoursStart, - "hours_end": action.AlertsFilter.Timeframe.HoursEnd, - }) + filter := map[string]interface{}{} + + if action.AlertsFilter.Timeframe != nil { + timeframe := []interface{}{} + timeframe = append(timeframe, map[string]interface{}{ + "days": action.AlertsFilter.Timeframe.Days, + "timezone": action.AlertsFilter.Timeframe.Timezone, + "hours_start": action.AlertsFilter.Timeframe.HoursStart, + "hours_end": action.AlertsFilter.Timeframe.HoursEnd, + }) + filter["timeframe"] = timeframe + } - alerts_filter = append(alerts_filter, map[string]interface{}{ - "kql": action.AlertsFilter.Kql, - "timeframe": timeframe, - }) + if action.AlertsFilter.Kql != nil { + filter["kql"] = action.AlertsFilter.Kql + } + + alerts_filter = append(alerts_filter, filter) } else { alerts_filter = nil } diff --git a/internal/kibana/alerting_test.go b/internal/kibana/alerting_test.go index 6119c5997..3ba648f49 100644 --- a/internal/kibana/alerting_test.go +++ b/internal/kibana/alerting_test.go @@ -112,7 +112,7 @@ func TestAccResourceAlertingRule(t *testing.T) { resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.frequency.0.summary", "true"), resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.frequency.0.notify_when", "onActionGroupChange"), resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.frequency.0.throttle", "10m"), - resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.kql", `kibana.alert.action_group: "slo.burnRate.alert" OR kibana.alert.action_group : "slo.burnRate.high"`), + resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.kql", ""), resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.0.days.0", "1"), resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.0.days.1", "2"), resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.0.days.2", "3"), @@ -138,10 +138,7 @@ func TestAccResourceAlertingRule(t *testing.T) { resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.frequency.0.notify_when", "onActionGroupChange"), resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.frequency.0.throttle", "10m"), resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.kql", `kibana.alert.action_group: "slo.burnRate.alert"`), - resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.0.days.0", "7"), - resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.0.timezone", "Pacific/Honolulu"), - resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.0.hours_start", "02:00"), - resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.0.hours_end", "03:00"), + resource.TestCheckResourceAttr("elasticstack_kibana_alerting_rule.test_rule", "actions.0.alerts_filter.0.timeframe.#", "0"), ), }, { @@ -428,13 +425,11 @@ resource "elasticstack_kibana_alerting_rule" "test_rule" { } alerts_filter { - kql = "kibana.alert.action_group: \"slo.burnRate.alert\" OR kibana.alert.action_group : \"slo.burnRate.high\"" - timeframe { days = [1,2,3] - timezone = "Africa/Accra" - hours_start = "01:00" - hours_end = "07:00" + timezone = "Africa/Accra" + hours_start = "01:00" + hours_end = "07:00" } } } @@ -503,13 +498,6 @@ resource "elasticstack_kibana_alerting_rule" "test_rule" { alerts_filter { kql = "kibana.alert.action_group: \"slo.burnRate.alert\"" - - timeframe { - days = [7] - timezone = "Pacific/Honolulu" - hours_start = "02:00" - hours_end = "03:00" - } } } } diff --git a/internal/models/alert_rule.go b/internal/models/alert_rule.go index c8d05d074..264639fa9 100644 --- a/internal/models/alert_rule.go +++ b/internal/models/alert_rule.go @@ -47,8 +47,8 @@ type ActionFrequency struct { } type ActionAlertsFilter struct { - Kql string - Timeframe AlertsFilterTimeframe + Kql *string + Timeframe *AlertsFilterTimeframe } type AlertsFilterTimeframe struct {