diff --git a/CHANGELOG.md b/CHANGELOG.md index c07160d9b..ef9124efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 3.10.1 (Mar 26, 2024) + +BUG FIXES: + +* `resource/pagerduty_team_membership`: fix(team membership): Allow 404 to propagate to avoid registering it in disassociated eps list ([#838](https://github.com/PagerDuty/terraform-provider-pagerduty/pull/838)) +* `resource/pagerduty_service_dependency`: Fix #832 service_dependency breaks if dependent services are deleted externally ([#834](https://github.com/PagerDuty/terraform-provider-pagerduty/pull/834)) + ## 3.10.0 (Mar 15, 2024) IMPROVEMENTS: diff --git a/pagerduty/resource_pagerduty_business_service.go b/pagerduty/resource_pagerduty_business_service.go index 358403634..1c6dd4397 100644 --- a/pagerduty/resource_pagerduty_business_service.go +++ b/pagerduty/resource_pagerduty_business_service.go @@ -135,6 +135,10 @@ func resourcePagerDutyBusinessServiceRead(d *schema.ResourceData, meta interface return retry.NonRetryableError(err) } + if err := handleNotFoundError(err, d); err == nil { + return nil + } + return retry.RetryableError(err) } else if businessService != nil { d.Set("name", businessService.Name) @@ -190,7 +194,7 @@ func resourcePagerDutyBusinessServiceDelete(d *schema.ResourceData, meta interfa log.Printf("[INFO] Deleting PagerDuty business service %s", d.Id()) if _, err := client.BusinessServices.Delete(d.Id()); err != nil { - return err + return handleNotFoundError(err, d) } d.SetId("") diff --git a/pagerduty/resource_pagerduty_service.go b/pagerduty/resource_pagerduty_service.go index 41b51a073..99de62f13 100644 --- a/pagerduty/resource_pagerduty_service.go +++ b/pagerduty/resource_pagerduty_service.go @@ -535,7 +535,7 @@ func resourcePagerDutyServiceUpdate(d *schema.ResourceData, meta interface{}) er updatedService, _, err := client.Services.Update(d.Id(), service) if err != nil { - return err + return handleNotFoundError(err, d) } return flattenService(d, updatedService) @@ -550,7 +550,7 @@ func resourcePagerDutyServiceDelete(d *schema.ResourceData, meta interface{}) er log.Printf("[INFO] Deleting PagerDuty service %s", d.Id()) if _, err := client.Services.Delete(d.Id()); err != nil { - return err + return handleNotFoundError(err, d) } d.SetId("") diff --git a/pagerduty/resource_pagerduty_service_dependency.go b/pagerduty/resource_pagerduty_service_dependency.go index 9a6abbbdf..d48a309fa 100644 --- a/pagerduty/resource_pagerduty_service_dependency.go +++ b/pagerduty/resource_pagerduty_service_dependency.go @@ -200,6 +200,10 @@ func resourcePagerDutyServiceDependencyDisassociate(ctx context.Context, d *sche return retry.NonRetryableError(err) } + if err = handleNotFoundError(err, d); err == nil { + return nil + } + // Delaying retry by 30s as recommended by PagerDuty // https://developer.pagerduty.com/docs/rest-api-v2/rate-limiting/#what-are-possible-workarounds-to-the-events-api-rate-limit time.Sleep(30 * time.Second) @@ -245,6 +249,10 @@ func resourcePagerDutyServiceDependencyDisassociate(ctx context.Context, d *sche return retry.NonRetryableError(err) } + if err = handleNotFoundError(err, d); err == nil { + return nil + } + // Delaying retry by 30s as recommended by PagerDuty // https://developer.pagerduty.com/docs/rest-api-v2/rate-limiting/#what-are-possible-workarounds-to-the-events-api-rate-limit time.Sleep(30 * time.Second) @@ -323,6 +331,9 @@ func findDependencySetState(ctx context.Context, depID, serviceID, serviceType s if isErrCode(err, http.StatusBadRequest) { return retry.NonRetryableError(err) } + if err = handleNotFoundError(err, d); err == nil { + return nil + } // Delaying retry by 30s as recommended by PagerDuty // https://developer.pagerduty.com/docs/rest-api-v2/rate-limiting/#what-are-possible-workarounds-to-the-events-api-rate-limit diff --git a/pagerduty/resource_pagerduty_service_dependency_test.go b/pagerduty/resource_pagerduty_service_dependency_test.go index f31cae7a1..a4f0edbd8 100644 --- a/pagerduty/resource_pagerduty_service_dependency_test.go +++ b/pagerduty/resource_pagerduty_service_dependency_test.go @@ -44,6 +44,15 @@ func TestAccPagerDutyBusinessServiceDependency_Basic(t *testing.T) { ), ExpectNonEmptyPlan: true, }, + // Validating that externally removed dependent service are + // detected and gracefully handled + { + Config: testAccCheckPagerDutyBusinessServiceDependencyConfig(service, businessService, username, email, escalationPolicy), + Check: resource.ComposeTestCheckFunc( + testAccExternallyDestroyedDependentService("pagerduty_service_dependency.foo", "pagerduty_business_service.foo", "pagerduty_service.foo"), + ), + ExpectNonEmptyPlan: true, + }, }, }) } @@ -326,6 +335,49 @@ func testAccExternallyDestroyServiceDependency(resName, depName, suppName string return nil } } +func testAccExternallyDestroyedDependentService(resName, depName, suppName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resName] + if !ok { + return fmt.Errorf("Not found: %s", resName) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Service Dependency ID is set for %q", resName) + } + + dep, ok := s.RootModule().Resources[depName] + if !ok { + return fmt.Errorf("Not found: %s", depName) + } + if dep.Primary.ID == "" { + return fmt.Errorf("No Dependent Business Service ID is set for %q", depName) + } + depServiceType := dep.Primary.Attributes["type"] + + supp, ok := s.RootModule().Resources[suppName] + if !ok { + return fmt.Errorf("Not found: %s", suppName) + } + if supp.Primary.ID == "" { + return fmt.Errorf("No Supporting Service ID is set for %q", suppName) + } + + client, _ := testAccProvider.Meta().(*Config).Client() + if depServiceType == "business_service" { + _, err := client.BusinessServices.Delete(dep.Primary.ID) + if err != nil { + return err + } + } else { + _, err := client.Services.Delete(dep.Primary.ID) + if err != nil { + return err + } + } + + return nil + } +} // Testing Technical Service Dependencies func TestAccPagerDutyTechnicalServiceDependency_Basic(t *testing.T) { @@ -361,6 +413,15 @@ func TestAccPagerDutyTechnicalServiceDependency_Basic(t *testing.T) { ), ExpectNonEmptyPlan: true, }, + // Validating that externally removed dependent service are + // detected and gracefully handled + { + Config: testAccCheckPagerDutyTechnicalServiceDependencyConfig(dependentService, supportingService, username, email, escalationPolicy), + Check: resource.ComposeTestCheckFunc( + testAccExternallyDestroyedDependentService("pagerduty_service_dependency.bar", "pagerduty_service.dependBar", "pagerduty_service.supportBar"), + ), + ExpectNonEmptyPlan: true, + }, }, }) }