Skip to content
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

Add support for thefrequency field in the Create Rule API #753

Merged
merged 11 commits into from
Sep 9, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Fix a provider panic when `elasticstack_kibana_action_connector` reads a non-existant connector ([#729](https://github.com/elastic/terraform-provider-elasticstack/pull/729))
- Add support for `remote_indicies` to `elasticstack_elasticsearch_security_role` & `elasticstack_kibana_security_role` (#723)[https://github.com/elastic/terraform-provider-elasticstack/pull/723]
- Fix error handling in `elasticstack_kibana_import_saved_objects` ([#738](https://github.com/elastic/terraform-provider-elasticstack/pull/738))
- Add the `Frequency` field to the Create Rule API ([#753](https://github.com/elastic/terraform-provider-elasticstack/pull/753))

## [0.11.6] - 2024-08-20

Expand Down
15 changes: 14 additions & 1 deletion docs/resources/kibana_alerting_rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ Could not create API key - Unsupported scheme "ApiKey" for granting API Key
- `consumer` (String) The name of the application or feature that owns the rule.
- `interval` (String) The check interval, which specifies how frequently the rule conditions are checked. The interval must be specified in seconds, minutes, hours or days.
- `name` (String) The name of the rule. While this name does not have to be unique, a distinctive name can help you identify a rule.
- `notify_when` (String) Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.
- `params` (String) The rule parameters, which differ for each rule type.
- `rule_type_id` (String) The ID of the rule type that you want to call when the rule is scheduled to run. For more information about the valid values, list the rule types using [Get rule types API](https://www.elastic.co/guide/en/kibana/master/list-rule-types-api.html) or refer to the [Rule types documentation](https://www.elastic.co/guide/en/kibana/master/rule-types.html).

Expand All @@ -64,6 +63,7 @@ Could not create API key - Unsupported scheme "ApiKey" for granting API Key
- `actions` (Block List) An action that runs under defined conditions. (see [below for nested schema](#nestedblock--actions))
- `alert_delay` (Number) A number that indicates how many consecutive runs need to meet the rule conditions for an alert to occur.
- `enabled` (Boolean) Indicates if you want to run the rule on an interval basis.
- `notify_when` (String) Deprecated in 8.13.0. Use the `notify_when` property in the action `frequency` object instead. Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.
- `rule_id` (String) A UUID v1 or v4 to use instead of a randomly generated ID.
- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used.
- `tags` (List of String) A list of tag names that are applied to the rule.
Expand All @@ -86,8 +86,21 @@ Required:

Optional:

- `frequency` (Block List, Max: 1) The properties that affect how often actions are generated. If the rule type supports setting summary to true, the action can be a summary of alerts at the specified notification interval. Otherwise, an action runs for each alert at the specified notification interval. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level. (see [below for nested schema](#nestedblock--actions--frequency))
- `group` (String) The group name, which affects when the action runs (for example, when the threshold is met or when the alert is recovered). Each rule type has a list of valid action group names.

<a id="nestedblock--actions--frequency"></a>
### Nested Schema for `actions.frequency`

Required:

- `notify_when` (String) Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.
- `summary` (Boolean) Indicates whether the action is a summary.

Optional:

- `throttle` (String) Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values.

## Import

Import is supported using the following syntax:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
provider "elasticstack" {
kibana {}
}

resource "elasticstack_kibana_alerting_rule" "example_with_action_frequency" {
name = "%s"
consumer = "alerts"
params = jsonencode({
aggType = "avg"
groupBy = "top"
termSize = 10
timeWindowSize = 10
timeWindowUnit = "s"
threshold = [10]
thresholdComparator = ">"
index = ["test-index"]
timeField = "@timestamp"
aggField = "version"
termField = "name"
})
rule_type_id = ".index-threshold"
interval = "1m"
enabled = true

actions {
# Should be the id of a MS Teams connector
cnasikas marked this conversation as resolved.
Show resolved Hide resolved
id = elasticstack_kibana_action_connector.index_example.connector_id
group = "threshold met"
params = jsonencode({
"message" : "foobar"
})

frequency {
summary = false
notify_when = "onActionGroupChange"
}
}
}
36 changes: 32 additions & 4 deletions internal/clients/kibana/alerting.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,24 @@ func ruleResponseToModel(spaceID string, res *alerting.RuleResponseProperties) *

actions := []models.AlertingRuleAction{}
for _, action := range res.Actions {
actions = append(actions, models.AlertingRuleAction{

a := models.AlertingRuleAction{
Group: action.Group,
ID: action.Id,
Params: action.Params,
})
}

if !alerting.IsNil(action.Frequency) {
frequency := unwrapOptionalField(action.Frequency)

a.Frequency = &models.AlertingRuleActionFrequency{
Summary: frequency.Summary,
NotifyWhen: (string)(frequency.NotifyWhen),
Throttle: frequency.Throttle.Get(),
}
}

actions = append(actions, a)
}

var alertDelay *float32
Expand Down Expand Up @@ -68,11 +81,26 @@ func ruleActionsToActionsInner(ruleActions []models.AlertingRuleAction) []alerti
actions := []alerting.ActionsInner{}
for index := range ruleActions {
action := ruleActions[index]
actions = append(actions, alerting.ActionsInner{
actionToAppend := alerting.ActionsInner{
Group: action.Group,
Id: action.ID,
Params: action.Params,
})
}

if !alerting.IsNil(action.Frequency) {
frequency := alerting.ActionsInnerFrequency{
Summary: action.Frequency.Summary,
NotifyWhen: (alerting.NotifyWhen)(action.Frequency.NotifyWhen),
}

if action.Frequency.Throttle != nil {
frequency.Throttle = *alerting.NewNullableString(action.Frequency.Throttle)
}

actionToAppend.Frequency = &frequency
}

actions = append(actions, actionToAppend)
}
return actions
}
Expand Down
28 changes: 28 additions & 0 deletions internal/clients/kibana/alerting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,25 @@ func Test_ruleResponseToModel(t *testing.T) {
Group: "group-1",
Id: "id",
Params: map[string]interface{}{},
Frequency: makePtr(alerting.ActionsInnerFrequency{
Summary: true,
NotifyWhen: "onThrottleInterval",
Throttle: *alerting.NewNullableString(makePtr("10s")),
}),
},
{
Group: "group-2",
Id: "id",
Params: map[string]interface{}{},
Frequency: makePtr(alerting.ActionsInnerFrequency{
Summary: true,
NotifyWhen: "onActionGroupChange",
}),
},
{
Group: "group-3",
Id: "id",
Params: map[string]interface{}{},
},
},
ExecutionStatus: alerting.RuleResponsePropertiesExecutionStatus{
Expand Down Expand Up @@ -113,11 +127,25 @@ func Test_ruleResponseToModel(t *testing.T) {
Group: "group-1",
ID: "id",
Params: map[string]interface{}{},
Frequency: &models.AlertingRuleActionFrequency{
Summary: true,
NotifyWhen: "onThrottleInterval",
Throttle: makePtr("10s"),
},
},
{
Group: "group-2",
ID: "id",
Params: map[string]interface{}{},
Frequency: &models.AlertingRuleActionFrequency{
Summary: true,
NotifyWhen: "onActionGroupChange",
},
},
{
Group: "group-3",
ID: "id",
Params: map[string]interface{}{},
},
},
AlertDelay: makePtr(float32(4)),
Expand Down
87 changes: 76 additions & 11 deletions internal/kibana/alerting.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package kibana
import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/elastic/terraform-provider-elasticstack/internal/clients"
Expand All @@ -16,6 +17,7 @@ import (
)

var alertDelayMinSupportedVersion = version.Must(version.NewVersion("8.13.0"))
var frequencyMinSupportedVersion = version.Must(version.NewVersion("8.6.0"))

func ResourceAlertingRule() *schema.Resource {
apikeySchema := map[string]*schema.Schema{
Expand Down Expand Up @@ -45,9 +47,9 @@ func ResourceAlertingRule() *schema.Resource {
ForceNew: true,
},
"notify_when": {
Description: "Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.",
Description: "Deprecated in 8.13.0. Use the `notify_when` property in the action `frequency` object instead. Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.",
Type: schema.TypeString,
Required: true,
Optional: true,
adcoelho marked this conversation as resolved.
Show resolved Hide resolved
ValidateFunc: validation.StringInSlice([]string{"onActionGroupChange", "onActiveAlert", "onThrottleInterval"}, false),
},
"params": {
Expand Down Expand Up @@ -93,6 +95,34 @@ func ResourceAlertingRule() *schema.Resource {
ValidateFunc: validation.StringIsJSON,
DiffSuppressFunc: utils.DiffJsonSuppress,
},
"frequency": {
Description: "The properties that affect how often actions are generated. If the rule type supports setting summary to true, the action can be a summary of alerts at the specified notification interval. Otherwise, an action runs for each alert at the specified notification interval. NOTE: You cannot specify these parameters when `notify_when` or `throttle` are defined at the rule level.",
Type: schema.TypeList,
MinItems: 0,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"summary": {
Description: "Indicates whether the action is a summary.",
Type: schema.TypeBool,
Required: true,
},
"notify_when": {
Description: "Defines how often alerts generate actions. Valid values include: `onActionGroupChange`: Actions run when the alert status changes; `onActiveAlert`: Actions run when the alert becomes active and at each check interval while the rule conditions are met; `onThrottleInterval`: Actions run when the alert becomes active and at the interval specified in the throttle property while the rule conditions are met. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `notify_when` values.",
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"onActionGroupChange", "onActiveAlert", "onThrottleInterval"}, false),
},
"throttle": {
Description: "Defines how often an alert generates repeated actions. This custom action interval must be specified in seconds, minutes, hours, or days. For example, 10m or 1h. This property is applicable only if `notify_when` is `onThrottleInterval`. NOTE: This is a rule level property; if you update the rule in Kibana, it is automatically changed to use action-specific `throttle` values.",
Type: schema.TypeString,
Optional: true,
ValidateFunc: utils.StringIsDuration,
},
},
},
},
},
},
},
Expand All @@ -115,7 +145,6 @@ func ResourceAlertingRule() *schema.Resource {
Optional: true,
ValidateFunc: utils.StringIsDuration,
},

"scheduled_task_id": {
Description: "ID of the scheduled task that will execute the alert.",
Type: schema.TypeString,
Expand Down Expand Up @@ -207,7 +236,7 @@ func getAlertingRuleFromResourceData(d *schema.ResourceData, serverVersion *vers
rule.AlertDelay = utils.Pointer(float32(v.(float64)))
}

actions, diags := getActionsFromResourceData(d)
actions, diags := getActionsFromResourceData(d, serverVersion)
if diags.HasError() {
return models.AlertingRule{}, diags
}
Expand All @@ -222,11 +251,11 @@ func getAlertingRuleFromResourceData(d *schema.ResourceData, serverVersion *vers
return rule, diags
}

func getActionsFromResourceData(d *schema.ResourceData) ([]models.AlertingRuleAction, diag.Diagnostics) {
func getActionsFromResourceData(d *schema.ResourceData, serverVersion *version.Version) ([]models.AlertingRuleAction, diag.Diagnostics) {
actions := []models.AlertingRuleAction{}
if v, ok := d.GetOk("actions"); ok {
resourceActions := v.([]interface{})
for _, a := range resourceActions {
for i, a := range resourceActions {
action := a.(map[string]interface{})
paramsStr := action["params"].(string)
var params map[string]interface{}
Expand All @@ -235,11 +264,32 @@ func getActionsFromResourceData(d *schema.ResourceData) ([]models.AlertingRuleAc
return []models.AlertingRuleAction{}, diag.FromErr(err)
}

actions = append(actions, models.AlertingRuleAction{
a := models.AlertingRuleAction{
Group: action["group"].(string),
ID: action["id"].(string),
Params: params,
})
}

currentAction := fmt.Sprintf("actions.%d", i)

if _, ok := d.GetOk(currentAction + ".frequency"); ok {
if serverVersion.LessThan(frequencyMinSupportedVersion) {
return []models.AlertingRuleAction{}, diag.Errorf("actions.frequency is only supported for Elasticsearch v8.6 or higher")
}

frequency := models.AlertingRuleActionFrequency{
Summary: d.Get(currentAction + ".frequency.0.summary").(bool),
NotifyWhen: d.Get(currentAction + ".frequency.0.notify_when").(string),
}

if throttle := getOrNilString(currentAction+".frequency.0.throttle", d); throttle != nil && *throttle != "" {
frequency.Throttle = throttle
}

a.Frequency = &frequency
}

actions = append(actions, a)
}
}

Expand Down Expand Up @@ -380,12 +430,27 @@ func resourceRuleRead(ctx context.Context, d *schema.ResourceData, meta interfac
if err != nil {
return diag.FromErr(err)
}

frequency := []interface{}{}

if action.Frequency != nil {
frequency = append(frequency, map[string]interface{}{
"summary": action.Frequency.Summary,
"notify_when": action.Frequency.NotifyWhen,
"throttle": action.Frequency.Throttle,
})
} else {
frequency = nil
}

actions = append(actions, map[string]interface{}{
"group": action.Group,
"id": action.ID,
"params": string(params),
"group": action.Group,
"id": action.ID,
"params": string(params),
"frequency": frequency,
})
}

if err := d.Set("actions", actions); err != nil {
return diag.FromErr(err)
}
Expand Down
17 changes: 13 additions & 4 deletions internal/models/alert_rule.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package models

import "time"
import (
"time"
)

type AlertingRule struct {
RuleID string
Expand All @@ -26,12 +28,19 @@ type AlertingRuleSchedule struct {
}

type AlertingRuleAction struct {
Group string
ID string
Params map[string]interface{}
Group string
ID string
Params map[string]interface{}
Frequency *AlertingRuleActionFrequency
}

type AlertingRuleExecutionStatus struct {
LastExecutionDate *time.Time
Status *string
}

type AlertingRuleActionFrequency struct {
Summary bool
NotifyWhen string
Throttle *string
}
Loading