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 data retention policies #817

Merged
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
@@ -1,4 +1,5 @@
# UNRELEASED
* Allow managing workspace and organization data retention policies by @mwudka [#801](https://github.com/hashicorp/go-tfe/pull/817)

## Bug Fixes
* Removed unused field `AgentPoolID` from the Workspace model. (Callers should be using the `AgentPool` relation instead) by @brandonc [#815](https://github.com/hashicorp/go-tfe/pull/815)
Expand Down
19 changes: 19 additions & 0 deletions data_retention_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfe

type DataRetentionPolicy struct {
ID string `jsonapi:"primary,data-retention-policies"`
DeleteOlderThanNDays int `jsonapi:"attr,delete-older-than-n-days"`
}

type DataRetentionPolicySetOptions struct {
// Type is a public field utilized by JSON:API to
// set the resource type via the field tag.
// It is not a user-defined value and does not need to be set.
// https://jsonapi.org/format/#crud-creating
Type string `jsonapi:"primary,data-retention-policies"`

DeleteOlderThanNDays int `jsonapi:"attr,delete-older-than-n-days"`
}
44 changes: 44 additions & 0 deletions mocks/organization_mocks.go

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

44 changes: 44 additions & 0 deletions mocks/workspace_mocks.go

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

71 changes: 71 additions & 0 deletions organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ type Organizations interface {

// ReadRunQueue shows the current run queue of an organization.
ReadRunQueue(ctx context.Context, organization string, options ReadRunQueueOptions) (*RunQueue, error)

// ReadDataRetentionPolicy reads an organization's data retention policy
// **Note: This functionality is only available in Terraform Enterprise.**
ReadDataRetentionPolicy(ctx context.Context, organization string) (*DataRetentionPolicy, error)

// SetDataRetentionPolicy sets an organization's data retention policy
// **Note: This functionality is only available in Terraform Enterprise.**
SetDataRetentionPolicy(ctx context.Context, organization string, options DataRetentionPolicySetOptions) (*DataRetentionPolicy, error)

// DeleteDataRetentionPolicy deletes an organization's data retention policy
// **Note: This functionality is only available in Terraform Enterprise.**
DeleteDataRetentionPolicy(ctx context.Context, organization string) error
}

// organizations implements Organizations.
Expand Down Expand Up @@ -93,6 +105,9 @@ type Organization struct {
// Relations
DefaultProject *Project `jsonapi:"relation,default-project"`
DefaultAgentPool *AgentPool `jsonapi:"relation,default-agent-pool"`

// **Note: This functionality is only available in Terraform Enterprise.**
DataRetentionPolicy *DataRetentionPolicy `jsonapi:"relation,data-retention-policy"`
}

// OrganizationIncludeOpt represents the available options for include query params.
Expand Down Expand Up @@ -416,6 +431,62 @@ func (s *organizations) ReadRunQueue(ctx context.Context, organization string, o
return rq, nil
}

func (s *organizations) ReadDataRetentionPolicy(ctx context.Context, organization string) (*DataRetentionPolicy, error) {
if !validStringID(&organization) {
return nil, ErrInvalidOrg
}

u := fmt.Sprintf("organizations/%s/relationships/data-retention-policy", url.QueryEscape(organization))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}

dataRetentionPolicy := &DataRetentionPolicy{}
err = req.Do(ctx, dataRetentionPolicy)

if err != nil {
return nil, err
}

return dataRetentionPolicy, nil
}

func (s *organizations) SetDataRetentionPolicy(ctx context.Context, organization string, options DataRetentionPolicySetOptions) (*DataRetentionPolicy, error) {
if !validStringID(&organization) {
return nil, ErrInvalidOrg
}

u := fmt.Sprintf("organizations/%s/relationships/data-retention-policy", url.QueryEscape(organization))
req, err := s.client.NewRequest("PATCH", u, &options)
if err != nil {
return nil, err
}

dataRetentionPolicy := &DataRetentionPolicy{}
err = req.Do(ctx, dataRetentionPolicy)

if err != nil {
return nil, err
}

return dataRetentionPolicy, nil
}

func (s *organizations) DeleteDataRetentionPolicy(ctx context.Context, organization string) error {
if !validStringID(&organization) {
return ErrInvalidOrg
}

u := fmt.Sprintf("organizations/%s/relationships/data-retention-policy", url.QueryEscape(organization))
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return err
}

return req.Do(ctx, nil)
}

func (o OrganizationCreateOptions) valid() error {
if !validString(o.Name) {
return ErrRequiredName
Expand Down
53 changes: 53 additions & 0 deletions organization_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,59 @@ func TestOrganizationsAllowForceDeleteSetting(t *testing.T) {
})
}

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

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

orgTest, orgTestCleanup := createOrganization(t, client)
t.Cleanup(orgTestCleanup)

dataRetentionPolicy, err := client.Organizations.ReadDataRetentionPolicy(ctx, orgTest.Name)
assert.Equal(t, ErrResourceNotFound, err)
require.Nil(t, dataRetentionPolicy)

organization, err := client.Organizations.Read(ctx, orgTest.Name)
require.NoError(t, err)
require.Nil(t, organization.DataRetentionPolicy)

t.Run("set data retention policy", func(t *testing.T) {
createdDataRetentionPolicy, err := client.Organizations.SetDataRetentionPolicy(ctx, orgTest.Name, DataRetentionPolicySetOptions{DeleteOlderThanNDays: 33})
require.NoError(t, err)
require.Equal(t, 33, createdDataRetentionPolicy.DeleteOlderThanNDays)
require.Contains(t, createdDataRetentionPolicy.ID, "drp-")

dataRetentionPolicy, err = client.Organizations.ReadDataRetentionPolicy(ctx, orgTest.Name)
require.NoError(t, err)
require.Equal(t, 33, dataRetentionPolicy.DeleteOlderThanNDays)
require.Equal(t, createdDataRetentionPolicy.ID, dataRetentionPolicy.ID)
require.Contains(t, dataRetentionPolicy.ID, "drp-")

organization, err := client.Organizations.Read(ctx, orgTest.Name)
require.NoError(t, err)
require.Equal(t, dataRetentionPolicy.ID, organization.DataRetentionPolicy.ID)
})

t.Run("update data retention policy", func(t *testing.T) {
_, err = client.Organizations.SetDataRetentionPolicy(ctx, orgTest.Name, DataRetentionPolicySetOptions{DeleteOlderThanNDays: 45})
require.NoError(t, err)

dataRetentionPolicy, err = client.Organizations.ReadDataRetentionPolicy(ctx, orgTest.Name)
require.NoError(t, err)
require.Equal(t, 45, dataRetentionPolicy.DeleteOlderThanNDays)
})

t.Run("delete data retention policy", func(t *testing.T) {
err = client.Organizations.DeleteDataRetentionPolicy(ctx, orgTest.Name)
require.NoError(t, err)

dataRetentionPolicy, err = client.Organizations.ReadDataRetentionPolicy(ctx, orgTest.Name)
assert.Equal(t, ErrResourceNotFound, err)
require.Nil(t, dataRetentionPolicy)
})
}

func orgItemsContainsName(items []*Organization, name string) bool {
hasName := false
for _, item := range items {
Expand Down
71 changes: 71 additions & 0 deletions workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ type Workspaces interface {

// RemoveTags removes tags from a workspace
RemoveTags(ctx context.Context, workspaceID string, options WorkspaceRemoveTagsOptions) error

// ReadDataRetentionPolicy reads a workspace's data retention policy
// **Note: This functionality is only available in Terraform Enterprise.**
ReadDataRetentionPolicy(ctx context.Context, workspaceID string) (*DataRetentionPolicy, error)

// SetDataRetentionPolicy sets a workspace's data retention policy
// **Note: This functionality is only available in Terraform Enterprise.**
SetDataRetentionPolicy(ctx context.Context, workspaceID string, options DataRetentionPolicySetOptions) (*DataRetentionPolicy, error)

// DeleteDataRetentionPolicy deletes a workspace's data retention policy
// **Note: This functionality is only available in Terraform Enterprise.**
DeleteDataRetentionPolicy(ctx context.Context, workspaceID string) error
}

// workspaces implements Workspaces.
Expand Down Expand Up @@ -165,6 +177,9 @@ type Workspace struct {
Tags []*Tag `jsonapi:"relation,tags"`
CurrentConfigurationVersion *ConfigurationVersion `jsonapi:"relation,current-configuration-version,omitempty"`

// **Note: This functionality is only available in Terraform Enterprise.**
DataRetentionPolicy *DataRetentionPolicy `jsonapi:"relation,data-retention-policy"`

// Links
Links map[string]interface{} `jsonapi:"links,omitempty"`
}
Expand Down Expand Up @@ -1182,6 +1197,62 @@ func (s *workspaces) RemoveTags(ctx context.Context, workspaceID string, options
return req.Do(ctx, nil)
}

func (s *workspaces) ReadDataRetentionPolicy(ctx context.Context, workspaceID string) (*DataRetentionPolicy, error) {
if !validStringID(&workspaceID) {
return nil, ErrInvalidWorkspaceID
}

u := fmt.Sprintf("workspaces/%s/relationships/data-retention-policy", url.QueryEscape(workspaceID))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}

dataRetentionPolicy := &DataRetentionPolicy{}
err = req.Do(ctx, dataRetentionPolicy)

if err != nil {
return nil, err
}

return dataRetentionPolicy, nil
}

func (s *workspaces) SetDataRetentionPolicy(ctx context.Context, workspaceID string, options DataRetentionPolicySetOptions) (*DataRetentionPolicy, error) {
if !validStringID(&workspaceID) {
return nil, ErrInvalidWorkspaceID
}

u := fmt.Sprintf("workspaces/%s/relationships/data-retention-policy", url.QueryEscape(workspaceID))
req, err := s.client.NewRequest("PATCH", u, &options)
if err != nil {
return nil, err
}

dataRetentionPolicy := &DataRetentionPolicy{}
err = req.Do(ctx, dataRetentionPolicy)

if err != nil {
return nil, err
}

return dataRetentionPolicy, nil
}

func (s *workspaces) DeleteDataRetentionPolicy(ctx context.Context, workspaceID string) error {
if !validStringID(&workspaceID) {
return ErrInvalidWorkspaceID
}

u := fmt.Sprintf("workspaces/%s/relationships/data-retention-policy", url.QueryEscape(workspaceID))
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return err
}

return req.Do(ctx, nil)
}

func (o WorkspaceCreateOptions) valid() error {
if !validString(o.Name) {
return ErrRequiredName
Expand Down
Loading