VariableBlock rejects optional object attribute defaults
- Dominant language
- Go
- Stars
- 3
- Forks
- 13
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`VariableBlock` cannot use optional object attributes with defaults in a variable type, even though HCL supports this syntax for type constraints:
```hcl
variable "provider" {
type = object({
type = optional(string, "openai")
endpoint = string
retry = optional(object({
attempts = optional(number, 3)
}), {})
})
default = {
endpoint = "https://example.test/v1"
}
}
```
Planning fails while parsing the variable type. This prevents callers from passing a partial object and relying on field-level defaults, which is particularly useful for provider-style configuration.
## Reproduction test
The following test can be added directly to `variable_test.go` in the existing `variableSuite`:
```go
func (s *variableSuite) TestExecuteBeforePlan_ObjectOptionalAttributeDefaults() {
s.dummyFsWithFiles(map[string]string{
"test.hcl": `variable "provider" {
type = object({
type = optional(string, "openai")
endpoint = string
retry = optional(object({
attempts = optional(number, 3)
}), {})
})
default = {
endpoint = "https://example.test/v1"
}
}`,
})
config, err := BuildDummyConfig("/", "", nil, nil)
require.NoError(s.T(), err)
variable := Blocks[*VariableBlock](config)[0]
s.Equal(cty.ObjectVal(map[string]cty.Value{
"type": cty.StringVal("openai"),
"endpoint": cty.StringVal("https://example.test/v1"),
"retry": cty.ObjectVal(map[string]cty.Value{
"attempts": cty.NumberIntVal(3),
}),
}), *variable.variableValue)
}
```
Run:
```text
go test ./... -run 'TestVariableSuite/TestExecuteBeforePlan_ObjectOptionalAttributeDefaults' -count=1
```
Actual result:
```text
--- FAIL: TestVariableSuite/TestExecuteBeforePlan_ObjectOptionalAttributeDefaults
Received unexpected error:
test.hcl:3,16-24: Invalid type specification;
Optional attribute modifier is only for type constraints, not for exact types.,
and 2 other diagnostic(s)
```
Configurations containing `optional(type, default)` can also report:
```text
Optional attribute modifier expects only one argument: the attribute type.
```
## Expected behavior
`BuildDummyConfig` should succeed. The evaluated `var.provider` value should be:
```hcl
{
type = "openai"
endpoint = "https://example.test/v1"
retry = {
attempts = 3
}
}
```
A top-level variable `default` is not an equivalent workaround: once a caller supplies a partial object, it does not merge defaults into omitted nested attributes.
## Root cause
On current `main`, `VariableBlock.parseVariableType` uses:
```go
t, diag := typeexpr.Type(typeAttr.Expr)
```
`typeexpr.Type` parses an exact type. HCL exposes `typeexpr.TypeConstraintWithDefaults` specifically for variable-style type constraints with `optional(type, default)`.
`TypeConstraintWithDefaults` returns both the target `cty.Type` and a `*typeexpr.Defaults` tree. The defaults must be retained by `VariableBlock` and applied to the selected input value before conversion. Merely changing the parser without calling `Defaults.Apply` would accept the syntax but still lose the intended defaults.
## Suggested direction
1. Parse variable types with `typeexpr.TypeConstraintWithDefaults`.
2. Store the returned defaults on `VariableBlock`.
3. Apply them to values from CLI files, environment variables, prompts, and the variable-level `default` before `convert.Convert`.
4. Preserve the current type conversion and validation behavior.
5. Add coverage for nested optional object defaults and partial caller-supplied objects.
Observed against Azure/golden `main` at `6e9a3fc2760e6f8440e4dfe8d0b1360886588bdd`.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in variable_test.go with the provided variableSuite reproduction, then trace VariableBlock.parseVariableType and the paths that process CLI files, environment variables, prompts, and variable defaults. Compare the current typeexpr.Type call with TypeConstraintWithDefaults and verify that nested defaults are applied before conversion. Done means the focused test passes and partial objects receive the documented nested defaults without changing existing conversion or validation behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100