Skip to content

Commit

Permalink
Merge branch 'master' into ORCA-4263
Browse files Browse the repository at this point in the history
  • Loading branch information
swbradshaw authored Nov 27, 2023
2 parents 33a16f4 + 67bec0a commit da691f6
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 56 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/hashicorp/terraform-exec v0.16.0
github.com/hashicorp/terraform-json v0.13.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.11.0
github.com/heimweh/go-pagerduty v0.0.0-20231025174125-1492ec59406f
github.com/heimweh/go-pagerduty v0.0.0-20231125004736-fa2590da432f
)

require (
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,8 @@ github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaa
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty
0 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
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 da691f6

Please sign in to comment.