Skip to content

Commit

Permalink
Merge pull request #561 from imjaroiswebdev/issue-358-graceful-destro…
Browse files Browse the repository at this point in the history
…y-schedule

Support for gracefully destroy `pagerduty_schedule`
  • Loading branch information
imjaroiswebdev authored Aug 25, 2022
2 parents 9357012 + 8f8228c commit c6fe9ee
Show file tree
Hide file tree
Showing 2 changed files with 249 additions and 1 deletion.
103 changes: 102 additions & 1 deletion pagerduty/resource_pagerduty_schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,23 @@ func resourcePagerDutyScheduleDelete(d *schema.ResourceData, meta interface{}) e
return err
}

log.Printf("[INFO] Deleting PagerDuty schedule: %s", d.Id())
log.Printf("[INFO] Starting deletion process of Schedule %s", d.Id())

log.Printf("[INFO] Listing Escalation Policies that use schedule : %s", d.Id())
// Extracting Escalation Policies that use this Schedule
epsAssociatedToSchedule, err := extractEPsAssociatedToSchedule(client, d.Id())
if err != nil {
return err
}

log.Printf("[INFO] Dissociating Escalation Policies that use the Schedule: %s", d.Id())
// Dissociating Schedule from Escalation Policies before deleting the Schedule
err = dissociateScheduleFromEPs(client, d.Id(), epsAssociatedToSchedule)
if err != nil {
return err
}

log.Printf("[INFO] Deleting PagerDuty schedule: %s", d.Id())
// Retrying to give other resources (such as escalation policies) to delete
retryErr := resource.Retry(2*time.Minute, func() *resource.RetryError {
if _, err := client.Schedules.Delete(d.Id()); err != nil {
Expand Down Expand Up @@ -546,3 +561,89 @@ func flattenScheFinalSchedule(finalSche *pagerduty.SubSchedule) []map[string]int

return res
}

func extractEPsAssociatedToSchedule(c *pagerduty.Client, id string) ([]string, error) {
var s *pagerduty.Schedule
retryErr := resource.Retry(10*time.Second, func() *resource.RetryError {
resp, _, err := c.Schedules.Get(id, &pagerduty.GetScheduleOptions{})
if err != nil {
time.Sleep(2 * time.Second)
return resource.RetryableError(err)
}
s = resp
return nil
})
if retryErr != nil {
return nil, retryErr
}

eps := []string{}
for _, ep := range s.EscalationPolicies {
eps = append(eps, ep.ID)
}
return eps, nil
}

func dissociateScheduleFromEPs(c *pagerduty.Client, scheduleID string, eps []string) error {
for _, epID := range eps {
isEPFound := false
var ep *pagerduty.EscalationPolicy
errorMessage := fmt.Sprintf("Error while trying to dissociate Schedule %q from Escalation Policy %q", scheduleID, epID)
retryErr := resource.Retry(10*time.Second, func() *resource.RetryError {
resp, _, err := c.EscalationPolicies.Get(epID, &pagerduty.GetEscalationPolicyOptions{})
if err != nil {
if isErrCode(err, 404) {
return nil
}
return resource.RetryableError(err)
}
ep = resp
isEPFound = true
return nil
})
if retryErr != nil {
return fmt.Errorf("%w; %s", retryErr, errorMessage)
}

if !isEPFound {
continue
}
err := removeScheduleFromEP(c, scheduleID, ep)
if err != nil {
return fmt.Errorf("%w; %s", err, errorMessage)
}
}
return nil
}

func removeScheduleFromEP(c *pagerduty.Client, scheduleID string, ep *pagerduty.EscalationPolicy) error {
needsToUpdate := false
epr := ep.EscalationRules
for _, r := range epr {
for index, target := range r.Targets {
if target.Type == "schedule_reference" && target.ID == scheduleID {
// Remove Schedule as a configured Target from the Escalation Rules
// slice
r.Targets = append(r.Targets[:index], r.Targets[index+1:]...)
needsToUpdate = true
}
}
}
if !needsToUpdate {
return nil
}
ep.EscalationRules = epr

retryErr := resource.Retry(10*time.Second, func() *resource.RetryError {
_, _, err := c.EscalationPolicies.Update(ep.ID, ep)
if err != nil && !isErrCode(err, 404) {
return resource.RetryableError(err)
}
return nil
})
if retryErr != nil {
return retryErr
}

return nil
}
147 changes: 147 additions & 0 deletions pagerduty/resource_pagerduty_schedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,45 @@ func TestAccPagerDutyScheduleWithTeams_Basic(t *testing.T) {
},
})
}

func TestAccPagerDutyScheduleWithTeams_EscalationPolicyDependant(t *testing.T) {
username := fmt.Sprintf("tf-%s", acctest.RandString(5))
email := fmt.Sprintf("%s@foo.test", username)
schedule := fmt.Sprintf("tf-%s", acctest.RandString(5))
location := "America/New_York"
start := timeNowInLoc(location).Add(24 * time.Hour).Round(1 * time.Hour).Format(time.RFC3339)
rotationVirtualStart := timeNowInLoc(location).Add(24 * time.Hour).Round(1 * time.Hour).Format(time.RFC3339)
team := fmt.Sprintf("tf-%s", acctest.RandString(5))
escalationPolicy := fmt.Sprintf("ts-%s", acctest.RandString(5))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckPagerDutyScheduleDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckPagerDutyScheduleWithTeamsEscalationPolicyDependantConfig(username, email, schedule, location, start, rotationVirtualStart, team, escalationPolicy),
Check: resource.ComposeTestCheckFunc(
testAccCheckPagerDutyScheduleExists("pagerduty_schedule.foo"),
resource.TestCheckResourceAttr(
"pagerduty_schedule.foo", "name", schedule),
resource.TestCheckResourceAttr(
"pagerduty_schedule.foo", "description", "foo"),
resource.TestCheckResourceAttr(
"pagerduty_escalation_policy.foo", "name", escalationPolicy),
),
},
{
Config: testAccCheckPagerDutyScheduleWithTeamsEscalationPolicyDependantConfigUpdated(username, email, team, escalationPolicy),
Check: resource.ComposeTestCheckFunc(
testAccCheckPagerDutyScheduleNoExists("pagerduty_schedule.foo"),
resource.TestCheckResourceAttr(
"pagerduty_escalation_policy.foo", "name", escalationPolicy),
),
},
},
})
}
func TestAccPagerDutyScheduleOverflow_Basic(t *testing.T) {
username := fmt.Sprintf("tf-%s", acctest.RandString(5))
email := fmt.Sprintf("%s@foo.test", username)
Expand Down Expand Up @@ -413,6 +452,31 @@ func testAccCheckPagerDutyScheduleExists(n string) resource.TestCheckFunc {
}
}

func testAccCheckPagerDutyScheduleNoExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return nil
}
if rs != nil && rs.Primary.ID == "" {
return nil
}

client, _ := testAccProvider.Meta().(*Config).Client()

found, _, err := client.Schedules.Get(rs.Primary.ID, &pagerduty.GetScheduleOptions{})
if err != nil {
return err
}

if found.ID == rs.Primary.ID {
return fmt.Errorf("Schedule still exists: %v - %v", rs.Primary.ID, found)
}

return nil
}
}

func testAccCheckPagerDutyScheduleConfig(username, email, schedule, location, start, rotationVirtualStart string) string {
return fmt.Sprintf(`
resource "pagerduty_user" "foo" {
Expand Down Expand Up @@ -768,6 +832,7 @@ resource "pagerduty_schedule" "foo" {
}
`, username, email, team, schedule, location, start, rotationVirtualStart)
}

func testAccCheckPagerDutyScheduleWithTeamsConfigUpdated(username, email, schedule, location, start, rotationVirtualStart, team string) string {
return fmt.Sprintf(`
resource "pagerduty_user" "foo" {
Expand Down Expand Up @@ -804,3 +869,85 @@ resource "pagerduty_schedule" "foo" {
}
`, username, email, team, schedule, location, start, rotationVirtualStart)
}

func testAccCheckPagerDutyScheduleWithTeamsEscalationPolicyDependantConfig(username, email, schedule, location, start, rotationVirtualStart, team, escalationPolicy string) string {
return fmt.Sprintf(`
resource "pagerduty_user" "foo" {
name = "%s"
email = "%s"
}
resource "pagerduty_team" "foo" {
name = "%s"
description = "fighters"
}
resource "pagerduty_schedule" "foo" {
name = "%s"
time_zone = "%s"
description = "foo"
teams = [pagerduty_team.foo.id]
layer {
name = "foo"
start = "%s"
rotation_virtual_start = "%s"
rotation_turn_length_seconds = 86400
users = [pagerduty_user.foo.id]
restriction {
type = "daily_restriction"
start_time_of_day = "08:00:00"
duration_seconds = 32101
}
}
}
resource "pagerduty_escalation_policy" "foo" {
name = "%s"
num_loops = 2
teams = [pagerduty_team.foo.id]
rule {
escalation_delay_in_minutes = 10
target {
type = "user_reference"
id = pagerduty_user.foo.id
}
target {
type = "schedule_reference"
id = pagerduty_schedule.foo.id
}
}
}
`, username, email, team, schedule, location, start, rotationVirtualStart, escalationPolicy)
}
func testAccCheckPagerDutyScheduleWithTeamsEscalationPolicyDependantConfigUpdated(username, email, team, escalationPolicy string) string {
return fmt.Sprintf(`
resource "pagerduty_user" "foo" {
name = "%s"
email = "%s"
}
resource "pagerduty_team" "foo" {
name = "%s"
description = "bar"
}
resource "pagerduty_escalation_policy" "foo" {
name = "%s"
num_loops = 2
teams = [pagerduty_team.foo.id]
rule {
escalation_delay_in_minutes = 10
target {
type = "user_reference"
id = pagerduty_user.foo.id
}
}
}
`, username, email, team, escalationPolicy)
}

0 comments on commit c6fe9ee

Please sign in to comment.