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