Azure / Azure/golden

VariableBlock rejects optional object attribute defaults

Open
#98 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.