Skip to content

Commit

Permalink
add support for intelligent alert grouping time window
Browse files Browse the repository at this point in the history
  • Loading branch information
imjaroiswebdev committed Nov 25, 2023
1 parent 5840657 commit 2a7ef54
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 58 deletions.
115 changes: 59 additions & 56 deletions pagerduty/resource_pagerduty_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand All @@ -115,6 +112,12 @@ func resourcePagerDutyService() *schema.Resource {
"any",
}),
},
"time_window": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateDiagFunc: validateIntelligentTimeWindow,
},
},
},
},
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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
}

Expand All @@ -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{} {
Expand All @@ -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 {
Expand All @@ -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}
Expand Down
55 changes: 53 additions & 2 deletions pagerduty/resource_pagerduty_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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"),
),
},
},
Expand Down Expand Up @@ -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"
Expand All @@ -1235,7 +1286,7 @@ resource "pagerduty_service" "foo" {
alert_grouping_parameters {
type = "intelligent"
config {
time_window = 0
time_window = 900
}
}
}
Expand Down
1 change: 1 addition & 0 deletions website/docs/r/service.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down

0 comments on commit 2a7ef54

Please sign in to comment.