diff --git a/pagerduty/resource_pagerduty_service.go b/pagerduty/resource_pagerduty_service.go index e5cf5219e..edf0ec66d 100644 --- a/pagerduty/resource_pagerduty_service.go +++ b/pagerduty/resource_pagerduty_service.go @@ -8,6 +8,8 @@ import ( "strconv" "time" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -95,11 +97,6 @@ func resourcePagerDutyService() *schema.Resource { Type: schema.TypeInt, Optional: true, }, - "time_window": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - }, "fields": { Type: schema.TypeList, Optional: true, @@ -115,6 +112,12 @@ func resourcePagerDutyService() *schema.Resource { "any", }), }, + "time_window": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateDiagFunc: validateIntelligentTimeWindow, + }, }, }, }, @@ -333,44 +336,43 @@ func customizePagerDutyServiceDiff(context context.Context, diff *schema.Resourc } if agpType, ok := diff.Get("alert_grouping_parameters.0.type").(string); ok { - _, isTimeoutSet := diff.GetOkExists("alert_grouping_parameters.0.config.0.timeout") - aggregateVal, isAggregateSet := diff.GetOkExists("alert_grouping_parameters.0.config.0.aggregate") - fieldsVal, isFieldsSet := diff.GetOkExists("alert_grouping_parameters.0.config.0.fields") - _, isTimeWindowSet := diff.GetOkExists("alert_grouping_parameters.0.config.0.time_window") - - // alert_grouping_parameters.type = time - // Required -> config.timeout - if agpType == "time" { - if isAggregateSet || isFieldsSet || isTimeWindowSet { - return fmt.Errorf("Alert grouping parameters configuration of type \"time\" only supports setting \"timeout\" attribute") - } - if !isTimeoutSet { - return fmt.Errorf("When using Alert grouping parameters configuration of type \"time\" is in use, attribute \"timeout\" is required") - } + agppath := "alert_grouping_parameters.0.config.0." + timeoutVal := diff.Get(agppath + "timeout").(int) + aggregateVal := diff.Get(agppath + "aggregate").(string) + fieldsVal := diff.Get(agppath + "fields").([]interface{}) + timeWindowVal := diff.Get(agppath + "time_window").(int) + + if agpType == "time" && (aggregateVal != "" || len(fieldsVal) > 0 || timeWindowVal > 0) { + return fmt.Errorf("Alert grouping parameters configuration of type \"time\" only supports setting \"timeout\" attribute") } - // alert_grouping_parameters.type = content_based - // Required -> config.aggregate - // Required -> config.fields - if agpType == "content_based" { - if isTimeoutSet || isTimeWindowSet { - return fmt.Errorf("Alert grouping parameters configuration of type \"content_based\" only supports setting \"aggregate\" and \"fields\" attributes") - } - if !isAggregateSet || !isFieldsSet || aggregateVal == "" || len(fieldsVal.([]interface{})) == 0 { - return fmt.Errorf("When using Alert grouping parameters configuration of type \"content_based\" is in use, attributes \"aggregate\" and \"fields\" are required") - } + if agpType == "content_based" && (timeoutVal > 0 || timeWindowVal > 0) { + return fmt.Errorf("Alert grouping parameters configuration of type \"content_based\" only supports setting \"aggregate\" and \"fields\" attributes") } - // alert_grouping_parameters.type = intelligent - // Optional -> config.time_window - if agpType == "intelligent" { - if isAggregateSet || isFieldsSet || isTimeoutSet { - return fmt.Errorf("Alert grouping parameters configuration of type \"intelligent\" only supports setting the optional \"time_window\" attribute") - } + if agpType == "content_based" && (aggregateVal == "" || len(fieldsVal) == 0) { + return fmt.Errorf("When using Alert grouping parameters configuration of type \"content_based\" is in use, attributes \"aggregate\" and \"fields\" are required") + } + if agpType == "intelligent" && (aggregateVal != "" || len(fieldsVal) > 0 || timeoutVal > 0) { + return fmt.Errorf("Alert grouping parameters configuration of type \"intelligent\" only supports setting the optional attribute \"time_window\"") } } return nil } +func validateIntelligentTimeWindow(v interface{}, p cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + tw := v.(int) + if tw < 300 || tw > 3600 { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: fmt.Sprintf("Intelligent alert grouping time window value must be between 300 and 3600, current setting is %d", tw), + AttributePath: p, + }) + } + return diags +} + func buildServiceStruct(d *schema.ResourceData) (*pagerduty.Service, error) { service := pagerduty.Service{ Name: d.Get("name").(string), @@ -631,23 +633,18 @@ func expandAlertGroupingParameters(v interface{}) *pagerduty.AlertGroupingParame } // First We capture a possible nil value for the interface to avoid the a // panic - pre := v.([]interface{})[0] - if isNilFunc(pre) { + ragp, ok := v.([]interface{}) + if !ok || isNilFunc(ragp[0]) { return nil } - riur := pre.(map[string]interface{}) + ragpVal := ragp[0].(map[string]interface{}) groupingType := "" - if riur["type"].(string) != "" { - groupingType = riur["type"].(string) + if ragpVal["type"].(string) != "" { + groupingType = ragpVal["type"].(string) alertGroupingParameters.Type = &groupingType } - // For Intelligent grouping type, config is null - // alertGroupingParameters.Config = nil - // if groupingType == "content_based" || groupingType == "time" { - // alertGroupingParameters.Config = expandAlertGroupingConfig(riur["config"], groupingType) - // } - alertGroupingParameters.Config = expandAlertGroupingConfig(riur["config"], groupingType) + alertGroupingParameters.Config = expandAlertGroupingConfig(ragpVal["config"]) return alertGroupingParameters } @@ -663,27 +660,32 @@ func expandAutoPauseNotificationsParameters(v interface{}) *pagerduty.AutoPauseN return autoPauseNotificationsParameters } -func expandAlertGroupingConfig(v interface{}, groupingType string) *pagerduty.AlertGroupingConfig { +func expandAlertGroupingConfig(v interface{}) *pagerduty.AlertGroupingConfig { alertGroupingConfig := &pagerduty.AlertGroupingConfig{} - if len(v.([]interface{})) == 0 || v.([]interface{})[0] == nil { + rconfig := v.([]interface{}) + if len(rconfig) == 0 || rconfig[0] == nil { return nil } - riur := v.([]interface{})[0].(map[string]interface{}) + config := rconfig[0].(map[string]interface{}) alertGroupingConfig.Fields = []string{} - if val, ok := riur["fields"]; ok { + if val, ok := config["fields"]; ok { for _, field := range val.([]interface{}) { alertGroupingConfig.Fields = append(alertGroupingConfig.Fields, field.(string)) } } - if val, ok := riur["aggregate"]; ok { + if val, ok := config["aggregate"]; ok { agg := val.(string) alertGroupingConfig.Aggregate = &agg } - if val, ok := riur["timeout"]; ok && groupingType == "time" { + if val, ok := config["timeout"]; ok { to := val.(int) alertGroupingConfig.Timeout = &to } + if val, ok := config["time_window"]; ok { + to := val.(int) + alertGroupingConfig.TimeWindow = &to + } return alertGroupingConfig } func flattenAlertGroupingParameters(v *pagerduty.AlertGroupingParameters) interface{} { @@ -692,7 +694,7 @@ func flattenAlertGroupingParameters(v *pagerduty.AlertGroupingParameters) interf if v.Config == nil && v.Type == nil { return []interface{}{alertGroupingParameters} } else { - alertGroupingParameters = map[string]interface{}{"type": "", "config": []map[string]interface{}{{"aggregate": nil, "fields": nil, "timeout": nil}}} + alertGroupingParameters = map[string]interface{}{"type": "", "config": []map[string]interface{}{{"aggregate": nil, "fields": nil, "timeout": nil, "time_window": nil}}} } if v.Type != nil { @@ -708,9 +710,10 @@ func flattenAlertGroupingParameters(v *pagerduty.AlertGroupingParameters) interf func flattenAlertGroupingConfig(v *pagerduty.AlertGroupingConfig) interface{} { alertGroupingConfig := map[string]interface{}{ - "aggregate": v.Aggregate, - "fields": v.Fields, - "timeout": v.Timeout, + "aggregate": v.Aggregate, + "fields": v.Fields, + "timeout": v.Timeout, + "time_window": v.TimeWindow, } return []interface{}{alertGroupingConfig} diff --git a/pagerduty/resource_pagerduty_service_test.go b/pagerduty/resource_pagerduty_service_test.go index a1363a19b..d66d4ed16 100644 --- a/pagerduty/resource_pagerduty_service_test.go +++ b/pagerduty/resource_pagerduty_service_test.go @@ -460,6 +460,20 @@ func TestAccPagerDutyService_AlertContentGroupingIntelligentTimeWindow(t *testin Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyServiceConfigWithAlertContentGroupingIntelligentTimeWindow(username, email, escalationPolicy, service), + Check: resource.ComposeTestCheckFunc( + testAccCheckPagerDutyServiceExists("pagerduty_service.foo"), + resource.TestCheckResourceAttr( + "pagerduty_service.foo", "name", service), + resource.TestCheckResourceAttr( + "pagerduty_service.foo", "description", "foo"), + resource.TestCheckResourceAttr( + "pagerduty_service.foo", "alert_creation", "create_alerts_and_incidents"), + resource.TestCheckResourceAttr( + "pagerduty_service.foo", "alert_grouping_parameters.0.type", "intelligent"), + ), + }, + { + Config: testAccCheckPagerDutyServiceConfigWithAlertContentGroupingIntelligentTimeWindowUpdated(username, email, escalationPolicy, service), Check: resource.ComposeTestCheckFunc( testAccCheckPagerDutyServiceExists("pagerduty_service.foo"), resource.TestCheckResourceAttr( @@ -471,7 +485,7 @@ func TestAccPagerDutyService_AlertContentGroupingIntelligentTimeWindow(t *testin resource.TestCheckResourceAttr( "pagerduty_service.foo", "alert_grouping_parameters.0.type", "intelligent"), resource.TestCheckResourceAttr( - "pagerduty_service.foo", "alert_grouping_parameters.0.config.0.time_window", "0"), + "pagerduty_service.foo", "alert_grouping_parameters.0.config.0.time_window", "900"), ), }, }, @@ -1225,6 +1239,43 @@ resource "pagerduty_escalation_policy" "foo" { } } +resource "pagerduty_service" "foo" { + name = "%s" + description = "foo" + auto_resolve_timeout = 1800 + acknowledgement_timeout = 1800 + escalation_policy = pagerduty_escalation_policy.foo.id + alert_creation = "create_alerts_and_incidents" + alert_grouping_parameters { + type = "intelligent" + } +} +`, username, email, escalationPolicy, service) +} +func testAccCheckPagerDutyServiceConfigWithAlertContentGroupingIntelligentTimeWindowUpdated(username, email, escalationPolicy, service string) string { + return fmt.Sprintf(` +resource "pagerduty_user" "foo" { + name = "%s" + email = "%s" + color = "green" + role = "user" + job_title = "foo" + description = "foo" +} + +resource "pagerduty_escalation_policy" "foo" { + name = "%s" + description = "bar" + num_loops = 2 + rule { + escalation_delay_in_minutes = 10 + target { + type = "user_reference" + id = pagerduty_user.foo.id + } + } +} + resource "pagerduty_service" "foo" { name = "%s" description = "foo" @@ -1235,7 +1286,7 @@ resource "pagerduty_service" "foo" { alert_grouping_parameters { type = "intelligent" config { - time_window = 0 + time_window = 900 } } } diff --git a/website/docs/r/service.html.markdown b/website/docs/r/service.html.markdown index a3932438d..a09323478 100644 --- a/website/docs/r/service.html.markdown +++ b/website/docs/r/service.html.markdown @@ -71,6 +71,7 @@ The `alert_grouping_parameters` block contains the following arguments: * `timeout` - (Optional) The duration in minutes within which to automatically group incoming alerts. This setting applies only when `type` is set to `time`. To continue grouping alerts until the incident is resolved, set this value to `0`. * `aggregate` - (Optional) One of `any` or `all`. This setting applies only when `type` is set to `content_based`. Group alerts based on one or all of `fields` value(s). * `fields` - (Optional) Alerts will be grouped together if the content of these fields match. This setting applies only when `type` is set to `content_based`. + * `time_window` - (Optional) The maximum amount of time allowed between Alerts. Value must be between `300` and `3600`. Any Alerts arriving greater than `time_window` seconds apart will not be grouped together. This is a rolling time window and is counted from the most recently grouped alert. The window is extended every time a new alert is added to the group, up to 24 hours. The `auto_pause_notifications_parameters` block contains the following arguments: