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 bea7724 commit 6c46a84
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 56 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,5 @@ require (
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

replace github.com/heimweh/go-pagerduty => github.com/imjaroiswebdev/go-pagerduty v0.0.0-20231121222439-a8eebacfaaf7
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,6 @@ github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/heimweh/go-pagerduty v0.0.0-20231025174125-1492ec59406f h1:aKlHoRz5VjwQbc5aH8hIMMmdXZIYEJvyIw3DAiHPolc=
github.com/heimweh/go-pagerduty v0.0.0-20231025174125-1492ec59406f/go.mod h1:r59w5iyN01Qvi734yA5hZldbSeJJmsJzee/1kQ/MK7s=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
Expand All @@ -260,6 +258,8 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/imjaroiswebdev/go-pagerduty v0.0.0-20231121222439-a8eebacfaaf7 h1:RkWjn7LnEvx2AY5ddeax1CMdUnUKRJX2MoYkktxUcTo=
github.com/imjaroiswebdev/go-pagerduty v0.0.0-20231121222439-a8eebacfaaf7/go.mod h1:r59w5iyN01Qvi734yA5hZldbSeJJmsJzee/1kQ/MK7s=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
Expand Down
147 changes: 97 additions & 50 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 All @@ -16,35 +18,11 @@ import (

func resourcePagerDutyService() *schema.Resource {
return &schema.Resource{
Create: resourcePagerDutyServiceCreate,
Read: resourcePagerDutyServiceRead,
Update: resourcePagerDutyServiceUpdate,
Delete: resourcePagerDutyServiceDelete,
CustomizeDiff: func(context context.Context, diff *schema.ResourceDiff, i interface{}) error {
in := diff.Get("incident_urgency_rule.#").(int)
for i := 0; i <= in; i++ {
t := diff.Get(fmt.Sprintf("incident_urgency_rule.%d.type", i)).(string)
if t == "use_support_hours" && diff.Get(fmt.Sprintf("incident_urgency_rule.%d.urgency", i)).(string) != "" {
return fmt.Errorf("general urgency cannot be set for a use_support_hours incident urgency rule type")
}
}

// Due to alert_grouping_parameters.type = null is a valid configuration
// for disabling Service's Alert Grouping configuration and having an
// empty alert_grouping_parameters.config block is also valid, API ignore
// this input fields, and turns out that API response for Service
// configuration doesn't bring a representation of this HCL, which leads
// to a permadiff, described in
// https://github.com/PagerDuty/terraform-provider-pagerduty/issues/700
//
// So, bellow is the formated representation alert_grouping_parameters
// value when this permadiff appears and must be ignored.
ignoreThisAlertGroupingParamsConfigDiff := `[]interface {}{map[string]interface {}{"config":[]interface {}{interface {}(nil)}, "type":""}}`
if agpdiff, ok := diff.Get("alert_grouping_parameters").([]interface{}); ok && diff.NewValueKnown("alert_grouping_parameters") && fmt.Sprintf("%#v", agpdiff) == ignoreThisAlertGroupingParamsConfigDiff {
diff.Clear("alert_grouping_parameters")
}
return nil
},
Create: resourcePagerDutyServiceCreate,
Read: resourcePagerDutyServiceRead,
Update: resourcePagerDutyServiceUpdate,
Delete: resourcePagerDutyServiceDelete,
CustomizeDiff: customizePagerDutyServiceDiff,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Expand Down Expand Up @@ -134,6 +112,12 @@ func resourcePagerDutyService() *schema.Resource {
"any",
}),
},
"time_window": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateDiagFunc: validateIntelligentTimeWindow,
},
},
},
},
Expand Down Expand Up @@ -327,6 +311,68 @@ func resourcePagerDutyService() *schema.Resource {
}
}

func customizePagerDutyServiceDiff(context context.Context, diff *schema.ResourceDiff, i interface{}) error {
in := diff.Get("incident_urgency_rule.#").(int)
for i := 0; i <= in; i++ {
t := diff.Get(fmt.Sprintf("incident_urgency_rule.%d.type", i)).(string)
if t == "use_support_hours" && diff.Get(fmt.Sprintf("incident_urgency_rule.%d.urgency", i)).(string) != "" {
return fmt.Errorf("general urgency cannot be set for a use_support_hours incident urgency rule type")
}
}

// Due to alert_grouping_parameters.type = null is a valid configuration
// for disabling Service's Alert Grouping configuration and having an
// empty alert_grouping_parameters.config block is also valid, API ignore
// this input fields, and turns out that API response for Service
// configuration doesn't bring a representation of this HCL, which leads
// to a permadiff, described in
// https://github.com/PagerDuty/terraform-provider-pagerduty/issues/700
//
// So, bellow is the formated representation alert_grouping_parameters
// value when this permadiff appears and must be ignored.
ignoreThisAlertGroupingParamsConfigDiff := `[]interface {}{map[string]interface {}{"config":[]interface {}{interface {}(nil)}, "type":""}}`
if agpdiff, ok := diff.Get("alert_grouping_parameters").([]interface{}); ok && diff.NewValueKnown("alert_grouping_parameters") && fmt.Sprintf("%#v", agpdiff) == ignoreThisAlertGroupingParamsConfigDiff {
diff.Clear("alert_grouping_parameters")
}

if agpType, ok := diff.Get("alert_grouping_parameters.0.type").(string); ok {
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")
}
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")
}
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 @@ -587,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
log.Printf("[MYDEBUG] config: %#v", alertGroupingParameters.Config)
if groupingType == "content_based" || groupingType == "time" {
alertGroupingParameters.Config = expandAlertGroupingConfig(riur["config"], groupingType)
}
alertGroupingParameters.Config = expandAlertGroupingConfig(ragpVal["config"])
return alertGroupingParameters
}

Expand All @@ -619,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 @@ -648,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 @@ -664,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
123 changes: 123 additions & 0 deletions pagerduty/resource_pagerduty_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,51 @@ func TestAccPagerDutyService_AlertContentGrouping(t *testing.T) {
})
}

func TestAccPagerDutyService_AlertContentGroupingIntelligentTimeWindow(t *testing.T) {
username := fmt.Sprintf("tf-%s", acctest.RandString(5))
email := fmt.Sprintf("%[email protected]", username)
escalationPolicy := fmt.Sprintf("tf-%s", acctest.RandString(5))
service := fmt.Sprintf("tf-%s", acctest.RandString(5))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPagerDutyServiceDestroy,
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(
"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"),
resource.TestCheckResourceAttr(
"pagerduty_service.foo", "alert_grouping_parameters.0.config.0.time_window", "900"),
),
},
},
})
}

func TestAccPagerDutyService_AutoPauseNotificationsParameters(t *testing.T) {
username := fmt.Sprintf("tf-%s", acctest.RandString(5))
email := fmt.Sprintf("%[email protected]", username)
Expand Down Expand Up @@ -1170,6 +1215,84 @@ resource "pagerduty_service" "foo" {
`, username, email, escalationPolicy, service)
}

func testAccCheckPagerDutyServiceConfigWithAlertContentGroupingIntelligentTimeWindow(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"
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"
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"
config {
time_window = 900
}
}
}
`, username, email, escalationPolicy, service)
}

func testAccCheckPagerDutyServiceConfigWithAlertContentGroupingUpdated(username, email, escalationPolicy, service string) string {
return fmt.Sprintf(`
resource "pagerduty_user" "foo" {
Expand Down
7 changes: 4 additions & 3 deletions vendor/github.com/heimweh/go-pagerduty/pagerduty/service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6c46a84

Please sign in to comment.