Skip to content

Commit

Permalink
Merge pull request #675 from hashicorp/el/tf-722
Browse files Browse the repository at this point in the history
[#TF-722] Allow safe and force delete through the tfe provider
  • Loading branch information
emlanctot committed Nov 17, 2022
2 parents 64301f3 + 44c63aa commit bfd19c3
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

FEATURES:
* r/tfe_organization: Add `allow_force_delete_workspaces` attribute to set whether admins are permitted to delete workspaces with resource under management. ([#661](https://github.com/hashicorp/terraform-provider-tfe/pull/661))
* r/tfe_workspace: Add `force_delete` attribute to set whether workspaces will be force deleted when removed through the provider. Otherwise, they will be safe deleted. ([#675](https://github.com/hashicorp/terraform-provider-tfe/pull/675))

## v0.38.0 (October 24, 2022)

Expand Down
17 changes: 15 additions & 2 deletions tfe/client_mock_workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tfe
import (
"context"
"errors"
"fmt"
"io"

tfe "github.com/hashicorp/go-tfe"
Expand Down Expand Up @@ -37,6 +38,7 @@ func (m *mockWorkspaces) Create(ctx context.Context, organization string, option
Organization: &tfe.Organization{
Name: organization,
},
Permissions: &tfe.WorkspacePermissions{},
}

m.workspaceNames[workspaceNamesKey{organization, *options.Name}] = ws
Expand Down Expand Up @@ -66,7 +68,12 @@ func (m *mockWorkspaces) Readme(ctx context.Context, workspaceID string) (io.Rea
}

func (m *mockWorkspaces) ReadByID(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
panic("not implemented")
for _, workspace := range m.workspaceNames {
if workspace.ID == workspaceID {
return workspace, nil
}
}
return nil, tfe.ErrResourceNotFound
}

func (m *mockWorkspaces) Update(ctx context.Context, organization, workspace string, options tfe.WorkspaceUpdateOptions) (*tfe.Workspace, error) {
Expand All @@ -82,7 +89,13 @@ func (m *mockWorkspaces) Delete(ctx context.Context, organization, workspace str
}

func (m *mockWorkspaces) DeleteByID(ctx context.Context, workspaceID string) error {
panic("not implemented")
for key, workspace := range m.workspaceNames {
if workspace.ID == workspaceID {
delete(m.workspaceNames, key)
return nil
}
}
return fmt.Errorf("no workspace found with id %s", workspaceID)
}

func (m *mockWorkspaces) RemoveVCSConnection(ctx context.Context, organization, workspace string) (*tfe.Workspace, error) {
Expand Down
51 changes: 49 additions & 2 deletions tfe/resource_tfe_workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ func resourceTFEWorkspace() *schema.Resource {
},
},
},
"force_delete": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
}
Expand Down Expand Up @@ -671,15 +676,48 @@ func resourceTFEWorkspaceDelete(d *schema.ResourceData, meta interface{}) error
id := d.Id()

log.Printf("[DEBUG] Delete workspace %s", id)
err := tfeClient.Workspaces.DeleteByID(ctx, id)

ws, err := tfeClient.Workspaces.ReadByID(ctx, id)
if err != nil {
if err == tfe.ErrResourceNotFound {
return nil
}
return fmt.Errorf(
"Error deleting workspace %s: %w", id, err)
"Error reading workspace %s: %w", id, err)
}

forceDelete := d.Get("force_delete").(bool)

// presence of Permissions.CanForceDelete will determine if current version of TFE supports safe deletes
if ws.Permissions.CanForceDelete == nil {
if forceDelete {
err = tfeClient.Workspaces.DeleteByID(ctx, id)
} else {
return fmt.Errorf(
"Error deleting workspace %s: This workspace must be force deleted by setting force_delete=true", id)
}
} else if *ws.Permissions.CanForceDelete {
if forceDelete {
err = tfeClient.Workspaces.DeleteByID(ctx, id)
} else {
err = tfeClient.Workspaces.SafeDeleteByID(ctx, id)
return errWorkspaceSafeDeleteWithPermission(id, err)
}
} else {
if forceDelete {
return fmt.Errorf(
"Error deleting workspace %s: missing required permissions to set force delete workspaces in the organization.", id)
}
err = tfeClient.Workspaces.SafeDeleteByID(ctx, id)
}

if err != nil {
if err == tfe.ErrResourceNotFound {
return nil
}
return fmt.Errorf(
"Error deleting workspace %s: %w", id, err)
}
return nil
}

Expand Down Expand Up @@ -767,3 +805,12 @@ func resourceTFEWorkspaceImporter(ctx context.Context, d *schema.ResourceData, m
func warnWorkspaceTagsCasing(ctx context.Context, tag string) {
log.Printf("[WARN] The tag \"%s\" contains uppercase characters that will be transformed by the API. Please update your configuration to lowercase tags in order to avoid conflicts with state.", tag)
}

func errWorkspaceSafeDeleteWithPermission(workspaceID string, err error) error {
if err != nil {
if strings.HasPrefix(err.Error(), "conflict") {
return fmt.Errorf("Error deleting workspace %s: %w\nTo delete this workspace without destroying the managed resources, add force_delete = true to the resource config.", workspaceID, err)
}
}
return nil
}
Loading

0 comments on commit bfd19c3

Please sign in to comment.