Skip to content

Commit

Permalink
interpolate: Add support for computed TypeList attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed May 31, 2015
1 parent 7430fd5 commit 025619a
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 4 deletions.
48 changes: 45 additions & 3 deletions terraform/interpolate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package terraform

import (
"fmt"
"log"
"os"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -327,6 +329,11 @@ func (i *Interpolater) computeResourceVariable(
return attr, nil
}

// computed list attribute
if _, ok := r.Primary.Attributes[v.Field+".#"]; ok {
return i.interpolateListAttribute(v.Field, r.Primary.Attributes)
}

// At apply time, we can't do the "maybe has it" check below
// that we need for plans since parent elements might be computed.
// Therefore, it is an error and we're missing the key.
Expand Down Expand Up @@ -410,8 +417,8 @@ func (i *Interpolater) computeResourceMultiVariable(
}

var values []string
for i := 0; i < count; i++ {
id := fmt.Sprintf("%s.%d", v.ResourceId(), i)
for j := 0; j < count; j++ {
id := fmt.Sprintf("%s.%d", v.ResourceId(), j)

// If we're dealing with only a single resource, then the
// ID doesn't have a trailing index.
Expand All @@ -430,7 +437,15 @@ func (i *Interpolater) computeResourceMultiVariable(

attr, ok := r.Primary.Attributes[v.Field]
if !ok {
continue
// computed list attribute
_, ok := r.Primary.Attributes[v.Field+".#"]
if !ok {
continue
}
attr, err = i.interpolateListAttribute(v.Field, r.Primary.Attributes)
if err != nil {
return "", err
}
}

values = append(values, attr)
Expand All @@ -448,6 +463,33 @@ func (i *Interpolater) computeResourceMultiVariable(
return strings.Join(values, config.InterpSplitDelim), nil
}

func (i *Interpolater) interpolateListAttribute(
resourceID string,
attributes map[string]string) (string, error) {

attr := attributes[resourceID+".#"]
log.Printf("[DEBUG] Interpolating computed list attribute %s (%s)",
resourceID, attr)

value, err := strconv.ParseInt(attr, 0, 0)
if err != nil {
return "", err
}

count := int(value)
if count == 0 {
return "", nil
}

var members []string
for i := 0; i < count; i++ {
id := fmt.Sprintf("%s.%d", resourceID, i)
members = append(members, attributes[id])
}

return strings.Join(members, config.InterpSplitDelim), nil
}

func (i *Interpolater) resourceVariableInfo(
scope *InterpolationScope,
v *config.ResourceVariable) (*ModuleState, *config.Resource, error) {
Expand Down
205 changes: 204 additions & 1 deletion terraform/interpolate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"reflect"
"strings"
"sync"
"testing"

Expand Down Expand Up @@ -136,6 +137,208 @@ func TestInterpolater_pathRoot(t *testing.T) {
})
}

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{
Type: "aws_route53_zone",
Dependencies: []string{},
Primary: &InstanceState{
ID: "AAABBBCCCDDDEEE",
Attributes: map[string]string{
"name_servers.#": "4",
"name_servers.0": "ns-1334.awsdns-38.org",
"name_servers.1": "ns-1680.awsdns-18.co.uk",
"name_servers.2": "ns-498.awsdns-62.com",
"name_servers.3": "ns-601.awsdns-11.net",
"listeners.#": "1",
"listeners.0": "red",
"tags.#": "1",
"tags.Name": "reindeer",
"nothing.#": "0",
},
},
},
},
},
},
}

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

scope := &InterpolationScope{
Path: rootModulePath,
}

name_servers := []string{
"ns-1334.awsdns-38.org",
"ns-1680.awsdns-18.co.uk",
"ns-498.awsdns-62.com",
"ns-601.awsdns-11.net",
}
expectedNameServers := strings.Join(name_servers, config.InterpSplitDelim)

// More than 1 element
testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers", ast.Variable{
Value: expectedNameServers,
Type: ast.TypeString,
})

// Exactly 1 element
testInterpolate(t, i, scope, "aws_route53_zone.yada.listeners", ast.Variable{
Value: "red",
Type: ast.TypeString,
})

// Zero elements
testInterpolate(t, i, scope, "aws_route53_zone.yada.nothing", ast.Variable{
Value: "",
Type: ast.TypeString,
})

// Maps still need to work
testInterpolate(t, i, scope, "aws_route53_zone.yada.tags.Name", ast.Variable{
Value: "reindeer",
Type: ast.TypeString,
})
}

func TestInterpolator_resourceMultiAttributesWithResourceCount(t *testing.T) {
i := getInterpolaterFixture(t)
scope := &InterpolationScope{
Path: rootModulePath,
}

name_servers := []string{
"ns-1334.awsdns-38.org",
"ns-1680.awsdns-18.co.uk",
"ns-498.awsdns-62.com",
"ns-601.awsdns-11.net",
"ns-000.awsdns-38.org",
"ns-444.awsdns-18.co.uk",
"ns-999.awsdns-62.com",
"ns-666.awsdns-11.net",
}

// More than 1 element
expectedNameServers := strings.Join(name_servers[0:4], config.InterpSplitDelim)
testInterpolate(t, i, scope, "aws_route53_zone.terra.0.name_servers", ast.Variable{
Value: expectedNameServers,
Type: ast.TypeString,
})
// More than 1 element in both
expectedNameServers = strings.Join(name_servers[0:8], config.InterpSplitDelim)
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.name_servers", ast.Variable{
Value: expectedNameServers,
Type: ast.TypeString,
})

// Exactly 1 element
testInterpolate(t, i, scope, "aws_route53_zone.terra.0.listeners", ast.Variable{
Value: "red",
Type: ast.TypeString,
})
// Exactly 1 element in both
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.listeners", ast.Variable{
Value: strings.Join([]string{"red", "blue"}, config.InterpSplitDelim),
Type: ast.TypeString,
})

// Zero elements
testInterpolate(t, i, scope, "aws_route53_zone.terra.0.nothing", ast.Variable{
Value: "",
Type: ast.TypeString,
})
// Zero + zero elements
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.nothing", ast.Variable{
Value: config.InterpSplitDelim, // Not sure if this is desired behaviour?
Type: ast.TypeString,
})
// Zero + 1 element
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special", ast.Variable{
Value: "extra",
Type: ast.TypeString,
})

// Maps still need to work
testInterpolate(t, i, scope, "aws_route53_zone.terra.0.tags.Name", ast.Variable{
Value: "reindeer",
Type: ast.TypeString,
})
// Maps still need to work in both
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.tags.Name", ast.Variable{
Value: strings.Join([]string{"reindeer", "white-hart"}, config.InterpSplitDelim),
Type: ast.TypeString,
})
}

func getInterpolaterFixture(t *testing.T) *Interpolater {
lock := new(sync.RWMutex)
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_route53_zone.terra.0": &ResourceState{
Type: "aws_route53_zone",
Dependencies: []string{},
Primary: &InstanceState{
ID: "AAABBBCCCDDDEEE",
Attributes: map[string]string{
"name_servers.#": "4",
"name_servers.0": "ns-1334.awsdns-38.org",
"name_servers.1": "ns-1680.awsdns-18.co.uk",
"name_servers.2": "ns-498.awsdns-62.com",
"name_servers.3": "ns-601.awsdns-11.net",
"listeners.#": "1",
"listeners.0": "red",
"tags.#": "1",
"tags.Name": "reindeer",
"nothing.#": "0",
},
},
},
"aws_route53_zone.terra.1": &ResourceState{
Type: "aws_route53_zone",
Dependencies: []string{},
Primary: &InstanceState{
ID: "EEEFFFGGGHHHIII",
Attributes: map[string]string{
"name_servers.#": "4",
"name_servers.0": "ns-000.awsdns-38.org",
"name_servers.1": "ns-444.awsdns-18.co.uk",
"name_servers.2": "ns-999.awsdns-62.com",
"name_servers.3": "ns-666.awsdns-11.net",
"listeners.#": "1",
"listeners.0": "blue",
"special.#": "1",
"special.0": "extra",
"tags.#": "1",
"tags.Name": "white-hart",
"nothing.#": "0",
},
},
},
},
},
},
}

return &Interpolater{
Module: testModule(t, "interpolate-multi-vars"),
StateLock: lock,
State: state,
}
}

func testInterpolate(
t *testing.T, i *Interpolater,
scope *InterpolationScope,
Expand All @@ -156,6 +359,6 @@ func testInterpolate(
"foo": expectedVar,
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
t.Fatalf("actual: %#v\nexpected: %#v", actual, expected)
}
}
7 changes: 7 additions & 0 deletions terraform/test-fixtures/interpolate-multi-vars/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "aws_route53_zone" "yada" {

}

resource "aws_route53_zone" "terra" {
count = 2
}

0 comments on commit 025619a

Please sign in to comment.