cloud: Add tags to workspace if necessary when fetching state
This commit is contained in:
parent
12651c0a81
commit
1da7031855
|
@ -529,15 +529,9 @@ func (b *Cloud) StateMgr(name string) (statemgr.Full, error) {
|
||||||
// Create a workspace
|
// Create a workspace
|
||||||
options := tfe.WorkspaceCreateOptions{
|
options := tfe.WorkspaceCreateOptions{
|
||||||
Name: tfe.String(name),
|
Name: tfe.String(name),
|
||||||
|
Tags: b.WorkspaceMapping.tfeTags(),
|
||||||
}
|
}
|
||||||
|
|
||||||
var tags []*tfe.Tag
|
|
||||||
for _, tag := range b.WorkspaceMapping.Tags {
|
|
||||||
t := tfe.Tag{Name: tag}
|
|
||||||
tags = append(tags, &t)
|
|
||||||
}
|
|
||||||
options.Tags = tags
|
|
||||||
|
|
||||||
log.Printf("[TRACE] cloud: Creating Terraform Cloud workspace %s/%s", b.organization, name)
|
log.Printf("[TRACE] cloud: Creating Terraform Cloud workspace %s/%s", b.organization, name)
|
||||||
workspace, err = b.client.Workspaces.Create(context.Background(), b.organization, options)
|
workspace, err = b.client.Workspaces.Create(context.Background(), b.organization, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -569,6 +563,17 @@ func (b *Cloud) StateMgr(name string) (statemgr.Full, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.workspaceTagsRequireUpdate(workspace, b.WorkspaceMapping) {
|
||||||
|
options := tfe.WorkspaceAddTagsOptions{
|
||||||
|
Tags: b.WorkspaceMapping.tfeTags(),
|
||||||
|
}
|
||||||
|
log.Printf("[TRACE] cloud: Adding tags for Terraform Cloud workspace %s/%s", b.organization, name)
|
||||||
|
err = b.client.Workspaces.AddTags(context.Background(), workspace.ID, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error updating workspace %s: %v", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is a fallback error check. Most code paths should use other
|
// This is a fallback error check. Most code paths should use other
|
||||||
// mechanisms to check the version, then set the ignoreVersionConflict
|
// mechanisms to check the version, then set the ignoreVersionConflict
|
||||||
// field to true. This check is only in place to ensure that we don't
|
// field to true. This check is only in place to ensure that we don't
|
||||||
|
@ -913,6 +918,25 @@ func (b *Cloud) cliColorize() *colorstring.Colorize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Cloud) workspaceTagsRequireUpdate(workspace *tfe.Workspace, workspaceMapping WorkspaceMapping) bool {
|
||||||
|
if workspaceMapping.Strategy() != WorkspaceTagsStrategy {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
existingTags := map[string]struct{}{}
|
||||||
|
for _, t := range workspace.TagNames {
|
||||||
|
existingTags[t] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range workspaceMapping.Tags {
|
||||||
|
if _, ok := existingTags[tag]; !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type WorkspaceMapping struct {
|
type WorkspaceMapping struct {
|
||||||
Name string
|
Name string
|
||||||
Tags []string
|
Tags []string
|
||||||
|
@ -941,6 +965,21 @@ func (wm WorkspaceMapping) Strategy() workspaceStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wm WorkspaceMapping) tfeTags() []*tfe.Tag {
|
||||||
|
var tags []*tfe.Tag
|
||||||
|
|
||||||
|
if wm.Strategy() != WorkspaceTagsStrategy {
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range wm.Tags {
|
||||||
|
t := tfe.Tag{Name: tag}
|
||||||
|
tags = append(tags, &t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
func generalError(msg string, err error) error {
|
func generalError(msg string, err error) error {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
expect "github.com/Netflix/go-expect"
|
expect "github.com/Netflix/go-expect"
|
||||||
tfe "github.com/hashicorp/go-tfe"
|
tfe "github.com/hashicorp/go-tfe"
|
||||||
"github.com/hashicorp/terraform/internal/e2e"
|
"github.com/hashicorp/terraform/internal/e2e"
|
||||||
|
tfversion "github.com/hashicorp/terraform/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
|
func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
|
||||||
|
@ -360,6 +361,108 @@ func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"migrating multiple workspaces to cloud using tags strategy; existing workspaces": {
|
||||||
|
operations: []operationSets{
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
tfBlock := terraformConfigLocalBackend()
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init"},
|
||||||
|
expectedCmdOutput: `Successfully configured the backend "local"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply", "-auto-approve"},
|
||||||
|
postInputOutput: []string{`Apply complete!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "new", "identity"},
|
||||||
|
expectedCmdOutput: `Created and switched to workspace "identity"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply", "-auto-approve"},
|
||||||
|
postInputOutput: []string{`Apply complete!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "new", "billing"},
|
||||||
|
expectedCmdOutput: `Created and switched to workspace "billing"!`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"apply", "-auto-approve"},
|
||||||
|
postInputOutput: []string{`Apply complete!`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "select", "default"},
|
||||||
|
expectedCmdOutput: `Switched to workspace "default".`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prep: func(t *testing.T, orgName, dir string) {
|
||||||
|
tag := "app"
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("identity"),
|
||||||
|
TerraformVersion: tfe.String(tfversion.String()),
|
||||||
|
})
|
||||||
|
_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
|
||||||
|
Name: tfe.String("billing"),
|
||||||
|
TerraformVersion: tfe.String(tfversion.String()),
|
||||||
|
})
|
||||||
|
tfBlock := terraformConfigCloudBackendTags(orgName, tag)
|
||||||
|
writeMainTF(t, tfBlock, dir)
|
||||||
|
},
|
||||||
|
commands: []tfCommand{
|
||||||
|
{
|
||||||
|
command: []string{"init", "-migrate-state"},
|
||||||
|
expectedCmdOutput: `Terraform Cloud requires all workspaces to be given an explicit name.`,
|
||||||
|
userInput: []string{"dev", "1", "app-*", "1"},
|
||||||
|
postInputOutput: []string{
|
||||||
|
`Would you like to rename your workspaces?`,
|
||||||
|
"What pattern would you like to add to all your workspaces?",
|
||||||
|
"The currently selected workspace (default) does not exist.",
|
||||||
|
"Terraform Cloud has been successfully initialized!"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "select", "app-dev"},
|
||||||
|
expectedCmdOutput: `Switched to workspace "app-dev".`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "select", "app-billing"},
|
||||||
|
expectedCmdOutput: `Switched to workspace "app-billing".`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: []string{"workspace", "select", "app-identity"},
|
||||||
|
expectedCmdOutput: `Switched to workspace "app-identity".`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validations: func(t *testing.T, orgName string) {
|
||||||
|
wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{
|
||||||
|
Tags: tfe.String("app"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(wsList.Items) != 3 {
|
||||||
|
t.Fatalf("Expected the number of workspaecs to be 3, but got %d", len(wsList.Items))
|
||||||
|
}
|
||||||
|
expectedWorkspaceNames := []string{"app-billing", "app-dev", "app-identity"}
|
||||||
|
for _, ws := range wsList.Items {
|
||||||
|
hasName := false
|
||||||
|
for _, expectedNames := range expectedWorkspaceNames {
|
||||||
|
if expectedNames == ws.Name {
|
||||||
|
hasName = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasName {
|
||||||
|
t.Fatalf("Worksapce %s is not in the expected list of workspaces", ws.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
|
|
|
@ -1391,7 +1391,7 @@ func (m *MockWorkspaces) Tags(ctx context.Context, workspaceID string, options t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockWorkspaces) AddTags(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagsOptions) error {
|
func (m *MockWorkspaces) AddTags(ctx context.Context, workspaceID string, options tfe.WorkspaceAddTagsOptions) error {
|
||||||
panic("not implemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockWorkspaces) RemoveTags(ctx context.Context, workspaceID string, options tfe.WorkspaceRemoveTagsOptions) error {
|
func (m *MockWorkspaces) RemoveTags(ctx context.Context, workspaceID string, options tfe.WorkspaceRemoveTagsOptions) error {
|
||||||
|
|
Loading…
Reference in New Issue