From d5d607497a88cb19d379e9c7aa554b8de46f8950 Mon Sep 17 00:00:00 2001 From: Sergey Opria Date: Thu, 25 Aug 2022 11:03:01 -0400 Subject: [PATCH] feat(index rate alert): add support of index rate alert api Added Index Rate Alert resource The API allows only PUT and GET endpoints We use PUT endpoint for Create, Update and Delete resource functions Delete resource disables index rate alert config Ref: LOG-13487 --- docs/resources/logdna_index_rate_alert.md | 76 +++++ examples/index_rate_alert.tf | 16 + logdna/common_test.go | 10 +- logdna/data_source_alert_test.go | 24 +- logdna/provider.go | 1 + logdna/request_types.go | 46 +++ logdna/resource_alert_test.go | 84 ++--- logdna/resource_index_rate_alert.go | 223 +++++++++++++ logdna/resource_index_rate_alert_test.go | 364 ++++++++++++++++++++++ logdna/resource_view_test.go | 100 +++--- logdna/response_types.go | 15 + 11 files changed, 850 insertions(+), 109 deletions(-) create mode 100644 docs/resources/logdna_index_rate_alert.md create mode 100644 examples/index_rate_alert.tf create mode 100644 logdna/resource_index_rate_alert.go create mode 100644 logdna/resource_index_rate_alert_test.go diff --git a/docs/resources/logdna_index_rate_alert.md b/docs/resources/logdna_index_rate_alert.md new file mode 100644 index 0000000..ae680b6 --- /dev/null +++ b/docs/resources/logdna_index_rate_alert.md @@ -0,0 +1,76 @@ +# Resource: `logdna_index_rate_alert` + +Manages [LogDNA Index Rate Alert](https://docs.mezmo.com/docs/index-rate-alerts). Configuring alerts based on the index rate or retention and storage rate of your log data helps you track unusual behavior in your systems. For example, if there's a sudden spike in volume, Mezmo's Index Rate Alert feature tells you which applications or sources produced the data spike. It also shows any recently added sources. Index rate alerts can also help managers who are responsible for budgets to analyze and predict storage costs. + +To get started, all you need to do is to specify a configuration and one of our currently supported alerts recipients: email, Slack, or PagerDuty. + +Be aware that only one index rate alert configuration is allowed per account + +## Example - Index Rate Alert + +```hcl +provider "logdna" { + servicekey = "xxxxxxxxxxxxxxxxxxxxxxxx" + url = "https://api.logdna.com" # (Optional) specify a LogDNA region +} + +resource "logdna_index_rate_alert" "config" { + max_lines = 3 + max_z_score = 3 + threshold_alert = "separate" + frequency = "hourly" + enabled = true + channels { + email = ["test@test.com"] + slack = ["https://slack_url/key"] + pagerduty = ["service_key"] + } +} +``` + +## Destroy +There is not a DELETE endpoint supported by the Index Rate Alert API. For this reason, removing the Index Rate Alert Config effectively disables it. (set enabled to false in DB) + +## Import + +Index Rate Alert can be imported by static ID "config", which can be found using the [Get Index Rate Alert API](https://docs.mezmo.com/log-analysis-api/ref#get-index-rate-alert): + +1. Custom HTTP Headers - `servicekey: ` or `apikey: ` +```sh +curl --request GET \ + --url /v1/config/index-rate \ + --header 'Accept: application/json' \ + --header 'servicekey: ' +``` +2. Basic Auth - `Authorization: Basic `.
+Credentials is a string formatted as `:`. Our usage here entails substituting `` as the username and leaving the password blank. The colon separator should still be included in the resulting string `:` +```sh +curl --request GET \ + --url /v1/config/index-rate \ + --header 'Accept: application/json' \ + --header 'Authorization: Basic ' +``` + +```sh +terraform import logdna_index_rate_alert.config config +``` + +Note that only the alert channels supported by this provider will be imported. + +## Argument Reference + +The following arguments are supported by `logdna_alert`: + +- `max_lines`: The number of lines required in order to set off the alert, type _int_ +- `max_z_score`: The number of standard deviations above the 30-day average lines in order to set off the alert, type _int_ +- `threshold_alert`: Set if you want alerts to be triggered if one or both of the max lines and standard deviation have been triggered or individually, type _string_ ["separate" | "both"] +- `frequency`: Notify recipients once per hour or once per day (starting from the first passing of the threshold) until the index rate declines back below the thresholds, ceasing all alerts., type _string_ ["hourly" | "daily"] +- `enabled`: (Required) Enable an existing configuration, type _boolean_ + +### channels + +`channels` supports the following arguments: + +- `email`: **_[]string_** An array of email addresses (strings) to notify +- `slack`: **_[]string_** An array of slack hook urls (strings) to notify +- `pagerduty`: **_[]string_** An array of pagerduty service integration keys (strings) to notify diff --git a/examples/index_rate_alert.tf b/examples/index_rate_alert.tf new file mode 100644 index 0000000..6c53f9d --- /dev/null +++ b/examples/index_rate_alert.tf @@ -0,0 +1,16 @@ +provider "logdna" { + servicekey = "Your service key goes here" +} + +resource "logdna_index_rate_alert" "config" { + max_lines = 3 + max_z_score = 3 + threshold_alert = "separate" + frequency = "hourly" + enabled = true + channels { + email = ["test@test.com"] + slack = ["https://slack_url/key"] + pagerduty = ["service_key"] + } +} \ No newline at end of file diff --git a/logdna/common_test.go b/logdna/common_test.go index 154a74f..09e5626 100644 --- a/logdna/common_test.go +++ b/logdna/common_test.go @@ -39,7 +39,7 @@ var rsDefaults = map[string]map[string]string{ }, } var chnlDefaults = map[string]map[string]string{ - "email": { + "email_channel": { "emails": `["test@logdna.com"]`, "immediate": `"false"`, "operator": `"absence"`, @@ -48,7 +48,7 @@ var chnlDefaults = map[string]map[string]string{ "triggerinterval": `"15m"`, "triggerlimit": `15`, }, - "pagerduty": { + "pagerduty_channel": { "immediate": `"false"`, "operator": `"presence"`, "key": `"Your PagerDuty API key goes here"`, @@ -56,7 +56,7 @@ var chnlDefaults = map[string]map[string]string{ "triggerinterval": `"15m"`, "triggerlimit": `15`, }, - "slack": { + "slack_channel": { "immediate": `"false"`, "operator": `"absence"`, "terminal": `"true"`, @@ -64,7 +64,7 @@ var chnlDefaults = map[string]map[string]string{ "triggerlimit": `15`, "url": `"https://hooks.slack.com/services/identifier/secret"`, }, - "webhook": { + "webhook_channel": { "headers": "{\n" + "\t\t\thello = \"test3\"\n" + "\t\t\ttest = \"test2\"\n" + @@ -124,7 +124,7 @@ func fmtResourceBlock(objTyp, rsName string, rsArgs map[string]string, chArgs ma rgxDgt := regexp.MustCompile(`\d+`) for chName, chArgs := range chArgs { - fmt.Fprintf(&rsCfg, "\t%s_channel {\n", rgxDgt.ReplaceAllString(chName, "")) + fmt.Fprintf(&rsCfg, "\t%s {\n", rgxDgt.ReplaceAllString(chName, "")) fmt.Fprint(&rsCfg, fmtBlockArgs(2, chArgs)) fmt.Fprintf(&rsCfg, "\t}\n") } diff --git a/logdna/data_source_alert_test.go b/logdna/data_source_alert_test.go index 1c82c27..d54cc61 100644 --- a/logdna/data_source_alert_test.go +++ b/logdna/data_source_alert_test.go @@ -16,26 +16,26 @@ data "logdna_alert" "remote" { func TestDataAlert_BulkChannels(t *testing.T) { emArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "email1": cloneDefaults(chnlDefaults["email"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "email1_channel": cloneDefaults(chnlDefaults["email_channel"]), } emsCfg := fmtTestConfigResource("alert", "test", globalPcArgs, alertDefaults, emArgs, nilLst) pdArgs := map[string]map[string]string{ - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "pagerduty1": cloneDefaults(chnlDefaults["pagerduty"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "pagerduty1_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), } pdsCfg := fmtTestConfigResource("alert", "test", globalPcArgs, alertDefaults, pdArgs, nilLst) slArgs := map[string]map[string]string{ - "slack": cloneDefaults(chnlDefaults["slack"]), - "slack1": cloneDefaults(chnlDefaults["slack"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "slack1_channel": cloneDefaults(chnlDefaults["slack_channel"]), } slsCfg := fmtTestConfigResource("alert", "test", globalPcArgs, alertDefaults, slArgs, nilLst) wbArgs := map[string]map[string]string{ - "webhook": cloneDefaults(chnlDefaults["webhook"]), - "webhook1": cloneDefaults(chnlDefaults["webhook"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), + "webhook1_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } wbsCfg := fmtTestConfigResource("alert", "test", globalPcArgs, alertDefaults, wbArgs, nilLst) @@ -99,10 +99,10 @@ func TestDataAlert_BulkChannels(t *testing.T) { func TestDataSourceAlert_MultipleChannels(t *testing.T) { chArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "slack": cloneDefaults(chnlDefaults["slack"]), - "webhook": cloneDefaults(chnlDefaults["webhook"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } fmtCfg := fmt.Sprintf("%s\n%s", fmtTestConfigResource("alert", "test", globalPcArgs, alertDefaults, chArgs, nilLst), ds) diff --git a/logdna/provider.go b/logdna/provider.go index e1c0652..1c452e1 100644 --- a/logdna/provider.go +++ b/logdna/provider.go @@ -39,6 +39,7 @@ func Provider() *schema.Provider { "logdna_ingestion_exclusion": resourceIngestionExclusion(), "logdna_archive": resourceArchiveConfig(), "logdna_key": resourceKey(), + "logdna_index_rate_alert": resourceIndexRateAlert(), }, ConfigureFunc: providerConfigure, } diff --git a/logdna/request_types.go b/logdna/request_types.go index 0c010d6..cc433b8 100644 --- a/logdna/request_types.go +++ b/logdna/request_types.go @@ -7,6 +7,7 @@ package logdna import ( "encoding/json" "fmt" + "errors" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -54,6 +55,21 @@ type keyRequest struct { Name string `json:"name,omitempty"` } +type indexRateAlertChannelRequest struct { + Email []string `json:"email,omitempty"` + Pagerduty []string `json:"pagerduty,omitempty"` + Slack []string `json:"slack,omitempty"` +} + +type indexRateAlertRequest struct { + MaxLines int `json:"max_lines,omitempty"` + MaxZScore int `json:"max_z_score,omitempty"` + ThresholdAlert string `json:"threshold_alert,omitempty"` + Frequency string `json:"frequency,omitempty"` + Channels indexRateAlertChannelRequest `json:"channels,omitempty"` + Enabled bool `json:"enabled,omitempty"` +} + func (view *viewRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostics { // This function pulls from the schema in preparation to JSON marshal var diags diag.Diagnostics @@ -107,6 +123,36 @@ func (key *keyRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostic return diags } +func (doc *indexRateAlertRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostics { + // This function pulls from the schema in preparation to JSON marshal + var diags diag.Diagnostics + + var channels = d.Get("channels").([]interface{}) + + if len(channels) > 1 { + return diag.FromErr( + errors.New("Index rate alert resource supports only one channels object"), + ) + } + + doc.MaxLines = d.Get("max_lines").(int) + doc.MaxZScore = d.Get("max_z_score").(int) + doc.Enabled = d.Get("enabled").(bool) + doc.ThresholdAlert = d.Get("threshold_alert").(string) + doc.Frequency = d.Get("frequency").(string) + + var indexRateAlertChannel indexRateAlertChannelRequest + var channel = channels[0].(map[string]interface{}) + + indexRateAlertChannel.Email = listToStrings(channel["email"].([]interface{})) + indexRateAlertChannel.Pagerduty = listToStrings(channel["pagerduty"].([]interface{})) + indexRateAlertChannel.Slack = listToStrings(channel["slack"].([]interface{})) + + doc.Channels = indexRateAlertChannel + + return diags +} + func aggregateAllChannelsFromSchema( d *schema.ResourceData, diags *diag.Diagnostics, diff --git a/logdna/resource_alert_test.go b/logdna/resource_alert_test.go index 48a39e9..eade13b 100644 --- a/logdna/resource_alert_test.go +++ b/logdna/resource_alert_test.go @@ -24,7 +24,7 @@ func TestAlert_ErrorProviderUrl(t *testing.T) { } func TestAlert_ErrorResourceName(t *testing.T) { - args := cloneDefaults(chnlDefaults["alert"]) + args := cloneDefaults(chnlDefaults["alert_channel"]) args["name"] = "" resource.Test(t, resource.TestCase{ @@ -39,24 +39,24 @@ func TestAlert_ErrorResourceName(t *testing.T) { } func TestAlert_ErrorsChannel(t *testing.T) { - imArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - imArgs["email"]["immediate"] = `"not a bool"` + imArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + imArgs["email_channel"]["immediate"] = `"not a bool"` immdte := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, imArgs, nilLst) - opArgs := map[string]map[string]string{"pagerduty": cloneDefaults(chnlDefaults["pagerduty"])} - opArgs["pagerduty"]["operator"] = `1000` + opArgs := map[string]map[string]string{"pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"])} + opArgs["pagerduty_channel"]["operator"] = `1000` opratr := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, opArgs, nilLst) - trArgs := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - trArgs["webhook"]["terminal"] = `"invalid"` + trArgs := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + trArgs["webhook_channel"]["terminal"] = `"invalid"` trmnal := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, trArgs, nilLst) - tiArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - tiArgs["email"]["triggerinterval"] = `18` + tiArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + tiArgs["email_channel"]["triggerinterval"] = `18` tintvl := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, tiArgs, nilLst) - tlArgs := map[string]map[string]string{"slack": cloneDefaults(chnlDefaults["slack"])} - tlArgs["slack"]["triggerlimit"] = `0` + tlArgs := map[string]map[string]string{"slack_channel": cloneDefaults(chnlDefaults["slack_channel"])} + tlArgs["slack_channel"]["triggerlimit"] = `0` tlimit := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, tlArgs, nilLst) resource.Test(t, resource.TestCase{ @@ -87,12 +87,12 @@ func TestAlert_ErrorsChannel(t *testing.T) { } func TestAlert_ErrorsEmailChannel(t *testing.T) { - msArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - msArgs["email"]["emails"] = "" + msArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + msArgs["email_channel"]["emails"] = "" misngE := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, msArgs, nilLst) - inArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - inArgs["email"]["emails"] = `"not an array of strings"` + inArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + inArgs["email_channel"]["emails"] = `"not an array of strings"` invldE := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, inArgs, nilLst) resource.Test(t, resource.TestCase{ @@ -111,8 +111,8 @@ func TestAlert_ErrorsEmailChannel(t *testing.T) { } func TestAlert_ErrorsPagerDutyChannel(t *testing.T) { - chArgs := map[string]map[string]string{"pagerduty": cloneDefaults(chnlDefaults["pagerduty"])} - chArgs["pagerduty"]["key"] = "" + chArgs := map[string]map[string]string{"pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"])} + chArgs["pagerduty_channel"]["key"] = "" resource.Test(t, resource.TestCase{ Providers: testAccProviders, @@ -126,12 +126,12 @@ func TestAlert_ErrorsPagerDutyChannel(t *testing.T) { } func TestAlert_ErrorsSlackChannel(t *testing.T) { - ulInvd := map[string]map[string]string{"slack": cloneDefaults(chnlDefaults["slack"])} - ulInvd["slack"]["url"] = `"this is not a valid url"` + ulInvd := map[string]map[string]string{"slack_channel": cloneDefaults(chnlDefaults["slack_channel"])} + ulInvd["slack_channel"]["url"] = `"this is not a valid url"` ulCfgE := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, ulInvd, nilLst) - ulMsng := map[string]map[string]string{"slack": cloneDefaults(chnlDefaults["slack"])} - ulMsng["slack"]["url"] = "" + ulMsng := map[string]map[string]string{"slack_channel": cloneDefaults(chnlDefaults["slack_channel"])} + ulMsng["slack_channel"]["url"] = "" ulCfgM := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, ulMsng, nilLst) resource.Test(t, resource.TestCase{ @@ -150,20 +150,20 @@ func TestAlert_ErrorsSlackChannel(t *testing.T) { } func TestAlert_ErrorsWebhookChannel(t *testing.T) { - btArgs := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - btArgs["webhook"]["bodytemplate"] = `"{\"test\": }"` + btArgs := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + btArgs["webhook_channel"]["bodytemplate"] = `"{\"test\": }"` btCfgE := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, btArgs, nilLst) - mdArgs := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - mdArgs["webhook"]["method"] = `"false"` + mdArgs := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + mdArgs["webhook_channel"]["method"] = `"false"` mdCfgE := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, mdArgs, nilLst) - ulInvd := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - ulInvd["webhook"]["url"] = `"this is not a valid url"` + ulInvd := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + ulInvd["webhook_channel"]["url"] = `"this is not a valid url"` ulCfgE := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, ulInvd, nilLst) - ulMsng := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - ulMsng["webhook"]["url"] = "" + ulMsng := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + ulMsng["webhook_channel"]["url"] = "" ulCfgM := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, ulMsng, nilLst) resource.Test(t, resource.TestCase{ @@ -190,7 +190,7 @@ func TestAlert_ErrorsWebhookChannel(t *testing.T) { } func TestAlert_Basic(t *testing.T) { - chArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} + chArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} iniCfg := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, chArgs, nilLst) rsArgs := cloneDefaults(rsDefaults["alert"]) @@ -238,26 +238,26 @@ func TestAlert_Basic(t *testing.T) { func TestAlert_BulkChannels(t *testing.T) { emArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "email1": cloneDefaults(chnlDefaults["email"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "email1_channel": cloneDefaults(chnlDefaults["email_channel"]), } emsCfg := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, emArgs, nilLst) pdArgs := map[string]map[string]string{ - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "pagerduty1": cloneDefaults(chnlDefaults["pagerduty"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "pagerduty1_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), } pdsCfg := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, pdArgs, nilLst) slArgs := map[string]map[string]string{ - "slack": cloneDefaults(chnlDefaults["slack"]), - "slack1": cloneDefaults(chnlDefaults["slack"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "slack1_channel": cloneDefaults(chnlDefaults["slack_channel"]), } slsCfg := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, slArgs, nilLst) wbArgs := map[string]map[string]string{ - "webhook": cloneDefaults(chnlDefaults["webhook"]), - "webhook1": cloneDefaults(chnlDefaults["webhook"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), + "webhook1_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } wbsCfg := fmtTestConfigResource("alert", "new", globalPcArgs, alertDefaults, wbArgs, nilLst) @@ -322,10 +322,10 @@ func TestAlert_BulkChannels(t *testing.T) { func TestAlert_MultipleChannels(t *testing.T) { chArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "slack": cloneDefaults(chnlDefaults["slack"]), - "webhook": cloneDefaults(chnlDefaults["webhook"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } resource.Test(t, resource.TestCase{ diff --git a/logdna/resource_index_rate_alert.go b/logdna/resource_index_rate_alert.go new file mode 100644 index 0000000..614d842 --- /dev/null +++ b/logdna/resource_index_rate_alert.go @@ -0,0 +1,223 @@ +package logdna + +import ( + "context" + "encoding/json" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +const indexRateAlertConfigID = "config" + +/** + * Create/Update index rate alert resource + * As API does not allow the POST method, this method calls PUT to be used for both create and update. + * which allows upsert and create a new index rate alert config record is not exist + * Only one config per account is allowed + */ +func resourceIndexRateAlertUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pc := m.(*providerConfig) + + indexRateAlert := indexRateAlertRequest{} + + if diags = indexRateAlert.CreateRequestBody(d); diags.HasError() { + return diags + } + + req := newRequestConfig( + pc, + "PUT", + "/v1/config/index-rate", + indexRateAlert, + ) + + body, err := req.MakeRequest() + log.Printf("[DEBUG] %s %s, payload is: %s", req.method, req.apiURL, body) + + if err != nil { + return diag.FromErr(err) + } + + createdIndexRateAlert := indexRateAlertResponse{} + err = json.Unmarshal(body, &createdIndexRateAlert) + + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] %s %s SUCCESS. Remote resource updated.", req.method, req.apiURL) + + d.SetId(indexRateAlertConfigID) + + return resourceIndexRateAlertRead(ctx, d, m) +} + +func resourceIndexRateAlertRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pc := m.(*providerConfig) + + req := newRequestConfig( + pc, + "GET", + "/v1/config/index-rate", + nil, + ) + + body, err := req.MakeRequest() + + log.Printf("[DEBUG] GET IndexRateAlert raw response body %s\n", body) + + if err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Cannot read the remote IndexRateAlert resource", + Detail: err.Error(), + }) + return diags + } + + indexRateAlert := indexRateAlertResponse{} + + err = json.Unmarshal(body, &indexRateAlert) + if err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Cannot unmarshal response from the remote indexRateAlert resource", + Detail: err.Error(), + }) + return diags + } + log.Printf("[DEBUG] The GET indexRateAlert structure is as follows: %+v\n", indexRateAlert) + + var channels []interface{} + + integrations := make(map[string]interface{}) + + integrations["email"] = indexRateAlert.Channels.Email + integrations["pagerduty"] = indexRateAlert.Channels.Pagerduty + integrations["slack"] = indexRateAlert.Channels.Slack + + channels = append(channels, integrations) + + appendError(d.Set("max_lines", indexRateAlert.MaxLines), &diags) + appendError(d.Set("max_z_score", indexRateAlert.MaxZScore), &diags) + appendError(d.Set("threshold_alert", indexRateAlert.ThresholdAlert), &diags) + appendError(d.Set("frequency", indexRateAlert.Frequency), &diags) + appendError(d.Set("channels", channels), &diags) + appendError(d.Set("enabled", indexRateAlert.Enabled), &diags) + + d.SetId(indexRateAlertConfigID) + + return diags +} + +/** + * Delete index rate alert resource + * As API does not allow DELETE method this method calls PUT + * We considering delete action as just disabling a config + */ +func resourceIndexRateAlertDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pc := m.(*providerConfig) + + resourceIndexRateAlertRead(ctx, d, m) + + indexRateAlert := indexRateAlertRequest{} + + if diags = indexRateAlert.CreateRequestBody(d); diags.HasError() { + return diags + } + + indexRateAlert.Enabled = false + + req := newRequestConfig( + pc, + "PUT", + "/v1/config/index-rate", + indexRateAlert, + ) + + body, err := req.MakeRequest() + log.Printf("[DEBUG] %s %s disable IndexRateAlert %s", req.method, req.apiURL, body) + + if err != nil { + return diag.FromErr(err) + } + + d.SetId("") + + return nil +} + +func resourceIndexRateAlert() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceIndexRateAlertUpdate, + UpdateContext: resourceIndexRateAlertUpdate, + ReadContext: resourceIndexRateAlertRead, + DeleteContext: resourceIndexRateAlertDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "max_lines": { + Type: schema.TypeInt, + Optional: true, + Description: "Max number of lines for alert", + }, + "max_z_score": { + Type: schema.TypeInt, + Optional: true, + Description: "Max Z score before alerting", + }, + "threshold_alert": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"separate", "both"}, false), + }, + "frequency": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"hourly", "daily"}, false), + }, + "channels": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "pagerduty": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "slack": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} diff --git a/logdna/resource_index_rate_alert_test.go b/logdna/resource_index_rate_alert_test.go new file mode 100644 index 0000000..69bc47a --- /dev/null +++ b/logdna/resource_index_rate_alert_test.go @@ -0,0 +1,364 @@ +package logdna + +import ( + "regexp" + "testing" + "strings" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +var escapeChar = regexp.MustCompile(`\"|\[|\]|,`) + +func TestIndexRateAlert_ErrorProviderUrl(t *testing.T) { + pcArgs := []string{serviceKey, "https://api.logdna.co"} + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "threshold_alert": `"separate"`, + "frequency": `"hourly"`, + "enabled": `false`, + } + + chArgs := map[string]map[string]string{ + "channels": { + "email": `["test@logdna.com", "test2@logdna.com"]`, + }, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmtTestConfigResource("index_rate_alert", "test_config", pcArgs, iraArgs, chArgs, nilLst), + ExpectError: regexp.MustCompile("Error: error during HTTP request: Put \"https://api.logdna.co/v1/config/index-rate\": dial tcp: lookup api.logdna.co.+?"), + }, + }, + }) +} + +func TestIndexRateAlert_ErrorResourceThresholdAlertInvalid(t *testing.T) { + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "threshold_alert": `"invalid"`, + "frequency": `"hourly"`, + "enabled": `false`, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraArgs, nilOpt, nilLst), + ExpectError: regexp.MustCompile("Error: expected threshold_alert to be one of .+?"), + }, + }, + }) +} + +func TestIndexRateAlert_ErrorResourceThresholdAlertMissed(t *testing.T) { + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "frequency": `"hourly"`, + "enabled": `false`, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraArgs, nilOpt, nilLst), + ExpectError: regexp.MustCompile("The argument \"threshold_alert\" is required, but no definition was found."), + }, + }, + }) +} + +func TestIndexRateAlert_ErrorResourceFrequencyInvalid(t *testing.T) { + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "threshold_alert": `"separate"`, + "frequency": `"ivalid"`, + "enabled": `false`, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraArgs, nilOpt, nilLst), + ExpectError: regexp.MustCompile("Error: expected frequency to be one of .+?"), + }, + }, + }) +} + +func TestIndexRateAlert_ErrorResourceFrequencyMissed(t *testing.T) { + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "threshold_alert": `"separate"`, + "enabled": `false`, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraArgs, nilOpt, nilLst), + ExpectError: regexp.MustCompile("The argument \"frequency\" is required, but no definition was found."), + }, + }, + }) +} + +func TestIndexRateAlert_ErrorResourceEnable(t *testing.T) { + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "threshold_alert": `"separate"`, + "frequency": `"hourly"`, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraArgs, nilOpt, nilLst), + ExpectError: regexp.MustCompile("The argument \"enabled\" is required, but no definition was found."), + }, + }, + }) +} + +func TestIndexRateAlert_ErrorChannels(t *testing.T) { + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "threshold_alert": `"separate"`, + "frequency": `"hourly"`, + "enabled": `false`, + } + + chArgs := map[string]map[string]string{ + "channels": { + "email": `["test@logdna.com"]`, + }, + "channels1": { + "email": `["test2@logdna.com"]`, + }, + } + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraArgs, chArgs, nilLst), + ExpectError: regexp.MustCompile("Index rate alert resource supports only one channels object"), + }, + }, + }) +} + +func TestIndexRateAlert_Basic(t *testing.T) { + iraArgs := map[string]string{ + "max_lines": `3`, + "max_z_score": `3`, + "threshold_alert": `"separate"`, + "frequency": `"hourly"`, + "enabled": `false`, + } + + chArgs := map[string]map[string]string{ + "channels": { + "email": `["test@logdna.com", "test2@logdna.com"]`, + "slack": `["https://hooks.slack.com/KEY"]`, + "pagerduty": `["ndt3k75rsw520d8t55dv35decdyt3mkcb3r"]`, + }, + } + + iraUpdArgs := map[string]string{ + "max_lines": `5`, + "max_z_score": `5`, + "threshold_alert": `"separate"`, + "frequency": `"hourly"`, + "enabled": `true`, + } + + chUpdArgs := map[string]map[string]string{ + "channels": { + "email": `["test_updated@logdna.com"]`, + "slack": `["https://hooks.slack.com/UPDATED_KEY", "https://hooks.slack.com/KEY_2"]`, + "pagerduty": `["new3k75rsw520d8t55dv35decdyt3mkcnew"]`, + }, + } + + createdEmails := strings.Split( + escapeChar.ReplaceAllString(chArgs["channels"]["email"], ""), + " ", + ) + + createdSlack := strings.Split( + escapeChar.ReplaceAllString(chArgs["channels"]["slack"], ""), + " ", + ) + + createdPagerduty := strings.Split( + escapeChar.ReplaceAllString(chArgs["channels"]["pagerduty"], ""), + " ", + ) + + updatedEmails := strings.Split( + escapeChar.ReplaceAllString(chUpdArgs["channels"]["email"], ""), + " ", + ) + + updatedSlack := strings.Split( + escapeChar.ReplaceAllString(chUpdArgs["channels"]["slack"], ""), + " ", + ) + + updatedPagerduty := strings.Split( + escapeChar.ReplaceAllString(chUpdArgs["channels"]["pagerduty"], ""), + " ", + ) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // NOTE It tests a index rate alert create operation + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraArgs, chArgs, nilLst), + Check: resource.ComposeTestCheckFunc( + testResourceExists("index_rate_alert", "test_config"), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "max_lines", + escapeChar.ReplaceAllString(iraArgs["max_lines"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "max_z_score", + escapeChar.ReplaceAllString(iraArgs["max_z_score"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "threshold_alert", + escapeChar.ReplaceAllString(iraArgs["threshold_alert"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "frequency", + escapeChar.ReplaceAllString(iraArgs["frequency"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.email.#", + strconv.Itoa(len(createdEmails)), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.email.0", + createdEmails[0], + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.email.1", + createdEmails[1], + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.slack.#", + strconv.Itoa(len(createdSlack)), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.slack.0", + createdSlack[0], + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.pagerduty.#", + strconv.Itoa(len(createdPagerduty)), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.pagerduty.0", + createdPagerduty[0], + ), + ), + }, + { + // NOTE It tests a index rate alert config update operation + Config: fmtTestConfigResource("index_rate_alert", "test_config", globalPcArgs, iraUpdArgs, chUpdArgs, nilLst), + Check: resource.ComposeTestCheckFunc( + testResourceExists("index_rate_alert", "test_config"), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "max_lines", + escapeChar.ReplaceAllString(iraUpdArgs["max_lines"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "max_z_score", + escapeChar.ReplaceAllString(iraUpdArgs["max_z_score"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "threshold_alert", + escapeChar.ReplaceAllString(iraUpdArgs["threshold_alert"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "frequency", + escapeChar.ReplaceAllString(iraUpdArgs["frequency"], ""), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.email.#", + strconv.Itoa(len(updatedEmails)), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.email.0", + updatedEmails[0], + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.slack.#", + strconv.Itoa(len(updatedSlack)), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.slack.0", + updatedSlack[0], + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.slack.1", + updatedSlack[1], + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.pagerduty.#", + strconv.Itoa(len(updatedPagerduty)), + ), + resource.TestCheckResourceAttr( + "logdna_index_rate_alert.test_config", + "channels.0.pagerduty.0", + updatedPagerduty[0], + ), + ), + }, + { + ResourceName: "logdna_index_rate_alert.test_config", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/logdna/resource_view_test.go b/logdna/resource_view_test.go index ba4c100..0b6e73f 100644 --- a/logdna/resource_view_test.go +++ b/logdna/resource_view_test.go @@ -83,24 +83,24 @@ func TestView_ErrorsResourceFields(t *testing.T) { } func TestView_ErrorsChannel(t *testing.T) { - imArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - imArgs["email"]["immediate"] = `"not a bool"` + imArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + imArgs["email_channel"]["immediate"] = `"not a bool"` immdte := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, imArgs, nilLst) - opArgs := map[string]map[string]string{"pagerduty": cloneDefaults(chnlDefaults["pagerduty"])} - opArgs["pagerduty"]["operator"] = `1000` + opArgs := map[string]map[string]string{"pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"])} + opArgs["pagerduty_channel"]["operator"] = `1000` opratr := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, opArgs, nilLst) - trArgs := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - trArgs["webhook"]["terminal"] = `"invalid"` + trArgs := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + trArgs["webhook_channel"]["terminal"] = `"invalid"` trmnal := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, trArgs, nilLst) - tiArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - tiArgs["email"]["triggerinterval"] = `18` + tiArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + tiArgs["email_channel"]["triggerinterval"] = `18` tintvl := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, tiArgs, nilLst) - tlArgs := map[string]map[string]string{"slack": cloneDefaults(chnlDefaults["slack"])} - tlArgs["slack"]["triggerlimit"] = `0` + tlArgs := map[string]map[string]string{"slack_channel": cloneDefaults(chnlDefaults["slack_channel"])} + tlArgs["slack_channel"]["triggerlimit"] = `0` tlimit := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, tlArgs, nilLst) resource.Test(t, resource.TestCase{ @@ -131,12 +131,12 @@ func TestView_ErrorsChannel(t *testing.T) { } func TestView_ErrorsEmailChannel(t *testing.T) { - msArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - msArgs["email"]["emails"] = "" + msArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + msArgs["email_channel"]["emails"] = "" misngE := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, msArgs, nilLst) - inArgs := map[string]map[string]string{"email": cloneDefaults(chnlDefaults["email"])} - inArgs["email"]["emails"] = `"not an array of strings"` + inArgs := map[string]map[string]string{"email_channel": cloneDefaults(chnlDefaults["email_channel"])} + inArgs["email_channel"]["emails"] = `"not an array of strings"` invldE := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, inArgs, nilLst) resource.Test(t, resource.TestCase{ @@ -155,8 +155,8 @@ func TestView_ErrorsEmailChannel(t *testing.T) { } func TestView_ErrorsPagerDutyChannel(t *testing.T) { - chArgs := map[string]map[string]string{"pagerduty": cloneDefaults(chnlDefaults["pagerduty"])} - chArgs["pagerduty"]["key"] = "" + chArgs := map[string]map[string]string{"pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"])} + chArgs["pagerduty_channel"]["key"] = "" resource.Test(t, resource.TestCase{ Providers: testAccProviders, @@ -170,12 +170,12 @@ func TestView_ErrorsPagerDutyChannel(t *testing.T) { } func TestView_ErrorsSlackChannel(t *testing.T) { - ulInvd := map[string]map[string]string{"slack": cloneDefaults(chnlDefaults["slack"])} - ulInvd["slack"]["url"] = `"this is not a valid url"` + ulInvd := map[string]map[string]string{"slack_channel": cloneDefaults(chnlDefaults["slack_channel"])} + ulInvd["slack_channel"]["url"] = `"this is not a valid url"` ulCfgE := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, ulInvd, nilLst) - ulMsng := map[string]map[string]string{"slack": cloneDefaults(chnlDefaults["slack"])} - ulMsng["slack"]["url"] = "" + ulMsng := map[string]map[string]string{"slack_channel": cloneDefaults(chnlDefaults["slack_channel"])} + ulMsng["slack_channel"]["url"] = "" ulCfgM := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, ulMsng, nilLst) resource.Test(t, resource.TestCase{ @@ -194,24 +194,24 @@ func TestView_ErrorsSlackChannel(t *testing.T) { } func TestView_ErrorsWebhookChannel(t *testing.T) { - btArgs := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - btArgs["webhook"]["bodytemplate"] = `"{\"test\": }"` + btArgs := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + btArgs["webhook_channel"]["bodytemplate"] = `"{\"test\": }"` btCfgE := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, btArgs, nilLst) - hdArgs := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - hdArgs["webhook"]["headers"] = `["headers", "invalid", "array"]` + hdArgs := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + hdArgs["webhook_channel"]["headers"] = `["headers", "invalid", "array"]` hdCfgE := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, hdArgs, nilLst) - mdArgs := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - mdArgs["webhook"]["method"] = `"false"` + mdArgs := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + mdArgs["webhook_channel"]["method"] = `"false"` mdCfgE := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, mdArgs, nilLst) - ulInvd := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - ulInvd["webhook"]["url"] = `"this is not a valid url"` + ulInvd := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + ulInvd["webhook_channel"]["url"] = `"this is not a valid url"` ulCfgE := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, ulInvd, nilLst) - ulMsng := map[string]map[string]string{"webhook": cloneDefaults(chnlDefaults["webhook"])} - ulMsng["webhook"]["url"] = "" + ulMsng := map[string]map[string]string{"webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"])} + ulMsng["webhook_channel"]["url"] = "" ulCfgM := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, ulMsng, nilLst) resource.Test(t, resource.TestCase{ @@ -279,26 +279,26 @@ func TestView_Basic(t *testing.T) { func TestView_BulkChannels(t *testing.T) { emArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "email1": cloneDefaults(chnlDefaults["email"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "email1_channel": cloneDefaults(chnlDefaults["email_channel"]), } emsCfg := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, emArgs, nilLst) pdArgs := map[string]map[string]string{ - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "pagerduty1": cloneDefaults(chnlDefaults["pagerduty"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "pagerduty1_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), } pdsCfg := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, pdArgs, nilLst) slArgs := map[string]map[string]string{ - "slack": cloneDefaults(chnlDefaults["slack"]), - "slack1": cloneDefaults(chnlDefaults["slack"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "slack1_channel": cloneDefaults(chnlDefaults["slack_channel"]), } slsCfg := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, slArgs, nilLst) wbArgs := map[string]map[string]string{ - "webhook": cloneDefaults(chnlDefaults["webhook"]), - "webhook1": cloneDefaults(chnlDefaults["webhook"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), + "webhook1_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } wbsCfg := fmtTestConfigResource("view", "new", globalPcArgs, viewDefaults, wbArgs, nilLst) @@ -367,10 +367,10 @@ func TestView_BulkChannels(t *testing.T) { func TestView_MultipleChannels(t *testing.T) { chArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "slack": cloneDefaults(chnlDefaults["slack"]), - "webhook": cloneDefaults(chnlDefaults["webhook"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } dependencies := []string{"logdna_category.cat_1", "logdna_category.cat_2"} @@ -504,10 +504,10 @@ func TestView_MultipleChannels(t *testing.T) { func TestView_PresetAlert(t *testing.T) { chArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "slack": cloneDefaults(chnlDefaults["slack"]), - "webhook": cloneDefaults(chnlDefaults["webhook"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } dependenciesIns := []string{ @@ -627,10 +627,10 @@ func TestView_PresetAlert(t *testing.T) { func TestView_ErrorsConflictPresetId(t *testing.T) { chArgs := map[string]map[string]string{ - "email": cloneDefaults(chnlDefaults["email"]), - "pagerduty": cloneDefaults(chnlDefaults["pagerduty"]), - "slack": cloneDefaults(chnlDefaults["slack"]), - "webhook": cloneDefaults(chnlDefaults["webhook"]), + "email_channel": cloneDefaults(chnlDefaults["email_channel"]), + "pagerduty_channel": cloneDefaults(chnlDefaults["pagerduty_channel"]), + "slack_channel": cloneDefaults(chnlDefaults["slack_channel"]), + "webhook_channel": cloneDefaults(chnlDefaults["webhook_channel"]), } rsArgs := cloneDefaults(rsDefaults["view"]) diff --git a/logdna/response_types.go b/logdna/response_types.go index 18d38f7..af8bf21 100644 --- a/logdna/response_types.go +++ b/logdna/response_types.go @@ -84,6 +84,21 @@ type categoryResponse struct { Id string `json:"id"` } +type indexRateAlertChannelResponse struct { + Email []string `json:"email,omitempty"` + Pagerduty []string `json:"pagerduty,omitempty"` + Slack []string `json:"slack,omitempty"` +} + +type indexRateAlertResponse struct { + MaxLines int `json:"max_lines,omitempty"` + MaxZScore int `json:"max_z_score,omitempty"` + ThresholdAlert string `json:"threshold_alert,omitempty"` + Frequency string `json:"frequency,omitempty"` + Channels indexRateAlertChannelResponse `json:"channels,omitempty"` + Enabled bool `json:"enabled,omitempty"` +} + func (view *viewResponse) MapChannelsToSchema() (map[string][]interface{}, diag.Diagnostics) { channels := view.Channels channelIntegrations, diags := mapAllChannelsToSchema("view", &channels)