3489 lines
62 KiB
Go
3489 lines
62 KiB
Go
package schema
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
)
|
|
|
|
func TestResourceDataGet(t *testing.T) {
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Key string
|
|
Value interface{}
|
|
}{
|
|
// #0
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "foo",
|
|
New: "bar",
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
},
|
|
|
|
// #1
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
|
|
Value: "foo",
|
|
},
|
|
|
|
// #2
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo!",
|
|
NewExtra: "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: "foo",
|
|
},
|
|
|
|
// #3
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "bar",
|
|
},
|
|
},
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
|
|
Value: "bar",
|
|
},
|
|
|
|
// #4
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "foo",
|
|
New: "bar",
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
},
|
|
|
|
// #5
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"port": &Schema{
|
|
Type: TypeInt,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"port": "80",
|
|
},
|
|
},
|
|
|
|
Diff: nil,
|
|
|
|
Key: "port",
|
|
|
|
Value: 80,
|
|
},
|
|
|
|
// #6
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.0": "1",
|
|
"ports.1": "2",
|
|
"ports.2": "5",
|
|
},
|
|
},
|
|
|
|
Key: "ports.1",
|
|
|
|
Value: 2,
|
|
},
|
|
|
|
// #7
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.0": "1",
|
|
"ports.1": "2",
|
|
"ports.2": "5",
|
|
},
|
|
},
|
|
|
|
Key: "ports.#",
|
|
|
|
Value: 3,
|
|
},
|
|
|
|
// #8
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Key: "ports.#",
|
|
|
|
Value: 0,
|
|
},
|
|
|
|
// #9
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.0": "1",
|
|
"ports.1": "2",
|
|
"ports.2": "5",
|
|
},
|
|
},
|
|
|
|
Key: "ports",
|
|
|
|
Value: []interface{}{1, 2, 5},
|
|
},
|
|
|
|
// #10
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ingress": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"from": &Schema{
|
|
Type: TypeInt,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ingress.#": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "1",
|
|
},
|
|
"ingress.0.from": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "8080",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "ingress.0",
|
|
|
|
Value: map[string]interface{}{
|
|
"from": 8080,
|
|
},
|
|
},
|
|
|
|
// #11
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ingress": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"from": &Schema{
|
|
Type: TypeInt,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ingress.#": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "1",
|
|
},
|
|
"ingress.0.from": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "8080",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "ingress",
|
|
|
|
Value: []interface{}{
|
|
map[string]interface{}{
|
|
"from": 8080,
|
|
},
|
|
},
|
|
},
|
|
|
|
// #12 Computed get
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
|
|
Value: "foo",
|
|
},
|
|
|
|
// #13 Full object
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "",
|
|
|
|
Value: map[string]interface{}{
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
|
|
// #14 List of maps
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"config_vars": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{
|
|
Type: TypeMap,
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"config_vars.#": &terraform.ResourceAttrDiff{
|
|
Old: "0",
|
|
New: "2",
|
|
},
|
|
"config_vars.0.foo": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "bar",
|
|
},
|
|
"config_vars.1.bar": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "baz",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "config_vars",
|
|
|
|
Value: []interface{}{
|
|
map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
map[string]interface{}{
|
|
"bar": "baz",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #15 List of maps in state
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"config_vars": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{
|
|
Type: TypeMap,
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"config_vars.#": "2",
|
|
"config_vars.0.foo": "baz",
|
|
"config_vars.1.bar": "bar",
|
|
},
|
|
},
|
|
|
|
Diff: nil,
|
|
|
|
Key: "config_vars",
|
|
|
|
Value: []interface{}{
|
|
map[string]interface{}{
|
|
"foo": "baz",
|
|
},
|
|
map[string]interface{}{
|
|
"bar": "bar",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #16 List of maps with removal in diff
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"config_vars": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{
|
|
Type: TypeMap,
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"config_vars.#": "1",
|
|
"config_vars.0.FOO": "bar",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"config_vars.#": &terraform.ResourceAttrDiff{
|
|
Old: "1",
|
|
New: "0",
|
|
},
|
|
"config_vars.0.FOO": &terraform.ResourceAttrDiff{
|
|
Old: "bar",
|
|
NewRemoved: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "config_vars",
|
|
|
|
Value: []interface{}{},
|
|
},
|
|
|
|
// #17 Sets
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.80": "80",
|
|
},
|
|
},
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
|
|
Value: []interface{}{80},
|
|
},
|
|
|
|
// #18
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"data": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"index": &Schema{
|
|
Type: TypeInt,
|
|
Required: true,
|
|
},
|
|
|
|
"value": &Schema{
|
|
Type: TypeString,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
Set: func(a interface{}) int {
|
|
m := a.(map[string]interface{})
|
|
return m["index"].(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"data.#": "1",
|
|
"data.10.index": "10",
|
|
"data.10.value": "50",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"data.10.value": &terraform.ResourceAttrDiff{
|
|
Old: "50",
|
|
New: "80",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "data",
|
|
|
|
Value: []interface{}{
|
|
map[string]interface{}{
|
|
"index": 10,
|
|
"value": "80",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #19 Empty Set
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
|
|
Value: []interface{}{},
|
|
},
|
|
|
|
// #20 Float zero
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ratio": &Schema{
|
|
Type: TypeFloat,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ratio",
|
|
|
|
Value: 0.0,
|
|
},
|
|
|
|
// #21 Float given
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ratio": &Schema{
|
|
Type: TypeFloat,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ratio": "0.5",
|
|
},
|
|
},
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ratio",
|
|
|
|
Value: 0.5,
|
|
},
|
|
|
|
// #22 Float diff
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ratio": &Schema{
|
|
Type: TypeFloat,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ratio": "-0.5",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ratio": &terraform.ResourceAttrDiff{
|
|
Old: "-0.5",
|
|
New: "33.0",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "ratio",
|
|
|
|
Value: 33.0,
|
|
},
|
|
|
|
// #23 Sets with removed elements
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.80": "80",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "2",
|
|
New: "1",
|
|
},
|
|
"ports.80": &terraform.ResourceAttrDiff{
|
|
Old: "80",
|
|
New: "80",
|
|
},
|
|
"ports.8080": &terraform.ResourceAttrDiff{
|
|
Old: "8080",
|
|
New: "0",
|
|
NewRemoved: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "ports",
|
|
|
|
Value: []interface{}{80},
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
v := d.Get(tc.Key)
|
|
if s, ok := v.(*Set); ok {
|
|
v = s.List()
|
|
}
|
|
|
|
if !reflect.DeepEqual(v, tc.Value) {
|
|
t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceDataGetChange(t *testing.T) {
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Key string
|
|
OldValue interface{}
|
|
NewValue interface{}
|
|
}{
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
|
|
OldValue: "",
|
|
NewValue: "foo",
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
|
|
OldValue: "foo",
|
|
NewValue: "foo",
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
o, n := d.GetChange(tc.Key)
|
|
if !reflect.DeepEqual(o, tc.OldValue) {
|
|
t.Fatalf("Old Bad: %d\n\n%#v", i, o)
|
|
}
|
|
if !reflect.DeepEqual(n, tc.NewValue) {
|
|
t.Fatalf("New Bad: %d\n\n%#v", i, n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceDataGetOk(t *testing.T) {
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Key string
|
|
Value interface{}
|
|
Ok bool
|
|
}{
|
|
/*
|
|
* Primitives
|
|
*/
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
Ok: false,
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "",
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
Ok: false,
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
Ok: false,
|
|
},
|
|
|
|
/*
|
|
* Lists
|
|
*/
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{},
|
|
Ok: false,
|
|
},
|
|
|
|
/*
|
|
* Map
|
|
*/
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeMap,
|
|
Optional: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: map[string]interface{}{},
|
|
Ok: false,
|
|
},
|
|
|
|
/*
|
|
* Set
|
|
*/
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int { return a.(int) },
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{},
|
|
Ok: false,
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int { return a.(int) },
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports.0",
|
|
Value: 0,
|
|
Ok: false,
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int { return a.(int) },
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "0",
|
|
New: "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{},
|
|
Ok: false,
|
|
},
|
|
|
|
// Further illustrates and clarifiies the GetOk semantics from #933, and
|
|
// highlights the limitation that zero-value config is currently
|
|
// indistinguishable from unset config.
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"from_port": &Schema{
|
|
Type: TypeInt,
|
|
Optional: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"from_port": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "from_port",
|
|
Value: 0,
|
|
Ok: false,
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
v, ok := d.GetOk(tc.Key)
|
|
if s, ok := v.(*Set); ok {
|
|
v = s.List()
|
|
}
|
|
|
|
if !reflect.DeepEqual(v, tc.Value) {
|
|
t.Fatalf("Bad: %d\n\n%#v", i, v)
|
|
}
|
|
if ok != tc.Ok {
|
|
t.Fatalf("%d: expected ok: %t, got: %t", i, tc.Ok, ok)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceDataGetOkExists(t *testing.T) {
|
|
cases := []struct {
|
|
Name string
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Key string
|
|
Value interface{}
|
|
Ok bool
|
|
}{
|
|
/*
|
|
* Primitives
|
|
*/
|
|
{
|
|
Name: "string-literal-empty",
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": {
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": {
|
|
Old: "",
|
|
New: "",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
Ok: true,
|
|
},
|
|
|
|
{
|
|
Name: "string-computed-empty",
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": {
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": {
|
|
Old: "",
|
|
New: "",
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
Ok: false,
|
|
},
|
|
|
|
{
|
|
Name: "string-optional-computed-nil-diff",
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": {
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
Value: "",
|
|
Ok: false,
|
|
},
|
|
|
|
/*
|
|
* Lists
|
|
*/
|
|
|
|
{
|
|
Name: "list-optional",
|
|
Schema: map[string]*Schema{
|
|
"ports": {
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{},
|
|
Ok: false,
|
|
},
|
|
|
|
/*
|
|
* Map
|
|
*/
|
|
|
|
{
|
|
Name: "map-optional",
|
|
Schema: map[string]*Schema{
|
|
"ports": {
|
|
Type: TypeMap,
|
|
Optional: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: map[string]interface{}{},
|
|
Ok: false,
|
|
},
|
|
|
|
/*
|
|
* Set
|
|
*/
|
|
|
|
{
|
|
Name: "set-optional",
|
|
Schema: map[string]*Schema{
|
|
"ports": {
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int { return a.(int) },
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{},
|
|
Ok: false,
|
|
},
|
|
|
|
{
|
|
Name: "set-optional-key",
|
|
Schema: map[string]*Schema{
|
|
"ports": {
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int { return a.(int) },
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports.0",
|
|
Value: 0,
|
|
Ok: false,
|
|
},
|
|
|
|
{
|
|
Name: "bool-literal-empty",
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": {
|
|
Type: TypeBool,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": {
|
|
Old: "",
|
|
New: "",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: false,
|
|
Ok: true,
|
|
},
|
|
|
|
{
|
|
Name: "bool-literal-set",
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": {
|
|
Type: TypeBool,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": {
|
|
New: "true",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
Value: true,
|
|
Ok: true,
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("%s err: %s", tc.Name, err)
|
|
}
|
|
|
|
v, ok := d.GetOkExists(tc.Key)
|
|
if s, ok := v.(*Set); ok {
|
|
v = s.List()
|
|
}
|
|
|
|
if !reflect.DeepEqual(v, tc.Value) {
|
|
t.Fatalf("Bad %s: \n%#v", tc.Name, v)
|
|
}
|
|
if ok != tc.Ok {
|
|
t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Ok, ok)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResourceDataTimeout(t *testing.T) {
|
|
cases := []struct {
|
|
Name string
|
|
Rd *ResourceData
|
|
Expected *ResourceTimeout
|
|
}{
|
|
{
|
|
Name: "Basic example default",
|
|
Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 15, 0)},
|
|
Expected: expectedTimeoutForValues(10, 3, 0, 15, 0),
|
|
},
|
|
{
|
|
Name: "Resource and config match update, create",
|
|
Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 3, 0, 0)},
|
|
Expected: expectedTimeoutForValues(10, 0, 3, 0, 0),
|
|
},
|
|
{
|
|
Name: "Resource provides default",
|
|
Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 0, 7)},
|
|
Expected: expectedTimeoutForValues(10, 7, 7, 7, 7),
|
|
},
|
|
{
|
|
Name: "Resource provides default and delete",
|
|
Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 15, 7)},
|
|
Expected: expectedTimeoutForValues(10, 7, 7, 15, 7),
|
|
},
|
|
{
|
|
Name: "Resource provides default, config overwrites other values",
|
|
Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 0, 13)},
|
|
Expected: expectedTimeoutForValues(10, 3, 13, 13, 13),
|
|
},
|
|
{
|
|
Name: "Resource has no config",
|
|
Rd: &ResourceData{},
|
|
Expected: expectedTimeoutForValues(0, 0, 0, 0, 0),
|
|
},
|
|
}
|
|
|
|
keys := timeoutKeys()
|
|
for i, c := range cases {
|
|
t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) {
|
|
|
|
for _, k := range keys {
|
|
got := c.Rd.Timeout(k)
|
|
var ex *time.Duration
|
|
switch k {
|
|
case TimeoutCreate:
|
|
ex = c.Expected.Create
|
|
case TimeoutRead:
|
|
ex = c.Expected.Read
|
|
case TimeoutUpdate:
|
|
ex = c.Expected.Update
|
|
case TimeoutDelete:
|
|
ex = c.Expected.Delete
|
|
case TimeoutDefault:
|
|
ex = c.Expected.Default
|
|
}
|
|
|
|
if got > 0 && ex == nil {
|
|
t.Fatalf("Unexpected value in (%s), case %d check 1:\n\texpected: %#v\n\tgot: %#v", k, i, ex, got)
|
|
}
|
|
if got == 0 && ex != nil {
|
|
t.Fatalf("Unexpected value in (%s), case %d check 2:\n\texpected: %#v\n\tgot: %#v", k, i, *ex, got)
|
|
}
|
|
|
|
// confirm values
|
|
if ex != nil {
|
|
if got != *ex {
|
|
t.Fatalf("Timeout %s case (%d) expected (%s), got (%s)", k, i, *ex, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResourceDataHasChange(t *testing.T) {
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Key string
|
|
Change bool
|
|
}{
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
|
|
Change: true,
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "availability_zone",
|
|
|
|
Change: false,
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"tags": &Schema{
|
|
Type: TypeMap,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"tags.Name": &terraform.ResourceAttrDiff{
|
|
Old: "foo",
|
|
New: "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "tags",
|
|
|
|
Change: true,
|
|
},
|
|
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int { return a.(int) },
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.80": "80",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "1",
|
|
New: "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "ports",
|
|
|
|
Change: true,
|
|
},
|
|
|
|
// https://github.com/hashicorp/terraform/issues/927
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int { return a.(int) },
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.80": "80",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"tags.foo": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "bar",
|
|
},
|
|
},
|
|
},
|
|
|
|
Key: "ports",
|
|
|
|
Change: false,
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := d.HasChange(tc.Key)
|
|
if actual != tc.Change {
|
|
t.Fatalf("Bad: %d %#v", i, actual)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceDataSet(t *testing.T) {
|
|
var testNilPtr *string
|
|
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Key string
|
|
Value interface{}
|
|
Err bool
|
|
GetKey string
|
|
GetValue interface{}
|
|
|
|
// GetPreProcess can be set to munge the return value before being
|
|
// compared to GetValue
|
|
GetPreProcess func(interface{}) interface{}
|
|
}{
|
|
// #0: Basic good
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
Value: "foo",
|
|
|
|
GetKey: "availability_zone",
|
|
GetValue: "foo",
|
|
},
|
|
|
|
// #1: Basic int
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"port": &Schema{
|
|
Type: TypeInt,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "port",
|
|
Value: 80,
|
|
|
|
GetKey: "port",
|
|
GetValue: 80,
|
|
},
|
|
|
|
// #2: Basic bool
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"vpc": &Schema{
|
|
Type: TypeBool,
|
|
Optional: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "vpc",
|
|
Value: true,
|
|
|
|
GetKey: "vpc",
|
|
GetValue: true,
|
|
},
|
|
|
|
// #3
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"vpc": &Schema{
|
|
Type: TypeBool,
|
|
Optional: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "vpc",
|
|
Value: false,
|
|
|
|
GetKey: "vpc",
|
|
GetValue: false,
|
|
},
|
|
|
|
// #4: Invalid type
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
Value: 80,
|
|
Err: true,
|
|
|
|
GetKey: "availability_zone",
|
|
GetValue: "",
|
|
},
|
|
|
|
// #5: List of primitives, set list
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: []int{1, 2, 5},
|
|
|
|
GetKey: "ports",
|
|
GetValue: []interface{}{1, 2, 5},
|
|
},
|
|
|
|
// #6: List of primitives, set list with error
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{1, "NOPE", 5},
|
|
Err: true,
|
|
|
|
GetKey: "ports",
|
|
GetValue: []interface{}{},
|
|
},
|
|
|
|
// #7: Set a list of maps
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"config_vars": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{
|
|
Type: TypeMap,
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "config_vars",
|
|
Value: []interface{}{
|
|
map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
map[string]interface{}{
|
|
"bar": "baz",
|
|
},
|
|
},
|
|
Err: false,
|
|
|
|
GetKey: "config_vars",
|
|
GetValue: []interface{}{
|
|
map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
map[string]interface{}{
|
|
"bar": "baz",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #8: Set, with list
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.0": "100",
|
|
"ports.1": "80",
|
|
"ports.2": "80",
|
|
},
|
|
},
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{100, 125, 125},
|
|
|
|
GetKey: "ports",
|
|
GetValue: []interface{}{100, 125},
|
|
},
|
|
|
|
// #9: Set, with Set
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.100": "100",
|
|
"ports.80": "80",
|
|
"ports.81": "81",
|
|
},
|
|
},
|
|
|
|
Key: "ports",
|
|
Value: &Set{
|
|
m: map[string]interface{}{
|
|
"1": 1,
|
|
"2": 2,
|
|
},
|
|
},
|
|
|
|
GetKey: "ports",
|
|
GetValue: []interface{}{1, 2},
|
|
},
|
|
|
|
// #10: Set single item
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "2",
|
|
"ports.100": "100",
|
|
"ports.80": "80",
|
|
},
|
|
},
|
|
|
|
Key: "ports.100",
|
|
Value: 256,
|
|
Err: true,
|
|
|
|
GetKey: "ports",
|
|
GetValue: []interface{}{100, 80},
|
|
},
|
|
|
|
// #11: Set with nested set
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"port": &Schema{
|
|
Type: TypeInt,
|
|
},
|
|
|
|
"set": &Schema{
|
|
Type: TypeSet,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Set: func(a interface{}) int {
|
|
return a.(map[string]interface{})["port"].(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Key: "ports",
|
|
Value: []interface{}{
|
|
map[string]interface{}{
|
|
"port": 80,
|
|
},
|
|
},
|
|
|
|
GetKey: "ports",
|
|
GetValue: []interface{}{
|
|
map[string]interface{}{
|
|
"port": 80,
|
|
"set": []interface{}{},
|
|
},
|
|
},
|
|
|
|
GetPreProcess: func(v interface{}) interface{} {
|
|
if v == nil {
|
|
return v
|
|
}
|
|
s, ok := v.([]interface{})
|
|
if !ok {
|
|
return v
|
|
}
|
|
for _, v := range s {
|
|
m, ok := v.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
if m["set"] == nil {
|
|
continue
|
|
}
|
|
if s, ok := m["set"].(*Set); ok {
|
|
m["set"] = s.List()
|
|
}
|
|
}
|
|
|
|
return v
|
|
},
|
|
},
|
|
|
|
// #12: List of floats, set list
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ratios": &Schema{
|
|
Type: TypeList,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeFloat},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ratios",
|
|
Value: []float64{1.0, 2.2, 5.5},
|
|
|
|
GetKey: "ratios",
|
|
GetValue: []interface{}{1.0, 2.2, 5.5},
|
|
},
|
|
|
|
// #12: Set of floats, set list
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ratios": &Schema{
|
|
Type: TypeSet,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeFloat},
|
|
Set: func(a interface{}) int {
|
|
return int(math.Float64bits(a.(float64)))
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "ratios",
|
|
Value: []float64{1.0, 2.2, 5.5},
|
|
|
|
GetKey: "ratios",
|
|
GetValue: []interface{}{1.0, 2.2, 5.5},
|
|
},
|
|
|
|
// #13: Basic pointer
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
Value: testPtrTo("foo"),
|
|
|
|
GetKey: "availability_zone",
|
|
GetValue: "foo",
|
|
},
|
|
|
|
// #14: Basic nil value
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
Value: testPtrTo(nil),
|
|
|
|
GetKey: "availability_zone",
|
|
GetValue: "",
|
|
},
|
|
|
|
// #15: Basic nil pointer
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Key: "availability_zone",
|
|
Value: testNilPtr,
|
|
|
|
GetKey: "availability_zone",
|
|
GetValue: "",
|
|
},
|
|
}
|
|
|
|
oldEnv := os.Getenv(PanicOnErr)
|
|
os.Setenv(PanicOnErr, "")
|
|
defer os.Setenv(PanicOnErr, oldEnv)
|
|
|
|
for i, tc := range cases {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
err = d.Set(tc.Key, tc.Value)
|
|
if err != nil != tc.Err {
|
|
t.Fatalf("%d err: %s", i, err)
|
|
}
|
|
|
|
v := d.Get(tc.GetKey)
|
|
if s, ok := v.(*Set); ok {
|
|
v = s.List()
|
|
}
|
|
|
|
if tc.GetPreProcess != nil {
|
|
v = tc.GetPreProcess(v)
|
|
}
|
|
|
|
if !reflect.DeepEqual(v, tc.GetValue) {
|
|
t.Fatalf("Get Bad: %d\n\n%#v", i, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceDataState_dynamicAttributes(t *testing.T) {
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Set map[string]interface{}
|
|
UnsafeSet map[string]string
|
|
Result *terraform.InstanceState
|
|
}{
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"__has_dynamic_attributes": {
|
|
Type: TypeString,
|
|
Optional: true,
|
|
},
|
|
|
|
"schema_field": {
|
|
Type: TypeString,
|
|
Required: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Set: map[string]interface{}{
|
|
"schema_field": "present",
|
|
},
|
|
|
|
UnsafeSet: map[string]string{
|
|
"test1": "value",
|
|
"test2": "value",
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"schema_field": "present",
|
|
"test1": "value",
|
|
"test2": "value",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
for k, v := range tc.Set {
|
|
d.Set(k, v)
|
|
}
|
|
|
|
for k, v := range tc.UnsafeSet {
|
|
d.UnsafeSetFieldRaw(k, v)
|
|
}
|
|
|
|
// Set an ID so that the state returned is not nil
|
|
idSet := false
|
|
if d.Id() == "" {
|
|
idSet = true
|
|
d.SetId("foo")
|
|
}
|
|
|
|
actual := d.State()
|
|
|
|
// If we set an ID, then undo what we did so the comparison works
|
|
if actual != nil && idSet {
|
|
actual.ID = ""
|
|
delete(actual.Attributes, "id")
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, tc.Result) {
|
|
t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceDataState_schema(t *testing.T) {
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
State *terraform.InstanceState
|
|
Diff *terraform.InstanceDiff
|
|
Set map[string]interface{}
|
|
Result *terraform.InstanceState
|
|
Partial []string
|
|
}{
|
|
// #0 Basic primitive in diff
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #1 Basic primitive set override
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"availability_zone": "bar",
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "bar",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #2
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"vpc": &Schema{
|
|
Type: TypeBool,
|
|
Optional: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Set: map[string]interface{}{
|
|
"vpc": true,
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"vpc": "true",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #3 Basic primitive with StateFunc set
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
StateFunc: func(interface{}) string { return "" },
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
NewExtra: "foo!",
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #4 List
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.0": "80",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "1",
|
|
New: "2",
|
|
},
|
|
"ports.1": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "2",
|
|
"ports.0": "80",
|
|
"ports.1": "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #5 List of resources
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ingress": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"from": &Schema{
|
|
Type: TypeInt,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ingress.#": "1",
|
|
"ingress.0.from": "80",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ingress.#": &terraform.ResourceAttrDiff{
|
|
Old: "1",
|
|
New: "2",
|
|
},
|
|
"ingress.0.from": &terraform.ResourceAttrDiff{
|
|
Old: "80",
|
|
New: "150",
|
|
},
|
|
"ingress.1.from": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ingress.#": "2",
|
|
"ingress.0.from": "150",
|
|
"ingress.1.from": "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #6 List of maps
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"config_vars": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{
|
|
Type: TypeMap,
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"config_vars.#": "2",
|
|
"config_vars.0.%": "2",
|
|
"config_vars.0.foo": "bar",
|
|
"config_vars.0.bar": "bar",
|
|
"config_vars.1.%": "1",
|
|
"config_vars.1.bar": "baz",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"config_vars.0.bar": &terraform.ResourceAttrDiff{
|
|
NewRemoved: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"config_vars": []map[string]interface{}{
|
|
map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
map[string]interface{}{
|
|
"baz": "bang",
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"config_vars.#": "2",
|
|
"config_vars.0.%": "1",
|
|
"config_vars.0.foo": "bar",
|
|
"config_vars.1.%": "1",
|
|
"config_vars.1.baz": "bang",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #7 List of maps with removal in diff
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"config_vars": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{
|
|
Type: TypeMap,
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"config_vars.#": "1",
|
|
"config_vars.0.FOO": "bar",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"config_vars.#": &terraform.ResourceAttrDiff{
|
|
Old: "1",
|
|
New: "0",
|
|
},
|
|
"config_vars.0.FOO": &terraform.ResourceAttrDiff{
|
|
Old: "bar",
|
|
NewRemoved: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"config_vars.#": "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #8 Basic state with other keys
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"id": "bar",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
ID: "bar",
|
|
Attributes: map[string]string{
|
|
"id": "bar",
|
|
"availability_zone": "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #9 Sets
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.100": "100",
|
|
"ports.80": "80",
|
|
"ports.81": "81",
|
|
},
|
|
},
|
|
|
|
Diff: nil,
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.80": "80",
|
|
"ports.81": "81",
|
|
"ports.100": "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #10
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Set: map[string]interface{}{
|
|
"ports": []interface{}{100, 80},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "2",
|
|
"ports.80": "80",
|
|
"ports.100": "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #11
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"order": &Schema{
|
|
Type: TypeInt,
|
|
},
|
|
|
|
"a": &Schema{
|
|
Type: TypeList,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
|
|
"b": &Schema{
|
|
Type: TypeList,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
},
|
|
Set: func(a interface{}) int {
|
|
m := a.(map[string]interface{})
|
|
return m["order"].(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "2",
|
|
"ports.10.order": "10",
|
|
"ports.10.a.#": "1",
|
|
"ports.10.a.0": "80",
|
|
"ports.20.order": "20",
|
|
"ports.20.b.#": "1",
|
|
"ports.20.b.0": "100",
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"ports": []interface{}{
|
|
map[string]interface{}{
|
|
"order": 20,
|
|
"b": []interface{}{100},
|
|
},
|
|
map[string]interface{}{
|
|
"order": 10,
|
|
"a": []interface{}{80},
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "2",
|
|
"ports.10.order": "10",
|
|
"ports.10.a.#": "1",
|
|
"ports.10.a.0": "80",
|
|
"ports.10.b.#": "0",
|
|
"ports.20.order": "20",
|
|
"ports.20.a.#": "0",
|
|
"ports.20.b.#": "1",
|
|
"ports.20.b.0": "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
/*
|
|
* PARTIAL STATES
|
|
*/
|
|
|
|
// #12 Basic primitive
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"availability_zone": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
RequiresNew: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Partial: []string{},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
|
|
// #13 List
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.0": "80",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "1",
|
|
New: "2",
|
|
},
|
|
"ports.1": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
Partial: []string{},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.0": "80",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #14
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Partial: []string{},
|
|
|
|
Set: map[string]interface{}{
|
|
"ports": []interface{}{},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
|
|
// #15 List of resources
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ingress": &Schema{
|
|
Type: TypeList,
|
|
Required: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"from": &Schema{
|
|
Type: TypeInt,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ingress.#": "1",
|
|
"ingress.0.from": "80",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ingress.#": &terraform.ResourceAttrDiff{
|
|
Old: "1",
|
|
New: "2",
|
|
},
|
|
"ingress.0.from": &terraform.ResourceAttrDiff{
|
|
Old: "80",
|
|
New: "150",
|
|
},
|
|
"ingress.1.from": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
Partial: []string{},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ingress.#": "1",
|
|
"ingress.0.from": "80",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #16 List of maps
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"config_vars": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{
|
|
Type: TypeMap,
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"config_vars.#": "2",
|
|
"config_vars.0.foo": "bar",
|
|
"config_vars.0.bar": "bar",
|
|
"config_vars.1.bar": "baz",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"config_vars.0.bar": &terraform.ResourceAttrDiff{
|
|
NewRemoved: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"config_vars": []map[string]interface{}{
|
|
map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
map[string]interface{}{
|
|
"baz": "bang",
|
|
},
|
|
},
|
|
},
|
|
|
|
Partial: []string{},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
// TODO: broken, shouldn't bar be removed?
|
|
"config_vars.#": "2",
|
|
"config_vars.0.%": "2",
|
|
"config_vars.0.foo": "bar",
|
|
"config_vars.0.bar": "bar",
|
|
"config_vars.1.%": "1",
|
|
"config_vars.1.bar": "baz",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #17 Sets
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.100": "100",
|
|
"ports.80": "80",
|
|
"ports.81": "81",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.120": &terraform.ResourceAttrDiff{
|
|
New: "120",
|
|
},
|
|
},
|
|
},
|
|
|
|
Partial: []string{},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.80": "80",
|
|
"ports.81": "81",
|
|
"ports.100": "100",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #18
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Partial: []string{},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
|
|
// #19 Maps
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"tags": &Schema{
|
|
Type: TypeMap,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"tags.Name": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"tags.%": "1",
|
|
"tags.Name": "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #20 empty computed map
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"tags": &Schema{
|
|
Type: TypeMap,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"tags.Name": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "foo",
|
|
},
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"tags": map[string]string{},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"tags.%": "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #21
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"foo": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"foo": &terraform.ResourceAttrDiff{
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{},
|
|
},
|
|
},
|
|
|
|
// #22
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"foo": &Schema{
|
|
Type: TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"foo": &terraform.ResourceAttrDiff{
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #23 Set of maps
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"index": &Schema{Type: TypeInt},
|
|
"uuids": &Schema{Type: TypeMap},
|
|
},
|
|
},
|
|
Set: func(a interface{}) int {
|
|
m := a.(map[string]interface{})
|
|
return m["index"].(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.10.uuids.#": &terraform.ResourceAttrDiff{
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"ports": []interface{}{
|
|
map[string]interface{}{
|
|
"index": 10,
|
|
"uuids": map[string]interface{}{
|
|
"80": "value",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.10.index": "10",
|
|
"ports.10.uuids.%": "1",
|
|
"ports.10.uuids.80": "value",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #24
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "3",
|
|
"ports.100": "100",
|
|
"ports.80": "80",
|
|
"ports.81": "81",
|
|
},
|
|
},
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
Old: "3",
|
|
New: "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #25
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeSet,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
Set: func(a interface{}) int {
|
|
return a.(int)
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Set: map[string]interface{}{
|
|
"ports": []interface{}{},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #26
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Schema{Type: TypeInt},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: nil,
|
|
|
|
Set: map[string]interface{}{
|
|
"ports": []interface{}{},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "0",
|
|
},
|
|
},
|
|
},
|
|
|
|
// #27 Set lists
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"ports": &Schema{
|
|
Type: TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &Resource{
|
|
Schema: map[string]*Schema{
|
|
"index": &Schema{Type: TypeInt},
|
|
"uuids": &Schema{Type: TypeMap},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
State: nil,
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
NewComputed: true,
|
|
},
|
|
},
|
|
},
|
|
|
|
Set: map[string]interface{}{
|
|
"ports": []interface{}{
|
|
map[string]interface{}{
|
|
"index": 10,
|
|
"uuids": map[string]interface{}{
|
|
"80": "value",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
Result: &terraform.InstanceState{
|
|
Attributes: map[string]string{
|
|
"ports.#": "1",
|
|
"ports.0.index": "10",
|
|
"ports.0.uuids.%": "1",
|
|
"ports.0.uuids.80": "value",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
for k, v := range tc.Set {
|
|
if err := d.Set(k, v); err != nil {
|
|
t.Fatalf("%d err: %s", i, err)
|
|
}
|
|
}
|
|
|
|
// Set an ID so that the state returned is not nil
|
|
idSet := false
|
|
if d.Id() == "" {
|
|
idSet = true
|
|
d.SetId("foo")
|
|
}
|
|
|
|
// If we have partial, then enable partial state mode.
|
|
if tc.Partial != nil {
|
|
d.Partial(true)
|
|
for _, k := range tc.Partial {
|
|
d.SetPartial(k)
|
|
}
|
|
}
|
|
|
|
actual := d.State()
|
|
|
|
// If we set an ID, then undo what we did so the comparison works
|
|
if actual != nil && idSet {
|
|
actual.ID = ""
|
|
delete(actual.Attributes, "id")
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, tc.Result) {
|
|
t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceData_nonStringValuesInMap(t *testing.T) {
|
|
cases := []struct {
|
|
Schema map[string]*Schema
|
|
Diff *terraform.InstanceDiff
|
|
MapFieldName string
|
|
ItemName string
|
|
ExpectedType string
|
|
}{
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"boolMap": &Schema{
|
|
Type: TypeMap,
|
|
Elem: TypeBool,
|
|
Optional: true,
|
|
},
|
|
},
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"boolMap.%": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "1",
|
|
},
|
|
"boolMap.boolField": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "true",
|
|
},
|
|
},
|
|
},
|
|
MapFieldName: "boolMap",
|
|
ItemName: "boolField",
|
|
ExpectedType: "bool",
|
|
},
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"intMap": &Schema{
|
|
Type: TypeMap,
|
|
Elem: TypeInt,
|
|
Optional: true,
|
|
},
|
|
},
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"intMap.%": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "1",
|
|
},
|
|
"intMap.intField": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "8",
|
|
},
|
|
},
|
|
},
|
|
MapFieldName: "intMap",
|
|
ItemName: "intField",
|
|
ExpectedType: "int",
|
|
},
|
|
{
|
|
Schema: map[string]*Schema{
|
|
"floatMap": &Schema{
|
|
Type: TypeMap,
|
|
Elem: TypeFloat,
|
|
Optional: true,
|
|
},
|
|
},
|
|
Diff: &terraform.InstanceDiff{
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
"floatMap.%": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "1",
|
|
},
|
|
"floatMap.floatField": &terraform.ResourceAttrDiff{
|
|
Old: "",
|
|
New: "8.22",
|
|
},
|
|
},
|
|
},
|
|
MapFieldName: "floatMap",
|
|
ItemName: "floatField",
|
|
ExpectedType: "float64",
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
d, err := schemaMap(c.Schema).Data(nil, c.Diff)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
m, ok := d.Get(c.MapFieldName).(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("expected %q to be castable to a map", c.MapFieldName)
|
|
}
|
|
field, ok := m[c.ItemName]
|
|
if !ok {
|
|
t.Fatalf("expected %q in the map", c.ItemName)
|
|
}
|
|
|
|
typeName := reflect.TypeOf(field).Name()
|
|
if typeName != c.ExpectedType {
|
|
t.Fatalf("expected %q to be %q, it is %q.",
|
|
c.ItemName, c.ExpectedType, typeName)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResourceDataSetConnInfo(t *testing.T) {
|
|
d := &ResourceData{}
|
|
d.SetId("foo")
|
|
d.SetConnInfo(map[string]string{
|
|
"foo": "bar",
|
|
})
|
|
|
|
expected := map[string]string{
|
|
"foo": "bar",
|
|
}
|
|
|
|
actual := d.State()
|
|
if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
func TestResourceDataSetMeta_Timeouts(t *testing.T) {
|
|
d := &ResourceData{}
|
|
d.SetId("foo")
|
|
|
|
rt := ResourceTimeout{
|
|
Create: DefaultTimeout(7 * time.Minute),
|
|
}
|
|
|
|
d.timeouts = &rt
|
|
|
|
expected := expectedForValues(7, 0, 0, 0, 0)
|
|
|
|
actual := d.State()
|
|
if !reflect.DeepEqual(actual.Meta[TimeoutKey], expected) {
|
|
t.Fatalf("Bad Meta_timeout match:\n\texpected: %#v\n\tgot: %#v", expected, actual.Meta[TimeoutKey])
|
|
}
|
|
}
|
|
|
|
func TestResourceDataSetId(t *testing.T) {
|
|
d := &ResourceData{}
|
|
d.SetId("foo")
|
|
|
|
actual := d.State()
|
|
|
|
// SetId should set both the ID field as well as the attribute, to aid in
|
|
// transitioning to the new type system.
|
|
if actual.ID != "foo" && actual.Attributes["id"] != "foo" {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
func TestResourceDataSetId_clear(t *testing.T) {
|
|
d := &ResourceData{
|
|
state: &terraform.InstanceState{ID: "bar"},
|
|
}
|
|
d.SetId("")
|
|
|
|
actual := d.State()
|
|
if actual != nil {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
func TestResourceDataSetId_override(t *testing.T) {
|
|
d := &ResourceData{
|
|
state: &terraform.InstanceState{ID: "bar"},
|
|
}
|
|
d.SetId("foo")
|
|
|
|
actual := d.State()
|
|
if actual.ID != "foo" {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
func TestResourceDataSetType(t *testing.T) {
|
|
d := &ResourceData{}
|
|
d.SetId("foo")
|
|
d.SetType("bar")
|
|
|
|
actual := d.State()
|
|
if v := actual.Ephemeral.Type; v != "bar" {
|
|
t.Fatalf("bad: %#v", actual)
|
|
}
|
|
}
|
|
|
|
func testPtrTo(raw interface{}) interface{} {
|
|
return &raw
|
|
}
|