mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
fbc9cf9ddb
Fixes #4808 Fixes #5174
489 lines
12 KiB
Go
489 lines
12 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/hil/ast"
|
|
"github.com/hashicorp/terraform/config"
|
|
)
|
|
|
|
func TestInterpolater_countIndex(t *testing.T) {
|
|
i := &Interpolater{}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: rootModulePath,
|
|
Resource: &Resource{CountIndex: 42},
|
|
}
|
|
|
|
testInterpolate(t, i, scope, "count.index", ast.Variable{
|
|
Value: 42,
|
|
Type: ast.TypeInt,
|
|
})
|
|
}
|
|
|
|
func TestInterpolater_countIndexInWrongContext(t *testing.T) {
|
|
i := &Interpolater{}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: rootModulePath,
|
|
}
|
|
|
|
n := "count.index"
|
|
|
|
v, err := config.NewInterpolatedVariable(n)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedErr := fmt.Errorf("foo: count.index is only valid within resources")
|
|
|
|
_, err = i.Values(scope, map[string]config.InterpolatedVariable{
|
|
"foo": v,
|
|
})
|
|
|
|
if !reflect.DeepEqual(expectedErr, err) {
|
|
t.Fatalf("expected: %#v, got %#v", expectedErr, err)
|
|
}
|
|
}
|
|
|
|
func TestInterpolater_moduleVariable(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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
&ModuleState{
|
|
Path: []string{RootModuleName, "child"},
|
|
Outputs: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
i := &Interpolater{
|
|
State: state,
|
|
StateLock: lock,
|
|
}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: rootModulePath,
|
|
}
|
|
|
|
testInterpolate(t, i, scope, "module.child.foo", ast.Variable{
|
|
Value: "bar",
|
|
Type: ast.TypeString,
|
|
})
|
|
}
|
|
|
|
func TestInterpolater_pathCwd(t *testing.T) {
|
|
i := &Interpolater{}
|
|
scope := &InterpolationScope{}
|
|
|
|
expected, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
testInterpolate(t, i, scope, "path.cwd", ast.Variable{
|
|
Value: expected,
|
|
Type: ast.TypeString,
|
|
})
|
|
}
|
|
|
|
func TestInterpolater_pathModule(t *testing.T) {
|
|
mod := testModule(t, "interpolate-path-module")
|
|
i := &Interpolater{
|
|
Module: mod,
|
|
}
|
|
scope := &InterpolationScope{
|
|
Path: []string{RootModuleName, "child"},
|
|
}
|
|
|
|
path := mod.Child([]string{"child"}).Config().Dir
|
|
testInterpolate(t, i, scope, "path.module", ast.Variable{
|
|
Value: path,
|
|
Type: ast.TypeString,
|
|
})
|
|
}
|
|
|
|
func TestInterpolater_pathRoot(t *testing.T) {
|
|
mod := testModule(t, "interpolate-path-module")
|
|
i := &Interpolater{
|
|
Module: mod,
|
|
}
|
|
scope := &InterpolationScope{
|
|
Path: []string{RootModuleName, "child"},
|
|
}
|
|
|
|
path := mod.Config().Dir
|
|
testInterpolate(t, i, scope, "path.root", ast.Variable{
|
|
Value: path,
|
|
Type: ast.TypeString,
|
|
})
|
|
}
|
|
|
|
func TestInterpolater_resourceVariable(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{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
i := &Interpolater{
|
|
Module: testModule(t, "interpolate-resource-variable"),
|
|
State: state,
|
|
StateLock: lock,
|
|
}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: rootModulePath,
|
|
}
|
|
|
|
testInterpolate(t, i, scope, "aws_instance.web.foo", ast.Variable{
|
|
Value: "bar",
|
|
Type: ast.TypeString,
|
|
})
|
|
}
|
|
|
|
func TestInterpolater_resourceVariableMulti(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{
|
|
"foo": config.UnknownVariableValue,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
i := &Interpolater{
|
|
Module: testModule(t, "interpolate-resource-variable"),
|
|
State: state,
|
|
StateLock: lock,
|
|
}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: rootModulePath,
|
|
}
|
|
|
|
testInterpolate(t, i, scope, "aws_instance.web.*.foo", ast.Variable{
|
|
Value: config.UnknownVariableValue,
|
|
Type: ast.TypeString,
|
|
})
|
|
}
|
|
|
|
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 + 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 TestInterpolator_resourceMultiAttributesComputed(t *testing.T) {
|
|
lock := new(sync.RWMutex)
|
|
// The state would never be written with an UnknownVariableValue in it, but
|
|
// it can/does exist that way in memory during the plan phase.
|
|
state := &State{
|
|
Modules: []*ModuleState{
|
|
&ModuleState{
|
|
Path: rootModulePath,
|
|
Resources: map[string]*ResourceState{
|
|
"aws_route53_zone.yada": &ResourceState{
|
|
Type: "aws_route53_zone",
|
|
Primary: &InstanceState{
|
|
ID: "z-abc123",
|
|
Attributes: map[string]string{
|
|
"name_servers.#": config.UnknownVariableValue,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
i := &Interpolater{
|
|
Module: testModule(t, "interpolate-multi-vars"),
|
|
StateLock: lock,
|
|
State: state,
|
|
}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: rootModulePath,
|
|
}
|
|
|
|
testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers", ast.Variable{
|
|
Value: config.UnknownVariableValue,
|
|
Type: ast.TypeString,
|
|
})
|
|
}
|
|
|
|
func TestInterpolater_selfVarWithoutResource(t *testing.T) {
|
|
i := &Interpolater{}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: rootModulePath,
|
|
}
|
|
|
|
v, err := config.NewInterpolatedVariable("self.name")
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
_, err = i.Values(scope, map[string]config.InterpolatedVariable{"foo": v})
|
|
if err == nil {
|
|
t.Fatalf("expected err, got none")
|
|
}
|
|
}
|
|
|
|
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,
|
|
n string, expectedVar ast.Variable) {
|
|
v, err := config.NewInterpolatedVariable(n)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual, err := i.Values(scope, map[string]config.InterpolatedVariable{
|
|
"foo": v,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expected := map[string]ast.Variable{
|
|
"foo": expectedVar,
|
|
}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Fatalf("%q: actual: %#v\nexpected: %#v", n, actual, expected)
|
|
}
|
|
}
|