From cae8aabd30d3105e939f83d1db435b8850e8cc7e Mon Sep 17 00:00:00 2001 From: Carlos Gajardo Date: Fri, 8 Mar 2024 14:51:26 -0300 Subject: [PATCH] Migrate resource team --- pagerduty/provider.go | 1 + pagerduty/provider_test.go | 26 ++ pagerduty/resource_pagerduty_team.go | 1 + .../import_pagerduty_team_test.go | 6 +- pagerdutyplugin/provider.go | 1 + pagerdutyplugin/resource_pagerduty_team.go | 270 ++++++++++++++++++ .../resource_pagerduty_team_test.go | 53 ++-- 7 files changed, 325 insertions(+), 33 deletions(-) rename {pagerduty => pagerdutyplugin}/import_pagerduty_team_test.go (74%) create mode 100644 pagerdutyplugin/resource_pagerduty_team.go rename {pagerduty => pagerdutyplugin}/resource_pagerduty_team_test.go (82%) diff --git a/pagerduty/provider.go b/pagerduty/provider.go index 7e0c6be8e..7de7b701f 100644 --- a/pagerduty/provider.go +++ b/pagerduty/provider.go @@ -161,6 +161,7 @@ func Provider(isMux bool) *schema.Provider { delete(p.ResourcesMap, "pagerduty_addon") delete(p.ResourcesMap, "pagerduty_business_service") + delete(p.ResourcesMap, "pagerduty_team") } p.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { diff --git a/pagerduty/provider_test.go b/pagerduty/provider_test.go index 5077b3f02..57118dd9b 100644 --- a/pagerduty/provider_test.go +++ b/pagerduty/provider_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/heimweh/go-pagerduty/pagerduty" ) @@ -189,6 +190,7 @@ resource "pagerduty_service" "foo" { } `, username, email, escalationPolicy, service) } + func testAccCheckPagerDutyProviderAuthWithMultipleMethodsConfig(username, email, escalationPolicy, service string) string { return fmt.Sprintf(` provider "pagerduty" { @@ -331,3 +333,27 @@ func testAccGetPagerDutyAccountDomain(t *testing.T) string { } return accountDomain } + +func testAccCheckPagerDutyTeamDestroy(s *terraform.State) error { + client, _ := testAccProvider.Meta().(*Config).Client() + for _, r := range s.RootModule().Resources { + if r.Type != "pagerduty_team" { + continue + } + + if _, _, err := client.Teams.Get(r.Primary.ID); err == nil { + return fmt.Errorf("Team still exists") + } + + } + return nil +} + +func testAccCheckPagerDutyTeamConfig(team string) string { + return fmt.Sprintf(` + +resource "pagerduty_team" "foo" { + name = "%s" + description = "foo" +}`, team) +} diff --git a/pagerduty/resource_pagerduty_team.go b/pagerduty/resource_pagerduty_team.go index 71dd3f8f4..3b3ee1a6e 100644 --- a/pagerduty/resource_pagerduty_team.go +++ b/pagerduty/resource_pagerduty_team.go @@ -10,6 +10,7 @@ import ( "github.com/heimweh/go-pagerduty/pagerduty" ) +// Deprecated: Migrated to pagerdutyplugin.resourceTeam. Kept for testing purposes. func resourcePagerDutyTeam() *schema.Resource { return &schema.Resource{ Create: resourcePagerDutyTeamCreate, diff --git a/pagerduty/import_pagerduty_team_test.go b/pagerdutyplugin/import_pagerduty_team_test.go similarity index 74% rename from pagerduty/import_pagerduty_team_test.go rename to pagerdutyplugin/import_pagerduty_team_test.go index 5dbce3a58..8094b1206 100644 --- a/pagerduty/import_pagerduty_team_test.go +++ b/pagerdutyplugin/import_pagerduty_team_test.go @@ -12,9 +12,9 @@ func TestAccPagerDutyTeam_import(t *testing.T) { team := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyTeamDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyTeamDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyTeamConfig(team), diff --git a/pagerdutyplugin/provider.go b/pagerdutyplugin/provider.go index 44899e9a0..4a6f7e795 100644 --- a/pagerdutyplugin/provider.go +++ b/pagerdutyplugin/provider.go @@ -75,6 +75,7 @@ func (p *Provider) Resources(_ context.Context) [](func() resource.Resource) { func() resource.Resource { return &resourceServiceDependency{} }, func() resource.Resource { return &resourceTagAssignment{} }, func() resource.Resource { return &resourceTag{} }, + func() resource.Resource { return &resourceTeam{} }, func() resource.Resource { return &resourceUserHandoffNotificationRule{} }, } } diff --git a/pagerdutyplugin/resource_pagerduty_team.go b/pagerdutyplugin/resource_pagerduty_team.go new file mode 100644 index 000000000..a9187c80e --- /dev/null +++ b/pagerdutyplugin/resource_pagerduty_team.go @@ -0,0 +1,270 @@ +package pagerduty + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/PagerDuty/go-pagerduty" + "github.com/PagerDuty/terraform-provider-pagerduty/util" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" +) + +type resourceTeam struct{ client *pagerduty.Client } + +var ( + _ resource.ResourceWithConfigure = (*resourceTeam)(nil) + _ resource.ResourceWithImportState = (*resourceTeam)(nil) +) + +func (r *resourceTeam) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "pagerduty_team" +} + +func (r *resourceTeam) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{Computed: true}, + "name": schema.StringAttribute{Required: true}, + "description": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString("Managed by Terraform"), + }, + "html_url": schema.StringAttribute{Computed: true}, + "parent": schema.StringAttribute{Optional: true}, + "default_role": schema.StringAttribute{ + Computed: true, + Optional: true, + }, + }, + } +} + +func (r *resourceTeam) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var model resourceTeamModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + plan := buildPagerdutyTeam(&model) + log.Printf("[INFO] Creating PagerDuty team %s", plan.Name) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + response, err := r.client.CreateTeamWithContext(ctx, plan) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + plan.ID = response.ID + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error creating PagerDuty team %s", plan.Name), + err.Error(), + ) + return + } + + retryNotFound := true + model, err = requestGetTeam(ctx, r.client, plan, retryNotFound) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty team %s", plan.Name), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceTeam) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state resourceTeamModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Reading PagerDuty team %s", state.ID) + + plan := buildPagerdutyTeam(&state) + + retryNotFound := false + state, err := requestGetTeam(ctx, r.client, plan, retryNotFound) + if err != nil { + if util.IsNotFoundError(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading PagerDuty team %s", plan.ID), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *resourceTeam) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var model resourceTeamModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + + plan := buildPagerdutyTeam(&model) + if plan.ID == "" { + var id string + req.State.GetAttribute(ctx, path.Root("id"), &id) + plan.ID = id + } + log.Printf("[INFO] Updating PagerDuty team %s", plan.ID) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + team, err := r.client.UpdateTeamWithContext(ctx, plan.ID, plan) + if err != nil { + if util.IsBadRequestError(err) || util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + model = flattenTeam(team, plan) + return nil + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error updating PagerDuty team %s", plan.ID), + err.Error(), + ) + return + } + + retryNotFound := false + model, err = requestGetTeam(ctx, r.client, plan, retryNotFound) + if err != nil { + if util.IsNotFoundError(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + fmt.Sprintf("Error updating PagerDuty team %s", plan.ID), + err.Error(), + ) + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) +} + +func (r *resourceTeam) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var id types.String + + resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &id)...) + if resp.Diagnostics.HasError() { + return + } + log.Printf("[INFO] Deleting PagerDuty team %s", id) + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + err := r.client.DeleteTeamWithContext(ctx, id.ValueString()) + if err != nil { + if util.IsBadRequestError(err) || util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + return nil + }) + if err != nil && !util.IsNotFoundError(err) { + resp.Diagnostics.AddError( + fmt.Sprintf("Error deleting PagerDuty team %s", id), + err.Error(), + ) + return + } + + resp.State.RemoveResource(ctx) +} + +func (r *resourceTeam) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + resp.Diagnostics.Append(ConfigurePagerdutyClient(&r.client, req.ProviderData)...) +} + +func (r *resourceTeam) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +type resourceTeamModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + DefaultRole types.String `tfsdk:"default_role"` + Description types.String `tfsdk:"description"` + HTMLURL types.String `tfsdk:"html_url"` + Parent types.String `tfsdk:"parent"` +} + +func requestGetTeam(ctx context.Context, client *pagerduty.Client, plan *pagerduty.Team, retryNotFound bool) (resourceTeamModel, error) { + var model resourceTeamModel + + err := retry.RetryContext(ctx, 2*time.Minute, func() *retry.RetryError { + team, err := client.GetTeamWithContext(ctx, plan.ID) + if err != nil { + if util.IsBadRequestError(err) { + return retry.NonRetryableError(err) + } + if !retryNotFound && util.IsNotFoundError(err) { + return retry.NonRetryableError(err) + } + return retry.RetryableError(err) + } + model = flattenTeam(team, plan) + return nil + }) + + return model, err +} + +func buildPagerdutyTeam(model *resourceTeamModel) *pagerduty.Team { + var parent *pagerduty.APIObject + if !model.Parent.IsNull() && !model.Parent.IsUnknown() { + parent = &pagerduty.APIObject{ + ID: model.Parent.ValueString(), + Type: "team_reference", + } + } + team := pagerduty.Team{ + Name: model.Name.ValueString(), + Description: model.Description.ValueString(), + Parent: parent, + DefaultRole: model.DefaultRole.ValueString(), + } + team.ID = model.ID.ValueString() + return &team +} + +func flattenTeam(response *pagerduty.Team, plan *pagerduty.Team) resourceTeamModel { + model := resourceTeamModel{ + ID: types.StringValue(response.ID), + Name: types.StringValue(response.Name), + Description: types.StringValue(response.Description), + HTMLURL: types.StringValue(response.HTMLURL), + DefaultRole: types.StringValue(response.DefaultRole), + } + if plan.Parent != nil { + model.Parent = types.StringValue(plan.Parent.ID) + } + if response.Parent != nil { + model.Parent = types.StringValue(response.Parent.ID) + } + return model +} diff --git a/pagerduty/resource_pagerduty_team_test.go b/pagerdutyplugin/resource_pagerduty_team_test.go similarity index 82% rename from pagerduty/resource_pagerduty_team_test.go rename to pagerdutyplugin/resource_pagerduty_team_test.go index 883b0168e..fd35cdfb1 100644 --- a/pagerduty/resource_pagerduty_team_test.go +++ b/pagerdutyplugin/resource_pagerduty_team_test.go @@ -1,15 +1,16 @@ package pagerduty import ( + "context" "fmt" "log" "strings" "testing" + "github.com/PagerDuty/go-pagerduty" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - "github.com/heimweh/go-pagerduty/pagerduty" ) func init() { @@ -25,25 +26,16 @@ func init() { } func testSweepTeam(region string) error { - config, err := sharedConfigForRegion(region) + ctx := context.Background() + response, err := testAccProvider.client.ListTeamsWithContext(ctx, pagerduty.ListTeamOptions{}) if err != nil { return err } - client, err := config.Client() - if err != nil { - return err - } - - resp, _, err := client.Teams.List(&pagerduty.ListTeamsOptions{}) - if err != nil { - return err - } - - for _, team := range resp.Teams { + for _, team := range response.Teams { if strings.HasPrefix(team.Name, "test") || strings.HasPrefix(team.Name, "tf-") { log.Printf("Destroying team %s (%s)", team.Name, team.ID) - if _, err := client.Teams.Delete(team.ID); err != nil { + if err := testAccProvider.client.DeleteTeamWithContext(ctx, team.ID); err != nil { return err } } @@ -57,9 +49,9 @@ func TestAccPagerDutyTeam_Basic(t *testing.T) { teamUpdated := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyTeamDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyTeamDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyTeamConfig(team), @@ -111,9 +103,9 @@ func TestAccPagerDutyTeam_DefaultRole(t *testing.T) { defaultRoleUpdated := "none" resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyTeamDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyTeamDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyTeamDefaultRoleConfig(team, defaultRole), @@ -143,9 +135,9 @@ func TestAccPagerDutyTeam_Parent(t *testing.T) { parent := fmt.Sprintf("tf-%s", acctest.RandString(5)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPagerDutyTeamDestroy, + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(), + CheckDestroy: testAccCheckPagerDutyTeamDestroy, Steps: []resource.TestStep{ { Config: testAccCheckPagerDutyTeamWithParentConfig(team, parent), @@ -169,13 +161,14 @@ func TestAccPagerDutyTeam_Parent(t *testing.T) { } func testAccCheckPagerDutyTeamDestroy(s *terraform.State) error { - client, _ := testAccProvider.Meta().(*Config).Client() + ctx := context.Background() + for _, r := range s.RootModule().Resources { if r.Type != "pagerduty_team" { continue } - if _, _, err := client.Teams.Get(r.Primary.ID); err == nil { + if _, err := testAccProvider.client.GetTeamWithContext(ctx, r.Primary.ID); err == nil { return fmt.Errorf("Team still exists") } @@ -183,11 +176,11 @@ func testAccCheckPagerDutyTeamDestroy(s *terraform.State) error { return nil } -func testAccCheckPagerDutyTeamExists(n string) resource.TestCheckFunc { +func testAccCheckPagerDutyTeamExists(_ string) resource.TestCheckFunc { return func(s *terraform.State) error { - client, _ := testAccProvider.Meta().(*Config).Client() for _, r := range s.RootModule().Resources { - if _, _, err := client.Teams.Get(r.Primary.ID); err != nil { + ctx := context.Background() + if _, err := testAccProvider.client.GetTeamWithContext(ctx, r.Primary.ID); err != nil { return fmt.Errorf("Received an error retrieving team %s ID: %s", err, r.Primary.ID) } } @@ -246,8 +239,8 @@ func testAccExternallyDestroyTeam(n string) resource.TestCheckFunc { return fmt.Errorf("No Team ID is set") } - client, _ := testAccProvider.Meta().(*Config).Client() - _, err := client.Teams.Delete(rs.Primary.ID) + ctx := context.Background() + err := testAccProvider.client.DeleteTeamWithContext(ctx, rs.Primary.ID) if err != nil { return err }