Map CLI workspaces by TFC tags
This commit is contained in:
parent
7a243379fb
commit
6dcd0db265
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/mitchellh/cli"
|
||||
"github.com/mitchellh/colorstring"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
|
||||
backendLocal "github.com/hashicorp/terraform/internal/backend/local"
|
||||
)
|
||||
|
@ -104,17 +105,17 @@ func (b *Cloud) ConfigSchema() *configschema.Block {
|
|||
"hostname": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
Description: schemaDescriptions["hostname"],
|
||||
Description: schemaDescriptionHostname,
|
||||
},
|
||||
"organization": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
Description: schemaDescriptions["organization"],
|
||||
Description: schemaDescriptionOrganization,
|
||||
},
|
||||
"token": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
Description: schemaDescriptions["token"],
|
||||
Description: schemaDescriptionToken,
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -125,12 +126,17 @@ func (b *Cloud) ConfigSchema() *configschema.Block {
|
|||
"name": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
Description: schemaDescriptions["name"],
|
||||
Description: schemaDescriptionName,
|
||||
},
|
||||
"prefix": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
Description: schemaDescriptions["prefix"],
|
||||
Description: schemaDescriptionPrefix,
|
||||
},
|
||||
"tags": {
|
||||
Type: cty.Set(cty.String),
|
||||
Optional: true,
|
||||
Description: schemaDescriptionTags,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -159,6 +165,12 @@ func (b *Cloud) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
|||
if val := workspaces.GetAttr("prefix"); !val.IsNull() {
|
||||
workspaceMapping.prefix = val.AsString()
|
||||
}
|
||||
if val := workspaces.GetAttr("tags"); !val.IsNull() {
|
||||
err := gocty.FromCtyValue(val, &workspaceMapping.tags)
|
||||
if err != nil {
|
||||
log.Panicf("An unxpected error occurred: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch workspaceMapping.strategy() {
|
||||
|
@ -328,6 +340,15 @@ func (b *Cloud) setConfigurationFields(obj cty.Value) tfdiags.Diagnostics {
|
|||
if val := workspaces.GetAttr("prefix"); !val.IsNull() {
|
||||
b.workspaceMapping.prefix = val.AsString()
|
||||
}
|
||||
if val := workspaces.GetAttr("tags"); !val.IsNull() {
|
||||
var tags []string
|
||||
err := gocty.FromCtyValue(val, &tags)
|
||||
if err != nil {
|
||||
log.Panicf("An unxpected error occurred: %s", err)
|
||||
}
|
||||
|
||||
b.workspaceMapping.tags = tags
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we are forced to use the local backend.
|
||||
|
@ -526,6 +547,9 @@ func (b *Cloud) workspaces() ([]string, error) {
|
|||
options.Search = tfe.String(b.workspaceMapping.name)
|
||||
case workspacePrefixStrategy:
|
||||
options.Search = tfe.String(b.workspaceMapping.prefix)
|
||||
case workspaceTagsStrategy:
|
||||
taglist := strings.Join(b.workspaceMapping.tags, ",")
|
||||
options.Tags = &taglist
|
||||
}
|
||||
|
||||
// Create a slice to contain all the names.
|
||||
|
@ -551,7 +575,7 @@ func (b *Cloud) workspaces() ([]string, error) {
|
|||
}
|
||||
default:
|
||||
// Pass-through. "name" and "prefix" strategies are naive and do
|
||||
// client-side filtering above, but for any other future
|
||||
// client-side filtering above, but for tags and any other future
|
||||
// strategy this filtering should be left to the API.
|
||||
names = append(names, w.Name)
|
||||
}
|
||||
|
@ -627,6 +651,13 @@ func (b *Cloud) StateMgr(name string) (statemgr.Full, error) {
|
|||
Name: tfe.String(name),
|
||||
}
|
||||
|
||||
var tags []*tfe.Tag
|
||||
for _, tag := range b.workspaceMapping.tags {
|
||||
t := tfe.Tag{Name: tag}
|
||||
tags = append(tags, &t)
|
||||
}
|
||||
options.Tags = tags
|
||||
|
||||
// We only set the Terraform Version for the new workspace if this is
|
||||
// a release candidate or a final release.
|
||||
if tfversion.Prerelease == "" || strings.HasPrefix(tfversion.Prerelease, "rc") {
|
||||
|
@ -985,11 +1016,13 @@ func (b *Cloud) cliColorize() *colorstring.Colorize {
|
|||
type workspaceMapping struct {
|
||||
name string
|
||||
prefix string
|
||||
tags []string
|
||||
}
|
||||
|
||||
type workspaceStrategy string
|
||||
|
||||
const (
|
||||
workspaceTagsStrategy workspaceStrategy = "tags"
|
||||
workspaceNameStrategy workspaceStrategy = "name"
|
||||
workspacePrefixStrategy workspaceStrategy = "prefix"
|
||||
workspaceNoneStrategy workspaceStrategy = "none"
|
||||
|
@ -998,11 +1031,13 @@ const (
|
|||
|
||||
func (wm workspaceMapping) strategy() workspaceStrategy {
|
||||
switch {
|
||||
case wm.name != "" && wm.prefix == "":
|
||||
case len(wm.tags) > 0 && wm.name == "" && wm.prefix == "":
|
||||
return workspaceTagsStrategy
|
||||
case len(wm.tags) == 0 && wm.name != "" && wm.prefix == "":
|
||||
return workspaceNameStrategy
|
||||
case wm.name == "" && wm.prefix != "":
|
||||
case len(wm.tags) == 0 && wm.name == "" && wm.prefix != "":
|
||||
return workspacePrefixStrategy
|
||||
case wm.name == "" && wm.prefix == "":
|
||||
case len(wm.tags) == 0 && wm.name == "" && wm.prefix == "":
|
||||
return workspaceNoneStrategy
|
||||
default:
|
||||
// Any other combination is invalid as each strategy is mutually exclusive
|
||||
|
@ -1070,22 +1105,33 @@ const operationNotCanceled = `
|
|||
[reset][red]The remote operation was not cancelled.[reset]
|
||||
`
|
||||
|
||||
var schemaDescriptions = map[string]string{
|
||||
"hostname": "The Terraform Enterprise hostname to connect to. This optional argument defaults to app.terraform.io for use with Terraform Cloud.",
|
||||
"organization": "The name of the organization containing the targeted workspace(s).",
|
||||
"token": "The token used to authenticate with Terraform Cloud/Enterprise. Typically this argument should not be set,\n" +
|
||||
"and 'terraform login' used instead; your credentials will then be fetched from your CLI configuration file or configured credential helper.",
|
||||
"name": "The name of a single Terraform Cloud workspace to be used with this configuration.\n" +
|
||||
"When configured only the specified workspace can be used. This option conflicts\n" +
|
||||
"with \"prefix\".",
|
||||
"prefix": "A name prefix used to select remote Terraform Cloud workspaces to be used for this\n" +
|
||||
"single configuration. New workspaces will automatically be prefixed with this prefix. This option conflicts with \"name\".",
|
||||
}
|
||||
var (
|
||||
workspaceConfigurationHelp = fmt.Sprintf(
|
||||
`The 'workspaces' block configures how Terraform CLI maps its workspaces for this single
|
||||
configuration to workspaces within a Terraform Cloud organization. Three strategies are available:
|
||||
|
||||
var workspaceConfigurationHelp = fmt.Sprintf(`The 'workspaces' block configures how Terraform CLI maps its workspaces for this
|
||||
single configuration to workspaces within a Terraform Cloud organization. Two strategies are available:
|
||||
[bold]tags[reset] - %s
|
||||
|
||||
[bold]name[reset] - %s
|
||||
|
||||
[bold]prefix[reset] - %s
|
||||
`, schemaDescriptions["name"], schemaDescriptions["prefix"])
|
||||
[bold]prefix[reset] - %s`, schemaDescriptionTags, schemaDescriptionName, schemaDescriptionPrefix)
|
||||
|
||||
schemaDescriptionHostname = `The Terraform Enterprise hostname to connect to. This optional argument defaults to app.terraform.io
|
||||
for use with Terraform Cloud.`
|
||||
|
||||
schemaDescriptionOrganization = `The name of the organization containing the targeted workspace(s).`
|
||||
|
||||
schemaDescriptionToken = `The token used to authenticate with Terraform Cloud/Enterprise. Typically this argument should not
|
||||
be set, and 'terraform login' used instead; your credentials will then be fetched from your CLI
|
||||
configuration file or configured credential helper.`
|
||||
|
||||
schemaDescriptionTags = `A set of tags used to select remote Terraform Cloud workspaces to be used for this single
|
||||
configuration. New workspaces will automatically be tagged with these tag values. Generally, this
|
||||
is the primary and recommended strategy to use. This option conflicts with "prefix" and "name".`
|
||||
|
||||
schemaDescriptionName = `The name of a single Terraform Cloud workspace to be used with this configuration When configured
|
||||
only the specified workspace can be used. This option conflicts with "tags" and "prefix".`
|
||||
|
||||
schemaDescriptionPrefix = `DEPRECATED. A name prefix used to select remote Terraform Cloud to be used for this single configuration. New
|
||||
workspaces will automatically be prefixed with this prefix. This option conflicts with "tags" and "name".`
|
||||
)
|
||||
|
|
|
@ -51,6 +51,7 @@ func TestCloud_PrepareConfig(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedErr: `Invalid organization value: The "organization" attribute value must not be empty.`,
|
||||
|
@ -60,17 +61,18 @@ func TestCloud_PrepareConfig(t *testing.T) {
|
|||
"organization": cty.StringVal("org"),
|
||||
"workspaces": cty.NullVal(cty.String),
|
||||
}),
|
||||
expectedErr: `Invalid workspaces configuration: Either workspace "name" or "prefix" is required.`,
|
||||
expectedErr: `Invalid workspaces configuration: Missing workspace mapping strategy. Either workspace "tags", "name", or "prefix" is required.`,
|
||||
},
|
||||
"workspace: empty name and empty prefix": {
|
||||
"workspace: empty tags, name, and prefix": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"organization": cty.StringVal("org"),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedErr: `Invalid workspaces configuration: Either workspace "name" or "prefix" is required.`,
|
||||
expectedErr: `Invalid workspaces configuration: Missing workspace mapping strategy. Either workspace "tags", "name", or "prefix" is required.`,
|
||||
},
|
||||
"workspace: name and prefix present": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
|
@ -78,9 +80,25 @@ func TestCloud_PrepareConfig(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.StringVal("app-"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedErr: `Invalid workspaces configuration: Only one of workspace "name" or "prefix" is allowed.`,
|
||||
expectedErr: `Invalid workspaces configuration: Only one of workspace "tags", "name", or "prefix" is allowed.`,
|
||||
},
|
||||
"workspace: name and tags present": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"organization": cty.StringVal("org"),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.SetVal(
|
||||
[]cty.Value{
|
||||
cty.StringVal("billing"),
|
||||
},
|
||||
),
|
||||
}),
|
||||
}),
|
||||
expectedErr: `Invalid workspaces configuration: Only one of workspace "tags", "name", or "prefix" is allowed.`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -113,6 +131,7 @@ func TestCloud_config(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
confErr: "organization \"nonexisting\" at host app.terraform.io not found",
|
||||
|
@ -125,6 +144,7 @@ func TestCloud_config(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
confErr: "Failed to request discovery document",
|
||||
|
@ -138,10 +158,27 @@ func TestCloud_config(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
confErr: "terraform login localhost",
|
||||
},
|
||||
"with_tags": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"hostname": cty.NullVal(cty.String),
|
||||
"organization": cty.StringVal("hashicorp"),
|
||||
"token": cty.NullVal(cty.String),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.SetVal(
|
||||
[]cty.Value{
|
||||
cty.StringVal("billing"),
|
||||
},
|
||||
),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
"with_a_name": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"hostname": cty.NullVal(cty.String),
|
||||
|
@ -150,6 +187,7 @@ func TestCloud_config(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
|
@ -161,10 +199,11 @@ func TestCloud_config(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.StringVal("my-app-"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
"without_either_a_name_and_a_prefix": {
|
||||
"without_a_name_prefix_or_tags": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"hostname": cty.NullVal(cty.String),
|
||||
"organization": cty.StringVal("hashicorp"),
|
||||
|
@ -172,9 +211,10 @@ func TestCloud_config(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
valErr: `Either workspace "name" or "prefix" is required`,
|
||||
valErr: `Missing workspace mapping strategy.`,
|
||||
},
|
||||
"with_both_a_name_and_a_prefix": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
|
@ -184,9 +224,27 @@ func TestCloud_config(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.StringVal("my-app-"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
valErr: `Only one of workspace "name" or "prefix" is allowed`,
|
||||
valErr: `Only one of workspace "tags", "name", or "prefix" is allowed.`,
|
||||
},
|
||||
"with_both_a_name_and_tags": {
|
||||
config: cty.ObjectVal(map[string]cty.Value{
|
||||
"hostname": cty.NullVal(cty.String),
|
||||
"organization": cty.StringVal("hashicorp"),
|
||||
"token": cty.NullVal(cty.String),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.SetVal(
|
||||
[]cty.Value{
|
||||
cty.StringVal("billing"),
|
||||
},
|
||||
),
|
||||
}),
|
||||
}),
|
||||
valErr: `Only one of workspace "tags", "name", or "prefix" is allowed.`,
|
||||
},
|
||||
"null config": {
|
||||
config: cty.NullVal(cty.EmptyObject),
|
||||
|
@ -222,6 +280,7 @@ func TestCloud_setConfigurationFields(t *testing.T) {
|
|||
expectedOrganziation string
|
||||
expectedWorkspacePrefix string
|
||||
expectedWorkspaceName string
|
||||
expectedWorkspaceTags []string
|
||||
expectedForceLocal bool
|
||||
setEnv func()
|
||||
resetEnv func()
|
||||
|
@ -234,6 +293,7 @@ func TestCloud_setConfigurationFields(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedHostname: "hashicorp.com",
|
||||
|
@ -246,6 +306,7 @@ func TestCloud_setConfigurationFields(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedHostname: defaultHostname,
|
||||
|
@ -258,6 +319,7 @@ func TestCloud_setConfigurationFields(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedHostname: "hashicorp.com",
|
||||
|
@ -271,12 +333,31 @@ func TestCloud_setConfigurationFields(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.StringVal("prod"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedHostname: "hashicorp.com",
|
||||
expectedOrganziation: "hashicorp",
|
||||
expectedWorkspacePrefix: "prod",
|
||||
},
|
||||
"with workspace tags set": {
|
||||
obj: cty.ObjectVal(map[string]cty.Value{
|
||||
"organization": cty.StringVal("hashicorp"),
|
||||
"hostname": cty.StringVal("hashicorp.com"),
|
||||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.SetVal(
|
||||
[]cty.Value{
|
||||
cty.StringVal("billing"),
|
||||
},
|
||||
),
|
||||
}),
|
||||
}),
|
||||
expectedHostname: "hashicorp.com",
|
||||
expectedOrganziation: "hashicorp",
|
||||
expectedWorkspaceTags: []string{"billing"},
|
||||
},
|
||||
"with force local set": {
|
||||
obj: cty.ObjectVal(map[string]cty.Value{
|
||||
"organization": cty.StringVal("hashicorp"),
|
||||
|
@ -284,6 +365,7 @@ func TestCloud_setConfigurationFields(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.StringVal("prod"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
expectedHostname: "hashicorp.com",
|
||||
|
@ -317,16 +399,51 @@ func TestCloud_setConfigurationFields(t *testing.T) {
|
|||
}
|
||||
|
||||
if tc.expectedHostname != "" && b.hostname != tc.expectedHostname {
|
||||
t.Fatalf("%s: expected hostname %s to match actual hostname %s", name, tc.expectedHostname, b.hostname)
|
||||
t.Fatalf("%s: expected hostname %s to match configured hostname %s", name, b.hostname, tc.expectedHostname)
|
||||
}
|
||||
if tc.expectedOrganziation != "" && b.organization != tc.expectedOrganziation {
|
||||
t.Fatalf("%s: expected organization %s to match actual organization %s", name, tc.expectedOrganziation, b.organization)
|
||||
t.Fatalf("%s: expected organization (%s) to match configured organization (%s)", name, b.organization, tc.expectedOrganziation)
|
||||
}
|
||||
if tc.expectedWorkspacePrefix != "" && b.workspaceMapping.prefix != tc.expectedWorkspacePrefix {
|
||||
t.Fatalf("%s: expected workspace prefix %s to match actual workspace prefix %s", name, tc.expectedWorkspacePrefix, b.workspaceMapping.prefix)
|
||||
t.Fatalf("%s: expected workspace prefix mapping (%s) to match configured workspace prefix (%s)", name, b.workspaceMapping.prefix, tc.expectedWorkspacePrefix)
|
||||
}
|
||||
if tc.expectedWorkspaceName != "" && b.workspaceMapping.name != tc.expectedWorkspaceName {
|
||||
t.Fatalf("%s: expected workspace name %s to match actual workspace name %s", name, tc.expectedWorkspaceName, b.workspaceMapping.name)
|
||||
t.Fatalf("%s: expected workspace name mapping (%s) to match configured workspace name (%s)", name, b.workspaceMapping.name, tc.expectedWorkspaceName)
|
||||
}
|
||||
if len(tc.expectedWorkspaceTags) > 0 {
|
||||
presentSet := make(map[string]struct{})
|
||||
for _, tag := range b.workspaceMapping.tags {
|
||||
presentSet[tag] = struct{}{}
|
||||
}
|
||||
|
||||
expectedSet := make(map[string]struct{})
|
||||
for _, tag := range tc.expectedWorkspaceTags {
|
||||
expectedSet[tag] = struct{}{}
|
||||
}
|
||||
|
||||
var missing []string
|
||||
var unexpected []string
|
||||
|
||||
for _, expected := range tc.expectedWorkspaceTags {
|
||||
if _, ok := presentSet[expected]; !ok {
|
||||
missing = append(missing, expected)
|
||||
}
|
||||
}
|
||||
|
||||
for _, actual := range b.workspaceMapping.tags {
|
||||
if _, ok := expectedSet[actual]; !ok {
|
||||
unexpected = append(missing, actual)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
t.Fatalf("%s: expected workspace tag mapping (%s) to contain the following tags: %s", name, b.workspaceMapping.tags, missing)
|
||||
}
|
||||
|
||||
if len(unexpected) > 0 {
|
||||
t.Fatalf("%s: expected workspace tag mapping (%s) to NOT contain the following tags: %s", name, b.workspaceMapping.tags, unexpected)
|
||||
}
|
||||
|
||||
}
|
||||
if tc.expectedForceLocal != false && b.forceLocal != tc.expectedForceLocal {
|
||||
t.Fatalf("%s: expected force local backend to be set ", name)
|
||||
|
@ -349,6 +466,7 @@ func TestCloud_versionConstraints(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
version: "0.11.1",
|
||||
|
@ -361,6 +479,7 @@ func TestCloud_versionConstraints(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
version: "0.0.1",
|
||||
|
@ -374,6 +493,7 @@ func TestCloud_versionConstraints(t *testing.T) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
}),
|
||||
version: "10.0.1",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
@ -9,21 +11,21 @@ var (
|
|||
invalidOrganizationConfigMissingValue = tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Invalid organization value",
|
||||
`The "organization" attribute value must not be empty.`,
|
||||
`The "organization" attribute value must not be empty.\n\n%s`,
|
||||
cty.Path{cty.GetAttrStep{Name: "organization"}},
|
||||
)
|
||||
|
||||
invalidWorkspaceConfigMissingValues = tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Invalid workspaces configuration",
|
||||
`Either workspace "name" or "prefix" is required.`,
|
||||
fmt.Sprintf("Missing workspace mapping strategy. Either workspace \"tags\", \"name\", or \"prefix\" is required.\n\n%s", workspaceConfigurationHelp),
|
||||
cty.Path{cty.GetAttrStep{Name: "workspaces"}},
|
||||
)
|
||||
|
||||
invalidWorkspaceConfigMisconfiguration = tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Invalid workspaces configuration",
|
||||
`Only one of workspace "name" or "prefix" is allowed.`,
|
||||
fmt.Sprintf("Only one of workspace \"tags\", \"name\", or \"prefix\" is allowed.\n\n%s", workspaceConfigurationHelp),
|
||||
cty.Path{cty.GetAttrStep{Name: "workspaces"}},
|
||||
)
|
||||
)
|
||||
|
|
|
@ -72,6 +72,7 @@ func testBackendDefault(t *testing.T) (*Cloud, func()) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
})
|
||||
return testBackend(t, obj)
|
||||
|
@ -85,6 +86,7 @@ func testBackendNoDefault(t *testing.T) (*Cloud, func()) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.NullVal(cty.String),
|
||||
"prefix": cty.StringVal("my-app-"),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
})
|
||||
return testBackend(t, obj)
|
||||
|
@ -98,6 +100,7 @@ func testBackendNoOperations(t *testing.T) (*Cloud, func()) {
|
|||
"workspaces": cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("prod"),
|
||||
"prefix": cty.NullVal(cty.String),
|
||||
"tags": cty.NullVal(cty.Set(cty.String)),
|
||||
}),
|
||||
})
|
||||
return testBackend(t, obj)
|
||||
|
|
Loading…
Reference in New Issue