Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inline defaults to optional object attribute type constraints #31154

Merged
merged 5 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions internal/configs/named_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Variable struct {
// ConstraintType is used for decoding and type conversions, and may
// contain nested ObjectWithOptionalAttr types.
ConstraintType cty.Type
TypeDefaults *typeexpr.Defaults

ParsingMode VariableParsingMode
Validations []*CheckRule
Expand Down Expand Up @@ -102,9 +103,10 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno
}

if attr, exists := content.Attributes["type"]; exists {
ty, parseMode, tyDiags := decodeVariableType(attr.Expr)
ty, tyDefaults, parseMode, tyDiags := decodeVariableType(attr.Expr)
diags = append(diags, tyDiags...)
v.ConstraintType = ty
v.TypeDefaults = tyDefaults
v.Type = ty.WithoutOptionalAttributesDeep()
v.ParsingMode = parseMode
}
Expand Down Expand Up @@ -137,6 +139,11 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno
// the type might not be set; we'll catch that during merge.
if v.ConstraintType != cty.NilType {
var err error
// If the type constraint has defaults, we must apply those
// defaults to the variable default value before type conversion.
if v.TypeDefaults != nil {
val = v.TypeDefaults.Apply(val)
}
val, err = convert.Convert(val, v.ConstraintType)
if err != nil {
diags = append(diags, &hcl.Diagnostic{
Expand Down Expand Up @@ -179,7 +186,7 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno
return v, diags
}

func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl.Diagnostics) {
func decodeVariableType(expr hcl.Expression) (cty.Type, *typeexpr.Defaults, VariableParsingMode, hcl.Diagnostics) {
if exprIsNativeQuotedString(expr) {
// If a user provides the pre-0.12 form of variable type argument where
// the string values "string", "list" and "map" are accepted, we
Expand All @@ -190,7 +197,7 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl
// in the normal codepath below.
val, diags := expr.Value(nil)
if diags.HasErrors() {
return cty.DynamicPseudoType, VariableParseHCL, diags
return cty.DynamicPseudoType, nil, VariableParseHCL, diags
}
str := val.AsString()
switch str {
Expand All @@ -201,25 +208,25 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl
Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"string\".",
Subject: expr.Range().Ptr(),
})
return cty.DynamicPseudoType, VariableParseLiteral, diags
return cty.DynamicPseudoType, nil, VariableParseLiteral, diags
case "list":
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid quoted type constraints",
Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"list\" and write list(string) instead to explicitly indicate that the list elements are strings.",
Subject: expr.Range().Ptr(),
})
return cty.DynamicPseudoType, VariableParseHCL, diags
return cty.DynamicPseudoType, nil, VariableParseHCL, diags
case "map":
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid quoted type constraints",
Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"map\" and write map(string) instead to explicitly indicate that the map elements are strings.",
Subject: expr.Range().Ptr(),
})
return cty.DynamicPseudoType, VariableParseHCL, diags
return cty.DynamicPseudoType, nil, VariableParseHCL, diags
default:
return cty.DynamicPseudoType, VariableParseHCL, hcl.Diagnostics{{
return cty.DynamicPseudoType, nil, VariableParseHCL, hcl.Diagnostics{{
Severity: hcl.DiagError,
Summary: "Invalid legacy variable type hint",
Detail: `To provide a full type expression, remove the surrounding quotes and give the type expression directly.`,
Expand All @@ -234,23 +241,23 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl
// elements are consistent. This is the same as list(any) or map(any).
switch hcl.ExprAsKeyword(expr) {
case "list":
return cty.List(cty.DynamicPseudoType), VariableParseHCL, nil
return cty.List(cty.DynamicPseudoType), nil, VariableParseHCL, nil
case "map":
return cty.Map(cty.DynamicPseudoType), VariableParseHCL, nil
return cty.Map(cty.DynamicPseudoType), nil, VariableParseHCL, nil
}

ty, diags := typeexpr.TypeConstraint(expr)
ty, typeDefaults, diags := typeexpr.TypeConstraintWithDefaults(expr)
if diags.HasErrors() {
return cty.DynamicPseudoType, VariableParseHCL, diags
return cty.DynamicPseudoType, nil, VariableParseHCL, diags
}

switch {
case ty.IsPrimitiveType():
// Primitive types use literal parsing.
return ty, VariableParseLiteral, diags
return ty, typeDefaults, VariableParseLiteral, diags
default:
// Everything else uses HCL parsing
return ty, VariableParseHCL, diags
return ty, typeDefaults, VariableParseHCL, diags
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ terraform {
variable "a" {
type = object({
foo = optional(string)
bar = optional(bool, true)
})
}

Expand Down
Loading