Skip to content

Commit

Permalink
Merge pull request #801 from hashicorp/mwudka/TF-8621-add-data-retent…
Browse files Browse the repository at this point in the history
…ion-policy-support

Allow managing StateVersion and ConfigurationVersion backing data
  • Loading branch information
nfagerlund authored Nov 6, 2023
2 parents f4b7207 + 70dde47 commit a245d58
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

## Features
* Removed BETA labels for StateVersion Upload method, ConfigurationVersion `provisional` field, and `save-plan` runs by @brandonc [#800](https://github.com/hashicorp/go-tfe/pull/800)
* Allow soft deleting, restoring, and permanently deleting StateVersion and ConfigurationVersion backing data by @mwudka [#801](https://github.com/hashicorp/go-tfe/pull/801)

# v.1.38.0

Expand Down
38 changes: 38 additions & 0 deletions configuration_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ type ConfigurationVersions interface {

// Download a configuration version. Only configuration versions in the uploaded state may be downloaded.
Download(ctx context.Context, cvID string) ([]byte, error)

// SoftDeleteBackingData soft deletes the configuration version's backing data
// **Note: This functionality is only available in Terraform Enterprise.**
SoftDeleteBackingData(ctx context.Context, svID string) error

// RestoreBackingData restores a soft deleted configuration version's backing data
// **Note: This functionality is only available in Terraform Enterprise.**
RestoreBackingData(ctx context.Context, svID string) error

// PermanentlyDeleteBackingData permanently deletes a soft deleted configuration version's backing data
// **Note: This functionality is only available in Terraform Enterprise.**
PermanentlyDeleteBackingData(ctx context.Context, svID string) error
}

// configurationVersions implements ConfigurationVersions.
Expand Down Expand Up @@ -356,3 +368,29 @@ func (s *configurationVersions) Download(ctx context.Context, cvID string) ([]by

return buf.Bytes(), nil
}

func (s *configurationVersions) SoftDeleteBackingData(ctx context.Context, cvID string) error {
return s.manageBackingData(ctx, cvID, "soft_delete_backing_data")
}

func (s *configurationVersions) RestoreBackingData(ctx context.Context, cvID string) error {
return s.manageBackingData(ctx, cvID, "restore_backing_data")
}

func (s *configurationVersions) PermanentlyDeleteBackingData(ctx context.Context, cvID string) error {
return s.manageBackingData(ctx, cvID, "permanently_delete_backing_data")
}

func (s *configurationVersions) manageBackingData(ctx context.Context, cvID, action string) error {
if !validStringID(&cvID) {
return ErrInvalidConfigVersionID
}

u := fmt.Sprintf("configuration-versions/%s/actions/%s", cvID, action)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return err
}

return req.Do(ctx, nil)
}
46 changes: 46 additions & 0 deletions configuration_version_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,49 @@ func TestConfigurationVersions_Unmarshal(t *testing.T) {
assert.Equal(t, cv.Provisional, true)
assert.Equal(t, cv.Speculative, true)
}

func TestConfigurationVersions_ManageBackingData(t *testing.T) {
skipUnlessEnterprise(t)

client := testClient(t)
ctx := context.Background()

workspace, workspaceCleanup := createWorkspace(t, client, nil)
t.Cleanup(workspaceCleanup)

nonCurrentCv, uploadedCvCleanup := createUploadedConfigurationVersion(t, client, workspace)
defer uploadedCvCleanup()

_, uploadedCvCleanup = createUploadedConfigurationVersion(t, client, workspace)
defer uploadedCvCleanup()

t.Run("soft delete backing data", func(t *testing.T) {
err := client.ConfigurationVersions.SoftDeleteBackingData(ctx, nonCurrentCv.ID)
require.NoError(t, err)

_, err = client.ConfigurationVersions.Download(ctx, nonCurrentCv.ID)
assert.Equal(t, ErrResourceNotFound, err)
})

t.Run("restore backing data", func(t *testing.T) {
err := client.ConfigurationVersions.RestoreBackingData(ctx, nonCurrentCv.ID)
require.NoError(t, err)

_, err = client.ConfigurationVersions.Download(ctx, nonCurrentCv.ID)
require.NoError(t, err)
})

t.Run("permanently delete backing data", func(t *testing.T) {
err := client.ConfigurationVersions.SoftDeleteBackingData(ctx, nonCurrentCv.ID)
require.NoError(t, err)

err = client.ConfigurationVersions.PermanentlyDeleteBackingData(ctx, nonCurrentCv.ID)
require.NoError(t, err)

err = client.ConfigurationVersions.RestoreBackingData(ctx, nonCurrentCv.ID)
require.ErrorContainsf(t, err, "transition not allowed", "Restore backing data should fail")

_, err = client.ConfigurationVersions.Download(ctx, nonCurrentCv.ID)
assert.Equal(t, ErrResourceNotFound, err)
})
}
72 changes: 72 additions & 0 deletions examples/backing_data/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package main

import (
"context"
"flag"
"fmt"
tfe "github.com/hashicorp/go-tfe"
"log"
"strings"
)

func main() {
action := flag.String("action", "", "Action (soft-delete|restore|permanently-delete")
externalId := flag.String("external-id", "", "External ID of StateVersion or ConfigurationVersion")

flag.Parse()

if action == nil || *action == "" {
log.Fatal("No Action provided")
}

if externalId == nil || *externalId == "" {
log.Fatal("No external ID provided")
}

ctx := context.Background()
client, err := tfe.NewClient(&tfe.Config{
RetryServerErrors: true,
})
if err != nil {
log.Fatal(err)
}

err = performAction(ctx, client, *action, *externalId)
if err != nil {
log.Fatalf("Error performing action: %v", err)
}
}

func performAction(ctx context.Context, client *tfe.Client, action string, id string) error {
externalIdParts := strings.Split(id, "-")
switch externalIdParts[0] {
case "cv":
switch action {
case "soft-delete":
return client.ConfigurationVersions.SoftDeleteBackingData(ctx, id)
case "restore":
return client.ConfigurationVersions.RestoreBackingData(ctx, id)
case "permanently-delete":
return client.ConfigurationVersions.PermanentlyDeleteBackingData(ctx, id)
default:
return fmt.Errorf("unsupported action: %s", action)
}
case "sv":
switch action {
case "soft-delete":
return client.StateVersions.SoftDeleteBackingData(ctx, id)
case "restore":
return client.StateVersions.RestoreBackingData(ctx, id)
case "permanently-delete":
return client.StateVersions.PermanentlyDeleteBackingData(ctx, id)
default:
return fmt.Errorf("unsupported action: %s", action)
}
default:
return fmt.Errorf("unsupported external ID: %s", id)
}
return nil
}
42 changes: 42 additions & 0 deletions mocks/configuration_version_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions mocks/state_version_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions state_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ type StateVersions interface {
// process outputs asynchronously. When consuming outputs or other async StateVersion fields, be sure to
// wait for ResourcesProcessed to become `true` before assuming they are empty.
ListOutputs(ctx context.Context, svID string, options *StateVersionOutputsListOptions) (*StateVersionOutputsList, error)

// SoftDeleteBackingData soft deletes the state version's backing data
// **Note: This functionality is only available in Terraform Enterprise.**
SoftDeleteBackingData(ctx context.Context, svID string) error

// RestoreBackingData restores a soft deleted state version's backing data
// **Note: This functionality is only available in Terraform Enterprise.**
RestoreBackingData(ctx context.Context, svID string) error

// PermanentlyDeleteBackingData permanently deletes a soft deleted state version's backing data
// **Note: This functionality is only available in Terraform Enterprise.**
PermanentlyDeleteBackingData(ctx context.Context, svID string) error
}

// stateVersions implements StateVersions.
Expand Down Expand Up @@ -398,6 +410,32 @@ func (s *stateVersions) ListOutputs(ctx context.Context, svID string, options *S
return sv, nil
}

func (s *stateVersions) SoftDeleteBackingData(ctx context.Context, svID string) error {
return s.manageBackingData(ctx, svID, "soft_delete_backing_data")
}

func (s *stateVersions) RestoreBackingData(ctx context.Context, svID string) error {
return s.manageBackingData(ctx, svID, "restore_backing_data")
}

func (s *stateVersions) PermanentlyDeleteBackingData(ctx context.Context, svID string) error {
return s.manageBackingData(ctx, svID, "permanently_delete_backing_data")
}

func (s *stateVersions) manageBackingData(ctx context.Context, svID, action string) error {
if !validStringID(&svID) {
return ErrInvalidStateVerID
}

u := fmt.Sprintf("state-versions/%s/actions/%s", svID, action)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return err
}

return req.Do(ctx, nil)
}

// check that StateVersionListOptions fields had valid values
func (o *StateVersionListOptions) valid() error {
if o == nil {
Expand Down
46 changes: 46 additions & 0 deletions state_version_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,49 @@ func TestStateVersionOutputs(t *testing.T) {
assert.Error(t, err)
})
}

func TestStateVersions_ManageBackingData(t *testing.T) {
skipUnlessEnterprise(t)

client := testClient(t)
ctx := context.Background()

workspace, workspaceCleanup := createWorkspace(t, client, nil)
t.Cleanup(workspaceCleanup)

nonCurrentStateVersion, svTestCleanup := createStateVersion(t, client, 0, workspace)
t.Cleanup(svTestCleanup)

_, svTestCleanup = createStateVersion(t, client, 0, workspace)
t.Cleanup(svTestCleanup)

t.Run("soft delete backing data", func(t *testing.T) {
err := client.StateVersions.SoftDeleteBackingData(ctx, nonCurrentStateVersion.ID)
require.NoError(t, err)

_, err = client.StateVersions.Download(ctx, nonCurrentStateVersion.DownloadURL)
assert.Equal(t, ErrResourceNotFound, err)
})

t.Run("restore backing data", func(t *testing.T) {
err := client.StateVersions.RestoreBackingData(ctx, nonCurrentStateVersion.ID)
require.NoError(t, err)

_, err = client.StateVersions.Download(ctx, nonCurrentStateVersion.DownloadURL)
require.NoError(t, err)
})

t.Run("permanently delete backing data", func(t *testing.T) {
err := client.StateVersions.SoftDeleteBackingData(ctx, nonCurrentStateVersion.ID)
require.NoError(t, err)

err = client.StateVersions.PermanentlyDeleteBackingData(ctx, nonCurrentStateVersion.ID)
require.NoError(t, err)

err = client.StateVersions.RestoreBackingData(ctx, nonCurrentStateVersion.ID)
require.ErrorContainsf(t, err, "transition not allowed", "Restore backing data should fail")

_, err = client.StateVersions.Download(ctx, nonCurrentStateVersion.DownloadURL)
assert.Equal(t, ErrResourceNotFound, err)
})
}

0 comments on commit a245d58

Please sign in to comment.