Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow managing StateVersion and ConfigurationVersion backing data #801

Merged
merged 4 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
* 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)
})
}
Loading