2014-08-16 01:32:43 +02:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
2017-03-02 18:07:49 +01:00
|
|
|
"fmt"
|
2015-01-28 21:39:32 +01:00
|
|
|
"math"
|
2017-11-07 22:32:08 +01:00
|
|
|
"os"
|
2014-08-16 01:32:43 +02:00
|
|
|
"reflect"
|
|
|
|
"testing"
|
2017-03-02 18:07:49 +01:00
|
|
|
"time"
|
2014-08-16 01:32:43 +02:00
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestResourceDataGet(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Schema map[string]*Schema
|
2014-09-17 02:07:13 +02:00
|
|
|
State *terraform.InstanceState
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff *terraform.InstanceDiff
|
2014-08-16 01:32:43 +02:00
|
|
|
Key string
|
|
|
|
Value interface{}
|
|
|
|
}{
|
2014-12-12 23:21:20 +01:00
|
|
|
// #0
|
2014-08-19 00:10:53 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-19 00:10:53 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
2014-08-28 00:43:47 +02:00
|
|
|
Old: "foo",
|
|
|
|
New: "bar",
|
2014-08-19 00:10:53 +02:00
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-08-22 08:03:04 +02:00
|
|
|
Key: "availability_zone",
|
|
|
|
Value: "",
|
2014-08-19 00:10:53 +02:00
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #1
|
2014-08-16 01:32:43 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-16 01:32:43 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
|
|
|
|
Value: "foo",
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #2
|
2014-08-22 17:57:44 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-22 17:57:44 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
2014-08-22 21:09:06 +02:00
|
|
|
Old: "",
|
|
|
|
New: "foo!",
|
|
|
|
NewExtra: "foo",
|
2014-08-22 17:57:44 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
Value: "foo",
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #3
|
2014-08-16 01:32:43 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-16 01:32:43 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
|
|
|
|
Value: "bar",
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #4
|
2014-08-28 00:43:47 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-28 00:43:47 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-28 00:43:47 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "foo",
|
|
|
|
New: "bar",
|
|
|
|
NewComputed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
Value: "",
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #5
|
2014-08-16 01:32:43 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"port": &Schema{
|
|
|
|
Type: TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-16 01:32:43 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"port": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Key: "port",
|
|
|
|
|
|
|
|
Value: 80,
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #6
|
2014-08-16 01:32:43 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-16 01:32:43 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ports.#": "3",
|
|
|
|
"ports.0": "1",
|
|
|
|
"ports.1": "2",
|
|
|
|
"ports.2": "5",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "ports.1",
|
|
|
|
|
|
|
|
Value: 2,
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #7
|
2014-08-16 01:32:43 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-16 01:32:43 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ports.#": "3",
|
|
|
|
"ports.0": "1",
|
|
|
|
"ports.1": "2",
|
|
|
|
"ports.2": "5",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "ports.#",
|
|
|
|
|
|
|
|
Value: 3,
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #8
|
2014-08-16 01:32:43 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
|
|
|
Key: "ports.#",
|
|
|
|
|
|
|
|
Value: 0,
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #9
|
2014-08-16 01:32:43 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-16 01:32:43 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ports.#": "3",
|
|
|
|
"ports.0": "1",
|
|
|
|
"ports.1": "2",
|
|
|
|
"ports.2": "5",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "ports",
|
|
|
|
|
|
|
|
Value: []interface{}{1, 2, 5},
|
|
|
|
},
|
2014-08-16 02:39:08 +02:00
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #10
|
2014-08-16 02:39:08 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ingress": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Resource{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"from": &Schema{
|
|
|
|
Type: TypeInt,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-16 02:39:08 +02:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #11
|
2014-08-16 02:39:08 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ingress": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Resource{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"from": &Schema{
|
|
|
|
Type: TypeInt,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-16 02:39:08 +02:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-16 22:32:21 +02:00
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #12 Computed get
|
2014-08-16 22:32:21 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-16 22:32:21 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
|
|
|
|
Value: "foo",
|
|
|
|
},
|
2014-08-16 22:55:10 +02:00
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #13 Full object
|
2014-08-16 22:55:10 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-16 22:55:10 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "",
|
|
|
|
|
|
|
|
Value: map[string]interface{}{
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
2014-08-18 23:00:03 +02:00
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #14 List of maps
|
2014-08-18 23:00:03 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"config_vars": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{
|
|
|
|
Type: TypeMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-18 23:00:03 +02:00
|
|
|
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",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #15 List of maps in state
|
2014-08-18 23:00:03 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"config_vars": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{
|
|
|
|
Type: TypeMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-18 23:00:03 +02:00
|
|
|
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",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-19 04:12:49 +02:00
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #16 List of maps with removal in diff
|
2014-08-19 04:12:49 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"config_vars": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{
|
|
|
|
Type: TypeMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-19 04:12:49 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"config_vars.#": "1",
|
|
|
|
"config_vars.0.FOO": "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-19 04:12:49 +02:00
|
|
|
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{}{},
|
|
|
|
},
|
2014-08-21 02:51:27 +02:00
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #17 Sets
|
2014-08-21 02:51:27 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
Set: func(a interface{}) int {
|
|
|
|
return a.(int)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-21 02:51:27 +02:00
|
|
|
Attributes: map[string]string{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "1",
|
|
|
|
"ports.80": "80",
|
2014-08-21 02:51:27 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Key: "ports",
|
|
|
|
|
|
|
|
Value: []interface{}{80},
|
|
|
|
},
|
2014-10-21 00:32:30 +02:00
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
// #18
|
2014-10-21 00:32:30 +02:00
|
|
|
{
|
|
|
|
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{
|
2014-12-12 23:21:20 +01:00
|
|
|
"data.#": "1",
|
|
|
|
"data.10.index": "10",
|
|
|
|
"data.10.value": "50",
|
2014-10-21 00:32:30 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
2014-12-12 23:21:20 +01:00
|
|
|
"data.10.value": &terraform.ResourceAttrDiff{
|
2014-10-21 00:32:30 +02:00
|
|
|
Old: "50",
|
|
|
|
New: "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "data",
|
|
|
|
|
|
|
|
Value: []interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"index": 10,
|
|
|
|
"value": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-01-09 03:47:35 +01:00
|
|
|
|
|
|
|
// #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{}{},
|
|
|
|
},
|
2015-01-28 21:22:47 +01:00
|
|
|
|
|
|
|
// #20 Float zero
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ratio": &Schema{
|
|
|
|
Type: TypeFloat,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Key: "ratio",
|
|
|
|
|
2015-01-28 22:20:14 +01:00
|
|
|
Value: 0.0,
|
2015-01-28 21:22:47 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
// #21 Float given
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ratio": &Schema{
|
|
|
|
Type: TypeFloat,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: &terraform.InstanceState{
|
|
|
|
Attributes: map[string]string{
|
2015-02-17 20:38:56 +01:00
|
|
|
"ratio": "0.5",
|
2015-01-28 21:22:47 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Key: "ratio",
|
|
|
|
|
2015-01-28 22:20:14 +01:00
|
|
|
Value: 0.5,
|
2015-01-28 21:22:47 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
// #22 Float diff
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ratio": &Schema{
|
|
|
|
Type: TypeFloat,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: &terraform.InstanceState{
|
|
|
|
Attributes: map[string]string{
|
2015-02-17 20:38:56 +01:00
|
|
|
"ratio": "-0.5",
|
2015-01-28 21:22:47 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Diff: &terraform.InstanceDiff{
|
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ratio": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "-0.5",
|
|
|
|
New: "33.0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "ratio",
|
|
|
|
|
2015-01-28 22:20:14 +01:00
|
|
|
Value: 33.0,
|
2015-01-28 21:22:47 +01:00
|
|
|
},
|
2015-06-26 06:52:49 +02:00
|
|
|
|
|
|
|
// #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},
|
|
|
|
},
|
2014-08-16 01:32:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2014-08-21 02:51:27 +02:00
|
|
|
if s, ok := v.(*Set); ok {
|
|
|
|
v = s.List()
|
|
|
|
}
|
|
|
|
|
2014-08-16 01:32:43 +02:00
|
|
|
if !reflect.DeepEqual(v, tc.Value) {
|
2015-01-09 03:02:19 +01:00
|
|
|
t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value)
|
2014-08-16 01:32:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-17 00:02:51 +02:00
|
|
|
|
2014-08-18 18:58:44 +02:00
|
|
|
func TestResourceDataGetChange(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Schema map[string]*Schema
|
2014-09-17 02:07:13 +02:00
|
|
|
State *terraform.InstanceState
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff *terraform.InstanceDiff
|
2014-08-18 18:58:44 +02:00
|
|
|
Key string
|
|
|
|
OldValue interface{}
|
|
|
|
NewValue interface{}
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-18 18:58:44 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
|
2014-08-22 08:03:04 +02:00
|
|
|
OldValue: "",
|
2014-08-18 18:58:44 +02:00
|
|
|
NewValue: "foo",
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-18 18:58:44 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-18 18:58:44 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-22 08:03:04 +02:00
|
|
|
func TestResourceDataGetOk(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Schema map[string]*Schema
|
2014-09-17 02:07:13 +02:00
|
|
|
State *terraform.InstanceState
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff *terraform.InstanceDiff
|
2014-08-22 08:03:04 +02:00
|
|
|
Key string
|
|
|
|
Value interface{}
|
|
|
|
Ok bool
|
|
|
|
}{
|
|
|
|
/*
|
|
|
|
* Primitives
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-22 08:03:04 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
Value: "",
|
2015-02-18 01:55:39 +01:00
|
|
|
Ok: false,
|
2014-08-22 08:03:04 +02:00
|
|
|
},
|
|
|
|
|
2015-01-14 20:50:58 +01:00
|
|
|
{
|
|
|
|
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,
|
|
|
|
},
|
|
|
|
|
2014-08-22 08:03:04 +02:00
|
|
|
{
|
|
|
|
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",
|
2015-01-09 03:47:35 +01:00
|
|
|
Value: []interface{}{},
|
2014-08-22 08:03:04 +02:00
|
|
|
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,
|
|
|
|
},
|
2015-02-18 01:58:47 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
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,
|
|
|
|
},
|
2015-05-04 21:58:12 +02:00
|
|
|
|
|
|
|
// 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,
|
|
|
|
},
|
2014-08-22 08:03:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2015-05-04 21:58:12 +02:00
|
|
|
t.Fatalf("%d: expected ok: %t, got: %t", i, tc.Ok, ok)
|
2014-08-22 08:03:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-03 18:05:19 +02:00
|
|
|
func TestResourceDataGetOkExists(t *testing.T) {
|
2017-08-02 17:18:59 +02:00
|
|
|
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,
|
|
|
|
},
|
2017-08-03 20:14:39 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
Name: "bool-literal-empty",
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": {
|
|
|
|
Type: TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
2017-08-03 23:53:07 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": {
|
|
|
|
Old: "",
|
|
|
|
New: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-08-03 20:14:39 +02:00
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
Value: false,
|
2017-08-03 23:53:07 +02:00
|
|
|
Ok: true,
|
2017-08-03 20:14:39 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
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,
|
|
|
|
},
|
2017-08-02 17:18:59 +02:00
|
|
|
}
|
|
|
|
|
2017-08-03 23:53:07 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
})
|
2017-08-02 17:18:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 18:07:49 +01:00
|
|
|
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),
|
|
|
|
},
|
2018-03-22 20:10:43 +01:00
|
|
|
{
|
|
|
|
Name: "Resource has no config",
|
|
|
|
Rd: &ResourceData{},
|
|
|
|
Expected: expectedTimeoutForValues(0, 0, 0, 0, 0),
|
|
|
|
},
|
2017-03-02 18:07:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2018-03-22 20:10:43 +01:00
|
|
|
t.Fatalf("Timeout %s case (%d) expected (%s), got (%s)", k, i, *ex, got)
|
2017-03-02 18:07:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-18 19:00:41 +02:00
|
|
|
func TestResourceDataHasChange(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Schema map[string]*Schema
|
2014-09-17 02:07:13 +02:00
|
|
|
State *terraform.InstanceState
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff *terraform.InstanceDiff
|
2014-08-18 19:00:41 +02:00
|
|
|
Key string
|
|
|
|
Change bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-18 19:00:41 +02:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-18 19:00:41 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-18 19:00:41 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "availability_zone",
|
|
|
|
|
|
|
|
Change: false,
|
|
|
|
},
|
2014-10-21 09:28:41 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
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{
|
2014-10-21 20:00:12 +02:00
|
|
|
Old: "foo",
|
|
|
|
New: "foo",
|
2014-10-21 09:28:41 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "tags",
|
|
|
|
|
|
|
|
Change: true,
|
|
|
|
},
|
2015-01-16 14:13:40 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
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,
|
|
|
|
},
|
2015-02-18 00:43:19 +01:00
|
|
|
|
|
|
|
// 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,
|
|
|
|
},
|
2014-08-18 19:00:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-17 00:02:51 +02:00
|
|
|
func TestResourceDataSet(t *testing.T) {
|
2015-03-03 08:37:43 +01:00
|
|
|
var testNilPtr *string
|
|
|
|
|
2014-08-17 00:02:51 +02:00
|
|
|
cases := []struct {
|
|
|
|
Schema map[string]*Schema
|
2014-09-17 02:07:13 +02:00
|
|
|
State *terraform.InstanceState
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff *terraform.InstanceDiff
|
2014-08-17 00:02:51 +02:00
|
|
|
Key string
|
|
|
|
Value interface{}
|
|
|
|
Err bool
|
|
|
|
GetKey string
|
|
|
|
GetValue interface{}
|
2015-01-09 20:51:29 +01:00
|
|
|
|
|
|
|
// GetPreProcess can be set to munge the return value before being
|
|
|
|
// compared to GetValue
|
|
|
|
GetPreProcess func(interface{}) interface{}
|
2014-08-17 00:02:51 +02:00
|
|
|
}{
|
2015-01-09 20:51:29 +01:00
|
|
|
// #0: Basic good
|
2014-08-17 00:02:51 +02:00
|
|
|
{
|
|
|
|
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",
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #1: Basic int
|
2014-08-17 00:02:51 +02:00
|
|
|
{
|
|
|
|
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,
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #2: Basic bool
|
2014-08-20 01:46:36 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"vpc": &Schema{
|
|
|
|
Type: TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Key: "vpc",
|
|
|
|
Value: true,
|
|
|
|
|
|
|
|
GetKey: "vpc",
|
|
|
|
GetValue: true,
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #3
|
2014-08-20 01:46:36 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"vpc": &Schema{
|
|
|
|
Type: TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Key: "vpc",
|
|
|
|
Value: false,
|
|
|
|
|
|
|
|
GetKey: "vpc",
|
|
|
|
GetValue: false,
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #4: Invalid type
|
2014-08-17 00:02:51 +02:00
|
|
|
{
|
|
|
|
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",
|
2014-08-22 08:03:04 +02:00
|
|
|
GetValue: "",
|
2014-08-17 00:02:51 +02:00
|
|
|
},
|
2014-08-17 20:38:16 +02:00
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #5: List of primitives, set list
|
2014-08-17 20:38:16 +02:00
|
|
|
{
|
|
|
|
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},
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #6: List of primitives, set list with error
|
2014-08-17 20:38:16 +02:00
|
|
|
{
|
|
|
|
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{}{},
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #7: Set a list of maps
|
2014-08-18 23:00:03 +02:00
|
|
|
{
|
|
|
|
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",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-19 00:17:18 +02:00
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #8: Set, with list
|
2014-08-21 03:11:14 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
Set: func(a interface{}) int {
|
|
|
|
return a.(int)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-21 03:11:14 +02:00
|
|
|
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},
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #9: Set, with Set
|
2014-08-21 03:11:14 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
Set: func(a interface{}) int {
|
|
|
|
return a.(int)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-21 03:11:14 +02:00
|
|
|
Attributes: map[string]string{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "3",
|
|
|
|
"ports.100": "100",
|
|
|
|
"ports.80": "80",
|
|
|
|
"ports.81": "81",
|
2014-08-21 03:11:14 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Key: "ports",
|
|
|
|
Value: &Set{
|
2015-11-18 11:24:04 +01:00
|
|
|
m: map[string]interface{}{
|
|
|
|
"1": 1,
|
|
|
|
"2": 2,
|
2014-08-21 03:11:14 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
GetKey: "ports",
|
|
|
|
GetValue: []interface{}{1, 2},
|
|
|
|
},
|
|
|
|
|
2015-01-09 20:51:29 +01:00
|
|
|
// #10: Set single item
|
2014-08-21 03:11:14 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
Set: func(a interface{}) int {
|
|
|
|
return a.(int)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-21 03:11:14 +02:00
|
|
|
Attributes: map[string]string{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "2",
|
|
|
|
"ports.100": "100",
|
|
|
|
"ports.80": "80",
|
2014-08-21 03:11:14 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-12-12 23:21:20 +01:00
|
|
|
Key: "ports.100",
|
2014-08-21 03:11:14 +02:00
|
|
|
Value: 256,
|
|
|
|
Err: true,
|
|
|
|
|
|
|
|
GetKey: "ports",
|
2015-11-18 11:24:04 +01:00
|
|
|
GetValue: []interface{}{100, 80},
|
2014-08-21 03:11:14 +02:00
|
|
|
},
|
2015-01-09 20:51:29 +01:00
|
|
|
|
|
|
|
// #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
|
|
|
|
},
|
|
|
|
},
|
2015-01-28 21:22:47 +01:00
|
|
|
|
|
|
|
// #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},
|
|
|
|
},
|
2015-03-03 06:06:14 +01:00
|
|
|
|
|
|
|
// #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",
|
2015-03-03 08:37:43 +01:00
|
|
|
Value: testNilPtr,
|
2015-03-03 06:06:14 +01:00
|
|
|
|
|
|
|
GetKey: "availability_zone",
|
|
|
|
GetValue: "",
|
|
|
|
},
|
2018-12-05 09:26:57 +01:00
|
|
|
|
|
|
|
// #16: Set in a list
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Elem: &Resource{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"set": &Schema{
|
|
|
|
Type: TypeSet,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
Set: func(a interface{}) int {
|
|
|
|
return a.(int)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
|
|
|
Key: "ports",
|
|
|
|
Value: []interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"set": []interface{}{
|
|
|
|
1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
GetKey: "ports",
|
|
|
|
GetValue: []interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"set": []interface{}{
|
|
|
|
1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
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
|
|
|
|
},
|
|
|
|
},
|
2014-08-17 00:02:51 +02:00
|
|
|
}
|
|
|
|
|
2017-11-07 22:32:08 +01:00
|
|
|
oldEnv := os.Getenv(PanicOnErr)
|
|
|
|
os.Setenv(PanicOnErr, "")
|
|
|
|
defer os.Setenv(PanicOnErr, oldEnv)
|
|
|
|
|
2014-08-17 00:02:51 +02:00
|
|
|
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)
|
2015-10-08 14:48:04 +02:00
|
|
|
if err != nil != tc.Err {
|
2014-08-17 00:02:51 +02:00
|
|
|
t.Fatalf("%d err: %s", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
v := d.Get(tc.GetKey)
|
2014-08-21 03:11:14 +02:00
|
|
|
if s, ok := v.(*Set); ok {
|
|
|
|
v = s.List()
|
|
|
|
}
|
2015-01-09 20:51:29 +01:00
|
|
|
|
|
|
|
if tc.GetPreProcess != nil {
|
|
|
|
v = tc.GetPreProcess(v)
|
|
|
|
}
|
|
|
|
|
2014-08-17 00:02:51 +02:00
|
|
|
if !reflect.DeepEqual(v, tc.GetValue) {
|
|
|
|
t.Fatalf("Get Bad: %d\n\n%#v", i, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-17 23:12:54 +02:00
|
|
|
|
core: Allow dynamic attributes in helper/schema
The helper/schema framework for building providers previously validated
in all cases that each field being set in state was in the schema.
However, in order to support remote state in a usable fashion, the need
has arisen for the top level attributes of the resource to be created
dynamically. In order to still be able to use helper/schema, this commit
adds the capability to assign additional fields.
Though I do not forsee this being used by providers other than remote
state (and that eventually may move into Terraform Core rather than
being a provider), the usage and semantics are:
To opt into dynamic attributes, add a schema attribute named
"__has_dynamic_attributes", and make it an optional string with no
default value, in order that it does not appear in diffs:
"__has_dynamic_attributes": {
Type: schema.TypeString
Optional: true
}
In the read callback, use the d.UnsafeSetFieldRaw(key, value) function
to set the dynamic attributes.
Note that other fields in the schema _are_ copied into state, and that
the names of the schema fields cannot currently be used as dynamic
attribute names, as we check to ensure a value is not already set for a
given key.
2016-06-11 12:23:32 +02:00
|
|
|
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) {
|
2014-08-17 23:12:54 +02:00
|
|
|
cases := []struct {
|
2014-08-27 05:19:44 +02:00
|
|
|
Schema map[string]*Schema
|
2014-09-17 02:07:13 +02:00
|
|
|
State *terraform.InstanceState
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff *terraform.InstanceDiff
|
2014-08-27 05:19:44 +02:00
|
|
|
Set map[string]interface{}
|
2014-09-17 02:07:13 +02:00
|
|
|
Result *terraform.InstanceState
|
2014-08-27 05:19:44 +02:00
|
|
|
Partial []string
|
2014-08-17 23:12:54 +02:00
|
|
|
}{
|
2015-01-10 00:07:02 +01:00
|
|
|
// #0 Basic primitive in diff
|
2014-08-17 23:12:54 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #1 Basic primitive set override
|
2014-08-17 23:12:54 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Set: map[string]interface{}{
|
|
|
|
"availability_zone": "bar",
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #2
|
2014-08-20 01:46:36 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"vpc": &Schema{
|
|
|
|
Type: TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
|
|
|
Set: map[string]interface{}{
|
|
|
|
"vpc": true,
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-20 01:46:36 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"vpc": "true",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #3 Basic primitive with StateFunc set
|
2014-08-22 17:45:54 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
StateFunc: func(interface{}) string { return "" },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-22 17:45:54 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
2014-08-22 21:09:06 +02:00
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
NewExtra: "foo!",
|
2014-08-22 17:45:54 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-22 17:45:54 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #4 List
|
2014-08-17 23:12:54 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ports.#": "1",
|
|
|
|
"ports.0": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "2",
|
|
|
|
},
|
|
|
|
"ports.1": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "100",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ports.#": "2",
|
|
|
|
"ports.0": "80",
|
|
|
|
"ports.1": "100",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #5 List of resources
|
2014-08-17 23:12:54 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ingress": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Resource{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"from": &Schema{
|
|
|
|
Type: TypeInt,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ingress.#": "1",
|
|
|
|
"ingress.0.from": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-17 23:12:54 +02:00
|
|
|
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",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-17 23:12:54 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ingress.#": "2",
|
|
|
|
"ingress.0.from": "150",
|
|
|
|
"ingress.1.from": "100",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-18 23:00:03 +02:00
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #6 List of maps
|
2014-08-18 23:00:03 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"config_vars": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{
|
|
|
|
Type: TypeMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-18 23:00:03 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"config_vars.#": "2",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"config_vars.0.%": "2",
|
2014-08-18 23:00:03 +02:00
|
|
|
"config_vars.0.foo": "bar",
|
2014-08-19 00:07:09 +02:00
|
|
|
"config_vars.0.bar": "bar",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"config_vars.1.%": "1",
|
2014-08-18 23:00:03 +02:00
|
|
|
"config_vars.1.bar": "baz",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-19 00:07:09 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"config_vars.0.bar": &terraform.ResourceAttrDiff{
|
|
|
|
NewRemoved: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-08-18 23:00:03 +02:00
|
|
|
Set: map[string]interface{}{
|
2015-01-09 03:02:19 +01:00
|
|
|
"config_vars": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": "bar",
|
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"baz": "bang",
|
|
|
|
},
|
2014-08-18 23:00:03 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-18 23:00:03 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"config_vars.#": "2",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"config_vars.0.%": "1",
|
2014-08-18 23:00:03 +02:00
|
|
|
"config_vars.0.foo": "bar",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"config_vars.1.%": "1",
|
2014-08-18 23:00:03 +02:00
|
|
|
"config_vars.1.baz": "bang",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-19 04:12:49 +02:00
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #7 List of maps with removal in diff
|
2014-08-19 04:12:49 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"config_vars": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{
|
|
|
|
Type: TypeMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-19 04:12:49 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"config_vars.#": "1",
|
|
|
|
"config_vars.0.FOO": "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-19 04:12:49 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"config_vars.#": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "0",
|
|
|
|
},
|
|
|
|
"config_vars.0.FOO": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "bar",
|
|
|
|
NewRemoved: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-10-11 00:58:38 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"config_vars.#": "0",
|
|
|
|
},
|
2014-08-19 04:12:49 +02:00
|
|
|
},
|
|
|
|
},
|
2014-08-20 19:37:59 +02:00
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #8 Basic state with other keys
|
2014-08-20 19:37:59 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-20 19:37:59 +02:00
|
|
|
ID: "bar",
|
|
|
|
Attributes: map[string]string{
|
|
|
|
"id": "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-20 19:37:59 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-20 19:37:59 +02:00
|
|
|
ID: "bar",
|
|
|
|
Attributes: map[string]string{
|
|
|
|
"id": "bar",
|
|
|
|
"availability_zone": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-21 02:51:27 +02:00
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #9 Sets
|
2014-08-21 02:51:27 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
Set: func(a interface{}) int {
|
|
|
|
return a.(int)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-21 02:51:27 +02:00
|
|
|
Attributes: map[string]string{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "3",
|
|
|
|
"ports.100": "100",
|
|
|
|
"ports.80": "80",
|
|
|
|
"ports.81": "81",
|
2014-08-21 02:51:27 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Diff: nil,
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-21 02:51:27 +02:00
|
|
|
Attributes: map[string]string{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "3",
|
|
|
|
"ports.80": "80",
|
|
|
|
"ports.81": "81",
|
|
|
|
"ports.100": "100",
|
2014-08-21 02:51:27 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-27 05:19:44 +02:00
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #10
|
2014-10-11 19:40:54 +02:00
|
|
|
{
|
|
|
|
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{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "2",
|
|
|
|
"ports.80": "80",
|
|
|
|
"ports.100": "100",
|
2014-10-11 19:40:54 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #11
|
2014-10-11 19:40:54 +02:00
|
|
|
{
|
|
|
|
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{
|
2014-12-12 23:21:20 +01:00
|
|
|
"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",
|
2014-10-11 19:40:54 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
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{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "2",
|
|
|
|
"ports.10.order": "10",
|
|
|
|
"ports.10.a.#": "1",
|
|
|
|
"ports.10.a.0": "80",
|
2015-01-10 21:18:32 +01:00
|
|
|
"ports.10.b.#": "0",
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.20.order": "20",
|
2015-01-10 21:18:32 +01:00
|
|
|
"ports.20.a.#": "0",
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.20.b.#": "1",
|
|
|
|
"ports.20.b.0": "100",
|
2014-10-11 19:40:54 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-08-27 05:19:44 +02:00
|
|
|
/*
|
|
|
|
* PARTIAL STATES
|
|
|
|
*/
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #12 Basic primitive
|
2014-08-27 05:19:44 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"availability_zone": &Schema{
|
|
|
|
Type: TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
State: nil,
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"availability_zone": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "foo",
|
|
|
|
RequiresNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Partial: []string{},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2015-01-15 19:35:44 +01:00
|
|
|
Attributes: map[string]string{},
|
2014-08-27 05:19:44 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #13 List
|
2014-08-27 05:19:44 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ports.#": "1",
|
|
|
|
"ports.0": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"ports.#": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "1",
|
|
|
|
New: "2",
|
|
|
|
},
|
|
|
|
"ports.1": &terraform.ResourceAttrDiff{
|
|
|
|
Old: "",
|
|
|
|
New: "100",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Partial: []string{},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ports.#": "1",
|
|
|
|
"ports.0": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #14
|
2014-10-11 00:58:38 +02:00
|
|
|
{
|
|
|
|
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{
|
2015-01-15 19:35:44 +01:00
|
|
|
Attributes: map[string]string{},
|
2014-10-11 00:58:38 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #15 List of resources
|
2014-08-27 05:19:44 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ingress": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Required: true,
|
|
|
|
Elem: &Resource{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"from": &Schema{
|
|
|
|
Type: TypeInt,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ingress.#": "1",
|
|
|
|
"ingress.0.from": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-27 05:19:44 +02:00
|
|
|
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{},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"ingress.#": "1",
|
|
|
|
"ingress.0.from": "80",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #16 List of maps
|
2014-08-27 05:19:44 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"config_vars": &Schema{
|
|
|
|
Type: TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{
|
|
|
|
Type: TypeMap,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
|
|
|
"config_vars.#": "2",
|
|
|
|
"config_vars.0.foo": "bar",
|
|
|
|
"config_vars.0.bar": "bar",
|
|
|
|
"config_vars.1.bar": "baz",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
|
|
|
"config_vars.0.bar": &terraform.ResourceAttrDiff{
|
|
|
|
NewRemoved: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Set: map[string]interface{}{
|
2015-01-09 03:02:19 +01:00
|
|
|
"config_vars": []map[string]interface{}{
|
|
|
|
map[string]interface{}{
|
|
|
|
"foo": "bar",
|
|
|
|
},
|
|
|
|
map[string]interface{}{
|
|
|
|
"baz": "bang",
|
|
|
|
},
|
2014-08-27 05:19:44 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Partial: []string{},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
2015-01-09 03:02:19 +01:00
|
|
|
// TODO: broken, shouldn't bar be removed?
|
2014-08-27 05:19:44 +02:00
|
|
|
"config_vars.#": "2",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"config_vars.0.%": "2",
|
2014-08-27 05:19:44 +02:00
|
|
|
"config_vars.0.foo": "bar",
|
|
|
|
"config_vars.0.bar": "bar",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"config_vars.1.%": "1",
|
2014-08-27 05:19:44 +02:00
|
|
|
"config_vars.1.bar": "baz",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #17 Sets
|
2014-08-27 05:19:44 +02:00
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"ports": &Schema{
|
|
|
|
Type: TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &Schema{Type: TypeInt},
|
|
|
|
Set: func(a interface{}) int {
|
|
|
|
return a.(int)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
State: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "3",
|
|
|
|
"ports.100": "100",
|
|
|
|
"ports.80": "80",
|
|
|
|
"ports.81": "81",
|
2014-08-27 05:19:44 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2014-09-18 01:33:24 +02:00
|
|
|
Diff: &terraform.InstanceDiff{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]*terraform.ResourceAttrDiff{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.120": &terraform.ResourceAttrDiff{
|
2014-08-27 05:19:44 +02:00
|
|
|
New: "120",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Partial: []string{},
|
|
|
|
|
2014-09-17 02:07:13 +02:00
|
|
|
Result: &terraform.InstanceState{
|
2014-08-27 05:19:44 +02:00
|
|
|
Attributes: map[string]string{
|
2014-12-12 23:21:20 +01:00
|
|
|
"ports.#": "3",
|
|
|
|
"ports.80": "80",
|
|
|
|
"ports.81": "81",
|
|
|
|
"ports.100": "100",
|
2014-08-27 05:19:44 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-10-11 00:58:38 +02:00
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #18
|
2014-10-11 00:58:38 +02:00
|
|
|
{
|
|
|
|
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{
|
2015-01-15 19:35:44 +01:00
|
|
|
Attributes: map[string]string{},
|
2014-10-11 00:58:38 +02:00
|
|
|
},
|
|
|
|
},
|
2014-10-21 09:06:37 +02:00
|
|
|
|
2015-01-10 00:07:02 +01:00
|
|
|
// #19 Maps
|
2014-10-21 09:06:37 +02:00
|
|
|
{
|
|
|
|
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{
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"tags.%": "1",
|
2014-10-21 09:06:37 +02:00
|
|
|
"tags.Name": "foo",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-05-06 17:21:22 +02:00
|
|
|
// #20 empty computed map
|
2014-10-21 09:06:37 +02:00
|
|
|
{
|
|
|
|
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{
|
2015-05-06 17:21:22 +02:00
|
|
|
Attributes: map[string]string{
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"tags.%": "0",
|
2015-05-06 17:21:22 +02:00
|
|
|
},
|
2014-10-21 09:06:37 +02:00
|
|
|
},
|
|
|
|
},
|
2015-01-15 19:35:44 +01:00
|
|
|
|
|
|
|
// #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{},
|
|
|
|
},
|
|
|
|
},
|
2015-01-15 20:08:06 +01:00
|
|
|
|
|
|
|
// #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",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-01-15 23:12:24 +01:00
|
|
|
|
|
|
|
// #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",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"ports.10.uuids.%": "1",
|
2015-01-15 23:12:24 +01:00
|
|
|
"ports.10.uuids.80": "value",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-02-17 20:38:56 +01:00
|
|
|
|
|
|
|
// #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",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-02-18 23:10:12 +01:00
|
|
|
|
|
|
|
// #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",
|
core: Use .% instead of .# for maps in state
The flatmapped representation of state prior to this commit encoded maps
and lists (and therefore by extension, sets) with a key corresponding to
the number of elements, or the unknown variable indicator under a .# key
and then individual items. For example, the list ["a", "b", "c"] would
have been encoded as:
listname.# = 3
listname.0 = "a"
listname.1 = "b"
listname.2 = "c"
And the map {"key1": "value1", "key2", "value2"} would have been encoded
as:
mapname.# = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
Sets use the hash code as the key - for example a set with a (fictional)
hashcode calculation may look like:
setname.# = 2
setname.12312512 = "value1"
setname.56345233 = "value2"
Prior to the work done to extend the type system, this was sufficient
since the internal representation of these was effectively the same.
However, following the separation of maps and lists into distinct
first-class types, this encoding presents a problem: given a state file,
it is impossible to tell the encoding of an empty list and an empty map
apart. This presents problems for the type checker during interpolation,
as many interpolation functions will operate on only one of these two
structures.
This commit therefore changes the representation in state of maps to use
a "%" as the key for the number of elements. Consequently the map above
will now be encoded as:
mapname.% = 2
mapname.key1 = "value1"
mapname.key2 = "value2"
This has the effect of an empty list (or set) now being encoded as:
listname.# = 0
And an empty map now being encoded as:
mapname.% = 0
Therefore we can eliminate some nasty guessing logic from the resource
variable supplier for interpolation, at the cost of having to migrate
state up front (to follow in a subsequent commit).
In order to reduce the number of potential situations in which resources
would be "forced new", we continue to accept "#" as the count key when
reading maps via helper/schema. There is no situation under which we can
allow "#" as an actual map key in any case, as it would not be
distinguishable from a list or set in state.
2016-06-05 10:34:43 +02:00
|
|
|
"ports.0.uuids.%": "1",
|
2015-02-18 23:10:12 +01:00
|
|
|
"ports.0.uuids.80": "value",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2014-08-17 23:12:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-22 07:19:33 +02:00
|
|
|
// Set an ID so that the state returned is not nil
|
|
|
|
idSet := false
|
|
|
|
if d.Id() == "" {
|
|
|
|
idSet = true
|
|
|
|
d.SetId("foo")
|
|
|
|
}
|
|
|
|
|
2014-08-27 05:19:44 +02:00
|
|
|
// If we have partial, then enable partial state mode.
|
|
|
|
if tc.Partial != nil {
|
|
|
|
d.Partial(true)
|
|
|
|
for _, k := range tc.Partial {
|
|
|
|
d.SetPartial(k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-17 23:12:54 +02:00
|
|
|
actual := d.State()
|
2014-08-22 07:19:33 +02:00
|
|
|
|
|
|
|
// If we set an ID, then undo what we did so the comparison works
|
|
|
|
if actual != nil && idSet {
|
|
|
|
actual.ID = ""
|
|
|
|
delete(actual.Attributes, "id")
|
|
|
|
}
|
|
|
|
|
2014-08-17 23:12:54 +02:00
|
|
|
if !reflect.DeepEqual(actual, tc.Result) {
|
2014-08-27 05:19:44 +02:00
|
|
|
t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result)
|
2014-08-17 23:12:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-18 05:48:50 +02:00
|
|
|
|
2016-08-10 14:45:02 +02:00
|
|
|
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: "",
|
2016-11-17 20:34:18 +01:00
|
|
|
New: "true",
|
2016-08-10 14:45:02 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
MapFieldName: "boolMap",
|
|
|
|
ItemName: "boolField",
|
|
|
|
ExpectedType: "bool",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Schema: map[string]*Schema{
|
|
|
|
"intMap": &Schema{
|
|
|
|
Type: TypeMap,
|
2016-11-17 20:34:18 +01:00
|
|
|
Elem: TypeInt,
|
2016-08-10 14:45:02 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-22 07:15:47 +02:00
|
|
|
func TestResourceDataSetConnInfo(t *testing.T) {
|
|
|
|
d := &ResourceData{}
|
2014-08-22 07:19:33 +02:00
|
|
|
d.SetId("foo")
|
2014-08-22 07:15:47 +02:00
|
|
|
d.SetConnInfo(map[string]string{
|
|
|
|
"foo": "bar",
|
|
|
|
})
|
|
|
|
|
|
|
|
expected := map[string]string{
|
|
|
|
"foo": "bar",
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := d.State()
|
2014-09-17 02:07:13 +02:00
|
|
|
if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) {
|
2014-08-19 00:41:12 +02:00
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 18:07:49 +01:00
|
|
|
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])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-18 05:48:50 +02:00
|
|
|
func TestResourceDataSetId(t *testing.T) {
|
|
|
|
d := &ResourceData{}
|
|
|
|
d.SetId("foo")
|
|
|
|
|
|
|
|
actual := d.State()
|
2018-07-12 03:13:43 +02:00
|
|
|
|
|
|
|
// 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" {
|
2014-08-18 05:48:50 +02:00
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceDataSetId_clear(t *testing.T) {
|
|
|
|
d := &ResourceData{
|
2014-09-17 02:07:13 +02:00
|
|
|
state: &terraform.InstanceState{ID: "bar"},
|
2014-08-18 05:48:50 +02:00
|
|
|
}
|
|
|
|
d.SetId("")
|
|
|
|
|
|
|
|
actual := d.State()
|
2014-08-22 07:19:33 +02:00
|
|
|
if actual != nil {
|
2014-08-18 05:48:50 +02:00
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestResourceDataSetId_override(t *testing.T) {
|
|
|
|
d := &ResourceData{
|
2014-09-17 02:07:13 +02:00
|
|
|
state: &terraform.InstanceState{ID: "bar"},
|
2014-08-18 05:48:50 +02:00
|
|
|
}
|
|
|
|
d.SetId("foo")
|
|
|
|
|
|
|
|
actual := d.State()
|
|
|
|
if actual.ID != "foo" {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
2015-03-03 06:06:14 +01:00
|
|
|
|
2016-04-26 21:25:50 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-03 06:06:14 +01:00
|
|
|
func testPtrTo(raw interface{}) interface{} {
|
|
|
|
return &raw
|
|
|
|
}
|