github.com/nalum/terraform@v0.3.2-0.20141223102918-aa2c22ffeff6/helper/schema/resource_data_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/terraform/terraform"
     8  )
     9  
    10  func TestResourceDataGet(t *testing.T) {
    11  	cases := []struct {
    12  		Schema map[string]*Schema
    13  		State  *terraform.InstanceState
    14  		Diff   *terraform.InstanceDiff
    15  		Key    string
    16  		Value  interface{}
    17  	}{
    18  		// #0
    19  		{
    20  			Schema: map[string]*Schema{
    21  				"availability_zone": &Schema{
    22  					Type:     TypeString,
    23  					Optional: true,
    24  					Computed: true,
    25  					ForceNew: true,
    26  				},
    27  			},
    28  
    29  			State: nil,
    30  
    31  			Diff: &terraform.InstanceDiff{
    32  				Attributes: map[string]*terraform.ResourceAttrDiff{
    33  					"availability_zone": &terraform.ResourceAttrDiff{
    34  						Old:         "foo",
    35  						New:         "bar",
    36  						NewComputed: true,
    37  					},
    38  				},
    39  			},
    40  
    41  			Key:   "availability_zone",
    42  			Value: "",
    43  		},
    44  
    45  		// #1
    46  		{
    47  			Schema: map[string]*Schema{
    48  				"availability_zone": &Schema{
    49  					Type:     TypeString,
    50  					Optional: true,
    51  					Computed: true,
    52  					ForceNew: true,
    53  				},
    54  			},
    55  
    56  			State: nil,
    57  
    58  			Diff: &terraform.InstanceDiff{
    59  				Attributes: map[string]*terraform.ResourceAttrDiff{
    60  					"availability_zone": &terraform.ResourceAttrDiff{
    61  						Old:         "",
    62  						New:         "foo",
    63  						RequiresNew: true,
    64  					},
    65  				},
    66  			},
    67  
    68  			Key: "availability_zone",
    69  
    70  			Value: "foo",
    71  		},
    72  
    73  		// #2
    74  		{
    75  			Schema: map[string]*Schema{
    76  				"availability_zone": &Schema{
    77  					Type:     TypeString,
    78  					Optional: true,
    79  					Computed: true,
    80  					ForceNew: true,
    81  				},
    82  			},
    83  
    84  			State: nil,
    85  
    86  			Diff: &terraform.InstanceDiff{
    87  				Attributes: map[string]*terraform.ResourceAttrDiff{
    88  					"availability_zone": &terraform.ResourceAttrDiff{
    89  						Old:      "",
    90  						New:      "foo!",
    91  						NewExtra: "foo",
    92  					},
    93  				},
    94  			},
    95  
    96  			Key:   "availability_zone",
    97  			Value: "foo",
    98  		},
    99  
   100  		// #3
   101  		{
   102  			Schema: map[string]*Schema{
   103  				"availability_zone": &Schema{
   104  					Type:     TypeString,
   105  					Optional: true,
   106  					Computed: true,
   107  					ForceNew: true,
   108  				},
   109  			},
   110  
   111  			State: &terraform.InstanceState{
   112  				Attributes: map[string]string{
   113  					"availability_zone": "bar",
   114  				},
   115  			},
   116  
   117  			Diff: nil,
   118  
   119  			Key: "availability_zone",
   120  
   121  			Value: "bar",
   122  		},
   123  
   124  		// #4
   125  		{
   126  			Schema: map[string]*Schema{
   127  				"availability_zone": &Schema{
   128  					Type:     TypeString,
   129  					Optional: true,
   130  					Computed: true,
   131  					ForceNew: true,
   132  				},
   133  			},
   134  
   135  			State: &terraform.InstanceState{
   136  				Attributes: map[string]string{
   137  					"availability_zone": "foo",
   138  				},
   139  			},
   140  
   141  			Diff: &terraform.InstanceDiff{
   142  				Attributes: map[string]*terraform.ResourceAttrDiff{
   143  					"availability_zone": &terraform.ResourceAttrDiff{
   144  						Old:         "foo",
   145  						New:         "bar",
   146  						NewComputed: true,
   147  					},
   148  				},
   149  			},
   150  
   151  			Key:   "availability_zone",
   152  			Value: "",
   153  		},
   154  
   155  		// #5
   156  		{
   157  			Schema: map[string]*Schema{
   158  				"port": &Schema{
   159  					Type:     TypeInt,
   160  					Optional: true,
   161  					Computed: true,
   162  					ForceNew: true,
   163  				},
   164  			},
   165  
   166  			State: &terraform.InstanceState{
   167  				Attributes: map[string]string{
   168  					"port": "80",
   169  				},
   170  			},
   171  
   172  			Diff: nil,
   173  
   174  			Key: "port",
   175  
   176  			Value: 80,
   177  		},
   178  
   179  		// #6
   180  		{
   181  			Schema: map[string]*Schema{
   182  				"ports": &Schema{
   183  					Type:     TypeList,
   184  					Required: true,
   185  					Elem:     &Schema{Type: TypeInt},
   186  				},
   187  			},
   188  
   189  			State: &terraform.InstanceState{
   190  				Attributes: map[string]string{
   191  					"ports.#": "3",
   192  					"ports.0": "1",
   193  					"ports.1": "2",
   194  					"ports.2": "5",
   195  				},
   196  			},
   197  
   198  			Key: "ports.1",
   199  
   200  			Value: 2,
   201  		},
   202  
   203  		// #7
   204  		{
   205  			Schema: map[string]*Schema{
   206  				"ports": &Schema{
   207  					Type:     TypeList,
   208  					Required: true,
   209  					Elem:     &Schema{Type: TypeInt},
   210  				},
   211  			},
   212  
   213  			State: &terraform.InstanceState{
   214  				Attributes: map[string]string{
   215  					"ports.#": "3",
   216  					"ports.0": "1",
   217  					"ports.1": "2",
   218  					"ports.2": "5",
   219  				},
   220  			},
   221  
   222  			Key: "ports.#",
   223  
   224  			Value: 3,
   225  		},
   226  
   227  		// #8
   228  		{
   229  			Schema: map[string]*Schema{
   230  				"ports": &Schema{
   231  					Type:     TypeList,
   232  					Required: true,
   233  					Elem:     &Schema{Type: TypeInt},
   234  				},
   235  			},
   236  
   237  			State: nil,
   238  
   239  			Key: "ports.#",
   240  
   241  			Value: 0,
   242  		},
   243  
   244  		// #9
   245  		{
   246  			Schema: map[string]*Schema{
   247  				"ports": &Schema{
   248  					Type:     TypeList,
   249  					Required: true,
   250  					Elem:     &Schema{Type: TypeInt},
   251  				},
   252  			},
   253  
   254  			State: &terraform.InstanceState{
   255  				Attributes: map[string]string{
   256  					"ports.#": "3",
   257  					"ports.0": "1",
   258  					"ports.1": "2",
   259  					"ports.2": "5",
   260  				},
   261  			},
   262  
   263  			Key: "ports",
   264  
   265  			Value: []interface{}{1, 2, 5},
   266  		},
   267  
   268  		// #10
   269  		{
   270  			Schema: map[string]*Schema{
   271  				"ingress": &Schema{
   272  					Type:     TypeList,
   273  					Required: true,
   274  					Elem: &Resource{
   275  						Schema: map[string]*Schema{
   276  							"from": &Schema{
   277  								Type:     TypeInt,
   278  								Required: true,
   279  							},
   280  						},
   281  					},
   282  				},
   283  			},
   284  
   285  			State: nil,
   286  
   287  			Diff: &terraform.InstanceDiff{
   288  				Attributes: map[string]*terraform.ResourceAttrDiff{
   289  					"ingress.#": &terraform.ResourceAttrDiff{
   290  						Old: "",
   291  						New: "1",
   292  					},
   293  					"ingress.0.from": &terraform.ResourceAttrDiff{
   294  						Old: "",
   295  						New: "8080",
   296  					},
   297  				},
   298  			},
   299  
   300  			Key: "ingress.0",
   301  
   302  			Value: map[string]interface{}{
   303  				"from": 8080,
   304  			},
   305  		},
   306  
   307  		// #11
   308  		{
   309  			Schema: map[string]*Schema{
   310  				"ingress": &Schema{
   311  					Type:     TypeList,
   312  					Required: true,
   313  					Elem: &Resource{
   314  						Schema: map[string]*Schema{
   315  							"from": &Schema{
   316  								Type:     TypeInt,
   317  								Required: true,
   318  							},
   319  						},
   320  					},
   321  				},
   322  			},
   323  
   324  			State: nil,
   325  
   326  			Diff: &terraform.InstanceDiff{
   327  				Attributes: map[string]*terraform.ResourceAttrDiff{
   328  					"ingress.#": &terraform.ResourceAttrDiff{
   329  						Old: "",
   330  						New: "1",
   331  					},
   332  					"ingress.0.from": &terraform.ResourceAttrDiff{
   333  						Old: "",
   334  						New: "8080",
   335  					},
   336  				},
   337  			},
   338  
   339  			Key: "ingress",
   340  
   341  			Value: []interface{}{
   342  				map[string]interface{}{
   343  					"from": 8080,
   344  				},
   345  			},
   346  		},
   347  
   348  		// #12 Computed get
   349  		{
   350  			Schema: map[string]*Schema{
   351  				"availability_zone": &Schema{
   352  					Type:     TypeString,
   353  					Computed: true,
   354  				},
   355  			},
   356  
   357  			State: &terraform.InstanceState{
   358  				Attributes: map[string]string{
   359  					"availability_zone": "foo",
   360  				},
   361  			},
   362  
   363  			Key: "availability_zone",
   364  
   365  			Value: "foo",
   366  		},
   367  
   368  		// #13 Full object
   369  		{
   370  			Schema: map[string]*Schema{
   371  				"availability_zone": &Schema{
   372  					Type:     TypeString,
   373  					Optional: true,
   374  					Computed: true,
   375  					ForceNew: true,
   376  				},
   377  			},
   378  
   379  			State: nil,
   380  
   381  			Diff: &terraform.InstanceDiff{
   382  				Attributes: map[string]*terraform.ResourceAttrDiff{
   383  					"availability_zone": &terraform.ResourceAttrDiff{
   384  						Old:         "",
   385  						New:         "foo",
   386  						RequiresNew: true,
   387  					},
   388  				},
   389  			},
   390  
   391  			Key: "",
   392  
   393  			Value: map[string]interface{}{
   394  				"availability_zone": "foo",
   395  			},
   396  		},
   397  
   398  		// #14 List of maps
   399  		{
   400  			Schema: map[string]*Schema{
   401  				"config_vars": &Schema{
   402  					Type:     TypeList,
   403  					Optional: true,
   404  					Computed: true,
   405  					Elem: &Schema{
   406  						Type: TypeMap,
   407  					},
   408  				},
   409  			},
   410  
   411  			State: nil,
   412  
   413  			Diff: &terraform.InstanceDiff{
   414  				Attributes: map[string]*terraform.ResourceAttrDiff{
   415  					"config_vars.#": &terraform.ResourceAttrDiff{
   416  						Old: "0",
   417  						New: "2",
   418  					},
   419  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
   420  						Old: "",
   421  						New: "bar",
   422  					},
   423  					"config_vars.1.bar": &terraform.ResourceAttrDiff{
   424  						Old: "",
   425  						New: "baz",
   426  					},
   427  				},
   428  			},
   429  
   430  			Key: "config_vars",
   431  
   432  			Value: []interface{}{
   433  				map[string]interface{}{
   434  					"foo": "bar",
   435  				},
   436  				map[string]interface{}{
   437  					"bar": "baz",
   438  				},
   439  			},
   440  		},
   441  
   442  		// #15 List of maps in state
   443  		{
   444  			Schema: map[string]*Schema{
   445  				"config_vars": &Schema{
   446  					Type:     TypeList,
   447  					Optional: true,
   448  					Computed: true,
   449  					Elem: &Schema{
   450  						Type: TypeMap,
   451  					},
   452  				},
   453  			},
   454  
   455  			State: &terraform.InstanceState{
   456  				Attributes: map[string]string{
   457  					"config_vars.#":     "2",
   458  					"config_vars.0.foo": "baz",
   459  					"config_vars.1.bar": "bar",
   460  				},
   461  			},
   462  
   463  			Diff: nil,
   464  
   465  			Key: "config_vars",
   466  
   467  			Value: []interface{}{
   468  				map[string]interface{}{
   469  					"foo": "baz",
   470  				},
   471  				map[string]interface{}{
   472  					"bar": "bar",
   473  				},
   474  			},
   475  		},
   476  
   477  		// #16 List of maps with removal in diff
   478  		{
   479  			Schema: map[string]*Schema{
   480  				"config_vars": &Schema{
   481  					Type:     TypeList,
   482  					Optional: true,
   483  					Computed: true,
   484  					Elem: &Schema{
   485  						Type: TypeMap,
   486  					},
   487  				},
   488  			},
   489  
   490  			State: &terraform.InstanceState{
   491  				Attributes: map[string]string{
   492  					"config_vars.#":     "1",
   493  					"config_vars.0.FOO": "bar",
   494  				},
   495  			},
   496  
   497  			Diff: &terraform.InstanceDiff{
   498  				Attributes: map[string]*terraform.ResourceAttrDiff{
   499  					"config_vars.#": &terraform.ResourceAttrDiff{
   500  						Old: "1",
   501  						New: "0",
   502  					},
   503  					"config_vars.0.FOO": &terraform.ResourceAttrDiff{
   504  						Old:        "bar",
   505  						NewRemoved: true,
   506  					},
   507  				},
   508  			},
   509  
   510  			Key: "config_vars",
   511  
   512  			Value: []interface{}{},
   513  		},
   514  
   515  		// #17 Sets
   516  		{
   517  			Schema: map[string]*Schema{
   518  				"ports": &Schema{
   519  					Type:     TypeSet,
   520  					Optional: true,
   521  					Computed: true,
   522  					Elem:     &Schema{Type: TypeInt},
   523  					Set: func(a interface{}) int {
   524  						return a.(int)
   525  					},
   526  				},
   527  			},
   528  
   529  			State: &terraform.InstanceState{
   530  				Attributes: map[string]string{
   531  					"ports.#":  "1",
   532  					"ports.80": "80",
   533  				},
   534  			},
   535  
   536  			Diff: nil,
   537  
   538  			Key: "ports",
   539  
   540  			Value: []interface{}{80},
   541  		},
   542  
   543  		// #18
   544  		{
   545  			Schema: map[string]*Schema{
   546  				"data": &Schema{
   547  					Type:     TypeSet,
   548  					Optional: true,
   549  					Elem: &Resource{
   550  						Schema: map[string]*Schema{
   551  							"index": &Schema{
   552  								Type:     TypeInt,
   553  								Required: true,
   554  							},
   555  
   556  							"value": &Schema{
   557  								Type:     TypeString,
   558  								Required: true,
   559  							},
   560  						},
   561  					},
   562  					Set: func(a interface{}) int {
   563  						m := a.(map[string]interface{})
   564  						return m["index"].(int)
   565  					},
   566  				},
   567  			},
   568  
   569  			State: &terraform.InstanceState{
   570  				Attributes: map[string]string{
   571  					"data.#":        "1",
   572  					"data.10.index": "10",
   573  					"data.10.value": "50",
   574  				},
   575  			},
   576  
   577  			Diff: &terraform.InstanceDiff{
   578  				Attributes: map[string]*terraform.ResourceAttrDiff{
   579  					"data.10.value": &terraform.ResourceAttrDiff{
   580  						Old: "50",
   581  						New: "80",
   582  					},
   583  				},
   584  			},
   585  
   586  			Key: "data",
   587  
   588  			Value: []interface{}{
   589  				map[string]interface{}{
   590  					"index": 10,
   591  					"value": "80",
   592  				},
   593  			},
   594  		},
   595  	}
   596  
   597  	for i, tc := range cases {
   598  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   599  		if err != nil {
   600  			t.Fatalf("err: %s", err)
   601  		}
   602  
   603  		v := d.Get(tc.Key)
   604  		if s, ok := v.(*Set); ok {
   605  			v = s.List()
   606  		}
   607  
   608  		if !reflect.DeepEqual(v, tc.Value) {
   609  			t.Fatalf("Bad: %d\n\n%#v", i, v)
   610  		}
   611  	}
   612  }
   613  
   614  func TestResourceDataGetChange(t *testing.T) {
   615  	cases := []struct {
   616  		Schema   map[string]*Schema
   617  		State    *terraform.InstanceState
   618  		Diff     *terraform.InstanceDiff
   619  		Key      string
   620  		OldValue interface{}
   621  		NewValue interface{}
   622  	}{
   623  		{
   624  			Schema: map[string]*Schema{
   625  				"availability_zone": &Schema{
   626  					Type:     TypeString,
   627  					Optional: true,
   628  					Computed: true,
   629  					ForceNew: true,
   630  				},
   631  			},
   632  
   633  			State: nil,
   634  
   635  			Diff: &terraform.InstanceDiff{
   636  				Attributes: map[string]*terraform.ResourceAttrDiff{
   637  					"availability_zone": &terraform.ResourceAttrDiff{
   638  						Old:         "",
   639  						New:         "foo",
   640  						RequiresNew: true,
   641  					},
   642  				},
   643  			},
   644  
   645  			Key: "availability_zone",
   646  
   647  			OldValue: "",
   648  			NewValue: "foo",
   649  		},
   650  
   651  		{
   652  			Schema: map[string]*Schema{
   653  				"availability_zone": &Schema{
   654  					Type:     TypeString,
   655  					Optional: true,
   656  					Computed: true,
   657  					ForceNew: true,
   658  				},
   659  			},
   660  
   661  			State: &terraform.InstanceState{
   662  				Attributes: map[string]string{
   663  					"availability_zone": "foo",
   664  				},
   665  			},
   666  
   667  			Diff: &terraform.InstanceDiff{
   668  				Attributes: map[string]*terraform.ResourceAttrDiff{
   669  					"availability_zone": &terraform.ResourceAttrDiff{
   670  						Old:         "",
   671  						New:         "foo",
   672  						RequiresNew: true,
   673  					},
   674  				},
   675  			},
   676  
   677  			Key: "availability_zone",
   678  
   679  			OldValue: "foo",
   680  			NewValue: "foo",
   681  		},
   682  	}
   683  
   684  	for i, tc := range cases {
   685  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   686  		if err != nil {
   687  			t.Fatalf("err: %s", err)
   688  		}
   689  
   690  		o, n := d.GetChange(tc.Key)
   691  		if !reflect.DeepEqual(o, tc.OldValue) {
   692  			t.Fatalf("Old Bad: %d\n\n%#v", i, o)
   693  		}
   694  		if !reflect.DeepEqual(n, tc.NewValue) {
   695  			t.Fatalf("New Bad: %d\n\n%#v", i, n)
   696  		}
   697  	}
   698  }
   699  
   700  func TestResourceDataGetOk(t *testing.T) {
   701  	cases := []struct {
   702  		Schema map[string]*Schema
   703  		State  *terraform.InstanceState
   704  		Diff   *terraform.InstanceDiff
   705  		Key    string
   706  		Value  interface{}
   707  		Ok     bool
   708  	}{
   709  		/*
   710  		 * Primitives
   711  		 */
   712  		{
   713  			Schema: map[string]*Schema{
   714  				"availability_zone": &Schema{
   715  					Type:     TypeString,
   716  					Optional: true,
   717  					Computed: true,
   718  					ForceNew: true,
   719  				},
   720  			},
   721  
   722  			State: nil,
   723  
   724  			Diff: &terraform.InstanceDiff{
   725  				Attributes: map[string]*terraform.ResourceAttrDiff{
   726  					"availability_zone": &terraform.ResourceAttrDiff{
   727  						Old: "",
   728  						New: "",
   729  					},
   730  				},
   731  			},
   732  
   733  			Key:   "availability_zone",
   734  			Value: "",
   735  			Ok:    true,
   736  		},
   737  
   738  		{
   739  			Schema: map[string]*Schema{
   740  				"availability_zone": &Schema{
   741  					Type:     TypeString,
   742  					Optional: true,
   743  					Computed: true,
   744  					ForceNew: true,
   745  				},
   746  			},
   747  
   748  			State: nil,
   749  
   750  			Diff: nil,
   751  
   752  			Key:   "availability_zone",
   753  			Value: "",
   754  			Ok:    false,
   755  		},
   756  
   757  		/*
   758  		 * Lists
   759  		 */
   760  
   761  		{
   762  			Schema: map[string]*Schema{
   763  				"ports": &Schema{
   764  					Type:     TypeList,
   765  					Optional: true,
   766  					Elem:     &Schema{Type: TypeInt},
   767  				},
   768  			},
   769  
   770  			State: nil,
   771  
   772  			Diff: nil,
   773  
   774  			Key:   "ports",
   775  			Value: []interface{}{},
   776  			Ok:    false,
   777  		},
   778  
   779  		/*
   780  		 * Map
   781  		 */
   782  
   783  		{
   784  			Schema: map[string]*Schema{
   785  				"ports": &Schema{
   786  					Type:     TypeMap,
   787  					Optional: true,
   788  				},
   789  			},
   790  
   791  			State: nil,
   792  
   793  			Diff: nil,
   794  
   795  			Key:   "ports",
   796  			Value: map[string]interface{}{},
   797  			Ok:    false,
   798  		},
   799  
   800  		/*
   801  		 * Set
   802  		 */
   803  
   804  		{
   805  			Schema: map[string]*Schema{
   806  				"ports": &Schema{
   807  					Type:     TypeSet,
   808  					Optional: true,
   809  					Elem:     &Schema{Type: TypeInt},
   810  					Set:      func(a interface{}) int { return a.(int) },
   811  				},
   812  			},
   813  
   814  			State: nil,
   815  
   816  			Diff: nil,
   817  
   818  			Key:   "ports",
   819  			Value: []interface{}{},
   820  			Ok:    false,
   821  		},
   822  
   823  		{
   824  			Schema: map[string]*Schema{
   825  				"ports": &Schema{
   826  					Type:     TypeSet,
   827  					Optional: true,
   828  					Elem:     &Schema{Type: TypeInt},
   829  					Set:      func(a interface{}) int { return a.(int) },
   830  				},
   831  			},
   832  
   833  			State: nil,
   834  
   835  			Diff: nil,
   836  
   837  			Key:   "ports.0",
   838  			Value: 0,
   839  			Ok:    false,
   840  		},
   841  	}
   842  
   843  	for i, tc := range cases {
   844  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   845  		if err != nil {
   846  			t.Fatalf("err: %s", err)
   847  		}
   848  
   849  		v, ok := d.GetOk(tc.Key)
   850  		if s, ok := v.(*Set); ok {
   851  			v = s.List()
   852  		}
   853  
   854  		if !reflect.DeepEqual(v, tc.Value) {
   855  			t.Fatalf("Bad: %d\n\n%#v", i, v)
   856  		}
   857  		if ok != tc.Ok {
   858  			t.Fatalf("Bad: %d\n\n%#v", i, ok)
   859  		}
   860  	}
   861  }
   862  
   863  func TestResourceDataHasChange(t *testing.T) {
   864  	cases := []struct {
   865  		Schema map[string]*Schema
   866  		State  *terraform.InstanceState
   867  		Diff   *terraform.InstanceDiff
   868  		Key    string
   869  		Change bool
   870  	}{
   871  		{
   872  			Schema: map[string]*Schema{
   873  				"availability_zone": &Schema{
   874  					Type:     TypeString,
   875  					Optional: true,
   876  					Computed: true,
   877  					ForceNew: true,
   878  				},
   879  			},
   880  
   881  			State: nil,
   882  
   883  			Diff: &terraform.InstanceDiff{
   884  				Attributes: map[string]*terraform.ResourceAttrDiff{
   885  					"availability_zone": &terraform.ResourceAttrDiff{
   886  						Old:         "",
   887  						New:         "foo",
   888  						RequiresNew: true,
   889  					},
   890  				},
   891  			},
   892  
   893  			Key: "availability_zone",
   894  
   895  			Change: true,
   896  		},
   897  
   898  		{
   899  			Schema: map[string]*Schema{
   900  				"availability_zone": &Schema{
   901  					Type:     TypeString,
   902  					Optional: true,
   903  					Computed: true,
   904  					ForceNew: true,
   905  				},
   906  			},
   907  
   908  			State: &terraform.InstanceState{
   909  				Attributes: map[string]string{
   910  					"availability_zone": "foo",
   911  				},
   912  			},
   913  
   914  			Diff: &terraform.InstanceDiff{
   915  				Attributes: map[string]*terraform.ResourceAttrDiff{
   916  					"availability_zone": &terraform.ResourceAttrDiff{
   917  						Old:         "",
   918  						New:         "foo",
   919  						RequiresNew: true,
   920  					},
   921  				},
   922  			},
   923  
   924  			Key: "availability_zone",
   925  
   926  			Change: false,
   927  		},
   928  
   929  		{
   930  			Schema: map[string]*Schema{
   931  				"tags": &Schema{
   932  					Type:     TypeMap,
   933  					Optional: true,
   934  					Computed: true,
   935  				},
   936  			},
   937  
   938  			State: nil,
   939  
   940  			Diff: &terraform.InstanceDiff{
   941  				Attributes: map[string]*terraform.ResourceAttrDiff{
   942  					"tags.Name": &terraform.ResourceAttrDiff{
   943  						Old: "foo",
   944  						New: "foo",
   945  					},
   946  				},
   947  			},
   948  
   949  			Key: "tags",
   950  
   951  			Change: true,
   952  		},
   953  	}
   954  
   955  	for i, tc := range cases {
   956  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   957  		if err != nil {
   958  			t.Fatalf("err: %s", err)
   959  		}
   960  
   961  		actual := d.HasChange(tc.Key)
   962  		if actual != tc.Change {
   963  			t.Fatalf("Bad: %d %#v", i, actual)
   964  		}
   965  	}
   966  }
   967  
   968  func TestResourceDataSet(t *testing.T) {
   969  	cases := []struct {
   970  		Schema   map[string]*Schema
   971  		State    *terraform.InstanceState
   972  		Diff     *terraform.InstanceDiff
   973  		Key      string
   974  		Value    interface{}
   975  		Err      bool
   976  		GetKey   string
   977  		GetValue interface{}
   978  	}{
   979  		// Basic good
   980  		{
   981  			Schema: map[string]*Schema{
   982  				"availability_zone": &Schema{
   983  					Type:     TypeString,
   984  					Optional: true,
   985  					Computed: true,
   986  					ForceNew: true,
   987  				},
   988  			},
   989  
   990  			State: nil,
   991  
   992  			Diff: nil,
   993  
   994  			Key:   "availability_zone",
   995  			Value: "foo",
   996  
   997  			GetKey:   "availability_zone",
   998  			GetValue: "foo",
   999  		},
  1000  
  1001  		// Basic int
  1002  		{
  1003  			Schema: map[string]*Schema{
  1004  				"port": &Schema{
  1005  					Type:     TypeInt,
  1006  					Optional: true,
  1007  					Computed: true,
  1008  					ForceNew: true,
  1009  				},
  1010  			},
  1011  
  1012  			State: nil,
  1013  
  1014  			Diff: nil,
  1015  
  1016  			Key:   "port",
  1017  			Value: 80,
  1018  
  1019  			GetKey:   "port",
  1020  			GetValue: 80,
  1021  		},
  1022  
  1023  		// Basic bool
  1024  		{
  1025  			Schema: map[string]*Schema{
  1026  				"vpc": &Schema{
  1027  					Type:     TypeBool,
  1028  					Optional: true,
  1029  				},
  1030  			},
  1031  
  1032  			State: nil,
  1033  
  1034  			Diff: nil,
  1035  
  1036  			Key:   "vpc",
  1037  			Value: true,
  1038  
  1039  			GetKey:   "vpc",
  1040  			GetValue: true,
  1041  		},
  1042  
  1043  		{
  1044  			Schema: map[string]*Schema{
  1045  				"vpc": &Schema{
  1046  					Type:     TypeBool,
  1047  					Optional: true,
  1048  				},
  1049  			},
  1050  
  1051  			State: nil,
  1052  
  1053  			Diff: nil,
  1054  
  1055  			Key:   "vpc",
  1056  			Value: false,
  1057  
  1058  			GetKey:   "vpc",
  1059  			GetValue: false,
  1060  		},
  1061  
  1062  		// Invalid type
  1063  		{
  1064  			Schema: map[string]*Schema{
  1065  				"availability_zone": &Schema{
  1066  					Type:     TypeString,
  1067  					Optional: true,
  1068  					Computed: true,
  1069  					ForceNew: true,
  1070  				},
  1071  			},
  1072  
  1073  			State: nil,
  1074  
  1075  			Diff: nil,
  1076  
  1077  			Key:   "availability_zone",
  1078  			Value: 80,
  1079  			Err:   true,
  1080  
  1081  			GetKey:   "availability_zone",
  1082  			GetValue: "",
  1083  		},
  1084  
  1085  		// List of primitives, set element
  1086  		{
  1087  			Schema: map[string]*Schema{
  1088  				"ports": &Schema{
  1089  					Type:     TypeList,
  1090  					Computed: true,
  1091  					Elem:     &Schema{Type: TypeInt},
  1092  				},
  1093  			},
  1094  
  1095  			State: &terraform.InstanceState{
  1096  				Attributes: map[string]string{
  1097  					"ports.#": "3",
  1098  					"ports.0": "1",
  1099  					"ports.1": "2",
  1100  					"ports.2": "5",
  1101  				},
  1102  			},
  1103  
  1104  			Diff: nil,
  1105  
  1106  			Key:   "ports.1",
  1107  			Value: 3,
  1108  
  1109  			GetKey:   "ports",
  1110  			GetValue: []interface{}{1, 3, 5},
  1111  		},
  1112  
  1113  		// List of primitives, set list
  1114  		{
  1115  			Schema: map[string]*Schema{
  1116  				"ports": &Schema{
  1117  					Type:     TypeList,
  1118  					Computed: true,
  1119  					Elem:     &Schema{Type: TypeInt},
  1120  				},
  1121  			},
  1122  
  1123  			State: nil,
  1124  
  1125  			Diff: nil,
  1126  
  1127  			Key:   "ports",
  1128  			Value: []int{1, 2, 5},
  1129  
  1130  			GetKey:   "ports",
  1131  			GetValue: []interface{}{1, 2, 5},
  1132  		},
  1133  
  1134  		// List of primitives, set list with error
  1135  		{
  1136  			Schema: map[string]*Schema{
  1137  				"ports": &Schema{
  1138  					Type:     TypeList,
  1139  					Computed: true,
  1140  					Elem:     &Schema{Type: TypeInt},
  1141  				},
  1142  			},
  1143  
  1144  			State: nil,
  1145  
  1146  			Diff: nil,
  1147  
  1148  			Key:   "ports",
  1149  			Value: []interface{}{1, "NOPE", 5},
  1150  			Err:   true,
  1151  
  1152  			GetKey:   "ports",
  1153  			GetValue: []interface{}{},
  1154  		},
  1155  
  1156  		// List of resource, set element
  1157  		{
  1158  			Schema: map[string]*Schema{
  1159  				"ingress": &Schema{
  1160  					Type:     TypeList,
  1161  					Computed: true,
  1162  					Elem: &Resource{
  1163  						Schema: map[string]*Schema{
  1164  							"from": &Schema{
  1165  								Type: TypeInt,
  1166  							},
  1167  						},
  1168  					},
  1169  				},
  1170  			},
  1171  
  1172  			State: &terraform.InstanceState{
  1173  				Attributes: map[string]string{
  1174  					"ingress.#":      "2",
  1175  					"ingress.0.from": "80",
  1176  					"ingress.1.from": "8080",
  1177  				},
  1178  			},
  1179  
  1180  			Diff: nil,
  1181  
  1182  			Key:   "ingress.1.from",
  1183  			Value: 9000,
  1184  
  1185  			GetKey: "ingress",
  1186  			GetValue: []interface{}{
  1187  				map[string]interface{}{
  1188  					"from": 80,
  1189  				},
  1190  				map[string]interface{}{
  1191  					"from": 9000,
  1192  				},
  1193  			},
  1194  		},
  1195  
  1196  		// List of resource, set full resource element
  1197  		{
  1198  			Schema: map[string]*Schema{
  1199  				"ingress": &Schema{
  1200  					Type:     TypeList,
  1201  					Computed: true,
  1202  					Elem: &Resource{
  1203  						Schema: map[string]*Schema{
  1204  							"from": &Schema{
  1205  								Type: TypeInt,
  1206  							},
  1207  						},
  1208  					},
  1209  				},
  1210  			},
  1211  
  1212  			State: &terraform.InstanceState{
  1213  				Attributes: map[string]string{
  1214  					"ingress.#":      "2",
  1215  					"ingress.0.from": "80",
  1216  					"ingress.1.from": "8080",
  1217  				},
  1218  			},
  1219  
  1220  			Diff: nil,
  1221  
  1222  			Key: "ingress.1",
  1223  			Value: map[string]interface{}{
  1224  				"from": 9000,
  1225  			},
  1226  
  1227  			GetKey: "ingress",
  1228  			GetValue: []interface{}{
  1229  				map[string]interface{}{
  1230  					"from": 80,
  1231  				},
  1232  				map[string]interface{}{
  1233  					"from": 9000,
  1234  				},
  1235  			},
  1236  		},
  1237  
  1238  		// List of resource, set full resource element, with error
  1239  		{
  1240  			Schema: map[string]*Schema{
  1241  				"ingress": &Schema{
  1242  					Type:     TypeList,
  1243  					Computed: true,
  1244  					Elem: &Resource{
  1245  						Schema: map[string]*Schema{
  1246  							"from": &Schema{
  1247  								Type: TypeInt,
  1248  							},
  1249  							"to": &Schema{
  1250  								Type: TypeInt,
  1251  							},
  1252  						},
  1253  					},
  1254  				},
  1255  			},
  1256  
  1257  			State: &terraform.InstanceState{
  1258  				Attributes: map[string]string{
  1259  					"ingress.#":      "2",
  1260  					"ingress.0.from": "80",
  1261  					"ingress.0.to":   "10",
  1262  					"ingress.1.from": "8080",
  1263  					"ingress.1.to":   "8080",
  1264  				},
  1265  			},
  1266  
  1267  			Diff: nil,
  1268  
  1269  			Key: "ingress.1",
  1270  			Value: map[string]interface{}{
  1271  				"from": 9000,
  1272  				"to":   "bar",
  1273  			},
  1274  			Err: true,
  1275  
  1276  			GetKey: "ingress",
  1277  			GetValue: []interface{}{
  1278  				map[string]interface{}{
  1279  					"from": 80,
  1280  					"to":   10,
  1281  				},
  1282  				map[string]interface{}{
  1283  					"from": 8080,
  1284  					"to":   8080,
  1285  				},
  1286  			},
  1287  		},
  1288  
  1289  		// Set a list of maps
  1290  		{
  1291  			Schema: map[string]*Schema{
  1292  				"config_vars": &Schema{
  1293  					Type:     TypeList,
  1294  					Optional: true,
  1295  					Computed: true,
  1296  					Elem: &Schema{
  1297  						Type: TypeMap,
  1298  					},
  1299  				},
  1300  			},
  1301  
  1302  			State: nil,
  1303  
  1304  			Diff: nil,
  1305  
  1306  			Key: "config_vars",
  1307  			Value: []interface{}{
  1308  				map[string]interface{}{
  1309  					"foo": "bar",
  1310  				},
  1311  				map[string]interface{}{
  1312  					"bar": "baz",
  1313  				},
  1314  			},
  1315  			Err: false,
  1316  
  1317  			GetKey: "config_vars",
  1318  			GetValue: []interface{}{
  1319  				map[string]interface{}{
  1320  					"foo": "bar",
  1321  				},
  1322  				map[string]interface{}{
  1323  					"bar": "baz",
  1324  				},
  1325  			},
  1326  		},
  1327  
  1328  		// Set a list of maps
  1329  		{
  1330  			Schema: map[string]*Schema{
  1331  				"config_vars": &Schema{
  1332  					Type:     TypeList,
  1333  					Optional: true,
  1334  					Computed: true,
  1335  					Elem: &Schema{
  1336  						Type: TypeMap,
  1337  					},
  1338  				},
  1339  			},
  1340  
  1341  			State: nil,
  1342  
  1343  			Diff: nil,
  1344  
  1345  			Key: "config_vars",
  1346  			Value: []interface{}{
  1347  				map[string]string{
  1348  					"foo": "bar",
  1349  				},
  1350  				map[string]string{
  1351  					"bar": "baz",
  1352  				},
  1353  			},
  1354  			Err: false,
  1355  
  1356  			GetKey: "config_vars",
  1357  			GetValue: []interface{}{
  1358  				map[string]interface{}{
  1359  					"foo": "bar",
  1360  				},
  1361  				map[string]interface{}{
  1362  					"bar": "baz",
  1363  				},
  1364  			},
  1365  		},
  1366  
  1367  		// Set, with list
  1368  		{
  1369  			Schema: map[string]*Schema{
  1370  				"ports": &Schema{
  1371  					Type:     TypeSet,
  1372  					Optional: true,
  1373  					Computed: true,
  1374  					Elem:     &Schema{Type: TypeInt},
  1375  					Set: func(a interface{}) int {
  1376  						return a.(int)
  1377  					},
  1378  				},
  1379  			},
  1380  
  1381  			State: &terraform.InstanceState{
  1382  				Attributes: map[string]string{
  1383  					"ports.#": "3",
  1384  					"ports.0": "100",
  1385  					"ports.1": "80",
  1386  					"ports.2": "80",
  1387  				},
  1388  			},
  1389  
  1390  			Key:   "ports",
  1391  			Value: []interface{}{100, 125, 125},
  1392  
  1393  			GetKey:   "ports",
  1394  			GetValue: []interface{}{100, 125},
  1395  		},
  1396  
  1397  		// Set, with Set
  1398  		{
  1399  			Schema: map[string]*Schema{
  1400  				"ports": &Schema{
  1401  					Type:     TypeSet,
  1402  					Optional: true,
  1403  					Computed: true,
  1404  					Elem:     &Schema{Type: TypeInt},
  1405  					Set: func(a interface{}) int {
  1406  						return a.(int)
  1407  					},
  1408  				},
  1409  			},
  1410  
  1411  			State: &terraform.InstanceState{
  1412  				Attributes: map[string]string{
  1413  					"ports.#":   "3",
  1414  					"ports.100": "100",
  1415  					"ports.80":  "80",
  1416  					"ports.81":  "81",
  1417  				},
  1418  			},
  1419  
  1420  			Key: "ports",
  1421  			Value: &Set{
  1422  				m: map[int]interface{}{
  1423  					1: 1,
  1424  					2: 2,
  1425  				},
  1426  			},
  1427  
  1428  			GetKey:   "ports",
  1429  			GetValue: []interface{}{1, 2},
  1430  		},
  1431  
  1432  		// Set single item
  1433  		{
  1434  			Schema: map[string]*Schema{
  1435  				"ports": &Schema{
  1436  					Type:     TypeSet,
  1437  					Optional: true,
  1438  					Computed: true,
  1439  					Elem:     &Schema{Type: TypeInt},
  1440  					Set: func(a interface{}) int {
  1441  						return a.(int)
  1442  					},
  1443  				},
  1444  			},
  1445  
  1446  			State: &terraform.InstanceState{
  1447  				Attributes: map[string]string{
  1448  					"ports.#":   "2",
  1449  					"ports.100": "100",
  1450  					"ports.80":  "80",
  1451  				},
  1452  			},
  1453  
  1454  			Key:   "ports.100",
  1455  			Value: 256,
  1456  			Err:   true,
  1457  
  1458  			GetKey:   "ports",
  1459  			GetValue: []interface{}{80, 100},
  1460  		},
  1461  	}
  1462  
  1463  	for i, tc := range cases {
  1464  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
  1465  		if err != nil {
  1466  			t.Fatalf("err: %s", err)
  1467  		}
  1468  
  1469  		err = d.Set(tc.Key, tc.Value)
  1470  		if (err != nil) != tc.Err {
  1471  			t.Fatalf("%d err: %s", i, err)
  1472  		}
  1473  
  1474  		v := d.Get(tc.GetKey)
  1475  		if s, ok := v.(*Set); ok {
  1476  			v = s.List()
  1477  		}
  1478  		if !reflect.DeepEqual(v, tc.GetValue) {
  1479  			t.Fatalf("Get Bad: %d\n\n%#v", i, v)
  1480  		}
  1481  	}
  1482  }
  1483  
  1484  func TestResourceDataState(t *testing.T) {
  1485  	cases := []struct {
  1486  		Schema  map[string]*Schema
  1487  		State   *terraform.InstanceState
  1488  		Diff    *terraform.InstanceDiff
  1489  		Set     map[string]interface{}
  1490  		Result  *terraform.InstanceState
  1491  		Partial []string
  1492  	}{
  1493  		// Basic primitive in diff
  1494  		{
  1495  			Schema: map[string]*Schema{
  1496  				"availability_zone": &Schema{
  1497  					Type:     TypeString,
  1498  					Optional: true,
  1499  					Computed: true,
  1500  					ForceNew: true,
  1501  				},
  1502  			},
  1503  
  1504  			State: nil,
  1505  
  1506  			Diff: &terraform.InstanceDiff{
  1507  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1508  					"availability_zone": &terraform.ResourceAttrDiff{
  1509  						Old:         "",
  1510  						New:         "foo",
  1511  						RequiresNew: true,
  1512  					},
  1513  				},
  1514  			},
  1515  
  1516  			Result: &terraform.InstanceState{
  1517  				Attributes: map[string]string{
  1518  					"availability_zone": "foo",
  1519  				},
  1520  			},
  1521  		},
  1522  
  1523  		// Basic primitive set override
  1524  		{
  1525  			Schema: map[string]*Schema{
  1526  				"availability_zone": &Schema{
  1527  					Type:     TypeString,
  1528  					Optional: true,
  1529  					Computed: true,
  1530  					ForceNew: true,
  1531  				},
  1532  			},
  1533  
  1534  			State: nil,
  1535  
  1536  			Diff: &terraform.InstanceDiff{
  1537  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1538  					"availability_zone": &terraform.ResourceAttrDiff{
  1539  						Old:         "",
  1540  						New:         "foo",
  1541  						RequiresNew: true,
  1542  					},
  1543  				},
  1544  			},
  1545  
  1546  			Set: map[string]interface{}{
  1547  				"availability_zone": "bar",
  1548  			},
  1549  
  1550  			Result: &terraform.InstanceState{
  1551  				Attributes: map[string]string{
  1552  					"availability_zone": "bar",
  1553  				},
  1554  			},
  1555  		},
  1556  
  1557  		{
  1558  			Schema: map[string]*Schema{
  1559  				"vpc": &Schema{
  1560  					Type:     TypeBool,
  1561  					Optional: true,
  1562  				},
  1563  			},
  1564  
  1565  			State: nil,
  1566  
  1567  			Diff: nil,
  1568  
  1569  			Set: map[string]interface{}{
  1570  				"vpc": true,
  1571  			},
  1572  
  1573  			Result: &terraform.InstanceState{
  1574  				Attributes: map[string]string{
  1575  					"vpc": "true",
  1576  				},
  1577  			},
  1578  		},
  1579  
  1580  		// Basic primitive with StateFunc set
  1581  		{
  1582  			Schema: map[string]*Schema{
  1583  				"availability_zone": &Schema{
  1584  					Type:      TypeString,
  1585  					Optional:  true,
  1586  					Computed:  true,
  1587  					StateFunc: func(interface{}) string { return "" },
  1588  				},
  1589  			},
  1590  
  1591  			State: nil,
  1592  
  1593  			Diff: &terraform.InstanceDiff{
  1594  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1595  					"availability_zone": &terraform.ResourceAttrDiff{
  1596  						Old:      "",
  1597  						New:      "foo",
  1598  						NewExtra: "foo!",
  1599  					},
  1600  				},
  1601  			},
  1602  
  1603  			Result: &terraform.InstanceState{
  1604  				Attributes: map[string]string{
  1605  					"availability_zone": "foo",
  1606  				},
  1607  			},
  1608  		},
  1609  
  1610  		// List
  1611  		{
  1612  			Schema: map[string]*Schema{
  1613  				"ports": &Schema{
  1614  					Type:     TypeList,
  1615  					Required: true,
  1616  					Elem:     &Schema{Type: TypeInt},
  1617  				},
  1618  			},
  1619  
  1620  			State: &terraform.InstanceState{
  1621  				Attributes: map[string]string{
  1622  					"ports.#": "1",
  1623  					"ports.0": "80",
  1624  				},
  1625  			},
  1626  
  1627  			Diff: &terraform.InstanceDiff{
  1628  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1629  					"ports.#": &terraform.ResourceAttrDiff{
  1630  						Old: "1",
  1631  						New: "2",
  1632  					},
  1633  					"ports.1": &terraform.ResourceAttrDiff{
  1634  						Old: "",
  1635  						New: "100",
  1636  					},
  1637  				},
  1638  			},
  1639  
  1640  			Result: &terraform.InstanceState{
  1641  				Attributes: map[string]string{
  1642  					"ports.#": "2",
  1643  					"ports.0": "80",
  1644  					"ports.1": "100",
  1645  				},
  1646  			},
  1647  		},
  1648  
  1649  		// List of resources
  1650  		{
  1651  			Schema: map[string]*Schema{
  1652  				"ingress": &Schema{
  1653  					Type:     TypeList,
  1654  					Required: true,
  1655  					Elem: &Resource{
  1656  						Schema: map[string]*Schema{
  1657  							"from": &Schema{
  1658  								Type:     TypeInt,
  1659  								Required: true,
  1660  							},
  1661  						},
  1662  					},
  1663  				},
  1664  			},
  1665  
  1666  			State: &terraform.InstanceState{
  1667  				Attributes: map[string]string{
  1668  					"ingress.#":      "1",
  1669  					"ingress.0.from": "80",
  1670  				},
  1671  			},
  1672  
  1673  			Diff: &terraform.InstanceDiff{
  1674  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1675  					"ingress.#": &terraform.ResourceAttrDiff{
  1676  						Old: "1",
  1677  						New: "2",
  1678  					},
  1679  					"ingress.0.from": &terraform.ResourceAttrDiff{
  1680  						Old: "80",
  1681  						New: "150",
  1682  					},
  1683  					"ingress.1.from": &terraform.ResourceAttrDiff{
  1684  						Old: "",
  1685  						New: "100",
  1686  					},
  1687  				},
  1688  			},
  1689  
  1690  			Result: &terraform.InstanceState{
  1691  				Attributes: map[string]string{
  1692  					"ingress.#":      "2",
  1693  					"ingress.0.from": "150",
  1694  					"ingress.1.from": "100",
  1695  				},
  1696  			},
  1697  		},
  1698  
  1699  		// List of maps
  1700  		{
  1701  			Schema: map[string]*Schema{
  1702  				"config_vars": &Schema{
  1703  					Type:     TypeList,
  1704  					Optional: true,
  1705  					Computed: true,
  1706  					Elem: &Schema{
  1707  						Type: TypeMap,
  1708  					},
  1709  				},
  1710  			},
  1711  
  1712  			State: &terraform.InstanceState{
  1713  				Attributes: map[string]string{
  1714  					"config_vars.#":     "2",
  1715  					"config_vars.0.foo": "bar",
  1716  					"config_vars.0.bar": "bar",
  1717  					"config_vars.1.bar": "baz",
  1718  				},
  1719  			},
  1720  
  1721  			Diff: &terraform.InstanceDiff{
  1722  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1723  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1724  						NewRemoved: true,
  1725  					},
  1726  				},
  1727  			},
  1728  
  1729  			Set: map[string]interface{}{
  1730  				"config_vars.1": map[string]interface{}{
  1731  					"baz": "bang",
  1732  				},
  1733  			},
  1734  
  1735  			Result: &terraform.InstanceState{
  1736  				Attributes: map[string]string{
  1737  					"config_vars.#":     "2",
  1738  					"config_vars.0.foo": "bar",
  1739  					"config_vars.1.baz": "bang",
  1740  				},
  1741  			},
  1742  		},
  1743  
  1744  		// List of maps with removal in diff
  1745  		{
  1746  			Schema: map[string]*Schema{
  1747  				"config_vars": &Schema{
  1748  					Type:     TypeList,
  1749  					Optional: true,
  1750  					Computed: true,
  1751  					Elem: &Schema{
  1752  						Type: TypeMap,
  1753  					},
  1754  				},
  1755  			},
  1756  
  1757  			State: &terraform.InstanceState{
  1758  				Attributes: map[string]string{
  1759  					"config_vars.#":     "1",
  1760  					"config_vars.0.FOO": "bar",
  1761  				},
  1762  			},
  1763  
  1764  			Diff: &terraform.InstanceDiff{
  1765  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1766  					"config_vars.#": &terraform.ResourceAttrDiff{
  1767  						Old: "1",
  1768  						New: "0",
  1769  					},
  1770  					"config_vars.0.FOO": &terraform.ResourceAttrDiff{
  1771  						Old:        "bar",
  1772  						NewRemoved: true,
  1773  					},
  1774  				},
  1775  			},
  1776  
  1777  			Result: &terraform.InstanceState{
  1778  				Attributes: map[string]string{
  1779  					"config_vars.#": "0",
  1780  				},
  1781  			},
  1782  		},
  1783  
  1784  		// Basic state with other keys
  1785  		{
  1786  			Schema: map[string]*Schema{
  1787  				"availability_zone": &Schema{
  1788  					Type:     TypeString,
  1789  					Optional: true,
  1790  					Computed: true,
  1791  					ForceNew: true,
  1792  				},
  1793  			},
  1794  
  1795  			State: &terraform.InstanceState{
  1796  				ID: "bar",
  1797  				Attributes: map[string]string{
  1798  					"id": "bar",
  1799  				},
  1800  			},
  1801  
  1802  			Diff: &terraform.InstanceDiff{
  1803  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1804  					"availability_zone": &terraform.ResourceAttrDiff{
  1805  						Old:         "",
  1806  						New:         "foo",
  1807  						RequiresNew: true,
  1808  					},
  1809  				},
  1810  			},
  1811  
  1812  			Result: &terraform.InstanceState{
  1813  				ID: "bar",
  1814  				Attributes: map[string]string{
  1815  					"id":                "bar",
  1816  					"availability_zone": "foo",
  1817  				},
  1818  			},
  1819  		},
  1820  
  1821  		// Sets
  1822  		{
  1823  			Schema: map[string]*Schema{
  1824  				"ports": &Schema{
  1825  					Type:     TypeSet,
  1826  					Optional: true,
  1827  					Computed: true,
  1828  					Elem:     &Schema{Type: TypeInt},
  1829  					Set: func(a interface{}) int {
  1830  						return a.(int)
  1831  					},
  1832  				},
  1833  			},
  1834  
  1835  			State: &terraform.InstanceState{
  1836  				Attributes: map[string]string{
  1837  					"ports.#":   "3",
  1838  					"ports.100": "100",
  1839  					"ports.80":  "80",
  1840  					"ports.81":  "81",
  1841  				},
  1842  			},
  1843  
  1844  			Diff: nil,
  1845  
  1846  			Result: &terraform.InstanceState{
  1847  				Attributes: map[string]string{
  1848  					"ports.#":   "3",
  1849  					"ports.80":  "80",
  1850  					"ports.81":  "81",
  1851  					"ports.100": "100",
  1852  				},
  1853  			},
  1854  		},
  1855  
  1856  		{
  1857  			Schema: map[string]*Schema{
  1858  				"ports": &Schema{
  1859  					Type:     TypeSet,
  1860  					Optional: true,
  1861  					Computed: true,
  1862  					Elem:     &Schema{Type: TypeInt},
  1863  					Set: func(a interface{}) int {
  1864  						return a.(int)
  1865  					},
  1866  				},
  1867  			},
  1868  
  1869  			State: nil,
  1870  
  1871  			Diff: nil,
  1872  
  1873  			Set: map[string]interface{}{
  1874  				"ports": []interface{}{100, 80},
  1875  			},
  1876  
  1877  			Result: &terraform.InstanceState{
  1878  				Attributes: map[string]string{
  1879  					"ports.#":   "2",
  1880  					"ports.80":  "80",
  1881  					"ports.100": "100",
  1882  				},
  1883  			},
  1884  		},
  1885  
  1886  		{
  1887  			Schema: map[string]*Schema{
  1888  				"ports": &Schema{
  1889  					Type:     TypeSet,
  1890  					Optional: true,
  1891  					Computed: true,
  1892  					Elem: &Resource{
  1893  						Schema: map[string]*Schema{
  1894  							"order": &Schema{
  1895  								Type: TypeInt,
  1896  							},
  1897  
  1898  							"a": &Schema{
  1899  								Type: TypeList,
  1900  								Elem: &Schema{Type: TypeInt},
  1901  							},
  1902  
  1903  							"b": &Schema{
  1904  								Type: TypeList,
  1905  								Elem: &Schema{Type: TypeInt},
  1906  							},
  1907  						},
  1908  					},
  1909  					Set: func(a interface{}) int {
  1910  						m := a.(map[string]interface{})
  1911  						return m["order"].(int)
  1912  					},
  1913  				},
  1914  			},
  1915  
  1916  			State: &terraform.InstanceState{
  1917  				Attributes: map[string]string{
  1918  					"ports.#":        "2",
  1919  					"ports.10.order": "10",
  1920  					"ports.10.a.#":   "1",
  1921  					"ports.10.a.0":   "80",
  1922  					"ports.20.order": "20",
  1923  					"ports.20.b.#":   "1",
  1924  					"ports.20.b.0":   "100",
  1925  				},
  1926  			},
  1927  
  1928  			Set: map[string]interface{}{
  1929  				"ports": []interface{}{
  1930  					map[string]interface{}{
  1931  						"order": 20,
  1932  						"b":     []interface{}{100},
  1933  					},
  1934  					map[string]interface{}{
  1935  						"order": 10,
  1936  						"a":     []interface{}{80},
  1937  					},
  1938  				},
  1939  			},
  1940  
  1941  			Result: &terraform.InstanceState{
  1942  				Attributes: map[string]string{
  1943  					"ports.#":        "2",
  1944  					"ports.10.order": "10",
  1945  					"ports.10.a.#":   "1",
  1946  					"ports.10.a.0":   "80",
  1947  					"ports.20.order": "20",
  1948  					"ports.20.b.#":   "1",
  1949  					"ports.20.b.0":   "100",
  1950  				},
  1951  			},
  1952  		},
  1953  
  1954  		/*
  1955  		 * PARTIAL STATES
  1956  		 */
  1957  
  1958  		// Basic primitive
  1959  		{
  1960  			Schema: map[string]*Schema{
  1961  				"availability_zone": &Schema{
  1962  					Type:     TypeString,
  1963  					Optional: true,
  1964  					Computed: true,
  1965  					ForceNew: true,
  1966  				},
  1967  			},
  1968  
  1969  			State: nil,
  1970  
  1971  			Diff: &terraform.InstanceDiff{
  1972  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1973  					"availability_zone": &terraform.ResourceAttrDiff{
  1974  						Old:         "",
  1975  						New:         "foo",
  1976  						RequiresNew: true,
  1977  					},
  1978  				},
  1979  			},
  1980  
  1981  			Partial: []string{},
  1982  
  1983  			Result: &terraform.InstanceState{
  1984  				Attributes: map[string]string{},
  1985  			},
  1986  		},
  1987  
  1988  		// List
  1989  		{
  1990  			Schema: map[string]*Schema{
  1991  				"ports": &Schema{
  1992  					Type:     TypeList,
  1993  					Required: true,
  1994  					Elem:     &Schema{Type: TypeInt},
  1995  				},
  1996  			},
  1997  
  1998  			State: &terraform.InstanceState{
  1999  				Attributes: map[string]string{
  2000  					"ports.#": "1",
  2001  					"ports.0": "80",
  2002  				},
  2003  			},
  2004  
  2005  			Diff: &terraform.InstanceDiff{
  2006  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2007  					"ports.#": &terraform.ResourceAttrDiff{
  2008  						Old: "1",
  2009  						New: "2",
  2010  					},
  2011  					"ports.1": &terraform.ResourceAttrDiff{
  2012  						Old: "",
  2013  						New: "100",
  2014  					},
  2015  				},
  2016  			},
  2017  
  2018  			Partial: []string{},
  2019  
  2020  			Result: &terraform.InstanceState{
  2021  				Attributes: map[string]string{
  2022  					"ports.#": "1",
  2023  					"ports.0": "80",
  2024  				},
  2025  			},
  2026  		},
  2027  
  2028  		{
  2029  			Schema: map[string]*Schema{
  2030  				"ports": &Schema{
  2031  					Type:     TypeList,
  2032  					Optional: true,
  2033  					Computed: true,
  2034  					Elem:     &Schema{Type: TypeInt},
  2035  				},
  2036  			},
  2037  
  2038  			State: nil,
  2039  
  2040  			Diff: &terraform.InstanceDiff{
  2041  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2042  					"ports.#": &terraform.ResourceAttrDiff{
  2043  						Old:         "",
  2044  						NewComputed: true,
  2045  					},
  2046  				},
  2047  			},
  2048  
  2049  			Partial: []string{},
  2050  
  2051  			Set: map[string]interface{}{
  2052  				"ports": []interface{}{},
  2053  			},
  2054  
  2055  			Result: &terraform.InstanceState{
  2056  				Attributes: map[string]string{
  2057  					"ports.#": "0",
  2058  				},
  2059  			},
  2060  		},
  2061  
  2062  		// List of resources
  2063  		{
  2064  			Schema: map[string]*Schema{
  2065  				"ingress": &Schema{
  2066  					Type:     TypeList,
  2067  					Required: true,
  2068  					Elem: &Resource{
  2069  						Schema: map[string]*Schema{
  2070  							"from": &Schema{
  2071  								Type:     TypeInt,
  2072  								Required: true,
  2073  							},
  2074  						},
  2075  					},
  2076  				},
  2077  			},
  2078  
  2079  			State: &terraform.InstanceState{
  2080  				Attributes: map[string]string{
  2081  					"ingress.#":      "1",
  2082  					"ingress.0.from": "80",
  2083  				},
  2084  			},
  2085  
  2086  			Diff: &terraform.InstanceDiff{
  2087  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2088  					"ingress.#": &terraform.ResourceAttrDiff{
  2089  						Old: "1",
  2090  						New: "2",
  2091  					},
  2092  					"ingress.0.from": &terraform.ResourceAttrDiff{
  2093  						Old: "80",
  2094  						New: "150",
  2095  					},
  2096  					"ingress.1.from": &terraform.ResourceAttrDiff{
  2097  						Old: "",
  2098  						New: "100",
  2099  					},
  2100  				},
  2101  			},
  2102  
  2103  			Partial: []string{},
  2104  
  2105  			Result: &terraform.InstanceState{
  2106  				Attributes: map[string]string{
  2107  					"ingress.#":      "1",
  2108  					"ingress.0.from": "80",
  2109  				},
  2110  			},
  2111  		},
  2112  
  2113  		// List of maps
  2114  		{
  2115  			Schema: map[string]*Schema{
  2116  				"config_vars": &Schema{
  2117  					Type:     TypeList,
  2118  					Optional: true,
  2119  					Computed: true,
  2120  					Elem: &Schema{
  2121  						Type: TypeMap,
  2122  					},
  2123  				},
  2124  			},
  2125  
  2126  			State: &terraform.InstanceState{
  2127  				Attributes: map[string]string{
  2128  					"config_vars.#":     "2",
  2129  					"config_vars.0.foo": "bar",
  2130  					"config_vars.0.bar": "bar",
  2131  					"config_vars.1.bar": "baz",
  2132  				},
  2133  			},
  2134  
  2135  			Diff: &terraform.InstanceDiff{
  2136  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2137  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  2138  						NewRemoved: true,
  2139  					},
  2140  				},
  2141  			},
  2142  
  2143  			Set: map[string]interface{}{
  2144  				"config_vars.1": map[string]interface{}{
  2145  					"baz": "bang",
  2146  				},
  2147  			},
  2148  
  2149  			Partial: []string{},
  2150  
  2151  			Result: &terraform.InstanceState{
  2152  				Attributes: map[string]string{
  2153  					"config_vars.#":     "2",
  2154  					"config_vars.0.foo": "bar",
  2155  					"config_vars.0.bar": "bar",
  2156  					"config_vars.1.bar": "baz",
  2157  				},
  2158  			},
  2159  		},
  2160  
  2161  		// Sets
  2162  		{
  2163  			Schema: map[string]*Schema{
  2164  				"ports": &Schema{
  2165  					Type:     TypeSet,
  2166  					Optional: true,
  2167  					Computed: true,
  2168  					Elem:     &Schema{Type: TypeInt},
  2169  					Set: func(a interface{}) int {
  2170  						return a.(int)
  2171  					},
  2172  				},
  2173  			},
  2174  
  2175  			State: &terraform.InstanceState{
  2176  				Attributes: map[string]string{
  2177  					"ports.#":   "3",
  2178  					"ports.100": "100",
  2179  					"ports.80":  "80",
  2180  					"ports.81":  "81",
  2181  				},
  2182  			},
  2183  
  2184  			Diff: &terraform.InstanceDiff{
  2185  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2186  					"ports.120": &terraform.ResourceAttrDiff{
  2187  						New: "120",
  2188  					},
  2189  				},
  2190  			},
  2191  
  2192  			Partial: []string{},
  2193  
  2194  			Result: &terraform.InstanceState{
  2195  				Attributes: map[string]string{
  2196  					"ports.#":   "3",
  2197  					"ports.80":  "80",
  2198  					"ports.81":  "81",
  2199  					"ports.100": "100",
  2200  				},
  2201  			},
  2202  		},
  2203  
  2204  		{
  2205  			Schema: map[string]*Schema{
  2206  				"ports": &Schema{
  2207  					Type:     TypeSet,
  2208  					Optional: true,
  2209  					Computed: true,
  2210  					Elem:     &Schema{Type: TypeInt},
  2211  					Set: func(a interface{}) int {
  2212  						return a.(int)
  2213  					},
  2214  				},
  2215  			},
  2216  
  2217  			State: nil,
  2218  
  2219  			Diff: &terraform.InstanceDiff{
  2220  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2221  					"ports.#": &terraform.ResourceAttrDiff{
  2222  						Old:         "",
  2223  						NewComputed: true,
  2224  					},
  2225  				},
  2226  			},
  2227  
  2228  			Partial: []string{},
  2229  
  2230  			Result: &terraform.InstanceState{
  2231  				Attributes: map[string]string{
  2232  					"ports.#": "0",
  2233  				},
  2234  			},
  2235  		},
  2236  
  2237  		// Maps
  2238  		{
  2239  			Schema: map[string]*Schema{
  2240  				"tags": &Schema{
  2241  					Type:     TypeMap,
  2242  					Optional: true,
  2243  					Computed: true,
  2244  				},
  2245  			},
  2246  
  2247  			State: nil,
  2248  
  2249  			Diff: &terraform.InstanceDiff{
  2250  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2251  					"tags.Name": &terraform.ResourceAttrDiff{
  2252  						Old: "",
  2253  						New: "foo",
  2254  					},
  2255  				},
  2256  			},
  2257  
  2258  			Result: &terraform.InstanceState{
  2259  				Attributes: map[string]string{
  2260  					"tags.Name": "foo",
  2261  				},
  2262  			},
  2263  		},
  2264  
  2265  		{
  2266  			Schema: map[string]*Schema{
  2267  				"tags": &Schema{
  2268  					Type:     TypeMap,
  2269  					Optional: true,
  2270  					Computed: true,
  2271  				},
  2272  			},
  2273  
  2274  			State: nil,
  2275  
  2276  			Diff: &terraform.InstanceDiff{
  2277  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2278  					"tags.Name": &terraform.ResourceAttrDiff{
  2279  						Old: "",
  2280  						New: "foo",
  2281  					},
  2282  				},
  2283  			},
  2284  
  2285  			Set: map[string]interface{}{
  2286  				"tags": map[string]string{},
  2287  			},
  2288  
  2289  			Result: &terraform.InstanceState{
  2290  				Attributes: map[string]string{},
  2291  			},
  2292  		},
  2293  	}
  2294  
  2295  	for i, tc := range cases {
  2296  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
  2297  		if err != nil {
  2298  			t.Fatalf("err: %s", err)
  2299  		}
  2300  
  2301  		for k, v := range tc.Set {
  2302  			if err := d.Set(k, v); err != nil {
  2303  				t.Fatalf("%d err: %s", i, err)
  2304  			}
  2305  		}
  2306  
  2307  		// Set an ID so that the state returned is not nil
  2308  		idSet := false
  2309  		if d.Id() == "" {
  2310  			idSet = true
  2311  			d.SetId("foo")
  2312  		}
  2313  
  2314  		// If we have partial, then enable partial state mode.
  2315  		if tc.Partial != nil {
  2316  			d.Partial(true)
  2317  			for _, k := range tc.Partial {
  2318  				d.SetPartial(k)
  2319  			}
  2320  		}
  2321  
  2322  		actual := d.State()
  2323  
  2324  		// If we set an ID, then undo what we did so the comparison works
  2325  		if actual != nil && idSet {
  2326  			actual.ID = ""
  2327  			delete(actual.Attributes, "id")
  2328  		}
  2329  
  2330  		if !reflect.DeepEqual(actual, tc.Result) {
  2331  			t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result)
  2332  		}
  2333  	}
  2334  }
  2335  
  2336  func TestResourceDataSetConnInfo(t *testing.T) {
  2337  	d := &ResourceData{}
  2338  	d.SetId("foo")
  2339  	d.SetConnInfo(map[string]string{
  2340  		"foo": "bar",
  2341  	})
  2342  
  2343  	expected := map[string]string{
  2344  		"foo": "bar",
  2345  	}
  2346  
  2347  	actual := d.State()
  2348  	if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) {
  2349  		t.Fatalf("bad: %#v", actual)
  2350  	}
  2351  }
  2352  
  2353  func TestResourceDataSetId(t *testing.T) {
  2354  	d := &ResourceData{}
  2355  	d.SetId("foo")
  2356  
  2357  	actual := d.State()
  2358  	if actual.ID != "foo" {
  2359  		t.Fatalf("bad: %#v", actual)
  2360  	}
  2361  }
  2362  
  2363  func TestResourceDataSetId_clear(t *testing.T) {
  2364  	d := &ResourceData{
  2365  		state: &terraform.InstanceState{ID: "bar"},
  2366  	}
  2367  	d.SetId("")
  2368  
  2369  	actual := d.State()
  2370  	if actual != nil {
  2371  		t.Fatalf("bad: %#v", actual)
  2372  	}
  2373  }
  2374  
  2375  func TestResourceDataSetId_override(t *testing.T) {
  2376  	d := &ResourceData{
  2377  		state: &terraform.InstanceState{ID: "bar"},
  2378  	}
  2379  	d.SetId("foo")
  2380  
  2381  	actual := d.State()
  2382  	if actual.ID != "foo" {
  2383  		t.Fatalf("bad: %#v", actual)
  2384  	}
  2385  }