Skip to content

Commit

Permalink
core: Fix interpolation of complex structures
Browse files Browse the repository at this point in the history
This commit makes two changes: map interpolation can now read flatmapped
structures, such as those present in remote state outputs, and lists are
sorted by the index instead of the value.
  • Loading branch information
jen20 committed Jun 11, 2016
1 parent dbf725b commit 8b7f261
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 16 deletions.
24 changes: 11 additions & 13 deletions terraform/interpolate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/hashicorp/hil/ast"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/flatmap"
)

const (
Expand Down Expand Up @@ -213,6 +214,7 @@ func (i *Interpolater) valueResourceVar(
result map[string]ast.Variable) error {
// If we're computing all dynamic fields, then module vars count
// and we mark it as computed.

if i.Operation == walkValidate {
result[n] = ast.Variable{
Value: config.UnknownVariableValue,
Expand Down Expand Up @@ -343,6 +345,7 @@ func (i *Interpolater) valueUserVar(
func (i *Interpolater) computeResourceVariable(
scope *InterpolationScope,
v *config.ResourceVariable) (*ast.Variable, error) {

id := v.ResourceId()
if v.Multi {
id = fmt.Sprintf("%s.%d", id, v.Index)
Expand Down Expand Up @@ -589,21 +592,19 @@ func (i *Interpolater) interpolateComplexTypeAttribute(
return unknownVariable(), nil
}

var keys []string
keys := make([]string, 0)
listElementKey := regexp.MustCompile("^" + resourceID + "\\.[0-9]+$")
for id, _ := range attributes {
if listElementKey.MatchString(id) {
keys = append(keys, id)
}
}
sort.Strings(keys)

var members []string
for _, key := range keys {
members = append(members, attributes[key])
}
// This behaviour still seems very broken to me... it retains BC but is
// probably going to cause problems in future
sort.Strings(members)

return hil.InterfaceToVariable(members)
}
Expand All @@ -620,19 +621,16 @@ func (i *Interpolater) interpolateComplexTypeAttribute(
return unknownVariable(), nil
}

var keys []string
resourceFlatMap := make(map[string]string)
mapElementKey := regexp.MustCompile("^" + resourceID + "\\.([^%]+)$")
for id, _ := range attributes {
if submatches := mapElementKey.FindAllStringSubmatch(id, -1); len(submatches) > 0 {
keys = append(keys, submatches[0][1])
for id, val := range attributes {
if mapElementKey.MatchString(id) {
resourceFlatMap[id] = val
}
}

members := make(map[string]interface{})
for _, key := range keys {
members[key] = attributes[resourceID+"."+key]
}
return hil.InterfaceToVariable(members)
expanded := flatmap.Expand(resourceFlatMap, resourceID)
return hil.InterfaceToVariable(expanded)
}

return ast.Variable{}, fmt.Errorf("No complex type %s found", resourceID)
Expand Down
95 changes: 92 additions & 3 deletions terraform/interpolate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,95 @@ func TestInterpolater_pathRoot(t *testing.T) {
})
}

func TestInterpolater_resourceVariableMap(t *testing.T) {
lock := new(sync.RWMutex)
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_instance.web": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "bar",
Attributes: map[string]string{
"amap.%": "3",
"amap.key1": "value1",
"amap.key2": "value2",
"amap.key3": "value3",
},
},
},
},
},
},
}

i := &Interpolater{
Module: testModule(t, "interpolate-resource-variable"),
State: state,
StateLock: lock,
}

scope := &InterpolationScope{
Path: rootModulePath,
}

expected := map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": "value3",
}

testInterpolate(t, i, scope, "aws_instance.web.amap",
interfaceToVariableSwallowError(expected))
}

func TestInterpolater_resourceVariableComplexMap(t *testing.T) {
lock := new(sync.RWMutex)
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_instance.web": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "bar",
Attributes: map[string]string{
"amap.%": "2",
"amap.key1.#": "2",
"amap.key1.0": "hello",
"amap.key1.1": "world",
"amap.key2.#": "1",
"amap.key2.0": "foo",
},
},
},
},
},
},
}

i := &Interpolater{
Module: testModule(t, "interpolate-resource-variable"),
State: state,
StateLock: lock,
}

scope := &InterpolationScope{
Path: rootModulePath,
}

expected := map[string]interface{}{
"key1": []interface{}{"hello", "world"},
"key2": []interface{}{"foo"},
}

testInterpolate(t, i, scope, "aws_instance.web.amap",
interfaceToVariableSwallowError(expected))
}

func TestInterpolater_resourceVariable(t *testing.T) {
lock := new(sync.RWMutex)
state := &State{
Expand Down Expand Up @@ -278,10 +367,10 @@ func TestInterpolator_resourceMultiAttributes(t *testing.T) {
lock := new(sync.RWMutex)
state := &State{
Modules: []*ModuleState{
&ModuleState{
{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_route53_zone.yada": &ResourceState{
"aws_route53_zone.yada": {
Type: "aws_route53_zone",
Dependencies: []string{},
Primary: &InstanceState{
Expand Down Expand Up @@ -354,8 +443,8 @@ func TestInterpolator_resourceMultiAttributesWithResourceCount(t *testing.T) {
"ns-601.awsdns-11.net",
"ns-000.awsdns-38.org",
"ns-444.awsdns-18.co.uk",
"ns-666.awsdns-11.net",
"ns-999.awsdns-62.com",
"ns-666.awsdns-11.net",
}

// More than 1 element
Expand Down

0 comments on commit 8b7f261

Please sign in to comment.