Skip to content

Commit

Permalink
allow action parameters to be specified as JSON (#335)
Browse files Browse the repository at this point in the history
* allow action parameters to be specified as JSON

* add test

* fix change on each run

* add notify_when to action test

* change test indent

* fix test for es < 7.11.0
  • Loading branch information
cgroschupp committed Mar 22, 2023
1 parent a0a5055 commit 1cc0cfa
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 14 deletions.
69 changes: 55 additions & 14 deletions es/resource_elasticsearch_kibana_alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/olivere/elastic/uritemplates"

Expand Down Expand Up @@ -152,7 +153,7 @@ func resourceElasticsearchKibanaAlert() *schema.Resource {
DiffSuppressFunc: suppressEquivalentJson,
},
"actions": {
Type: schema.TypeSet,
Type: schema.TypeList,
Optional: true,
Description: "Actions are invocations of Kibana services or integrations with third-party systems, that run as background tasks on the Kibana server when alert conditions are met.",
Elem: &schema.Resource{
Expand All @@ -178,6 +179,22 @@ func resourceElasticsearchKibanaAlert() *schema.Resource {
Optional: true,
Description: "Key value pairs passed to the action executor, e.g. a Mustache formatted `message`.",
},
"params_json": {
Type: schema.TypeString,
Optional: true,
Default: "",
ValidateFunc: validation.StringIsJSON,
Description: "JSON body of actions `params`. Either `params_json` or `params` must be specified.",
StateFunc: func(v interface{}) string {
json, _ := structure.NormalizeJsonString(v)
return json
},
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
newJson, _ := structure.NormalizeJsonString(new)
oldJson, _ := structure.NormalizeJsonString(old)
return newJson == oldJson
},
},
},
},
},
Expand Down Expand Up @@ -218,12 +235,12 @@ func resourceElasticsearchKibanaAlertRead(d *schema.ResourceData, meta interface
var alert kibana.Alert

providerConf := meta.(*ProviderConf)
esClient, err := getKibanaClient(providerConf)
kibanaClient, err := getKibanaClient(providerConf)
if err != nil {
return err
}

switch client := esClient.(type) {
switch client := kibanaClient.(type) {
case *elastic7.Client:
alert, err = kibanaGetAlert(client, id, spaceID)
default:
Expand Down Expand Up @@ -261,7 +278,12 @@ func resourceElasticsearchKibanaAlertRead(d *schema.ResourceData, meta interface
} else {
ds.set("conditions", flattenKibanaAlertConditions(alert.Params))
}
ds.set("actions", flattenKibanaAlertActions(alert.Actions))

actions, err := flattenKibanaAlertActions(alert.Actions, d.Get("actions").([]interface{}))
if err != nil {
return err
}
ds.set("actions", actions)

return ds.err
}
Expand Down Expand Up @@ -319,7 +341,7 @@ func resourceElasticsearchPostKibanaAlert(d *schema.ResourceData, meta interface
scheduleEntry := schedule[0].(map[string]interface{})
alertSchedule.Interval = scheduleEntry["interval"].(string)
}
actions, err := expandKibanaActionsList(d.Get("actions").(*schema.Set).List())
actions, err := expandKibanaActionsList(d.Get("actions").([]interface{}))
if err != nil {
return "", err
}
Expand Down Expand Up @@ -367,36 +389,55 @@ func resourceElasticsearchPostKibanaAlert(d *schema.ResourceData, meta interface
}

func expandKibanaActionsList(resourcesArray []interface{}) ([]kibana.AlertAction, error) {
actions := make([]kibana.AlertAction, 0, len(resourcesArray))
actions := []kibana.AlertAction{}
for _, resource := range resourcesArray {
data, ok := resource.(map[string]interface{})
if !ok {
return actions, fmt.Errorf("Error asserting data: %+v, %T", resource, resource)
}

var params map[string]interface{}

if paramsString, ok := data["params_json"]; ok && paramsString != "" {
err := json.Unmarshal([]byte(paramsString.(string)), &params)
if err != nil {
return actions, fmt.Errorf("error paramasString %v: %w", paramsString, err)
}
} else {
params = data["params"].(map[string]interface{})
}
action := kibana.AlertAction{
ID: data["id"].(string),
Group: data["group"].(string),
ActionTypeId: data["action_type_id"].(string),
Params: data["params"].(map[string]interface{}),
Params: params,
}
actions = append(actions, action)
}

return actions, nil
}

func flattenKibanaAlertActions(actions []kibana.AlertAction) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(actions))

for _, a := range actions {
m := make(map[string]interface{})
func flattenKibanaAlertActions(actions []kibana.AlertAction, actionsSchema []interface{}) ([]interface{}, error) {
result := []interface{}{}
for index, a := range actions {
m := map[string]interface{}{}
m["id"] = a.ID
m["group"] = a.Group
m["action_type_id"] = a.ActionTypeId
m["params"] = flattenMap(a.Params)
item := actionsSchema[index].(map[string]interface{})
if paramJson, ok := item["params_json"]; ok && paramJson != "" {
data, err := json.Marshal(a.Params)
if err != nil {
return nil, err
}
m["params_json"] = string(data)
} else {
m["params"] = flattenMap(a.Params)
}
result = append(result, m)
}
return result
return result, nil
}

func expandKibanaAlertConditions(raw map[string]interface{}) map[string]interface{} {
Expand Down
79 changes: 79 additions & 0 deletions es/resource_elasticsearch_kibana_alert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func TestAccElasticsearchKibanaAlert(t *testing.T) {

testConfig := testAccElasticsearchKibanaAlertV77(defaultActionID)
testParmsConfig := testAccElasticsearchKibanaAlertParamsJSONV77
testActionParmsConfig := testAccElasticsearchKibanaAlertJsonV77(defaultActionID)
elasticVersion, err := resourceElasticsearchKibanaGetVersion(meta)
if err != nil {
t.Skipf("err: %s", err)
Expand All @@ -67,6 +68,7 @@ func TestAccElasticsearchKibanaAlert(t *testing.T) {
if elasticVersion.GreaterThanOrEqual(versionV711) {
testConfig = testAccElasticsearchKibanaAlertV711
testParmsConfig = testAccElasticsearchKibanaAlertParamsJSONV711
testActionParmsConfig = testAccElasticsearchKibanaAlertJsonV711(defaultActionID)
}

log.Printf("[INFO] TestAccElasticsearchKibanaAlert %+v", elasticVersion)
Expand All @@ -92,6 +94,12 @@ func TestAccElasticsearchKibanaAlert(t *testing.T) {
testCheckElasticsearchKibanaAlertExists("elasticsearch_kibana_alert.test_params_json"),
),
},
{
Config: testActionParmsConfig,
Check: resource.ComposeTestCheckFunc(
testCheckElasticsearchKibanaAlertExists("elasticsearch_kibana_alert.test_action_json"),
),
},
},
})
}
Expand Down Expand Up @@ -286,6 +294,77 @@ resource "elasticsearch_kibana_alert" "test" {
`, actionID)
}

func testAccElasticsearchKibanaAlertJsonV77(actionID string) string {
return fmt.Sprintf(`
resource "elasticsearch_kibana_alert" "test_action_json" {
name = "terraform-alert"
schedule {
interval = "1m"
}
conditions {
aggregation_type = "avg"
term_size = 6
threshold_comparator = ">"
time_window_size = 5
time_window_unit = "m"
group_by = "top"
threshold = [1000]
index = [".test-index"]
time_field = "@timestamp"
aggregation_field = "sheet.version"
term_field = "name.keyword"
}
actions {
id = "%s"
action_type_id = ".index"
group = "threshold met"
params_json = <<EOF
{
"level" : "info",
"message" : "alert '{{alertName}}' is active for group '{{context.group}}':\n\n- Value: {{context.value}}\n- Conditions Met: {{context.conditions}} over {{params.timeWindowSize}}{{params.timeWindowUnit}}\n- Timestamp: {{context.date}}"
}
EOF
}
}
`, actionID)
}

func testAccElasticsearchKibanaAlertJsonV711(actionID string) string {
return fmt.Sprintf(`
resource "elasticsearch_kibana_alert" "test_action_json" {
name = "terraform-alert"
notify_when = "onActiveAlert"
schedule {
interval = "1m"
}
conditions {
aggregation_type = "avg"
term_size = 6
threshold_comparator = ">"
time_window_size = 5
time_window_unit = "m"
group_by = "top"
threshold = [1000]
index = [".test-index"]
time_field = "@timestamp"
aggregation_field = "sheet.version"
term_field = "name.keyword"
}
actions {
id = "%s"
action_type_id = ".index"
group = "threshold met"
params_json = <<EOF
{
"level" : "info",
"message" : "alert '{{alertName}}' is active for group '{{context.group}}':\n\n- Value: {{context.value}}\n- Conditions Met: {{context.conditions}} over {{params.timeWindowSize}}{{params.timeWindowUnit}}\n- Timestamp: {{context.date}}"
}
EOF
}
}
`, actionID)
}

var testAccElasticsearchKibanaAlertNoActionsV77 = `
resource "elasticsearch_kibana_alert" "test" {
name = "terraform-alert"
Expand Down

0 comments on commit 1cc0cfa

Please sign in to comment.