From 47cfce3613b5519e221d2b2da40937e737df3995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Antonio=20Reyes?= Date: Fri, 12 May 2023 23:02:05 -0400 Subject: [PATCH 1/3] handle recreation plan of teams on external deletion --- pagerduty/resource_pagerduty_team.go | 7 ++++-- pagerduty/resource_pagerduty_team_test.go | 29 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/pagerduty/resource_pagerduty_team.go b/pagerduty/resource_pagerduty_team.go index 7c5f71d19..28395e5b7 100644 --- a/pagerduty/resource_pagerduty_team.go +++ b/pagerduty/resource_pagerduty_team.go @@ -94,8 +94,11 @@ func resourcePagerDutyTeamRead(d *schema.ResourceData, meta interface{}) error { return resource.Retry(30*time.Second, func() *resource.RetryError { if team, _, err := client.Teams.Get(d.Id()); err != nil { - time.Sleep(2 * time.Second) - return resource.RetryableError(err) + errResp := handleNotFoundError(err, d) + if errResp != nil { + time.Sleep(2 * time.Second) + return resource.RetryableError(errResp) + } } else if team != nil { d.Set("name", team.Name) d.Set("description", team.Description) diff --git a/pagerduty/resource_pagerduty_team_test.go b/pagerduty/resource_pagerduty_team_test.go index 9de51be9b..ce4381138 100644 --- a/pagerduty/resource_pagerduty_team_test.go +++ b/pagerduty/resource_pagerduty_team_test.go @@ -83,6 +83,15 @@ func TestAccPagerDutyTeam_Basic(t *testing.T) { "pagerduty_team.foo", "description", "bar"), ), }, + // Validating that externally removed teams are detected and planed for + // re-creation + { + Config: testAccCheckPagerDutyTeamConfigUpdated(teamUpdated), + Check: resource.ComposeTestCheckFunc( + testAccExternallyDestroyTeam("pagerduty_team.foo"), + ), + ExpectNonEmptyPlan: true, + }, }, }) } @@ -173,3 +182,23 @@ resource "pagerduty_team" "foo" { parent = pagerduty_team.parent.id }`, parent, team) } + +func testAccExternallyDestroyTeam(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Team ID is set") + } + + client, _ := testAccProvider.Meta().(*Config).Client() + _, err := client.Teams.Delete(rs.Primary.ID) + if err != nil { + return err + } + + return nil + } +} From bee4c59340d11504b3d268ab7f617af78a37807b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Antonio=20Reyes?= Date: Fri, 12 May 2023 23:03:17 -0400 Subject: [PATCH 2/3] handle recreation plan of tag assigments on external deletion --- .../resource_pagerduty_tag_assignment.go | 54 ++++++++++++++ .../resource_pagerduty_tag_assignment_test.go | 70 ++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/pagerduty/resource_pagerduty_tag_assignment.go b/pagerduty/resource_pagerduty_tag_assignment.go index 3d8f805b6..6a822ed65 100644 --- a/pagerduty/resource_pagerduty_tag_assignment.go +++ b/pagerduty/resource_pagerduty_tag_assignment.go @@ -3,6 +3,7 @@ package pagerduty import ( "fmt" "log" + "net/http" "strings" "time" @@ -106,6 +107,15 @@ func resourcePagerDutyTagAssignmentRead(d *schema.ResourceData, meta interface{} log.Printf("[INFO] Reading PagerDuty tag assignment with tagID %s for %s entity with ID %s", assignment.TagID, assignment.EntityType, assignment.EntityID) + ok, err := isFoundTagAssignmentEntity(d.Get("entity_id").(string), d.Get("entity_type").(string), meta) + if err != nil { + return err + } + if !ok { + d.SetId("") + return nil + } + return resource.Retry(30*time.Second, func() *resource.RetryError { if tagResponse, _, err := client.Tags.ListTagsForEntity(assignment.EntityType, assignment.EntityID); err != nil { time.Sleep(2 * time.Second) @@ -202,6 +212,50 @@ func resourcePagerDutyTagAssignmentImport(d *schema.ResourceData, meta interface return []*schema.ResourceData{d}, err } +func isFoundTagAssignmentEntity(entityID, entityType string, meta interface{}) (bool, error) { + var isFound bool + client, err := meta.(*Config).Client() + if err != nil { + return isFound, err + } + + fetchUser := func(id string) (*pagerduty.User, *pagerduty.Response, error) { + return client.Users.Get(id, &pagerduty.GetUserOptions{}) + } + fetchTeam := func(id string) (*pagerduty.Team, *pagerduty.Response, error) { + return client.Teams.Get(id) + } + fetchEscalationPolicy := func(id string) (*pagerduty.EscalationPolicy, *pagerduty.Response, error) { + return client.EscalationPolicies.Get(id, &pagerduty.GetEscalationPolicyOptions{}) + } + retryErr := resource.Retry(30*time.Second, func() *resource.RetryError { + var err error + if entityType == "users" { + _, _, err = fetchUser(entityID) + } + if entityType == "teams" { + _, _, err = fetchTeam(entityID) + } + if entityType == "escalation_policies" { + _, _, err = fetchEscalationPolicy(entityID) + } + + if err != nil && isErrCode(err, http.StatusNotFound) { + return nil + } + if err != nil { + return resource.RetryableError(err) + } + isFound = true + + return nil + }) + if retryErr != nil { + return isFound, retryErr + } + return isFound, nil +} + func createAssignmentID(entityID, tagID string) string { return fmt.Sprintf("%v.%v", entityID, tagID) } diff --git a/pagerduty/resource_pagerduty_tag_assignment_test.go b/pagerduty/resource_pagerduty_tag_assignment_test.go index e6a7cfc82..2d2967ca6 100644 --- a/pagerduty/resource_pagerduty_tag_assignment_test.go +++ b/pagerduty/resource_pagerduty_tag_assignment_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/heimweh/go-pagerduty/pagerduty" ) func TestAccPagerDutyTagAssignment_User(t *testing.T) { @@ -32,6 +33,15 @@ func TestAccPagerDutyTagAssignment_User(t *testing.T) { "pagerduty_user.foo", "email", email), ), }, + // Validating that externally removed users with tag assigments are + // detected and tag assignment is planed for re-creation + { + Config: testAccCheckPagerDutyTagAssignmentConfig(tagLabel, username, email), + Check: resource.ComposeTestCheckFunc( + testAccExternallyDestroyTagAssignment("pagerduty_user.foo", "users"), + ), + ExpectNonEmptyPlan: true, + }, }, }) } @@ -54,6 +64,15 @@ func TestAccPagerDutyTagAssignment_Team(t *testing.T) { "pagerduty_team.foo", "name", team), ), }, + // Validating that externally removed teams with tag assigments are + // detected and tag assignment is planed for re-creation + { + Config: testAccCheckPagerDutyTagAssignmentTeamConfig(tagLabel, team), + Check: resource.ComposeTestCheckFunc( + testAccExternallyDestroyTagAssignment("pagerduty_team.foo", "teams"), + ), + ExpectNonEmptyPlan: true, + }, }, }) } @@ -71,13 +90,22 @@ func TestAccPagerDutyTagAssignment_EP(t *testing.T) { { Config: testAccCheckPagerDutyTagAssignmentEPConfig(tagLabel, username, email, ep), Check: resource.ComposeTestCheckFunc( - testAccCheckPagerDutyTagAssignmentExists("pagerduty_tag_assignment.foo", "teams"), + testAccCheckPagerDutyTagAssignmentExists("pagerduty_tag_assignment.foo", "escalation_policies"), resource.TestCheckResourceAttr( "pagerduty_tag.foo", "label", tagLabel), resource.TestCheckResourceAttr( "pagerduty_escalation_policy.foo", "name", ep), ), }, + // Validating that externally removed escalation policies with tag + // assigments are detected and tag assignment is planed for re-creation + { + Config: testAccCheckPagerDutyTagAssignmentEPConfig(tagLabel, username, email, ep), + Check: resource.ComposeTestCheckFunc( + testAccExternallyDestroyTagAssignment("pagerduty_escalation_policy.foo", "escalation_policies"), + ), + ExpectNonEmptyPlan: true, + }, }, }) } @@ -200,9 +228,47 @@ resource "pagerduty_escalation_policy" "foo" { } } resource "pagerduty_tag_assignment" "foo" { - entity_type = "teams" + entity_type = "escalation_policies" entity_id = pagerduty_escalation_policy.foo.id tag_id = pagerduty_tag.foo.id } `, tagLabel, username, email, ep) } + +func testAccExternallyDestroyTagAssignment(n, entityType string) resource.TestCheckFunc { + deleteUser := func(id string, client *pagerduty.Client) (*pagerduty.Response, error) { + return client.Users.Delete(id) + } + deleteTeam := func(id string, client *pagerduty.Client) (*pagerduty.Response, error) { + return client.Teams.Delete(id) + } + deleteEscalationPolicy := func(id string, client *pagerduty.Client) (*pagerduty.Response, error) { + return client.EscalationPolicies.Delete(id) + } + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Tag Assignment ID is set") + } + + client, _ := testAccProvider.Meta().(*Config).Client() + var err error + if entityType == "users" { + _, err = deleteUser(rs.Primary.ID, client) + } + if entityType == "teams" { + _, err = deleteTeam(rs.Primary.ID, client) + } + if entityType == "escalation_policies" { + _, err = deleteEscalationPolicy(rs.Primary.ID, client) + } + if err != nil { + return err + } + + return nil + } +} From 0c25cf63cdd599f6bba2a242e579bd3283d5bd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Antonio=20Reyes?= Date: Mon, 15 May 2023 18:31:56 -0400 Subject: [PATCH 3/3] validate resource state get deleted for tag assigment and team --- .../resource_pagerduty_tag_assignment_test.go | 27 +++++++++++++++++++ pagerduty/resource_pagerduty_team_test.go | 9 +++++++ 2 files changed, 36 insertions(+) diff --git a/pagerduty/resource_pagerduty_tag_assignment_test.go b/pagerduty/resource_pagerduty_tag_assignment_test.go index 2d2967ca6..c97ff209a 100644 --- a/pagerduty/resource_pagerduty_tag_assignment_test.go +++ b/pagerduty/resource_pagerduty_tag_assignment_test.go @@ -42,6 +42,15 @@ func TestAccPagerDutyTagAssignment_User(t *testing.T) { ), ExpectNonEmptyPlan: true, }, + { + Config: testAccCheckPagerDutyTagAssignmentConfig(tagLabel, username, email), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr( + "pagerduty_tag_assignment.foo", "id"), + ), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, }, }) } @@ -73,6 +82,15 @@ func TestAccPagerDutyTagAssignment_Team(t *testing.T) { ), ExpectNonEmptyPlan: true, }, + { + Config: testAccCheckPagerDutyTagAssignmentTeamConfig(tagLabel, team), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr( + "pagerduty_tag_assignment.foo", "id"), + ), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, }, }) } @@ -106,6 +124,15 @@ func TestAccPagerDutyTagAssignment_EP(t *testing.T) { ), ExpectNonEmptyPlan: true, }, + { + Config: testAccCheckPagerDutyTagAssignmentEPConfig(tagLabel, username, email, ep), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr( + "pagerduty_tag_assignment.foo", "id"), + ), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, }, }) } diff --git a/pagerduty/resource_pagerduty_team_test.go b/pagerduty/resource_pagerduty_team_test.go index ce4381138..7e513e91f 100644 --- a/pagerduty/resource_pagerduty_team_test.go +++ b/pagerduty/resource_pagerduty_team_test.go @@ -92,6 +92,15 @@ func TestAccPagerDutyTeam_Basic(t *testing.T) { ), ExpectNonEmptyPlan: true, }, + { + Config: testAccCheckPagerDutyTeamConfigUpdated(teamUpdated), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr( + "pagerduty_team.foo", "id"), + ), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, }, }) }