diff --git a/internal/cloud/backend.go b/internal/cloud/backend.go index 7557e3f79..305bf8861 100644 --- a/internal/cloud/backend.go +++ b/internal/cloud/backend.go @@ -63,12 +63,9 @@ type Cloud struct { // organization is the organization that contains the target workspaces. organization string - // workspace is used to map the default workspace to a TFC workspace. - workspace string - - // prefix is used to filter down a set of workspaces that use a single - // configuration. - prefix string + // workspaceMapping contains strategies for mapping CLI workspaces in the working directory + // to remote Terraform Cloud workspaces. + workspaceMapping workspaceMapping // services is used for service discovery services *disco.Disco @@ -183,6 +180,7 @@ func (b *Cloud) Configure(obj cty.Value) tfdiags.Diagnostics { if obj.IsNull() { return diags } + diagErr := b.setConfigurationFields(obj) if diagErr.HasErrors() { return diagErr @@ -326,10 +324,10 @@ func (b *Cloud) setConfigurationFields(obj cty.Value) tfdiags.Diagnostics { // PrepareConfig checks that you cannot set both of these. if val := workspaces.GetAttr("name"); !val.IsNull() { - b.workspace = val.AsString() + b.workspaceMapping.name = val.AsString() } if val := workspaces.GetAttr("prefix"); !val.IsNull() { - b.prefix = val.AsString() + b.workspaceMapping.prefix = val.AsString() } } @@ -514,7 +512,7 @@ func (b *Cloud) retryLogHook(attemptNum int, resp *http.Response) { // Workspaces implements backend.Enhanced. func (b *Cloud) Workspaces() ([]string, error) { - if b.prefix == "" { + if b.workspaceMapping.prefix == "" { return nil, backend.ErrWorkspacesNotSupported } return b.workspaces() @@ -524,10 +522,10 @@ func (b *Cloud) Workspaces() ([]string, error) { func (b *Cloud) workspaces() ([]string, error) { options := tfe.WorkspaceListOptions{} switch { - case b.workspace != "": - options.Search = tfe.String(b.workspace) - case b.prefix != "": - options.Search = tfe.String(b.prefix) + case b.workspaceMapping.name != "": + options.Search = tfe.String(b.workspaceMapping.name) + case b.workspaceMapping.prefix != "": + options.Search = tfe.String(b.workspaceMapping.prefix) } // Create a slice to contain all the names. @@ -540,12 +538,12 @@ func (b *Cloud) workspaces() ([]string, error) { } for _, w := range wl.Items { - if b.workspace != "" && w.Name == b.workspace { + if b.workspaceMapping.name != "" && w.Name == b.workspaceMapping.name { names = append(names, backend.DefaultStateName) continue } - if b.prefix != "" && strings.HasPrefix(w.Name, b.prefix) { - names = append(names, strings.TrimPrefix(w.Name, b.prefix)) + if b.workspaceMapping.prefix != "" && strings.HasPrefix(w.Name, b.workspaceMapping.prefix) { + names = append(names, strings.TrimPrefix(w.Name, b.workspaceMapping.prefix)) } } @@ -566,19 +564,19 @@ func (b *Cloud) workspaces() ([]string, error) { // DeleteWorkspace implements backend.Enhanced. func (b *Cloud) DeleteWorkspace(name string) error { - if b.workspace == "" && name == backend.DefaultStateName { + if b.workspaceMapping.name == "" && name == backend.DefaultStateName { return backend.ErrDefaultWorkspaceNotSupported } - if b.prefix == "" && name != backend.DefaultStateName { + if b.workspaceMapping.prefix == "" && name != backend.DefaultStateName { return backend.ErrWorkspacesNotSupported } // Configure the remote workspace name. switch { case name == backend.DefaultStateName: - name = b.workspace - case b.prefix != "" && !strings.HasPrefix(name, b.prefix): - name = b.prefix + name + name = b.workspaceMapping.name + case b.workspaceMapping.prefix != "" && !strings.HasPrefix(name, b.workspaceMapping.prefix): + name = b.workspaceMapping.prefix + name } client := &remoteClient{ @@ -594,19 +592,19 @@ func (b *Cloud) DeleteWorkspace(name string) error { // StateMgr implements backend.Enhanced. func (b *Cloud) StateMgr(name string) (statemgr.Full, error) { - if b.workspace == "" && name == backend.DefaultStateName { + if b.workspaceMapping.name == "" && name == backend.DefaultStateName { return nil, backend.ErrDefaultWorkspaceNotSupported } - if b.prefix == "" && name != backend.DefaultStateName { + if b.workspaceMapping.prefix == "" && name != backend.DefaultStateName { return nil, backend.ErrWorkspacesNotSupported } // Configure the remote workspace name. switch { case name == backend.DefaultStateName: - name = b.workspace - case b.prefix != "" && !strings.HasPrefix(name, b.prefix): - name = b.prefix + name + name = b.workspaceMapping.name + case b.workspaceMapping.prefix != "" && !strings.HasPrefix(name, b.workspaceMapping.prefix): + name = b.workspaceMapping.prefix + name } workspace, err := b.client.Workspaces.Read(context.Background(), b.organization, name) @@ -663,9 +661,9 @@ func (b *Cloud) Operation(ctx context.Context, op *backend.Operation) (*backend. name := op.Workspace switch { case op.Workspace == backend.DefaultStateName: - name = b.workspace - case b.prefix != "" && !strings.HasPrefix(op.Workspace, b.prefix): - name = b.prefix + op.Workspace + name = b.workspaceMapping.name + case b.workspaceMapping.prefix != "" && !strings.HasPrefix(op.Workspace, b.workspaceMapping.prefix): + name = b.workspaceMapping.prefix + op.Workspace } // Retrieve the workspace for this operation. @@ -974,6 +972,11 @@ func (b *Cloud) cliColorize() *colorstring.Colorize { } } +type workspaceMapping struct { + name string + prefix string +} + func generalError(msg string, err error) error { var diags tfdiags.Diagnostics diff --git a/internal/cloud/backend_apply_test.go b/internal/cloud/backend_apply_test.go index 1100c31dc..8fa2d9758 100644 --- a/internal/cloud/backend_apply_test.go +++ b/internal/cloud/backend_apply_test.go @@ -143,7 +143,7 @@ func TestCloud_applyWithoutPermissions(t *testing.T) { context.Background(), b.organization, tfe.WorkspaceCreateOptions{ - Name: tfe.String(b.prefix + "prod"), + Name: tfe.String(b.workspaceMapping.prefix + "prod"), }, ) if err != nil { @@ -183,7 +183,7 @@ func TestCloud_applyWithVCS(t *testing.T) { context.Background(), b.organization, tfe.WorkspaceCreateOptions{ - Name: tfe.String(b.prefix + "prod"), + Name: tfe.String(b.workspaceMapping.prefix + "prod"), VCSRepo: &tfe.VCSRepoOptions{}, }, ) @@ -900,7 +900,7 @@ func TestCloud_applyWithAutoApply(t *testing.T) { b.organization, tfe.WorkspaceCreateOptions{ AutoApply: tfe.Bool(true), - Name: tfe.String(b.prefix + "prod"), + Name: tfe.String(b.workspaceMapping.prefix + "prod"), }, ) if err != nil { @@ -1015,7 +1015,7 @@ func TestCloud_applyWorkspaceWithoutOperations(t *testing.T) { ctx, b.organization, tfe.WorkspaceCreateOptions{ - Name: tfe.String(b.prefix + "no-operations"), + Name: tfe.String(b.workspaceMapping.prefix + "no-operations"), }, ) if err != nil { @@ -1074,7 +1074,7 @@ func TestCloud_applyLockTimeout(t *testing.T) { ctx := context.Background() // Retrieve the workspace used to run this operation in. - w, err := b.client.Workspaces.Read(ctx, b.organization, b.workspace) + w, err := b.client.Workspaces.Read(ctx, b.organization, b.workspaceMapping.name) if err != nil { t.Fatalf("error retrieving workspace: %v", err) } @@ -1434,7 +1434,7 @@ func TestCloud_applyPolicySoftFailAutoApply(t *testing.T) { b.organization, tfe.WorkspaceCreateOptions{ AutoApply: tfe.Bool(true), - Name: tfe.String(b.prefix + "prod"), + Name: tfe.String(b.workspaceMapping.prefix + "prod"), }, ) if err != nil { @@ -1583,7 +1583,7 @@ func TestCloud_applyVersionCheck(t *testing.T) { _, err := b.client.Workspaces.Update( ctx, b.organization, - b.workspace, + b.workspaceMapping.name, tfe.WorkspaceUpdateOptions{ Operations: tfe.Bool(tc.hasOperations), TerraformVersion: tfe.String(tc.remoteVersion), diff --git a/internal/cloud/backend_context.go b/internal/cloud/backend_context.go index a564a6d3a..32a1e52a2 100644 --- a/internal/cloud/backend_context.go +++ b/internal/cloud/backend_context.go @@ -143,9 +143,9 @@ func (b *Cloud) getRemoteWorkspaceName(localWorkspaceName string) string { // The default workspace name is a special case, for when the backend // is configured to with to an exact remote workspace rather than with // a remote workspace _prefix_. - return b.workspace - case b.prefix != "" && !strings.HasPrefix(localWorkspaceName, b.prefix): - return b.prefix + localWorkspaceName + return b.workspaceMapping.name + case b.workspaceMapping.prefix != "" && !strings.HasPrefix(localWorkspaceName, b.workspaceMapping.prefix): + return b.workspaceMapping.prefix + localWorkspaceName default: return localWorkspaceName } diff --git a/internal/cloud/backend_plan_test.go b/internal/cloud/backend_plan_test.go index cd836b46d..45c1958b2 100644 --- a/internal/cloud/backend_plan_test.go +++ b/internal/cloud/backend_plan_test.go @@ -161,7 +161,7 @@ func TestCloud_planWithoutPermissions(t *testing.T) { context.Background(), b.organization, tfe.WorkspaceCreateOptions{ - Name: tfe.String(b.prefix + "prod"), + Name: tfe.String(b.workspaceMapping.prefix + "prod"), }, ) if err != nil { @@ -772,7 +772,7 @@ func TestCloud_planWorkspaceWithoutOperations(t *testing.T) { ctx, b.organization, tfe.WorkspaceCreateOptions{ - Name: tfe.String(b.prefix + "no-operations"), + Name: tfe.String(b.workspaceMapping.prefix + "no-operations"), }, ) if err != nil { @@ -818,7 +818,7 @@ func TestCloud_planLockTimeout(t *testing.T) { ctx := context.Background() // Retrieve the workspace used to run this operation in. - w, err := b.client.Workspaces.Read(ctx, b.organization, b.workspace) + w, err := b.client.Workspaces.Read(ctx, b.organization, b.workspaceMapping.name) if err != nil { t.Fatalf("error retrieving workspace: %v", err) } @@ -941,7 +941,7 @@ func TestCloud_planWithWorkingDirectory(t *testing.T) { } // Configure the workspace to use a custom working directory. - _, err := b.client.Workspaces.Update(context.Background(), b.organization, b.workspace, options) + _, err := b.client.Workspaces.Update(context.Background(), b.organization, b.workspaceMapping.name, options) if err != nil { t.Fatalf("error configuring working directory: %v", err) } @@ -986,7 +986,7 @@ func TestCloud_planWithWorkingDirectoryFromCurrentPath(t *testing.T) { } // Configure the workspace to use a custom working directory. - _, err := b.client.Workspaces.Update(context.Background(), b.organization, b.workspace, options) + _, err := b.client.Workspaces.Update(context.Background(), b.organization, b.workspaceMapping.name, options) if err != nil { t.Fatalf("error configuring working directory: %v", err) } diff --git a/internal/cloud/backend_test.go b/internal/cloud/backend_test.go index 7f02f08e8..cc45a3538 100644 --- a/internal/cloud/backend_test.go +++ b/internal/cloud/backend_test.go @@ -664,7 +664,7 @@ func TestCloud_StateMgr_versionCheck(t *testing.T) { if _, err := b.client.Workspaces.Update( context.Background(), b.organization, - b.workspace, + b.workspaceMapping.name, tfe.WorkspaceUpdateOptions{ TerraformVersion: tfe.String(v0140.String()), }, @@ -681,7 +681,7 @@ func TestCloud_StateMgr_versionCheck(t *testing.T) { if _, err := b.client.Workspaces.Update( context.Background(), b.organization, - b.workspace, + b.workspaceMapping.name, tfe.WorkspaceUpdateOptions{ TerraformVersion: tfe.String(v0135.String()), }, @@ -721,7 +721,7 @@ func TestCloud_StateMgr_versionCheckLatest(t *testing.T) { if _, err := b.client.Workspaces.Update( context.Background(), b.organization, - b.workspace, + b.workspaceMapping.name, tfe.WorkspaceUpdateOptions{ TerraformVersion: tfe.String("latest"), }, @@ -779,7 +779,7 @@ func TestCloud_VerifyWorkspaceTerraformVersion(t *testing.T) { if _, err := b.client.Workspaces.Update( context.Background(), b.organization, - b.workspace, + b.workspaceMapping.name, tfe.WorkspaceUpdateOptions{ Operations: tfe.Bool(tc.operations), TerraformVersion: tfe.String(tc.remote), @@ -830,7 +830,7 @@ func TestCloud_VerifyWorkspaceTerraformVersion_workspaceErrors(t *testing.T) { if _, err := b.client.Workspaces.Update( context.Background(), b.organization, - b.workspace, + b.workspaceMapping.name, tfe.WorkspaceUpdateOptions{ TerraformVersion: tfe.String("1.0.cheetarah"), }, @@ -878,7 +878,7 @@ func TestCloud_VerifyWorkspaceTerraformVersion_ignoreFlagSet(t *testing.T) { if _, err := b.client.Workspaces.Update( context.Background(), b.organization, - b.workspace, + b.workspaceMapping.name, tfe.WorkspaceUpdateOptions{ TerraformVersion: tfe.String(remote.String()), }, diff --git a/internal/cloud/testing.go b/internal/cloud/testing.go index 7bb145b3c..cfc8d3dce 100644 --- a/internal/cloud/testing.go +++ b/internal/cloud/testing.go @@ -161,9 +161,9 @@ func testBackend(t *testing.T, obj cty.Value) (*Cloud, func()) { } // Create the default workspace if required. - if b.workspace != "" { + if b.workspaceMapping.name != "" { _, err = b.client.Workspaces.Create(ctx, b.organization, tfe.WorkspaceCreateOptions{ - Name: tfe.String(b.workspace), + Name: tfe.String(b.workspaceMapping.name), }) if err != nil { t.Fatalf("error: %v", err)