configs/configupgrade: Do type inference with input variables
By collecting information about the input variables during analysis, we can return approximate type information for any references to those variables in expressions. Since Terraform 0.11 allowed maps of maps and lists of lists in certain circumstances even though this was documented as forbidden, we conservatively return collection types whose element types are unknown here, which allows us to do shallow inference on them but will cause us to get an incomplete result if any operations are performed on elements of the list or map value.
This commit is contained in:
parent
a2d9634dbf
commit
d9603d5bc5
|
@ -3,6 +3,7 @@ package configupgrade
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
hcl1 "github.com/hashicorp/hcl"
|
||||
hcl1ast "github.com/hashicorp/hcl/hcl/ast"
|
||||
|
@ -23,6 +24,7 @@ type analysis struct {
|
|||
ProvisionerSchemas map[string]*configschema.Block
|
||||
ResourceProviderType map[addrs.Resource]string
|
||||
ResourceHasCount map[addrs.Resource]bool
|
||||
VariableTypes map[string]string
|
||||
}
|
||||
|
||||
// analyze processes the configuration files included inside the receiver
|
||||
|
@ -34,6 +36,7 @@ func (u *Upgrader) analyze(ms ModuleSources) (*analysis, error) {
|
|||
ProvisionerSchemas: make(map[string]*configschema.Block),
|
||||
ResourceProviderType: make(map[addrs.Resource]string),
|
||||
ResourceHasCount: make(map[addrs.Resource]bool),
|
||||
VariableTypes: make(map[string]string),
|
||||
}
|
||||
|
||||
m := &moduledeps.Module{
|
||||
|
@ -188,6 +191,44 @@ func (u *Upgrader) analyze(ms ModuleSources) (*analysis, error) {
|
|||
ret.ResourceProviderType[rAddr] = inst.Type()
|
||||
}
|
||||
}
|
||||
|
||||
if variablesList := list.Filter("variable"); len(variablesList.Items) > 0 {
|
||||
variableObjs := variablesList.Children()
|
||||
for _, variableObj := range variableObjs.Items {
|
||||
if len(variableObj.Keys) != 1 {
|
||||
return nil, fmt.Errorf("variable block has wrong number of labels")
|
||||
}
|
||||
name := variableObj.Keys[0].Token.Value().(string)
|
||||
|
||||
var listVal *hcl1ast.ObjectList
|
||||
if ot, ok := variableObj.Val.(*hcl1ast.ObjectType); ok {
|
||||
listVal = ot.List
|
||||
} else {
|
||||
return nil, fmt.Errorf("variable %q: must be a block", name)
|
||||
}
|
||||
|
||||
var typeStr string
|
||||
if a := listVal.Filter("type"); len(a.Items) > 0 {
|
||||
err := hcl1.DecodeObject(&typeStr, a.Items[0].Val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error reading type for variable %q: %s", name, err)
|
||||
}
|
||||
} else if a := listVal.Filter("default"); len(a.Items) > 0 {
|
||||
switch a.Items[0].Val.(type) {
|
||||
case *hcl1ast.ObjectType:
|
||||
typeStr = "map"
|
||||
case *hcl1ast.ListType:
|
||||
typeStr = "list"
|
||||
default:
|
||||
typeStr = "string"
|
||||
}
|
||||
} else {
|
||||
typeStr = "string"
|
||||
}
|
||||
|
||||
ret.VariableTypes[name] = strings.TrimSpace(typeStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
providerFactories, err := u.Providers.ResolveProviders(m.PluginRequirements())
|
||||
|
|
|
@ -68,7 +68,6 @@ func (d analysisData) StaticValidateReferences(refs []*addrs.Reference, self add
|
|||
|
||||
func (d analysisData) GetCountAttr(addr addrs.CountAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
// All valid count attributes are numbers
|
||||
log.Printf("[TRACE] configupgrade: Determining type for %s", addr)
|
||||
return cty.UnknownVal(cty.Number), nil
|
||||
}
|
||||
|
||||
|
@ -149,8 +148,20 @@ func (d analysisData) GetTerraformAttr(addrs.TerraformAttr, tfdiags.SourceRange)
|
|||
return cty.UnknownVal(cty.String), nil
|
||||
}
|
||||
|
||||
func (d analysisData) GetInputVariable(addrs.InputVariable, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
func (d analysisData) GetInputVariable(addr addrs.InputVariable, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
// TODO: Collect shallow type information (list vs. map vs. string vs. unknown)
|
||||
// in analysis and then return a similarly-approximate type here.
|
||||
return cty.DynamicVal, nil
|
||||
log.Printf("[TRACE] configupgrade: Determining type for %s", addr)
|
||||
name := addr.Name
|
||||
typeName := d.an.VariableTypes[name]
|
||||
switch typeName {
|
||||
case "list":
|
||||
return cty.UnknownVal(cty.List(cty.DynamicPseudoType)), nil
|
||||
case "map":
|
||||
return cty.UnknownVal(cty.Map(cty.DynamicPseudoType)), nil
|
||||
case "string":
|
||||
return cty.UnknownVal(cty.String), nil
|
||||
default:
|
||||
return cty.DynamicVal, nil
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue