Compare commits

...

1 Commits

Author SHA1 Message Date
blink-so[bot] 2e60bde9b9 fix: support map(string) variables in tfvars files
Adds support for parsing map(string) type variables in .tfvars files.
Previously, only string, number, and list(string) types were supported,
causing template push to fail with "unsupported value type" errors when
using map variables.

The fix adds a case for cty.Object and cty.Map types that:
- Validates all values in the map are strings
- Serializes the map to JSON (consistent with list handling)
- Returns a clear error if non-string values are found

Fixes #21525
2026-01-15 21:32:10 +00:00
2 changed files with 94 additions and 0 deletions
+21
View File
@@ -146,6 +146,27 @@ func parseVariableValuesFromHCL(content []byte) ([]VariableValue, error) {
return nil, err
}
stringData[attribute.Name] = string(m)
case ctyType.IsObjectType() || ctyType.IsMapType():
// In case of objects/maps, Coder only supports the map(string) type.
result := map[string]string{}
var err error
_ = ctyValue.ForEachElement(func(key, val cty.Value) (stop bool) {
if !val.Type().Equals(cty.String) {
err = xerrors.Errorf("unsupported map value type for key %q: %s", key.AsString(), val.Type().GoString())
return true
}
result[key.AsString()] = val.AsString()
return false
})
if err != nil {
return nil, err
}
m, err := json.Marshal(result)
if err != nil {
return nil, err
}
stringData[attribute.Name] = string(m)
default:
return nil, xerrors.Errorf("unsupported value type (name: %s): %s", attribute.Name, ctyType.GoString())
}
+73
View File
@@ -175,3 +175,76 @@ cores: 2`
require.Error(t, err)
require.Contains(t, err.Error(), `use the equals sign "=" to introduce the argument value`)
}
func TestParseVariableValuesFromVarsFiles_MapOfStrings(t *testing.T) {
t.Parallel()
// Given
const (
hclFilename = "file.tfvars"
hclContent = `region = "us-east-1"
default_tags = {
owner_name = "John Doe"
owner_email = "john@example.com"
owner_slack = "@johndoe"
}`
)
// Prepare the .tfvars files
tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-map-of-strings-*")
require.NoError(t, err)
t.Cleanup(func() {
_ = os.RemoveAll(tempDir)
})
err = os.WriteFile(filepath.Join(tempDir, hclFilename), []byte(hclContent), 0o600)
require.NoError(t, err)
// When
actual, err := codersdk.ParseUserVariableValues([]string{
filepath.Join(tempDir, hclFilename),
}, "", nil)
// Then
require.NoError(t, err)
require.Len(t, actual, 2)
// Results are sorted by name
require.Equal(t, "default_tags", actual[0].Name)
require.JSONEq(t, `{"owner_email":"john@example.com","owner_name":"John Doe","owner_slack":"@johndoe"}`, actual[0].Value)
require.Equal(t, "region", actual[1].Name)
require.Equal(t, "us-east-1", actual[1].Value)
}
func TestParseVariableValuesFromVarsFiles_MapWithNonStringValues(t *testing.T) {
t.Parallel()
// Given - a map with non-string values should error
const (
hclFilename = "file.tfvars"
hclContent = `config = {
name = "test"
count = 5
}`
)
// Prepare the .tfvars files
tempDir, err := os.MkdirTemp(os.TempDir(), "test-parse-variable-values-from-vars-files-map-non-string-*")
require.NoError(t, err)
t.Cleanup(func() {
_ = os.RemoveAll(tempDir)
})
err = os.WriteFile(filepath.Join(tempDir, hclFilename), []byte(hclContent), 0o600)
require.NoError(t, err)
// When
actual, err := codersdk.ParseUserVariableValues([]string{
filepath.Join(tempDir, hclFilename),
}, "", nil)
// Then
require.Nil(t, actual)
require.Error(t, err)
require.Contains(t, err.Error(), "unsupported map value type")
}