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