diff --git a/internal/cloud/backend.go b/internal/cloud/backend.go index 2a0549689..6fd6e6ace 100644 --- a/internal/cloud/backend.go +++ b/internal/cloud/backend.go @@ -151,12 +151,7 @@ func (b *Cloud) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) { } if val := obj.GetAttr("organization"); val.IsNull() || val.AsString() == "" { - diags = diags.Append(tfdiags.AttributeValue( - tfdiags.Error, - "Invalid organization value", - `The "organization" attribute value must not be empty.`, - cty.Path{cty.GetAttrStep{Name: "organization"}}, - )) + diags = diags.Append(invalidOrganizationConfigMissingValue) } var name, prefix string @@ -171,22 +166,12 @@ func (b *Cloud) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) { // Make sure that we have either a workspace name or a prefix. if name == "" && prefix == "" { - diags = diags.Append(tfdiags.AttributeValue( - tfdiags.Error, - "Invalid workspaces configuration", - `Either workspace "name" or "prefix" is required.`, - cty.Path{cty.GetAttrStep{Name: "workspaces"}}, - )) + diags = diags.Append(invalidWorkspaceConfigMissingValues) } // Make sure that only one of workspace name or a prefix is configured. if name != "" && prefix != "" { - diags = diags.Append(tfdiags.AttributeValue( - tfdiags.Error, - "Invalid workspaces configuration", - `Only one of workspace "name" or "prefix" is allowed.`, - cty.Path{cty.GetAttrStep{Name: "workspaces"}}, - )) + diags = diags.Append(invalidWorkspaceConfigMisconfiguration) } return obj, diags diff --git a/internal/cloud/backend_test.go b/internal/cloud/backend_test.go index 7862ad0a3..66c3f5bef 100644 --- a/internal/cloud/backend_test.go +++ b/internal/cloud/backend_test.go @@ -39,6 +39,65 @@ func TestCloud_backendNoDefault(t *testing.T) { backend.TestBackendStates(t, b) } +func TestCloud_PrepareConfig(t *testing.T) { + cases := map[string]struct { + config cty.Value + expectedErr string + }{ + "null organization": { + config: cty.ObjectVal(map[string]cty.Value{ + "organization": cty.NullVal(cty.String), + "workspaces": cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("prod"), + "prefix": cty.NullVal(cty.String), + }), + }), + expectedErr: `Invalid organization value: The "organization" attribute value must not be empty.`, + }, + "null workspace": { + config: cty.ObjectVal(map[string]cty.Value{ + "organization": cty.StringVal("org"), + "workspaces": cty.NullVal(cty.String), + }), + expectedErr: `Invalid workspaces configuration: Either workspace "name" or "prefix" is required.`, + }, + "workspace: empty name and empty 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), + }), + }), + expectedErr: `Invalid workspaces configuration: Either workspace "name" or "prefix" is required.`, + }, + "workspace: name and prefix 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.StringVal("app-"), + }), + }), + expectedErr: `Invalid workspaces configuration: Only one of workspace "name" or "prefix" is allowed.`, + }, + } + + for name, tc := range cases { + s := testServer(t) + b := New(testDisco(s)) + + // Validate + _, valDiags := b.PrepareConfig(tc.config) + if valDiags.Err() != nil && tc.expectedErr != "" { + actualErr := valDiags.Err().Error() + if !strings.Contains(actualErr, tc.expectedErr) { + t.Fatalf("%s: unexpected validation result: %v", name, valDiags.Err()) + } + } + } +} + func TestCloud_config(t *testing.T) { cases := map[string]struct { config cty.Value diff --git a/internal/cloud/errors.go b/internal/cloud/errors.go new file mode 100644 index 000000000..273f8a157 --- /dev/null +++ b/internal/cloud/errors.go @@ -0,0 +1,29 @@ +package cloud + +import ( + "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +var ( + invalidOrganizationConfigMissingValue = tfdiags.AttributeValue( + tfdiags.Error, + "Invalid organization value", + `The "organization" attribute value must not be empty.`, + cty.Path{cty.GetAttrStep{Name: "organization"}}, + ) + + invalidWorkspaceConfigMissingValues = tfdiags.AttributeValue( + tfdiags.Error, + "Invalid workspaces configuration", + `Either workspace "name" or "prefix" is required.`, + cty.Path{cty.GetAttrStep{Name: "workspaces"}}, + ) + + invalidWorkspaceConfigMisconfiguration = tfdiags.AttributeValue( + tfdiags.Error, + "Invalid workspaces configuration", + `Only one of workspace "name" or "prefix" is allowed.`, + cty.Path{cty.GetAttrStep{Name: "workspaces"}}, + ) +)