Merge pull request #2157 from TimeIncOSS/interpolate-list-attr
interpolate: Interpolate computed list attributes
This commit is contained in:
commit
af5dac1ba1
|
@ -0,0 +1,29 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringList_slice(t *testing.T) {
|
||||
expected := []string{"apple", "banana", "pear"}
|
||||
l := NewStringList(expected)
|
||||
actual := l.Slice()
|
||||
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Fatalf("Expected %q, got %q", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringList_element(t *testing.T) {
|
||||
list := []string{"apple", "banana", "pear"}
|
||||
l := NewStringList(list)
|
||||
actual := l.Element(1)
|
||||
|
||||
expected := "banana"
|
||||
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected 2nd element from %q to be %q, got %q",
|
||||
list, expected, actual)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,10 @@ package terraform
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -327,6 +330,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.
|
||||
|
@ -410,8 +418,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.
|
||||
|
@ -430,6 +438,21 @@ func (i *Interpolater) computeResourceMultiVariable(
|
|||
|
||||
attr, ok := r.Primary.Attributes[v.Field]
|
||||
if !ok {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
if config.IsStringList(attr) {
|
||||
for _, s := range config.StringList(attr).Slice() {
|
||||
values = append(values, s)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -461,6 +484,26 @@ func (i *Interpolater) computeResourceMultiVariable(
|
|||
return config.NewStringList(values).String(), 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)
|
||||
|
||||
var members []string
|
||||
numberedListMember := regexp.MustCompile("^" + resourceID + "\\.[0-9]+$")
|
||||
for id, value := range attributes {
|
||||
if numberedListMember.MatchString(id) {
|
||||
members = append(members, value)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(members)
|
||||
return config.NewStringList(members).String(), nil
|
||||
}
|
||||
|
||||
func (i *Interpolater) resourceVariableInfo(
|
||||
scope *InterpolationScope,
|
||||
v *config.ResourceVariable) (*ModuleState, *config.Resource, error) {
|
||||
|
|
|
@ -210,6 +210,208 @@ func TestInterpolater_resourceVariableMulti(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 := config.NewStringList(name_servers).String()
|
||||
|
||||
// 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: config.NewStringList([]string{"red"}).String(),
|
||||
Type: ast.TypeString,
|
||||
})
|
||||
|
||||
// Zero elements
|
||||
testInterpolate(t, i, scope, "aws_route53_zone.yada.nothing", ast.Variable{
|
||||
Value: config.NewStringList([]string{}).String(),
|
||||
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-666.awsdns-11.net",
|
||||
"ns-999.awsdns-62.com",
|
||||
}
|
||||
|
||||
// More than 1 element
|
||||
expectedNameServers := config.NewStringList(name_servers[0:4]).String()
|
||||
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 = config.NewStringList(name_servers).String()
|
||||
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: config.NewStringList([]string{"red"}).String(),
|
||||
Type: ast.TypeString,
|
||||
})
|
||||
// Exactly 1 element in both
|
||||
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.listeners", ast.Variable{
|
||||
Value: config.NewStringList([]string{"red", "blue"}).String(),
|
||||
Type: ast.TypeString,
|
||||
})
|
||||
|
||||
// Zero elements
|
||||
testInterpolate(t, i, scope, "aws_route53_zone.terra.0.nothing", ast.Variable{
|
||||
Value: config.NewStringList([]string{}).String(),
|
||||
Type: ast.TypeString,
|
||||
})
|
||||
// Zero + zero elements
|
||||
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.nothing", ast.Variable{
|
||||
Value: config.NewStringList([]string{"", ""}).String(),
|
||||
Type: ast.TypeString,
|
||||
})
|
||||
// Zero + 1 element
|
||||
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special", ast.Variable{
|
||||
Value: config.NewStringList([]string{"extra"}).String(),
|
||||
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: config.NewStringList([]string{"reindeer", "white-hart"}).String(),
|
||||
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,
|
||||
|
@ -230,6 +432,6 @@ func testInterpolate(
|
|||
"foo": expectedVar,
|
||||
}
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
t.Fatalf("%q: actual: %#v\nexpected: %#v", n, actual, expected)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
resource "aws_route53_zone" "yada" {
|
||||
|
||||
}
|
||||
|
||||
resource "aws_route53_zone" "terra" {
|
||||
count = 2
|
||||
}
|
Loading…
Reference in New Issue