From d5db1caa9b72b7552249bea7a072f689d943634e Mon Sep 17 00:00:00 2001 From: gmuselli Date: Wed, 13 Apr 2022 17:27:14 -0400 Subject: [PATCH 1/9] Issue 463: Introduce catch_all --- pagerduty/resource_pagerduty_ruleset_rule.go | 63 +++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/pagerduty/resource_pagerduty_ruleset_rule.go b/pagerduty/resource_pagerduty_ruleset_rule.go index b1e50ffc9..0c326229f 100644 --- a/pagerduty/resource_pagerduty_ruleset_rule.go +++ b/pagerduty/resource_pagerduty_ruleset_rule.go @@ -1,6 +1,7 @@ package pagerduty import ( + "errors" "fmt" "log" "strings" @@ -33,6 +34,10 @@ func resourcePagerDutyRulesetRule() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "catch_all": { + Type: schema.TypeBool, + Optional: true, + }, "conditions": { Type: schema.TypeList, MaxItems: 1, @@ -319,7 +324,14 @@ func buildRulesetRuleStruct(d *schema.ResourceData) *pagerduty.RulesetRule { Type: "ruleset", ID: d.Get("ruleset").(string), }, - Conditions: expandConditions(d.Get("conditions").([]interface{})), + } + + if _, ok := d.GetOk(("catch_all")); ok { + rule.CatchAll = true + } + + if attr, ok := d.GetOk(("conditions")); ok { + rule.Conditions = expandConditions(attr.([]interface{})) } if attr, ok := d.GetOk("actions"); ok { @@ -745,6 +757,43 @@ func resourcePagerDutyRulesetRuleCreate(d *schema.ResourceData, meta interface{} log.Printf("[INFO] Creating PagerDuty ruleset rule for ruleset: %s", rule.Ruleset.ID) + // CatchAll rule is created by default. + // Indicating that provided Rule is CatchAll implies modifying it and not creating it + if rule.CatchAll { + + log.Printf("[INFO] Found catch_all rule for ruleset: %s", rule.Ruleset.ID) + + rulesetrules, _, err := client.Rulesets.ListRules(rule.Ruleset.ID) + + if err != nil { + return err + } + + if rulesetrules == nil { + return errors.New("No ruleset rule found. Catch-all Resource must exists") + } + + var catchallrule *pagerduty.RulesetRule + for _, rule := range rulesetrules.Rules { + if rule.CatchAll { + catchallrule = rule + break + } + } + + if catchallrule == nil { + return errors.New("No Catch-all rule found. Catch-all Resource must exists") + } + + if err := performRulesetRuleUpdate(rule.Ruleset.ID, catchallrule.ID, rule, client); err != nil { + return err + } + + d.SetId(catchallrule.ID) + + return resourcePagerDutyRulesetRuleRead(d, meta) + } + retryErr := resource.Retry(2*time.Minute, func() *resource.RetryError { if rule, _, err := client.Rulesets.CreateRule(rule.Ruleset.ID, rule); err != nil { return resource.RetryableError(err) @@ -812,8 +861,12 @@ func resourcePagerDutyRulesetRuleUpdate(d *schema.ResourceData, meta interface{} log.Printf("[INFO] Updating PagerDuty ruleset rule: %s", d.Id()) rulesetID := d.Get("ruleset").(string) + return performRulesetRuleUpdate(rulesetID, d.Id(), rule, client) +} + +func performRulesetRuleUpdate(rulesetID string, id string, rule *pagerduty.RulesetRule, client *pagerduty.Client) error { retryErr := resource.Retry(30*time.Second, func() *resource.RetryError { - if updatedRule, _, err := client.Rulesets.UpdateRule(rulesetID, d.Id(), rule); err != nil { + if updatedRule, _, err := client.Rulesets.UpdateRule(rulesetID, id, rule); err != nil { return resource.RetryableError(err) } else if rule.Position != nil && *updatedRule.Position != *rule.Position { log.Printf("[INFO] PagerDuty ruleset rule %s position %d needs to be %d", updatedRule.ID, *updatedRule.Position, *rule.Position) @@ -834,6 +887,12 @@ func resourcePagerDutyRulesetRuleDelete(d *schema.ResourceData, meta interface{} return err } + // Don't delete catch_all resource + if _, ok := d.GetOk(("catch_all")); ok { + d.SetId("") + return nil + } + log.Printf("[INFO] Deleting PagerDuty ruleset rule: %s", d.Id()) rulesetID := d.Get("ruleset").(string) From 818a134f4e0ef22cda190228cefac874546bf7a1 Mon Sep 17 00:00:00 2001 From: gmuselli Date: Thu, 14 Apr 2022 14:52:48 -0400 Subject: [PATCH 2/9] Add Delete feature --- pagerduty/resource_pagerduty_ruleset_rule.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pagerduty/resource_pagerduty_ruleset_rule.go b/pagerduty/resource_pagerduty_ruleset_rule.go index 0c326229f..3d3e8d529 100644 --- a/pagerduty/resource_pagerduty_ruleset_rule.go +++ b/pagerduty/resource_pagerduty_ruleset_rule.go @@ -887,14 +887,32 @@ func resourcePagerDutyRulesetRuleDelete(d *schema.ResourceData, meta interface{} return err } + rulesetID := d.Get("ruleset").(string) + // Don't delete catch_all resource if _, ok := d.GetOk(("catch_all")); ok { + + log.Printf("[INFO] Rule %s is a catch_all rule, don't delete it, reset it instead", d.Id()) + + rule, _, err := client.Rulesets.GetRule(rulesetID, d.Id()) + + if err != nil { + return err + } + + rule.Actions = nil + rule.TimeFrame = nil + + if err := performRulesetRuleUpdate(rulesetID, d.Id(), rule, client); err != nil { + return err + } + d.SetId("") + return nil } log.Printf("[INFO] Deleting PagerDuty ruleset rule: %s", d.Id()) - rulesetID := d.Get("ruleset").(string) retryErr := resource.Retry(30*time.Second, func() *resource.RetryError { if _, err := client.Rulesets.DeleteRule(rulesetID, d.Id()); err != nil { From b1b566f132339137616bf58676e14dbf983d8f8b Mon Sep 17 00:00:00 2001 From: Gavin Reynolds Date: Fri, 22 Apr 2022 19:44:55 +0100 Subject: [PATCH 3/9] Add TestAccPagerDutyRulesetRule_CatchAllRule Signed-off-by: Gavin Reynolds --- pagerduty/resource_pagerduty_ruleset_rule.go | 2 +- .../resource_pagerduty_ruleset_rule_test.go | 115 ++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/pagerduty/resource_pagerduty_ruleset_rule.go b/pagerduty/resource_pagerduty_ruleset_rule.go index 3d3e8d529..ada8cba06 100644 --- a/pagerduty/resource_pagerduty_ruleset_rule.go +++ b/pagerduty/resource_pagerduty_ruleset_rule.go @@ -868,7 +868,7 @@ func performRulesetRuleUpdate(rulesetID string, id string, rule *pagerduty.Rules retryErr := resource.Retry(30*time.Second, func() *resource.RetryError { if updatedRule, _, err := client.Rulesets.UpdateRule(rulesetID, id, rule); err != nil { return resource.RetryableError(err) - } else if rule.Position != nil && *updatedRule.Position != *rule.Position { + } else if rule.Position != nil && *updatedRule.Position != *rule.Position && rule.CatchAll != true { log.Printf("[INFO] PagerDuty ruleset rule %s position %d needs to be %d", updatedRule.ID, *updatedRule.Position, *rule.Position) return resource.RetryableError(fmt.Errorf("Error updating ruleset rule %s position %d needs to be %d", updatedRule.ID, *updatedRule.Position, *rule.Position)) } diff --git a/pagerduty/resource_pagerduty_ruleset_rule_test.go b/pagerduty/resource_pagerduty_ruleset_rule_test.go index 5b13ce290..e13b2df97 100644 --- a/pagerduty/resource_pagerduty_ruleset_rule_test.go +++ b/pagerduty/resource_pagerduty_ruleset_rule_test.go @@ -118,6 +118,53 @@ func TestAccPagerDutyRulesetRule_MultipleRules(t *testing.T) { }, }) } + +func TestAccPagerDutyRulesetRule_CatchAllRule(t *testing.T) { + ruleset := fmt.Sprintf("tf-%s", acctest.RandString(5)) + team := fmt.Sprintf("tf-%s", acctest.RandString(5)) + rule1 := fmt.Sprintf("tf-%s", acctest.RandString(5)) + catch_all_rule := fmt.Sprintf("tf-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPagerDutyRulesetRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckPagerDutyRulesetRuleConfigCatchAllRule(team, ruleset, rule1, catch_all_rule), + Check: resource.ComposeTestCheckFunc( + testAccCheckPagerDutyRulesetRuleExists("pagerduty_ruleset_rule.foo"), + testAccCheckPagerDutyRulesetRuleExists("pagerduty_ruleset_rule.catch_all"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "position", "0"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "position", "1"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "disabled", "false"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.#", "1"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.operator", "and"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.subconditions.0.operator", "contains"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.subconditions.0.parameter.#", "1"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.subconditions.0.parameter.0.value", "disk space"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "actions.0.annotate.0.value", rule1), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "catch_all", "true"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "actions.0.annotate.0.value", catch_all_rule), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "actions.0.suppress.0.value", "true"), + ), + }, + }, + }) +} + func testAccCheckPagerDutyRulesetRuleDestroy(s *terraform.State) error { client, _ := testAccProvider.Meta().(*Config).Client() for _, r := range s.RootModule().Resources { @@ -393,3 +440,71 @@ resource "pagerduty_ruleset_rule" "baz" { } `, team, ruleset, rule1, rule2, rule3) } + +func testAccCheckPagerDutyRulesetRuleConfigCatchAllRule(team, ruleset, rule1, catch_all_rule string) string { + return fmt.Sprintf(` +resource "pagerduty_team" "foo" { + name = "%s" +} + +resource "pagerduty_ruleset" "foo" { + name = "%s" + team { + id = pagerduty_team.foo.id + } +} +resource "pagerduty_ruleset_rule" "foo" { + ruleset = pagerduty_ruleset.foo.id + position = 0 + disabled = false + time_frame { + scheduled_weekly { + weekdays = [3,7] + timezone = "America/Los_Angeles" + start_time = "1000000" + duration = "3600000" + + } + } + conditions { + operator = "and" + subconditions { + operator = "contains" + parameter { + value = "disk space" + path = "summary" + } + } + } + actions { + route { + value = "P5DTL0K" + } + severity { + value = "warning" + } + annotate { + value = "%s" + } + extractions { + target = "dedup_key" + source = "source" + regex = "(.*)" + } + } +} +resource "pagerduty_ruleset_rule" "catch_all" { + ruleset = pagerduty_ruleset.foo.id + position = 1 + catch_all = true + actions { + annotate { + value = "%s" + } + suppress { + value = true + } + } +} +`, team, ruleset, rule1, catch_all_rule) +} From 0489eb5b3054e6d36f9af9056894d707db4b1e7c Mon Sep 17 00:00:00 2001 From: Gavin Reynolds Date: Fri, 22 Apr 2022 19:52:45 +0100 Subject: [PATCH 4/9] Add TestAccPagerDutyRulesetRule_CatchAllRuleRoute Signed-off-by: Gavin Reynolds --- .../resource_pagerduty_ruleset_rule_test.go | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/pagerduty/resource_pagerduty_ruleset_rule_test.go b/pagerduty/resource_pagerduty_ruleset_rule_test.go index e13b2df97..85aa19a35 100644 --- a/pagerduty/resource_pagerduty_ruleset_rule_test.go +++ b/pagerduty/resource_pagerduty_ruleset_rule_test.go @@ -165,6 +165,56 @@ func TestAccPagerDutyRulesetRule_CatchAllRule(t *testing.T) { }) } +func TestAccPagerDutyRulesetRule_CatchAllRuleRoute(t *testing.T) { + ruleset := fmt.Sprintf("tf-%s", acctest.RandString(5)) + team := fmt.Sprintf("tf-%s", acctest.RandString(5)) + rule1 := fmt.Sprintf("tf-%s", acctest.RandString(5)) + catch_all_rule := fmt.Sprintf("tf-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPagerDutyRulesetRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckPagerDutyRulesetRuleConfigCatchAllRuleRoute(team, ruleset, rule1, catch_all_rule), + Check: resource.ComposeTestCheckFunc( + testAccCheckPagerDutyRulesetRuleExists("pagerduty_ruleset_rule.foo"), + testAccCheckPagerDutyRulesetRuleExists("pagerduty_ruleset_rule.catch_all"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "position", "0"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "position", "1"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "disabled", "false"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.#", "1"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.operator", "and"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.subconditions.0.operator", "contains"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.subconditions.0.parameter.#", "1"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "conditions.0.subconditions.0.parameter.0.value", "disk space"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.foo", "actions.0.annotate.0.value", rule1), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "catch_all", "true"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "actions.0.annotate.0.value", catch_all_rule), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "actions.0.suppress.0.value", "false"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "actions.0.route.0.value", "P5DTL0K"), + resource.TestCheckResourceAttr( + "pagerduty_ruleset_rule.catch_all", "actions.0.severity.0.value", "info"), + ), + }, + }, + }) +} + func testAccCheckPagerDutyRulesetRuleDestroy(s *terraform.State) error { client, _ := testAccProvider.Meta().(*Config).Client() for _, r := range s.RootModule().Resources { @@ -508,3 +558,77 @@ resource "pagerduty_ruleset_rule" "catch_all" { } `, team, ruleset, rule1, catch_all_rule) } + +func testAccCheckPagerDutyRulesetRuleConfigCatchAllRuleRoute(team, ruleset, rule1, catch_all_rule string) string { + return fmt.Sprintf(` +resource "pagerduty_team" "foo" { + name = "%s" +} + +resource "pagerduty_ruleset" "foo" { + name = "%s" + team { + id = pagerduty_team.foo.id + } +} +resource "pagerduty_ruleset_rule" "foo" { + ruleset = pagerduty_ruleset.foo.id + position = 0 + disabled = false + time_frame { + scheduled_weekly { + weekdays = [3,7] + timezone = "America/Los_Angeles" + start_time = "1000000" + duration = "3600000" + + } + } + conditions { + operator = "and" + subconditions { + operator = "contains" + parameter { + value = "disk space" + path = "summary" + } + } + } + actions { + route { + value = "P5DTL0K" + } + severity { + value = "warning" + } + annotate { + value = "%s" + } + extractions { + target = "dedup_key" + source = "source" + regex = "(.*)" + } + } +} +resource "pagerduty_ruleset_rule" "catch_all" { + ruleset = pagerduty_ruleset.foo.id + position = 1 + catch_all = true + actions { + annotate { + value = "%s" + } + suppress { + value = false + } + route { + value = "P5DTL0K" + } + severity { + value = "info" + } + } +} +`, team, ruleset, rule1, catch_all_rule) +} From 6255a5abd62e7c091ac1786896a55dd7091e6a4e Mon Sep 17 00:00:00 2001 From: Gavin Reynolds Date: Fri, 22 Apr 2022 20:13:35 +0100 Subject: [PATCH 5/9] Reset all available actions in the catch_all rule if we "delete" it Signed-off-by: Gavin Reynolds --- pagerduty/resource_pagerduty_ruleset_rule.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pagerduty/resource_pagerduty_ruleset_rule.go b/pagerduty/resource_pagerduty_ruleset_rule.go index ada8cba06..95396ab0f 100644 --- a/pagerduty/resource_pagerduty_ruleset_rule.go +++ b/pagerduty/resource_pagerduty_ruleset_rule.go @@ -900,8 +900,15 @@ func resourcePagerDutyRulesetRuleDelete(d *schema.ResourceData, meta interface{} return err } - rule.Actions = nil - rule.TimeFrame = nil + // Reset all available actions back to the default state of the catch_all rule + rule.Actions.Annotate = nil + rule.Actions.EventAction = nil + rule.Actions.Extractions = nil + rule.Actions.Priority = nil + rule.Actions.Route = nil + rule.Actions.Severity = nil + rule.Actions.Suppress.Value = true + rule.Actions.Suspend = nil if err := performRulesetRuleUpdate(rulesetID, d.Id(), rule, client); err != nil { return err From e7912545111c5b3b30b02e74c0993147ac93f69c Mon Sep 17 00:00:00 2001 From: Gavin Reynolds Date: Fri, 22 Apr 2022 20:39:44 +0100 Subject: [PATCH 6/9] Don't omit when empty RuleActions, so we can "delete"/reset it to default Signed-off-by: Gavin Reynolds --- .../heimweh/go-pagerduty/pagerduty/ruleset.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vendor/github.com/heimweh/go-pagerduty/pagerduty/ruleset.go b/vendor/github.com/heimweh/go-pagerduty/pagerduty/ruleset.go index 9cd6a421d..f2091da5b 100644 --- a/vendor/github.com/heimweh/go-pagerduty/pagerduty/ruleset.go +++ b/vendor/github.com/heimweh/go-pagerduty/pagerduty/ruleset.go @@ -120,14 +120,14 @@ type ListRulesetRulesResponse struct { // RuleActions represents a rule action type RuleActions struct { - Suppress *RuleActionSuppress `json:"suppress,omitempty"` - Annotate *RuleActionParameter `json:"annotate,omitempty"` - Severity *RuleActionParameter `json:"severity,omitempty"` - Priority *RuleActionParameter `json:"priority,omitempty"` - Route *RuleActionParameter `json:"route,omitempty"` - EventAction *RuleActionParameter `json:"event_action,omitempty"` + Suppress *RuleActionSuppress `json:"suppress"` + Annotate *RuleActionParameter `json:"annotate"` + Severity *RuleActionParameter `json:"severity"` + Priority *RuleActionParameter `json:"priority"` + Route *RuleActionParameter `json:"route"` + EventAction *RuleActionParameter `json:"event_action"` Extractions []*RuleActionExtraction `json:"extractions,omitempty"` - Suspend *RuleActionIntParameter `json:"suspend,omitempty"` + Suspend *RuleActionIntParameter `json:"suspend"` } // RuleActionParameter represents a string parameter object on a rule action From ec1d925bef7a24c19dcafd4d340e532da7f778c9 Mon Sep 17 00:00:00 2001 From: Gavin Reynolds Date: Fri, 22 Apr 2022 20:49:47 +0100 Subject: [PATCH 7/9] Add catch_all docs for pagerduty_ruleset_rule Signed-off-by: Gavin Reynolds --- website/docs/r/ruleset_rule.html.markdown | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/website/docs/r/ruleset_rule.html.markdown b/website/docs/r/ruleset_rule.html.markdown index b4a3bd385..4be150815 100644 --- a/website/docs/r/ruleset_rule.html.markdown +++ b/website/docs/r/ruleset_rule.html.markdown @@ -95,6 +95,20 @@ resource "pagerduty_ruleset_rule" "foo" { } } } + +resource "pagerduty_ruleset_rule" "catch_all" { + ruleset = pagerduty_ruleset.foo.id + position = 1 + catch_all = true + actions { + annotate { + value = "From Terraform" + } + suppress { + value = true + } + } +} ``` ## Argument Reference @@ -104,6 +118,7 @@ The following arguments are supported: * `ruleset` - (Required) The ID of the ruleset that the rule belongs to. * `conditions` - (Required) Conditions evaluated to check if an event matches this event rule. Is always empty for the catch-all rule, though. * `position` - (Optional) Position/index of the rule within the ruleset. +* `catch_all` - (Optional) Indicates whether the Event Rule is the last Event Rule of the Ruleset that serves as a catch-all. It has limited functionality compared to other rules and always matches. * `disabled` - (Optional) Indicates whether the rule is disabled and would therefore not be evaluated. * `time_frame` - (Optional) Settings for [scheduling the rule](https://support.pagerduty.com/docs/rulesets#section-scheduled-event-rules). * `actions` - (Optional) Actions to apply to an event if the conditions match. From dffa6a804df55b0ca7d39496315be8d67e124695 Mon Sep 17 00:00:00 2001 From: gmuselli Date: Wed, 27 Apr 2022 14:09:28 -0400 Subject: [PATCH 8/9] Update dependencies --- go.mod | 2 +- go.sum | 2 + .../heimweh/go-pagerduty/pagerduty/user.go | 125 +++++++++++++++--- .../pagerduty/webhook_subscription.go | 2 +- vendor/modules.txt | 2 +- 5 files changed, 110 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index a6c3985cf..b3380923d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( cloud.google.com/go v0.71.0 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1 - github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb + github.com/heimweh/go-pagerduty v0.0.0-20220422231448-43095fe5ba3f golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd // indirect google.golang.org/api v0.35.0 // indirect google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb // indirect diff --git a/go.sum b/go.sum index ba73b62d9..9c893f216 100644 --- a/go.sum +++ b/go.sum @@ -284,6 +284,8 @@ github.com/heimweh/go-pagerduty v0.0.0-20211210233744-b65de43109c1 h1:49zl3n/g+f github.com/heimweh/go-pagerduty v0.0.0-20211210233744-b65de43109c1/go.mod h1:JtJGtgN0y9KOCaqFMZFaBCWskpO/KK3Ro9TwjP9ss6w= github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb h1:p3faOVCU8L4wab9mpxN9Cm/VNVkPD8GMEfD0sPHw9nY= github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb/go.mod h1:JtJGtgN0y9KOCaqFMZFaBCWskpO/KK3Ro9TwjP9ss6w= +github.com/heimweh/go-pagerduty v0.0.0-20220422231448-43095fe5ba3f h1:NLk7iDq85F2lz0q1gY32vZR506aYiNcgvV+Us1rX1q4= +github.com/heimweh/go-pagerduty v0.0.0-20220422231448-43095fe5ba3f/go.mod h1:JtJGtgN0y9KOCaqFMZFaBCWskpO/KK3Ro9TwjP9ss6w= 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= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= diff --git a/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go b/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go index b6314510a..bbbb3798f 100644 --- a/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go +++ b/vendor/github.com/heimweh/go-pagerduty/pagerduty/user.go @@ -124,6 +124,10 @@ type ListContactMethodsResponse struct { ContactMethods []*ContactMethod `json:"contact_methods,omitempty"` } +type ListNotificationRulesResponse struct { + NotificationRules []*NotificationRule `json:"notification_rules,omitempty"` +} + // ListUsersOptions represents options when listing users. type ListUsersOptions struct { Limit int `url:"limit,omitempty"` @@ -197,10 +201,18 @@ func (s *UserService) ListAll(o *ListUsersOptions) ([]*FullUser, error) { func (s *UserService) Create(user *User) (*User, *Response, error) { u := "/users" v := new(UserPayload) - resp, err := s.client.newRequestDo("POST", u, nil, &UserPayload{User: user}, &v) if err != nil { - return nil, nil, err + if e, ok := err.(*Error); !ok || strings.Compare(fmt.Sprintf("%v", e.Errors), "[Email has already been taken]") != 0 { + return nil, nil, err + } + + sUser, sResp, sErr := s.findExistingUser(user, err) + if sErr != nil { + return nil, nil, sErr + } + v.User = sUser + resp = sResp } if err = cachePutUser(v.User); err != nil { @@ -212,6 +224,28 @@ func (s *UserService) Create(user *User) (*User, *Response, error) { return v.User, resp, nil } +// findExistingUser searches for a user based on the email +func (s *UserService) findExistingUser(user *User, origErr error) (*User, *Response, error) { + resp, _, lErr := s.List(&ListUsersOptions{Query: user.Email}) + if lErr != nil { + return nil, nil, fmt.Errorf("[Email has already been taken] but failed to fetch existing users: %w", lErr) + } + + for _, u := range resp.Users { + if isSameUser(u, user) { + return s.Get(u.ID, &GetUserOptions{}) + } + } + + return nil, nil, origErr +} + +func isSameUser(existingU, newU *User) bool { + return existingU.Email == newU.Email && + existingU.Name == newU.Name && + existingU.Role == newU.Role +} + // Delete removes an existing user. func (s *UserService) Delete(id string) (*Response, error) { u := fmt.Sprintf("/users/%s", id) @@ -299,22 +333,16 @@ func (s *UserService) CreateContactMethod(userID string, contactMethod *ContactM resp, err := s.client.newRequestDo("POST", u, nil, &ContactMethodPayload{ContactMethod: contactMethod}, &v) if err != nil { - if e, ok := err.(*Error); ok && strings.Compare(fmt.Sprintf("%v", e.Errors), "[User Contact method must be unique]") == 0 { - resp, _, lErr := s.ListContactMethods(userID) - if lErr != nil { - return nil, nil, fmt.Errorf("user contact method is not unique and failed to fetch existing ones: %w", lErr) - } - - for _, contact := range resp.ContactMethods { - if isSameContactMethod(contact, contactMethod) { - return s.GetContactMethod(userID, contact.ID) - } - } - - return nil, nil, fmt.Errorf("user contact method address is used with different attributes (possibly label)") - } else { + if e, ok := err.(*Error); !ok || strings.Compare(fmt.Sprintf("%v", e.Errors), "[User Contact method must be unique]") != 0 { return nil, nil, err } + + sContact, sResp, sErr := s.findExistingContactMethod(userID, contactMethod) + if sErr != nil { + return nil, nil, sErr + } + v.ContactMethod = sContact + resp = sResp } if err = cachePutContactMethod(v.ContactMethod); err != nil { @@ -326,14 +354,27 @@ func (s *UserService) CreateContactMethod(userID string, contactMethod *ContactM return v.ContactMethod, resp, nil } +func (s *UserService) findExistingContactMethod(userID string, contactMethod *ContactMethod) (*ContactMethod, *Response, error) { + lResp, _, lErr := s.ListContactMethods(userID) + if lErr != nil { + return nil, nil, fmt.Errorf("[User Contact method must be unique] but failed to fetch existing ones: %w", lErr) + } + + for _, contact := range lResp.ContactMethods { + if isSameContactMethod(contact, contactMethod) { + return s.GetContactMethod(userID, contact.ID) + } + } + + return nil, nil, fmt.Errorf("[User Contact method must be unique]") +} + // isSameContactMethod checks if an existing contact method should be taken as the same as a new one users want to create. // note new contact method misses some fields like Self, HTMLURL. func isSameContactMethod(existingContact, newContact *ContactMethod) bool { return existingContact.Type == newContact.Type && existingContact.Address == newContact.Address && - existingContact.Label == newContact.Label && - existingContact.CountryCode == newContact.CountryCode && - existingContact.Summary == newContact.Summary + existingContact.CountryCode == newContact.CountryCode } // GetContactMethod retrieves a contact method for a user. @@ -383,6 +424,19 @@ func (s *UserService) DeleteContactMethod(userID, contactMethodID string) (*Resp return resp, err } +// ListNotificationRules lists contact methods for a user. +func (s *UserService) ListNotificationRules(userID string) (*ListNotificationRulesResponse, *Response, error) { + u := fmt.Sprintf("/users/%s/notification_rules", userID) + v := new(ListNotificationRulesResponse) + + resp, err := s.client.newRequestDo("GET", u, nil, nil, &v) + if err != nil { + return nil, nil, err + } + + return v, resp, nil +} + // CreateNotificationRule creates a new notification rule for a user. func (s *UserService) CreateNotificationRule(userID string, rule *NotificationRule) (*NotificationRule, *Response, error) { u := fmt.Sprintf("/users/%s/notification_rules", userID) @@ -390,7 +444,16 @@ func (s *UserService) CreateNotificationRule(userID string, rule *NotificationRu resp, err := s.client.newRequestDo("POST", u, nil, &NotificationRulePayload{NotificationRule: rule}, &v) if err != nil { - return nil, nil, err + if e, ok := err.(*Error); !ok || strings.Compare(fmt.Sprintf("%v", e.Errors), "[Channel Start delay must be unique for a given contact method]") != 0 { + return nil, nil, err + } + + sRule, sResp, sErr := s.findExistingNotificationRule(userID, rule) + if sErr != nil { + return nil, nil, sErr + } + v.NotificationRule = sRule + resp = sResp } if err = cachePutNotificationRule(v.NotificationRule); err != nil { @@ -402,6 +465,28 @@ func (s *UserService) CreateNotificationRule(userID string, rule *NotificationRu return v.NotificationRule, resp, nil } +func (s *UserService) findExistingNotificationRule(userID string, rule *NotificationRule) (*NotificationRule, *Response, error) { + lResp, _, lErr := s.ListNotificationRules(userID) + if lErr != nil { + return nil, nil, fmt.Errorf("[Channel Start delay must be unique for a given contact method]. Failed to fetch existing rules: %w", lErr) + } + + for _, nr := range lResp.NotificationRules { + if isSameNotificationRule(nr, rule) { + return s.GetNotificationRule(userID, nr.ID) + } + } + + return nil, nil, fmt.Errorf("[Channel Start delay must be unique for a given contact method]") +} + +func isSameNotificationRule(existingRule, newRule *NotificationRule) bool { + return existingRule.Urgency == newRule.Urgency && + existingRule.StartDelayInMinutes == newRule.StartDelayInMinutes && + existingRule.ContactMethod.Type == newRule.ContactMethod.Type && + existingRule.ContactMethod.ID == newRule.ContactMethod.ID +} + // GetNotificationRule retrieves a notification rule for a user. func (s *UserService) GetNotificationRule(userID string, ruleID string) (*NotificationRule, *Response, error) { u := fmt.Sprintf("/users/%s/notification_rules/%s", userID, ruleID) diff --git a/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go b/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go index 8390be7db..19f06ddbd 100644 --- a/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go +++ b/vendor/github.com/heimweh/go-pagerduty/pagerduty/webhook_subscription.go @@ -21,7 +21,7 @@ type DeliveryMethod struct { TemporarilyDisabled bool `json:"temporarily_disabled,omitempty"` Type string `json:"type,omitempty"` URL string `json:"url,omitempty"` - CustomHeaders []*CustomHeaders `json:"custom_headers,omitempty"` + CustomHeaders []*CustomHeaders `json:"custom_headers"` } type CustomHeaders struct { diff --git a/vendor/modules.txt b/vendor/modules.txt index 00587e7be..8f52ef197 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -124,7 +124,7 @@ github.com/hashicorp/terraform-registry-address github.com/hashicorp/terraform-svchost # github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d github.com/hashicorp/yamux -# github.com/heimweh/go-pagerduty v0.0.0-20220208023456-83fe435832fb +# github.com/heimweh/go-pagerduty v0.0.0-20220422231448-43095fe5ba3f ## explicit github.com/heimweh/go-pagerduty/pagerduty # github.com/klauspost/compress v1.11.2 From 8576530ac8d8ad5fa79258240b9e22c942870242 Mon Sep 17 00:00:00 2001 From: gmuselli Date: Wed, 27 Apr 2022 14:50:09 -0400 Subject: [PATCH 9/9] Fix np --- pagerduty/resource_pagerduty_ruleset_rule.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pagerduty/resource_pagerduty_ruleset_rule.go b/pagerduty/resource_pagerduty_ruleset_rule.go index 95396ab0f..da913fdc4 100644 --- a/pagerduty/resource_pagerduty_ruleset_rule.go +++ b/pagerduty/resource_pagerduty_ruleset_rule.go @@ -907,6 +907,7 @@ func resourcePagerDutyRulesetRuleDelete(d *schema.ResourceData, meta interface{} rule.Actions.Priority = nil rule.Actions.Route = nil rule.Actions.Severity = nil + rule.Actions.Suppress = new(pagerduty.RuleActionSuppress) rule.Actions.Suppress.Value = true rule.Actions.Suspend = nil