diff --git a/configs/configupgrade/analysis.go b/configs/configupgrade/analysis.go index 9eb93f5ac9..5f513a3337 100644 --- a/configs/configupgrade/analysis.go +++ b/configs/configupgrade/analysis.go @@ -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()) diff --git a/configs/configupgrade/analysis_expr.go b/configs/configupgrade/analysis_expr.go index d6dc02bcd4..44a65af158 100644 --- a/configs/configupgrade/analysis_expr.go +++ b/configs/configupgrade/analysis_expr.go @@ -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 + } }