From 55d57b88cd8d1c9ea7200a208d53750f0ff8b39a Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Thu, 7 Apr 2022 14:00:48 -0300 Subject: [PATCH 01/14] Add support for applying/removing variable set from workspaces --- variable_set.go | 75 +++++++++++++++++++++++++++++++++++++++++++- variable_set_test.go | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/variable_set.go b/variable_set.go index 1e2e438f2..17e00f253 100644 --- a/variable_set.go +++ b/variable_set.go @@ -29,7 +29,13 @@ type VariableSets interface { // Delete a variable set by ID. Delete(ctx context.Context, variableSetID string) error - // Update list of workspaces to which the variable set is applied to match the supplied list + // Apply variable set to workspaces in the supplied list. + ApplyToWorkspaces(ctx context.Context, variableSetID string, options *VariableSetApplyToWorkspacesOptions) error + + // Remove variable set from workspaces in the supplied list. + RemoveFromWorkspaces(ctx context.Context, variableSetID string, options *VariableSetRemoveFromWorkspacesOptions) error + + // Update list of workspaces to which the variable set is applied to match the supplied list. UpdateWorkspaces(ctx context.Context, variableSetID string, options *VariableSetUpdateWorkspacesOptions) (*VariableSet, error) } @@ -200,6 +206,7 @@ type VariableSetUpdateOptions struct { Global *bool `jsonapi:"attr,global,omitempty"` } +// Update an existing variable set. func (s *variableSets) Update(ctx context.Context, variableSetID string, options *VariableSetUpdateOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { return nil, ErrInvalidVariableSetID @@ -235,6 +242,72 @@ func (s *variableSets) Delete(ctx context.Context, variableSetID string) error { return s.client.do(ctx, req, nil) } +// VariableSetApplyToWorkspacesOptions represents the options for applying variable sets to workspaces. +type VariableSetApplyToWorkspacesOptions struct { + // The workspaces to apply the variable set to (additive). + Workspaces []*Workspace +} + +func (o *VariableSetApplyToWorkspacesOptions) valid() error { + for _, s := range o.Workspaces { + if !validStringID(&s.ID) { + return ErrRequiredWorkspaceID + } + } + return nil +} + +// Apply variable set to workspaces in the supplied list. +func (s *variableSets) ApplyToWorkspaces(ctx context.Context, variableSetID string, options *VariableSetApplyToWorkspacesOptions) error { + if !validStringID(&variableSetID) { + return ErrInvalidVariableSetID + } + if err := options.valid(); err != nil { + return err + } + + u := fmt.Sprintf("varsets/%s/relationships/workspaces", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("POST", u, options.Workspaces) + if err != nil { + return err + } + + return s.client.do(ctx, req, nil) +} + +// VariableSetRemoveFromWorkspacesOptions represents the options for removing variable sets from workspaces. +type VariableSetRemoveFromWorkspacesOptions struct { + // The workspaces to remove the variable set from. + Workspaces []*Workspace +} + +func (o *VariableSetRemoveFromWorkspacesOptions) valid() error { + for _, s := range o.Workspaces { + if !validStringID(&s.ID) { + return ErrRequiredWorkspaceID + } + } + return nil +} + +// Remove variable set from workspaces in the supplied list. +func (s *variableSets) RemoveFromWorkspaces(ctx context.Context, variableSetID string, options *VariableSetRemoveFromWorkspacesOptions) error { + if !validStringID(&variableSetID) { + return ErrInvalidVariableSetID + } + if err := options.valid(); err != nil { + return err + } + + u := fmt.Sprintf("varsets/%s/relationships/workspaces", url.QueryEscape(variableSetID)) + req, err := s.client.newRequest("DELETE", u, options.Workspaces) + if err != nil { + return err + } + + return s.client.do(ctx, req, nil) +} + // VariableSetUpdateWorkspacesOptions represents a subset of update options specifically for applying variable sets to workspaces type VariableSetUpdateWorkspacesOptions struct { // Type is a public field utilized by JSON:API to diff --git a/variable_set_test.go b/variable_set_test.go index 0290661bc..d817a6ab3 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -190,6 +190,67 @@ func TestVariableSetsDelete(t *testing.T) { }) } +func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + orgTest, orgTestCleanup := createOrganization(t, client) + defer orgTestCleanup() + + vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + + wTest1, _ := createWorkspace(t, client, orgTest) + wTest2, _ := createWorkspace(t, client, orgTest) + + t.Run("with first workspace added", func(t *testing.T) { + options := VariableSetApplyToWorkspacesOptions{ + Workspaces: []*Workspace{wTest1}, + } + + err := client.VariableSets.ApplyToWorkspaces(ctx, vsTest.ID, &options) + require.NoError(t, err) + + vsAfter, err := client.VariableSets.Read(ctx, vsTest.ID, nil) + require.NoError(t, err) + + // Variable set should be applied to [wTest1] + assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) + assert.Equal(t, options.Workspaces[0].ID, vsAfter.Workspaces[0].ID) + }) + + t.Run("with second workspace added", func(t *testing.T) { + options := VariableSetApplyToWorkspacesOptions{ + Workspaces: []*Workspace{wTest2}, + } + + err := client.VariableSets.ApplyToWorkspaces(ctx, vsTest.ID, &options) + require.NoError(t, err) + + vsAfter, err := client.VariableSets.Read(ctx, vsTest.ID, nil) + require.NoError(t, err) + + // Variable set should be applied to [wTest1, wTest2] + assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) + assert.Equal(t, options.Workspaces[1].ID, vsAfter.Workspaces[1].ID) + }) + + t.Run("with first workspace removed", func(t *testing.T) { + options := VariableSetRemoveFromWorkspacesOptions{ + Workspaces: []*Workspace{wTest1}, + } + + err := client.VariableSets.RemoveFromWorkspaces(ctx, vsTest.ID, &options) + require.NoError(t, err) + + vsAfter, err := client.VariableSets.Read(ctx, vsTest.ID, nil) + require.NoError(t, err) + + // Variable set should be applied to [wTest2] + assert.Equal(t, 1, len(vsAfter.Workspaces)) + assert.Equal(t, wTest2.ID, vsAfter.Workspaces[0].ID) + }) +} + func TestVariableSetsUpdateWorkspaces(t *testing.T) { client := testClient(t) ctx := context.Background() From 655808af9f9cc6c66d57a02a141dfd262c097396 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Thu, 7 Apr 2022 14:01:44 -0300 Subject: [PATCH 02/14] Add mocks for variable set endpoints --- generate_mocks.sh | 1 + mocks/variable_set_mocks.go | 153 ++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 mocks/variable_set_mocks.go diff --git a/generate_mocks.sh b/generate_mocks.sh index cdbe48711..df36385f3 100755 --- a/generate_mocks.sh +++ b/generate_mocks.sh @@ -49,5 +49,6 @@ mockgen -source=team_token.go -destination=mocks/team_token_mocks.go -package=mo mockgen -source=user.go -destination=mocks/user_mocks.go -package=mocks mockgen -source=user_token.go -destination=mocks/user_token_mocks.go -package=mocks mockgen -source=variable.go -destination=mocks/variable_mocks.go -package=mocks +mockgen -source=variable_set.go -destination=mocks/variable_set_mocks.go -package=mocks mockgen -source=workspace.go -destination=mocks/workspace_mocks.go -package=mocks mockgen -source=workspace_run_task.go -destination=mocks/workspace_run_tasks.go -package=mocks diff --git a/mocks/variable_set_mocks.go b/mocks/variable_set_mocks.go new file mode 100644 index 000000000..56d02c9ab --- /dev/null +++ b/mocks/variable_set_mocks.go @@ -0,0 +1,153 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: variable_set.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + tfe "github.com/hashicorp/go-tfe" +) + +// MockVariableSets is a mock of VariableSets interface. +type MockVariableSets struct { + ctrl *gomock.Controller + recorder *MockVariableSetsMockRecorder +} + +// MockVariableSetsMockRecorder is the mock recorder for MockVariableSets. +type MockVariableSetsMockRecorder struct { + mock *MockVariableSets +} + +// NewMockVariableSets creates a new mock instance. +func NewMockVariableSets(ctrl *gomock.Controller) *MockVariableSets { + mock := &MockVariableSets{ctrl: ctrl} + mock.recorder = &MockVariableSetsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVariableSets) EXPECT() *MockVariableSetsMockRecorder { + return m.recorder +} + +// ApplyToWorkspaces mocks base method. +func (m *MockVariableSets) ApplyToWorkspaces(ctx context.Context, variableSetID string, options *tfe.VariableSetApplyToWorkspacesOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyToWorkspaces", ctx, variableSetID, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// ApplyToWorkspaces indicates an expected call of ApplyToWorkspaces. +func (mr *MockVariableSetsMockRecorder) ApplyToWorkspaces(ctx, variableSetID, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyToWorkspaces", reflect.TypeOf((*MockVariableSets)(nil).ApplyToWorkspaces), ctx, variableSetID, options) +} + +// Create mocks base method. +func (m *MockVariableSets) Create(ctx context.Context, organization string, options *tfe.VariableSetCreateOptions) (*tfe.VariableSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, organization, options) + ret0, _ := ret[0].(*tfe.VariableSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockVariableSetsMockRecorder) Create(ctx, organization, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockVariableSets)(nil).Create), ctx, organization, options) +} + +// Delete mocks base method. +func (m *MockVariableSets) Delete(ctx context.Context, variableSetID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, variableSetID) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockVariableSetsMockRecorder) Delete(ctx, variableSetID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockVariableSets)(nil).Delete), ctx, variableSetID) +} + +// List mocks base method. +func (m *MockVariableSets) List(ctx context.Context, organization string, options *tfe.VariableSetListOptions) (*tfe.VariableSetList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, organization, options) + ret0, _ := ret[0].(*tfe.VariableSetList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockVariableSetsMockRecorder) List(ctx, organization, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockVariableSets)(nil).List), ctx, organization, options) +} + +// Read mocks base method. +func (m *MockVariableSets) Read(ctx context.Context, variableSetID string, options *tfe.VariableSetReadOptions) (*tfe.VariableSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Read", ctx, variableSetID, options) + ret0, _ := ret[0].(*tfe.VariableSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Read indicates an expected call of Read. +func (mr *MockVariableSetsMockRecorder) Read(ctx, variableSetID, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockVariableSets)(nil).Read), ctx, variableSetID, options) +} + +// RemoveFromWorkspaces mocks base method. +func (m *MockVariableSets) RemoveFromWorkspaces(ctx context.Context, variableSetID string, options *tfe.VariableSetRemoveFromWorkspacesOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveFromWorkspaces", ctx, variableSetID, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveFromWorkspaces indicates an expected call of RemoveFromWorkspaces. +func (mr *MockVariableSetsMockRecorder) RemoveFromWorkspaces(ctx, variableSetID, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFromWorkspaces", reflect.TypeOf((*MockVariableSets)(nil).RemoveFromWorkspaces), ctx, variableSetID, options) +} + +// Update mocks base method. +func (m *MockVariableSets) Update(ctx context.Context, variableSetID string, options *tfe.VariableSetUpdateOptions) (*tfe.VariableSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, variableSetID, options) + ret0, _ := ret[0].(*tfe.VariableSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockVariableSetsMockRecorder) Update(ctx, variableSetID, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockVariableSets)(nil).Update), ctx, variableSetID, options) +} + +// UpdateWorkspaces mocks base method. +func (m *MockVariableSets) UpdateWorkspaces(ctx context.Context, variableSetID string, options *tfe.VariableSetUpdateWorkspacesOptions) (*tfe.VariableSet, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateWorkspaces", ctx, variableSetID, options) + ret0, _ := ret[0].(*tfe.VariableSet) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateWorkspaces indicates an expected call of UpdateWorkspaces. +func (mr *MockVariableSetsMockRecorder) UpdateWorkspaces(ctx, variableSetID, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaces", reflect.TypeOf((*MockVariableSets)(nil).UpdateWorkspaces), ctx, variableSetID, options) +} From 37c6607990857d82625f77ac91e7baffdcf22db0 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Thu, 7 Apr 2022 14:16:25 -0300 Subject: [PATCH 03/14] Fix copy-and-paste error in tests --- variable_set_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set_test.go b/variable_set_test.go index d817a6ab3..e38fd0079 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -230,7 +230,7 @@ func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { require.NoError(t, err) // Variable set should be applied to [wTest1, wTest2] - assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) + assert.Equal(t, 2, len(vsAfter.Workspaces)) assert.Equal(t, options.Workspaces[1].ID, vsAfter.Workspaces[1].ID) }) From 3087f65a524eda9ece61789a30f74d6c4829f38c Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Thu, 7 Apr 2022 14:26:12 -0300 Subject: [PATCH 04/14] Fix index out of range panic in tests --- variable_set_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set_test.go b/variable_set_test.go index e38fd0079..a015ff0c5 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -231,7 +231,7 @@ func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { // Variable set should be applied to [wTest1, wTest2] assert.Equal(t, 2, len(vsAfter.Workspaces)) - assert.Equal(t, options.Workspaces[1].ID, vsAfter.Workspaces[1].ID) + assert.Equal(t, options.Workspaces[0].ID, vsAfter.Workspaces[1].ID) }) t.Run("with first workspace removed", func(t *testing.T) { From 3da9f34779e96b90a303f43828279c6b177e6e96 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Thu, 7 Apr 2022 16:21:41 -0300 Subject: [PATCH 05/14] Make variable set apply/remove tests clearer --- variable_set_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/variable_set_test.go b/variable_set_test.go index a015ff0c5..79104a67f 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -214,8 +214,8 @@ func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { require.NoError(t, err) // Variable set should be applied to [wTest1] - assert.Equal(t, len(options.Workspaces), len(vsAfter.Workspaces)) - assert.Equal(t, options.Workspaces[0].ID, vsAfter.Workspaces[0].ID) + assert.Equal(t, 1, len(vsAfter.Workspaces)) + assert.Equal(t, wTest1.ID, vsAfter.Workspaces[0].ID) }) t.Run("with second workspace added", func(t *testing.T) { @@ -231,7 +231,8 @@ func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { // Variable set should be applied to [wTest1, wTest2] assert.Equal(t, 2, len(vsAfter.Workspaces)) - assert.Equal(t, options.Workspaces[0].ID, vsAfter.Workspaces[1].ID) + assert.Equal(t, wTest1.ID, vsAfter.Workspaces[0].ID) + assert.Equal(t, wTest2.ID, vsAfter.Workspaces[1].ID) }) t.Run("with first workspace removed", func(t *testing.T) { From 4c0382e155d29dfa90ab18d32d99d8abc6006322 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Fri, 8 Apr 2022 10:50:05 -0300 Subject: [PATCH 06/14] Add comment warning re: applying/removing global varsets against workspaces --- variable_set.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variable_set.go b/variable_set.go index 17e00f253..3d2b1c978 100644 --- a/variable_set.go +++ b/variable_set.go @@ -291,6 +291,7 @@ func (o *VariableSetRemoveFromWorkspacesOptions) valid() error { } // Remove variable set from workspaces in the supplied list. +// Note: this method will return an error if the variable set has global = true. func (s *variableSets) RemoveFromWorkspaces(ctx context.Context, variableSetID string, options *VariableSetRemoveFromWorkspacesOptions) error { if !validStringID(&variableSetID) { return ErrInvalidVariableSetID @@ -309,6 +310,7 @@ func (s *variableSets) RemoveFromWorkspaces(ctx context.Context, variableSetID s } // VariableSetUpdateWorkspacesOptions represents a subset of update options specifically for applying variable sets to workspaces +// Note: this method will return an error if the variable set has global = true. type VariableSetUpdateWorkspacesOptions struct { // Type is a public field utilized by JSON:API to // set the resource type via the field tag. From 69fd1c56befea66f1e608b9431ee4c417fc38e8c Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Fri, 8 Apr 2022 10:53:03 -0300 Subject: [PATCH 07/14] Update/document changes in CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e848276..9c7bd0af7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Enhancements * Adds support for reading current state version outputs to StateVersionOutputs, which can be useful for reading outputs when users don't have the necessary permissions to read the entire state. +* Adds Variable Set methods for `ApplyToWorkspaces` and `RemoveFromWorkspaces` [#375](https://github.com/hashicorp/go-tfe/pull/375) # v1.1.0 From 1e1177a214c5fe764c3b027c038a068d9fb42782 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Fri, 8 Apr 2022 10:54:06 -0300 Subject: [PATCH 08/14] Move mistakenly-placed comment --- variable_set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variable_set.go b/variable_set.go index 3d2b1c978..a73fd21b7 100644 --- a/variable_set.go +++ b/variable_set.go @@ -258,6 +258,7 @@ func (o *VariableSetApplyToWorkspacesOptions) valid() error { } // Apply variable set to workspaces in the supplied list. +// Note: this method will return an error if the variable set has global = true. func (s *variableSets) ApplyToWorkspaces(ctx context.Context, variableSetID string, options *VariableSetApplyToWorkspacesOptions) error { if !validStringID(&variableSetID) { return ErrInvalidVariableSetID @@ -310,7 +311,6 @@ func (s *variableSets) RemoveFromWorkspaces(ctx context.Context, variableSetID s } // VariableSetUpdateWorkspacesOptions represents a subset of update options specifically for applying variable sets to workspaces -// Note: this method will return an error if the variable set has global = true. type VariableSetUpdateWorkspacesOptions struct { // Type is a public field utilized by JSON:API to // set the resource type via the field tag. From 938724df0ecd77b2fbbb7e91dbb5d1499179aae6 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Fri, 8 Apr 2022 15:40:31 -0300 Subject: [PATCH 09/14] Add deferred cleanup calls for variable set tests --- variable_set_test.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/variable_set_test.go b/variable_set_test.go index 79104a67f..7a1c41686 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -173,7 +173,8 @@ func TestVariableSetsDelete(t *testing.T) { orgTest, orgTestCleanup := createOrganization(t, client) defer orgTestCleanup() - vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup() t.Run("with valid ID", func(t *testing.T) { err := client.VariableSets.Delete(ctx, vsTest.ID) @@ -197,10 +198,13 @@ func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { orgTest, orgTestCleanup := createOrganization(t, client) defer orgTestCleanup() - vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup() - wTest1, _ := createWorkspace(t, client, orgTest) - wTest2, _ := createWorkspace(t, client, orgTest) + wTest1, wTest1Cleanup := createWorkspace(t, client, orgTest) + defer wTest1Cleanup() + wTest2, wTest2Cleanup := createWorkspace(t, client, orgTest) + defer wTest2Cleanup() t.Run("with first workspace added", func(t *testing.T) { options := VariableSetApplyToWorkspacesOptions{ @@ -259,9 +263,11 @@ func TestVariableSetsUpdateWorkspaces(t *testing.T) { orgTest, orgTestCleanup := createOrganization(t, client) defer orgTestCleanup() - vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) + defer vsTestCleanup() - wTest, _ := createWorkspace(t, client, orgTest) + wTest, wTestCleanup := createWorkspace(t, client, orgTest) + defer wTestCleanup() t.Run("with valid workspaces", func(t *testing.T) { options := VariableSetUpdateWorkspacesOptions{ From 1e64cb4976441ddb6325fc364a17926cd7f0c3fa Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Fri, 8 Apr 2022 15:46:06 -0300 Subject: [PATCH 10/14] Add missing type comments/descriptions for variable sets --- variable_set.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/variable_set.go b/variable_set.go index a73fd21b7..cebf3c3fc 100644 --- a/variable_set.go +++ b/variable_set.go @@ -39,15 +39,18 @@ type VariableSets interface { UpdateWorkspaces(ctx context.Context, variableSetID string, options *VariableSetUpdateWorkspacesOptions) (*VariableSet, error) } +// variableSets implements VariableSets. type variableSets struct { client *Client } +// VariableSetList represents a list of variable sets. type VariableSetList struct { *Pagination Items []*VariableSet } +// VariableSet represents a Terraform Enterprise variable set. type VariableSet struct { ID string `jsonapi:"primary,varsets"` Name string `jsonapi:"attr,name"` @@ -69,6 +72,7 @@ const ( VariableSetVars VariableSetIncludeOpt = "vars" ) +// VariableSetListOptions represents the options for listing variable sets. type VariableSetListOptions struct { ListOptions Include string `url:"include"` @@ -161,6 +165,7 @@ func (s *variableSets) Create(ctx context.Context, organization string, options return vl, nil } +// VariableSetReadOptions represents the options for reading variable sets. type VariableSetReadOptions struct { Include *[]VariableSetIncludeOpt `url:"include:omitempty"` } From ba9fb5dafb989f5242dfb8bcb7d7fb10e7830be1 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Fri, 8 Apr 2022 15:57:19 -0300 Subject: [PATCH 11/14] Re-order variable_set.go: types, receiver methods, validators --- variable_set.go | 230 ++++++++++++++++++++++++------------------------ 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/variable_set.go b/variable_set.go index cebf3c3fc..d666dae66 100644 --- a/variable_set.go +++ b/variable_set.go @@ -78,38 +78,33 @@ type VariableSetListOptions struct { Include string `url:"include"` } -func (o *VariableSetListOptions) valid() error { - return nil -} +// VariableSetCreateOptions represents the options for creating a new variable set within in a organization. +type VariableSetCreateOptions 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,varsets"` -// List all Variable Sets in the organization -func (s *variableSets) List(ctx context.Context, organization string, options *VariableSetListOptions) (*VariableSetList, error) { - if !validStringID(&organization) { - return nil, ErrInvalidOrg - } - if options != nil { - if err := options.valid(); err != nil { - return nil, err - } - } + // The name of the variable set. + // Affects variable precedence when there are conflicts between Variable Sets + // https://www.terraform.io/cloud-docs/api-docs/variable-sets#apply-variable-set-to-workspaces + Name *string `jsonapi:"attr,name"` - u := fmt.Sprintf("organizations/%s/varsets", url.QueryEscape(organization)) - req, err := s.client.newRequest("GET", u, options) - if err != nil { - return nil, err - } + // A description to provide context for the variable set. + Description *string `jsonapi:"attr,description,omitempty"` - vl := &VariableSetList{} - err = s.client.do(ctx, req, vl) - if err != nil { - return nil, err - } + // If true the variable set is considered in all runs in the organization. + Global *bool `jsonapi:"attr,global,omitempty"` +} - return vl, nil +// VariableSetReadOptions represents the options for reading variable sets. +type VariableSetReadOptions struct { + Include *[]VariableSetIncludeOpt `url:"include:omitempty"` } -// VariableSetCreateOptions represents the options for creating a new variable set within in a organization. -type VariableSetCreateOptions struct { +// VariableSetUpdateOptions represents the options for updating a variable set. +type VariableSetUpdateOptions 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. @@ -119,7 +114,7 @@ type VariableSetCreateOptions struct { // The name of the variable set. // Affects variable precedence when there are conflicts between Variable Sets // https://www.terraform.io/cloud-docs/api-docs/variable-sets#apply-variable-set-to-workspaces - Name *string `jsonapi:"attr,name"` + Name *string `jsonapi:"attr,name,omitempty"` // A description to provide context for the variable set. Description *string `jsonapi:"attr,description,omitempty"` @@ -128,17 +123,60 @@ type VariableSetCreateOptions struct { Global *bool `jsonapi:"attr,global,omitempty"` } -func (o *VariableSetCreateOptions) valid() error { - if o == nil { - return nil +// VariableSetApplyToWorkspacesOptions represents the options for applying variable sets to workspaces. +type VariableSetApplyToWorkspacesOptions struct { + // The workspaces to apply the variable set to (additive). + Workspaces []*Workspace +} + +// VariableSetRemoveFromWorkspacesOptions represents the options for removing variable sets from workspaces. +type VariableSetRemoveFromWorkspacesOptions struct { + // The workspaces to remove the variable set from. + Workspaces []*Workspace +} + +// VariableSetUpdateWorkspacesOptions represents a subset of update options specifically for applying variable sets to workspaces +type VariableSetUpdateWorkspacesOptions 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,varsets"` + + // The workspaces to be applied to. An empty set means remove all applied + Workspaces []*Workspace `jsonapi:"relation,workspaces"` +} + +type privateVariableSetUpdateWorkspacesOptions struct { + Type string `jsonapi:"primary,varsets"` + Global bool `jsonapi:"attr,global"` + Workspaces []*Workspace `jsonapi:"relation,workspaces"` +} + +// List all Variable Sets in the organization +func (s *variableSets) List(ctx context.Context, organization string, options *VariableSetListOptions) (*VariableSetList, error) { + if !validStringID(&organization) { + return nil, ErrInvalidOrg } - if !validString(o.Name) { - return ErrRequiredName + if options != nil { + if err := options.valid(); err != nil { + return nil, err + } } - if o.Global == nil { - return ErrRequiredGlobalFlag + + u := fmt.Sprintf("organizations/%s/varsets", url.QueryEscape(organization)) + req, err := s.client.newRequest("GET", u, options) + if err != nil { + return nil, err } - return nil + + vl := &VariableSetList{} + err = s.client.do(ctx, req, vl) + if err != nil { + return nil, err + } + + return vl, nil } // Create is used to create a new variable set. @@ -165,11 +203,6 @@ func (s *variableSets) Create(ctx context.Context, organization string, options return vl, nil } -// VariableSetReadOptions represents the options for reading variable sets. -type VariableSetReadOptions struct { - Include *[]VariableSetIncludeOpt `url:"include:omitempty"` -} - // Read is used to inspect a given variable set based on ID func (s *variableSets) Read(ctx context.Context, variableSetID string, options *VariableSetReadOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { @@ -191,26 +224,6 @@ func (s *variableSets) Read(ctx context.Context, variableSetID string, options * return vs, err } -// VariableSetUpdateOptions represents the options for updating a variable set. -type VariableSetUpdateOptions 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,varsets"` - - // The name of the variable set. - // Affects variable precedence when there are conflicts between Variable Sets - // https://www.terraform.io/cloud-docs/api-docs/variable-sets#apply-variable-set-to-workspaces - Name *string `jsonapi:"attr,name,omitempty"` - - // A description to provide context for the variable set. - Description *string `jsonapi:"attr,description,omitempty"` - - // If true the variable set is considered in all runs in the organization. - Global *bool `jsonapi:"attr,global,omitempty"` -} - // Update an existing variable set. func (s *variableSets) Update(ctx context.Context, variableSetID string, options *VariableSetUpdateOptions) (*VariableSet, error) { if !validStringID(&variableSetID) { @@ -247,21 +260,6 @@ func (s *variableSets) Delete(ctx context.Context, variableSetID string) error { return s.client.do(ctx, req, nil) } -// VariableSetApplyToWorkspacesOptions represents the options for applying variable sets to workspaces. -type VariableSetApplyToWorkspacesOptions struct { - // The workspaces to apply the variable set to (additive). - Workspaces []*Workspace -} - -func (o *VariableSetApplyToWorkspacesOptions) valid() error { - for _, s := range o.Workspaces { - if !validStringID(&s.ID) { - return ErrRequiredWorkspaceID - } - } - return nil -} - // Apply variable set to workspaces in the supplied list. // Note: this method will return an error if the variable set has global = true. func (s *variableSets) ApplyToWorkspaces(ctx context.Context, variableSetID string, options *VariableSetApplyToWorkspacesOptions) error { @@ -281,21 +279,6 @@ func (s *variableSets) ApplyToWorkspaces(ctx context.Context, variableSetID stri return s.client.do(ctx, req, nil) } -// VariableSetRemoveFromWorkspacesOptions represents the options for removing variable sets from workspaces. -type VariableSetRemoveFromWorkspacesOptions struct { - // The workspaces to remove the variable set from. - Workspaces []*Workspace -} - -func (o *VariableSetRemoveFromWorkspacesOptions) valid() error { - for _, s := range o.Workspaces { - if !validStringID(&s.ID) { - return ErrRequiredWorkspaceID - } - } - return nil -} - // Remove variable set from workspaces in the supplied list. // Note: this method will return an error if the variable set has global = true. func (s *variableSets) RemoveFromWorkspaces(ctx context.Context, variableSetID string, options *VariableSetRemoveFromWorkspacesOptions) error { @@ -315,31 +298,6 @@ func (s *variableSets) RemoveFromWorkspaces(ctx context.Context, variableSetID s return s.client.do(ctx, req, nil) } -// VariableSetUpdateWorkspacesOptions represents a subset of update options specifically for applying variable sets to workspaces -type VariableSetUpdateWorkspacesOptions 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,varsets"` - - // The workspaces to be applied to. An empty set means remove all applied - Workspaces []*Workspace `jsonapi:"relation,workspaces"` -} - -func (o *VariableSetUpdateWorkspacesOptions) valid() error { - if o == nil || o.Workspaces == nil { - return ErrRequiredWorkspacesList - } - return nil -} - -type privateVariableSetUpdateWorkspacesOptions struct { - Type string `jsonapi:"primary,varsets"` - Global bool `jsonapi:"attr,global"` - Workspaces []*Workspace `jsonapi:"relation,workspaces"` -} - // Update variable set to be applied to only the workspaces in the supplied list. func (s *variableSets) UpdateWorkspaces(ctx context.Context, variableSetID string, options *VariableSetUpdateWorkspacesOptions) (*VariableSet, error) { if err := options.valid(); err != nil { @@ -367,3 +325,45 @@ func (s *variableSets) UpdateWorkspaces(ctx context.Context, variableSetID strin return v, nil } + +func (o *VariableSetListOptions) valid() error { + return nil +} + +func (o *VariableSetCreateOptions) valid() error { + if o == nil { + return nil + } + if !validString(o.Name) { + return ErrRequiredName + } + if o.Global == nil { + return ErrRequiredGlobalFlag + } + return nil +} + +func (o *VariableSetApplyToWorkspacesOptions) valid() error { + for _, s := range o.Workspaces { + if !validStringID(&s.ID) { + return ErrRequiredWorkspaceID + } + } + return nil +} + +func (o *VariableSetRemoveFromWorkspacesOptions) valid() error { + for _, s := range o.Workspaces { + if !validStringID(&s.ID) { + return ErrRequiredWorkspaceID + } + } + return nil +} + +func (o *VariableSetUpdateWorkspacesOptions) valid() error { + if o == nil || o.Workspaces == nil { + return ErrRequiredWorkspacesList + } + return nil +} From 9b47554412fc863fb58dc192d7b136d4838aa32a Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Mon, 11 Apr 2022 10:40:51 -0300 Subject: [PATCH 12/14] Do not defer cleanup in a test that will delete the varset anyway --- variable_set_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variable_set_test.go b/variable_set_test.go index 7a1c41686..f2824d6c5 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -173,8 +173,8 @@ func TestVariableSetsDelete(t *testing.T) { orgTest, orgTestCleanup := createOrganization(t, client) defer orgTestCleanup() - vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) - defer vsTestCleanup() + // Do not defer cleanup since the next step in this test is to delete it + vsTest, _ := createVariableSet(t, client, orgTest, VariableSetCreateOptions{}) t.Run("with valid ID", func(t *testing.T) { err := client.VariableSets.Delete(ctx, vsTest.ID) From bd4f215a4bf912756de44e12b4a5aa3e33fbfd73 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Wed, 13 Apr 2022 17:20:13 -0300 Subject: [PATCH 13/14] Add invalid variable set apply/remove test cases --- variable_set_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/variable_set_test.go b/variable_set_test.go index f2824d6c5..b25903249 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -254,6 +254,41 @@ func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { assert.Equal(t, 1, len(vsAfter.Workspaces)) assert.Equal(t, wTest2.ID, vsAfter.Workspaces[0].ID) }) + + t.Run("when variable set ID is invalid", func(t *testing.T) { + applyOptions := VariableSetApplyToWorkspacesOptions{ + Workspaces: []*Workspace{wTest1}, + } + + err := client.VariableSets.ApplyToWorkspaces(ctx, badIdentifier, &applyOptions) + assert.EqualError(t, err, ErrInvalidVariableID.Error()) + + removeOptions := VariableSetRemoveFromWorkspacesOptions{ + Workspaces: []*Workspace{wTest1}, + } + err = client.VariableSets.RemoveFromWorkspaces(ctx, badIdentifier, &removeOptions) + assert.EqualError(t, err, ErrInvalidVariableID.Error()) + }) + + t.Run("when workspace ID is invalid", func(t *testing.T) { + badWorkspace := &Workspace{ + ID: badIdentifier, + } + + applyOptions := VariableSetApplyToWorkspacesOptions{ + Workspaces: []*Workspace{badWorkspace}, + } + + err := client.VariableSets.ApplyToWorkspaces(ctx, vsTest.ID, &applyOptions) + assert.EqualError(t, err, ErrRequiredWorkspaceID.Error()) + + removeOptions := VariableSetRemoveFromWorkspacesOptions{ + Workspaces: []*Workspace{badWorkspace}, + } + + err = client.VariableSets.RemoveFromWorkspaces(ctx, vsTest.ID, &removeOptions) + assert.EqualError(t, err, ErrRequiredWorkspaceID.Error()) + }) } func TestVariableSetsUpdateWorkspaces(t *testing.T) { From 0fac7ae180594f9ecd4c2cde694ad2a23ea86f04 Mon Sep 17 00:00:00 2001 From: Byron Wolfman Date: Wed, 13 Apr 2022 17:31:22 -0300 Subject: [PATCH 14/14] fix type; VariableID -> VariableSetID --- variable_set_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variable_set_test.go b/variable_set_test.go index b25903249..e967b0f85 100644 --- a/variable_set_test.go +++ b/variable_set_test.go @@ -261,13 +261,13 @@ func TestVariableSetsApplyToAndRemoveFromWorkspaces(t *testing.T) { } err := client.VariableSets.ApplyToWorkspaces(ctx, badIdentifier, &applyOptions) - assert.EqualError(t, err, ErrInvalidVariableID.Error()) + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) removeOptions := VariableSetRemoveFromWorkspacesOptions{ Workspaces: []*Workspace{wTest1}, } err = client.VariableSets.RemoveFromWorkspaces(ctx, badIdentifier, &removeOptions) - assert.EqualError(t, err, ErrInvalidVariableID.Error()) + assert.EqualError(t, err, ErrInvalidVariableSetID.Error()) }) t.Run("when workspace ID is invalid", func(t *testing.T) {