github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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  		// #19 Empty Set
   597  		{
   598  			Schema: map[string]*Schema{
   599  				"ports": &Schema{
   600  					Type:     TypeSet,
   601  					Optional: true,
   602  					Computed: true,
   603  					Elem:     &Schema{Type: TypeInt},
   604  					Set: func(a interface{}) int {
   605  						return a.(int)
   606  					},
   607  				},
   608  			},
   609  
   610  			State: nil,
   611  
   612  			Diff: nil,
   613  
   614  			Key: "ports",
   615  
   616  			Value: []interface{}{},
   617  		},
   618  	}
   619  
   620  	for i, tc := range cases {
   621  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   622  		if err != nil {
   623  			t.Fatalf("err: %s", err)
   624  		}
   625  
   626  		v := d.Get(tc.Key)
   627  		if s, ok := v.(*Set); ok {
   628  			v = s.List()
   629  		}
   630  
   631  		if !reflect.DeepEqual(v, tc.Value) {
   632  			t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value)
   633  		}
   634  	}
   635  }
   636  
   637  func TestResourceDataGetChange(t *testing.T) {
   638  	cases := []struct {
   639  		Schema   map[string]*Schema
   640  		State    *terraform.InstanceState
   641  		Diff     *terraform.InstanceDiff
   642  		Key      string
   643  		OldValue interface{}
   644  		NewValue interface{}
   645  	}{
   646  		{
   647  			Schema: map[string]*Schema{
   648  				"availability_zone": &Schema{
   649  					Type:     TypeString,
   650  					Optional: true,
   651  					Computed: true,
   652  					ForceNew: true,
   653  				},
   654  			},
   655  
   656  			State: nil,
   657  
   658  			Diff: &terraform.InstanceDiff{
   659  				Attributes: map[string]*terraform.ResourceAttrDiff{
   660  					"availability_zone": &terraform.ResourceAttrDiff{
   661  						Old:         "",
   662  						New:         "foo",
   663  						RequiresNew: true,
   664  					},
   665  				},
   666  			},
   667  
   668  			Key: "availability_zone",
   669  
   670  			OldValue: "",
   671  			NewValue: "foo",
   672  		},
   673  
   674  		{
   675  			Schema: map[string]*Schema{
   676  				"availability_zone": &Schema{
   677  					Type:     TypeString,
   678  					Optional: true,
   679  					Computed: true,
   680  					ForceNew: true,
   681  				},
   682  			},
   683  
   684  			State: &terraform.InstanceState{
   685  				Attributes: map[string]string{
   686  					"availability_zone": "foo",
   687  				},
   688  			},
   689  
   690  			Diff: &terraform.InstanceDiff{
   691  				Attributes: map[string]*terraform.ResourceAttrDiff{
   692  					"availability_zone": &terraform.ResourceAttrDiff{
   693  						Old:         "",
   694  						New:         "foo",
   695  						RequiresNew: true,
   696  					},
   697  				},
   698  			},
   699  
   700  			Key: "availability_zone",
   701  
   702  			OldValue: "foo",
   703  			NewValue: "foo",
   704  		},
   705  	}
   706  
   707  	for i, tc := range cases {
   708  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   709  		if err != nil {
   710  			t.Fatalf("err: %s", err)
   711  		}
   712  
   713  		o, n := d.GetChange(tc.Key)
   714  		if !reflect.DeepEqual(o, tc.OldValue) {
   715  			t.Fatalf("Old Bad: %d\n\n%#v", i, o)
   716  		}
   717  		if !reflect.DeepEqual(n, tc.NewValue) {
   718  			t.Fatalf("New Bad: %d\n\n%#v", i, n)
   719  		}
   720  	}
   721  }
   722  
   723  func TestResourceDataGetOk(t *testing.T) {
   724  	cases := []struct {
   725  		Schema map[string]*Schema
   726  		State  *terraform.InstanceState
   727  		Diff   *terraform.InstanceDiff
   728  		Key    string
   729  		Value  interface{}
   730  		Ok     bool
   731  	}{
   732  		/*
   733  		 * Primitives
   734  		 */
   735  		{
   736  			Schema: map[string]*Schema{
   737  				"availability_zone": &Schema{
   738  					Type:     TypeString,
   739  					Optional: true,
   740  					Computed: true,
   741  					ForceNew: true,
   742  				},
   743  			},
   744  
   745  			State: nil,
   746  
   747  			Diff: &terraform.InstanceDiff{
   748  				Attributes: map[string]*terraform.ResourceAttrDiff{
   749  					"availability_zone": &terraform.ResourceAttrDiff{
   750  						Old: "",
   751  						New: "",
   752  					},
   753  				},
   754  			},
   755  
   756  			Key:   "availability_zone",
   757  			Value: "",
   758  			Ok:    true,
   759  		},
   760  
   761  		{
   762  			Schema: map[string]*Schema{
   763  				"availability_zone": &Schema{
   764  					Type:     TypeString,
   765  					Optional: true,
   766  					Computed: true,
   767  					ForceNew: true,
   768  				},
   769  			},
   770  
   771  			State: nil,
   772  
   773  			Diff: nil,
   774  
   775  			Key:   "availability_zone",
   776  			Value: "",
   777  			Ok:    false,
   778  		},
   779  
   780  		/*
   781  		 * Lists
   782  		 */
   783  
   784  		{
   785  			Schema: map[string]*Schema{
   786  				"ports": &Schema{
   787  					Type:     TypeList,
   788  					Optional: true,
   789  					Elem:     &Schema{Type: TypeInt},
   790  				},
   791  			},
   792  
   793  			State: nil,
   794  
   795  			Diff: nil,
   796  
   797  			Key:   "ports",
   798  			Value: []interface{}{},
   799  			Ok:    false,
   800  		},
   801  
   802  		/*
   803  		 * Map
   804  		 */
   805  
   806  		{
   807  			Schema: map[string]*Schema{
   808  				"ports": &Schema{
   809  					Type:     TypeMap,
   810  					Optional: true,
   811  				},
   812  			},
   813  
   814  			State: nil,
   815  
   816  			Diff: nil,
   817  
   818  			Key:   "ports",
   819  			Value: map[string]interface{}{},
   820  			Ok:    false,
   821  		},
   822  
   823  		/*
   824  		 * Set
   825  		 */
   826  
   827  		{
   828  			Schema: map[string]*Schema{
   829  				"ports": &Schema{
   830  					Type:     TypeSet,
   831  					Optional: true,
   832  					Elem:     &Schema{Type: TypeInt},
   833  					Set:      func(a interface{}) int { return a.(int) },
   834  				},
   835  			},
   836  
   837  			State: nil,
   838  
   839  			Diff: nil,
   840  
   841  			Key:   "ports",
   842  			Value: []interface{}{},
   843  			Ok:    false,
   844  		},
   845  
   846  		{
   847  			Schema: map[string]*Schema{
   848  				"ports": &Schema{
   849  					Type:     TypeSet,
   850  					Optional: true,
   851  					Elem:     &Schema{Type: TypeInt},
   852  					Set:      func(a interface{}) int { return a.(int) },
   853  				},
   854  			},
   855  
   856  			State: nil,
   857  
   858  			Diff: nil,
   859  
   860  			Key:   "ports.0",
   861  			Value: 0,
   862  			Ok:    false,
   863  		},
   864  	}
   865  
   866  	for i, tc := range cases {
   867  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   868  		if err != nil {
   869  			t.Fatalf("err: %s", err)
   870  		}
   871  
   872  		v, ok := d.GetOk(tc.Key)
   873  		if s, ok := v.(*Set); ok {
   874  			v = s.List()
   875  		}
   876  
   877  		if !reflect.DeepEqual(v, tc.Value) {
   878  			t.Fatalf("Bad: %d\n\n%#v", i, v)
   879  		}
   880  		if ok != tc.Ok {
   881  			t.Fatalf("Bad: %d\n\n%#v", i, ok)
   882  		}
   883  	}
   884  }
   885  
   886  func TestResourceDataHasChange(t *testing.T) {
   887  	cases := []struct {
   888  		Schema map[string]*Schema
   889  		State  *terraform.InstanceState
   890  		Diff   *terraform.InstanceDiff
   891  		Key    string
   892  		Change bool
   893  	}{
   894  		{
   895  			Schema: map[string]*Schema{
   896  				"availability_zone": &Schema{
   897  					Type:     TypeString,
   898  					Optional: true,
   899  					Computed: true,
   900  					ForceNew: true,
   901  				},
   902  			},
   903  
   904  			State: nil,
   905  
   906  			Diff: &terraform.InstanceDiff{
   907  				Attributes: map[string]*terraform.ResourceAttrDiff{
   908  					"availability_zone": &terraform.ResourceAttrDiff{
   909  						Old:         "",
   910  						New:         "foo",
   911  						RequiresNew: true,
   912  					},
   913  				},
   914  			},
   915  
   916  			Key: "availability_zone",
   917  
   918  			Change: true,
   919  		},
   920  
   921  		{
   922  			Schema: map[string]*Schema{
   923  				"availability_zone": &Schema{
   924  					Type:     TypeString,
   925  					Optional: true,
   926  					Computed: true,
   927  					ForceNew: true,
   928  				},
   929  			},
   930  
   931  			State: &terraform.InstanceState{
   932  				Attributes: map[string]string{
   933  					"availability_zone": "foo",
   934  				},
   935  			},
   936  
   937  			Diff: &terraform.InstanceDiff{
   938  				Attributes: map[string]*terraform.ResourceAttrDiff{
   939  					"availability_zone": &terraform.ResourceAttrDiff{
   940  						Old:         "",
   941  						New:         "foo",
   942  						RequiresNew: true,
   943  					},
   944  				},
   945  			},
   946  
   947  			Key: "availability_zone",
   948  
   949  			Change: false,
   950  		},
   951  
   952  		{
   953  			Schema: map[string]*Schema{
   954  				"tags": &Schema{
   955  					Type:     TypeMap,
   956  					Optional: true,
   957  					Computed: true,
   958  				},
   959  			},
   960  
   961  			State: nil,
   962  
   963  			Diff: &terraform.InstanceDiff{
   964  				Attributes: map[string]*terraform.ResourceAttrDiff{
   965  					"tags.Name": &terraform.ResourceAttrDiff{
   966  						Old: "foo",
   967  						New: "foo",
   968  					},
   969  				},
   970  			},
   971  
   972  			Key: "tags",
   973  
   974  			Change: true,
   975  		},
   976  	}
   977  
   978  	for i, tc := range cases {
   979  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
   980  		if err != nil {
   981  			t.Fatalf("err: %s", err)
   982  		}
   983  
   984  		actual := d.HasChange(tc.Key)
   985  		if actual != tc.Change {
   986  			t.Fatalf("Bad: %d %#v", i, actual)
   987  		}
   988  	}
   989  }
   990  
   991  func TestResourceDataSet(t *testing.T) {
   992  	cases := []struct {
   993  		Schema   map[string]*Schema
   994  		State    *terraform.InstanceState
   995  		Diff     *terraform.InstanceDiff
   996  		Key      string
   997  		Value    interface{}
   998  		Err      bool
   999  		GetKey   string
  1000  		GetValue interface{}
  1001  
  1002  		// GetPreProcess can be set to munge the return value before being
  1003  		// compared to GetValue
  1004  		GetPreProcess func(interface{}) interface{}
  1005  	}{
  1006  		// #0: Basic good
  1007  		{
  1008  			Schema: map[string]*Schema{
  1009  				"availability_zone": &Schema{
  1010  					Type:     TypeString,
  1011  					Optional: true,
  1012  					Computed: true,
  1013  					ForceNew: true,
  1014  				},
  1015  			},
  1016  
  1017  			State: nil,
  1018  
  1019  			Diff: nil,
  1020  
  1021  			Key:   "availability_zone",
  1022  			Value: "foo",
  1023  
  1024  			GetKey:   "availability_zone",
  1025  			GetValue: "foo",
  1026  		},
  1027  
  1028  		// #1: Basic int
  1029  		{
  1030  			Schema: map[string]*Schema{
  1031  				"port": &Schema{
  1032  					Type:     TypeInt,
  1033  					Optional: true,
  1034  					Computed: true,
  1035  					ForceNew: true,
  1036  				},
  1037  			},
  1038  
  1039  			State: nil,
  1040  
  1041  			Diff: nil,
  1042  
  1043  			Key:   "port",
  1044  			Value: 80,
  1045  
  1046  			GetKey:   "port",
  1047  			GetValue: 80,
  1048  		},
  1049  
  1050  		// #2: Basic bool
  1051  		{
  1052  			Schema: map[string]*Schema{
  1053  				"vpc": &Schema{
  1054  					Type:     TypeBool,
  1055  					Optional: true,
  1056  				},
  1057  			},
  1058  
  1059  			State: nil,
  1060  
  1061  			Diff: nil,
  1062  
  1063  			Key:   "vpc",
  1064  			Value: true,
  1065  
  1066  			GetKey:   "vpc",
  1067  			GetValue: true,
  1068  		},
  1069  
  1070  		// #3
  1071  		{
  1072  			Schema: map[string]*Schema{
  1073  				"vpc": &Schema{
  1074  					Type:     TypeBool,
  1075  					Optional: true,
  1076  				},
  1077  			},
  1078  
  1079  			State: nil,
  1080  
  1081  			Diff: nil,
  1082  
  1083  			Key:   "vpc",
  1084  			Value: false,
  1085  
  1086  			GetKey:   "vpc",
  1087  			GetValue: false,
  1088  		},
  1089  
  1090  		// #4: Invalid type
  1091  		{
  1092  			Schema: map[string]*Schema{
  1093  				"availability_zone": &Schema{
  1094  					Type:     TypeString,
  1095  					Optional: true,
  1096  					Computed: true,
  1097  					ForceNew: true,
  1098  				},
  1099  			},
  1100  
  1101  			State: nil,
  1102  
  1103  			Diff: nil,
  1104  
  1105  			Key:   "availability_zone",
  1106  			Value: 80,
  1107  			Err:   true,
  1108  
  1109  			GetKey:   "availability_zone",
  1110  			GetValue: "",
  1111  		},
  1112  
  1113  		// #5: 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  		// #6: 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  		// #7: Set a list of maps
  1157  		{
  1158  			Schema: map[string]*Schema{
  1159  				"config_vars": &Schema{
  1160  					Type:     TypeList,
  1161  					Optional: true,
  1162  					Computed: true,
  1163  					Elem: &Schema{
  1164  						Type: TypeMap,
  1165  					},
  1166  				},
  1167  			},
  1168  
  1169  			State: nil,
  1170  
  1171  			Diff: nil,
  1172  
  1173  			Key: "config_vars",
  1174  			Value: []interface{}{
  1175  				map[string]interface{}{
  1176  					"foo": "bar",
  1177  				},
  1178  				map[string]interface{}{
  1179  					"bar": "baz",
  1180  				},
  1181  			},
  1182  			Err: false,
  1183  
  1184  			GetKey: "config_vars",
  1185  			GetValue: []interface{}{
  1186  				map[string]interface{}{
  1187  					"foo": "bar",
  1188  				},
  1189  				map[string]interface{}{
  1190  					"bar": "baz",
  1191  				},
  1192  			},
  1193  		},
  1194  
  1195  		// #8: Set, with list
  1196  		{
  1197  			Schema: map[string]*Schema{
  1198  				"ports": &Schema{
  1199  					Type:     TypeSet,
  1200  					Optional: true,
  1201  					Computed: true,
  1202  					Elem:     &Schema{Type: TypeInt},
  1203  					Set: func(a interface{}) int {
  1204  						return a.(int)
  1205  					},
  1206  				},
  1207  			},
  1208  
  1209  			State: &terraform.InstanceState{
  1210  				Attributes: map[string]string{
  1211  					"ports.#": "3",
  1212  					"ports.0": "100",
  1213  					"ports.1": "80",
  1214  					"ports.2": "80",
  1215  				},
  1216  			},
  1217  
  1218  			Key:   "ports",
  1219  			Value: []interface{}{100, 125, 125},
  1220  
  1221  			GetKey:   "ports",
  1222  			GetValue: []interface{}{100, 125},
  1223  		},
  1224  
  1225  		// #9: Set, with Set
  1226  		{
  1227  			Schema: map[string]*Schema{
  1228  				"ports": &Schema{
  1229  					Type:     TypeSet,
  1230  					Optional: true,
  1231  					Computed: true,
  1232  					Elem:     &Schema{Type: TypeInt},
  1233  					Set: func(a interface{}) int {
  1234  						return a.(int)
  1235  					},
  1236  				},
  1237  			},
  1238  
  1239  			State: &terraform.InstanceState{
  1240  				Attributes: map[string]string{
  1241  					"ports.#":   "3",
  1242  					"ports.100": "100",
  1243  					"ports.80":  "80",
  1244  					"ports.81":  "81",
  1245  				},
  1246  			},
  1247  
  1248  			Key: "ports",
  1249  			Value: &Set{
  1250  				m: map[int]interface{}{
  1251  					1: 1,
  1252  					2: 2,
  1253  				},
  1254  			},
  1255  
  1256  			GetKey:   "ports",
  1257  			GetValue: []interface{}{1, 2},
  1258  		},
  1259  
  1260  		// #10: Set single item
  1261  		{
  1262  			Schema: map[string]*Schema{
  1263  				"ports": &Schema{
  1264  					Type:     TypeSet,
  1265  					Optional: true,
  1266  					Computed: true,
  1267  					Elem:     &Schema{Type: TypeInt},
  1268  					Set: func(a interface{}) int {
  1269  						return a.(int)
  1270  					},
  1271  				},
  1272  			},
  1273  
  1274  			State: &terraform.InstanceState{
  1275  				Attributes: map[string]string{
  1276  					"ports.#":   "2",
  1277  					"ports.100": "100",
  1278  					"ports.80":  "80",
  1279  				},
  1280  			},
  1281  
  1282  			Key:   "ports.100",
  1283  			Value: 256,
  1284  			Err:   true,
  1285  
  1286  			GetKey:   "ports",
  1287  			GetValue: []interface{}{80, 100},
  1288  		},
  1289  
  1290  		// #11: Set with nested set
  1291  		{
  1292  			Schema: map[string]*Schema{
  1293  				"ports": &Schema{
  1294  					Type: TypeSet,
  1295  					Elem: &Resource{
  1296  						Schema: map[string]*Schema{
  1297  							"port": &Schema{
  1298  								Type: TypeInt,
  1299  							},
  1300  
  1301  							"set": &Schema{
  1302  								Type: TypeSet,
  1303  								Elem: &Schema{Type: TypeInt},
  1304  								Set: func(a interface{}) int {
  1305  									return a.(int)
  1306  								},
  1307  							},
  1308  						},
  1309  					},
  1310  					Set: func(a interface{}) int {
  1311  						return a.(map[string]interface{})["port"].(int)
  1312  					},
  1313  				},
  1314  			},
  1315  
  1316  			State: nil,
  1317  
  1318  			Key: "ports",
  1319  			Value: []interface{}{
  1320  				map[string]interface{}{
  1321  					"port": 80,
  1322  				},
  1323  			},
  1324  
  1325  			GetKey: "ports",
  1326  			GetValue: []interface{}{
  1327  				map[string]interface{}{
  1328  					"port": 80,
  1329  					"set":  []interface{}{},
  1330  				},
  1331  			},
  1332  
  1333  			GetPreProcess: func(v interface{}) interface{} {
  1334  				if v == nil {
  1335  					return v
  1336  				}
  1337  				s, ok := v.([]interface{})
  1338  				if !ok {
  1339  					return v
  1340  				}
  1341  				for _, v := range s {
  1342  					m, ok := v.(map[string]interface{})
  1343  					if !ok {
  1344  						continue
  1345  					}
  1346  					if m["set"] == nil {
  1347  						continue
  1348  					}
  1349  					if s, ok := m["set"].(*Set); ok {
  1350  						m["set"] = s.List()
  1351  					}
  1352  				}
  1353  
  1354  				return v
  1355  			},
  1356  		},
  1357  	}
  1358  
  1359  	for i, tc := range cases {
  1360  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
  1361  		if err != nil {
  1362  			t.Fatalf("err: %s", err)
  1363  		}
  1364  
  1365  		err = d.Set(tc.Key, tc.Value)
  1366  		if (err != nil) != tc.Err {
  1367  			t.Fatalf("%d err: %s", i, err)
  1368  		}
  1369  
  1370  		v := d.Get(tc.GetKey)
  1371  		if s, ok := v.(*Set); ok {
  1372  			v = s.List()
  1373  		}
  1374  
  1375  		if tc.GetPreProcess != nil {
  1376  			v = tc.GetPreProcess(v)
  1377  		}
  1378  
  1379  		if !reflect.DeepEqual(v, tc.GetValue) {
  1380  			t.Fatalf("Get Bad: %d\n\n%#v", i, v)
  1381  		}
  1382  	}
  1383  }
  1384  
  1385  func TestResourceDataState(t *testing.T) {
  1386  	cases := []struct {
  1387  		Schema  map[string]*Schema
  1388  		State   *terraform.InstanceState
  1389  		Diff    *terraform.InstanceDiff
  1390  		Set     map[string]interface{}
  1391  		Result  *terraform.InstanceState
  1392  		Partial []string
  1393  	}{
  1394  		// #0 Basic primitive in diff
  1395  		{
  1396  			Schema: map[string]*Schema{
  1397  				"availability_zone": &Schema{
  1398  					Type:     TypeString,
  1399  					Optional: true,
  1400  					Computed: true,
  1401  					ForceNew: true,
  1402  				},
  1403  			},
  1404  
  1405  			State: nil,
  1406  
  1407  			Diff: &terraform.InstanceDiff{
  1408  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1409  					"availability_zone": &terraform.ResourceAttrDiff{
  1410  						Old:         "",
  1411  						New:         "foo",
  1412  						RequiresNew: true,
  1413  					},
  1414  				},
  1415  			},
  1416  
  1417  			Result: &terraform.InstanceState{
  1418  				Attributes: map[string]string{
  1419  					"availability_zone": "foo",
  1420  				},
  1421  			},
  1422  		},
  1423  
  1424  		// #1 Basic primitive set override
  1425  		{
  1426  			Schema: map[string]*Schema{
  1427  				"availability_zone": &Schema{
  1428  					Type:     TypeString,
  1429  					Optional: true,
  1430  					Computed: true,
  1431  					ForceNew: true,
  1432  				},
  1433  			},
  1434  
  1435  			State: nil,
  1436  
  1437  			Diff: &terraform.InstanceDiff{
  1438  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1439  					"availability_zone": &terraform.ResourceAttrDiff{
  1440  						Old:         "",
  1441  						New:         "foo",
  1442  						RequiresNew: true,
  1443  					},
  1444  				},
  1445  			},
  1446  
  1447  			Set: map[string]interface{}{
  1448  				"availability_zone": "bar",
  1449  			},
  1450  
  1451  			Result: &terraform.InstanceState{
  1452  				Attributes: map[string]string{
  1453  					"availability_zone": "bar",
  1454  				},
  1455  			},
  1456  		},
  1457  
  1458  		// #2
  1459  		{
  1460  			Schema: map[string]*Schema{
  1461  				"vpc": &Schema{
  1462  					Type:     TypeBool,
  1463  					Optional: true,
  1464  				},
  1465  			},
  1466  
  1467  			State: nil,
  1468  
  1469  			Diff: nil,
  1470  
  1471  			Set: map[string]interface{}{
  1472  				"vpc": true,
  1473  			},
  1474  
  1475  			Result: &terraform.InstanceState{
  1476  				Attributes: map[string]string{
  1477  					"vpc": "true",
  1478  				},
  1479  			},
  1480  		},
  1481  
  1482  		// #3 Basic primitive with StateFunc set
  1483  		{
  1484  			Schema: map[string]*Schema{
  1485  				"availability_zone": &Schema{
  1486  					Type:      TypeString,
  1487  					Optional:  true,
  1488  					Computed:  true,
  1489  					StateFunc: func(interface{}) string { return "" },
  1490  				},
  1491  			},
  1492  
  1493  			State: nil,
  1494  
  1495  			Diff: &terraform.InstanceDiff{
  1496  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1497  					"availability_zone": &terraform.ResourceAttrDiff{
  1498  						Old:      "",
  1499  						New:      "foo",
  1500  						NewExtra: "foo!",
  1501  					},
  1502  				},
  1503  			},
  1504  
  1505  			Result: &terraform.InstanceState{
  1506  				Attributes: map[string]string{
  1507  					"availability_zone": "foo",
  1508  				},
  1509  			},
  1510  		},
  1511  
  1512  		// #4 List
  1513  		{
  1514  			Schema: map[string]*Schema{
  1515  				"ports": &Schema{
  1516  					Type:     TypeList,
  1517  					Required: true,
  1518  					Elem:     &Schema{Type: TypeInt},
  1519  				},
  1520  			},
  1521  
  1522  			State: &terraform.InstanceState{
  1523  				Attributes: map[string]string{
  1524  					"ports.#": "1",
  1525  					"ports.0": "80",
  1526  				},
  1527  			},
  1528  
  1529  			Diff: &terraform.InstanceDiff{
  1530  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1531  					"ports.#": &terraform.ResourceAttrDiff{
  1532  						Old: "1",
  1533  						New: "2",
  1534  					},
  1535  					"ports.1": &terraform.ResourceAttrDiff{
  1536  						Old: "",
  1537  						New: "100",
  1538  					},
  1539  				},
  1540  			},
  1541  
  1542  			Result: &terraform.InstanceState{
  1543  				Attributes: map[string]string{
  1544  					"ports.#": "2",
  1545  					"ports.0": "80",
  1546  					"ports.1": "100",
  1547  				},
  1548  			},
  1549  		},
  1550  
  1551  		// #5 List of resources
  1552  		{
  1553  			Schema: map[string]*Schema{
  1554  				"ingress": &Schema{
  1555  					Type:     TypeList,
  1556  					Required: true,
  1557  					Elem: &Resource{
  1558  						Schema: map[string]*Schema{
  1559  							"from": &Schema{
  1560  								Type:     TypeInt,
  1561  								Required: true,
  1562  							},
  1563  						},
  1564  					},
  1565  				},
  1566  			},
  1567  
  1568  			State: &terraform.InstanceState{
  1569  				Attributes: map[string]string{
  1570  					"ingress.#":      "1",
  1571  					"ingress.0.from": "80",
  1572  				},
  1573  			},
  1574  
  1575  			Diff: &terraform.InstanceDiff{
  1576  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1577  					"ingress.#": &terraform.ResourceAttrDiff{
  1578  						Old: "1",
  1579  						New: "2",
  1580  					},
  1581  					"ingress.0.from": &terraform.ResourceAttrDiff{
  1582  						Old: "80",
  1583  						New: "150",
  1584  					},
  1585  					"ingress.1.from": &terraform.ResourceAttrDiff{
  1586  						Old: "",
  1587  						New: "100",
  1588  					},
  1589  				},
  1590  			},
  1591  
  1592  			Result: &terraform.InstanceState{
  1593  				Attributes: map[string]string{
  1594  					"ingress.#":      "2",
  1595  					"ingress.0.from": "150",
  1596  					"ingress.1.from": "100",
  1597  				},
  1598  			},
  1599  		},
  1600  
  1601  		// #6 List of maps
  1602  		{
  1603  			Schema: map[string]*Schema{
  1604  				"config_vars": &Schema{
  1605  					Type:     TypeList,
  1606  					Optional: true,
  1607  					Computed: true,
  1608  					Elem: &Schema{
  1609  						Type: TypeMap,
  1610  					},
  1611  				},
  1612  			},
  1613  
  1614  			State: &terraform.InstanceState{
  1615  				Attributes: map[string]string{
  1616  					"config_vars.#":     "2",
  1617  					"config_vars.0.foo": "bar",
  1618  					"config_vars.0.bar": "bar",
  1619  					"config_vars.1.bar": "baz",
  1620  				},
  1621  			},
  1622  
  1623  			Diff: &terraform.InstanceDiff{
  1624  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1625  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1626  						NewRemoved: true,
  1627  					},
  1628  				},
  1629  			},
  1630  
  1631  			Set: map[string]interface{}{
  1632  				"config_vars": []map[string]interface{}{
  1633  					map[string]interface{}{
  1634  						"foo": "bar",
  1635  					},
  1636  					map[string]interface{}{
  1637  						"baz": "bang",
  1638  					},
  1639  				},
  1640  			},
  1641  
  1642  			Result: &terraform.InstanceState{
  1643  				Attributes: map[string]string{
  1644  					"config_vars.#":     "2",
  1645  					"config_vars.0.foo": "bar",
  1646  					"config_vars.1.baz": "bang",
  1647  				},
  1648  			},
  1649  		},
  1650  
  1651  		// #7 List of maps with removal in diff
  1652  		{
  1653  			Schema: map[string]*Schema{
  1654  				"config_vars": &Schema{
  1655  					Type:     TypeList,
  1656  					Optional: true,
  1657  					Computed: true,
  1658  					Elem: &Schema{
  1659  						Type: TypeMap,
  1660  					},
  1661  				},
  1662  			},
  1663  
  1664  			State: &terraform.InstanceState{
  1665  				Attributes: map[string]string{
  1666  					"config_vars.#":     "1",
  1667  					"config_vars.0.FOO": "bar",
  1668  				},
  1669  			},
  1670  
  1671  			Diff: &terraform.InstanceDiff{
  1672  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1673  					"config_vars.#": &terraform.ResourceAttrDiff{
  1674  						Old: "1",
  1675  						New: "0",
  1676  					},
  1677  					"config_vars.0.FOO": &terraform.ResourceAttrDiff{
  1678  						Old:        "bar",
  1679  						NewRemoved: true,
  1680  					},
  1681  				},
  1682  			},
  1683  
  1684  			Result: &terraform.InstanceState{
  1685  				Attributes: map[string]string{
  1686  					"config_vars.#": "0",
  1687  				},
  1688  			},
  1689  		},
  1690  
  1691  		// #8 Basic state with other keys
  1692  		{
  1693  			Schema: map[string]*Schema{
  1694  				"availability_zone": &Schema{
  1695  					Type:     TypeString,
  1696  					Optional: true,
  1697  					Computed: true,
  1698  					ForceNew: true,
  1699  				},
  1700  			},
  1701  
  1702  			State: &terraform.InstanceState{
  1703  				ID: "bar",
  1704  				Attributes: map[string]string{
  1705  					"id": "bar",
  1706  				},
  1707  			},
  1708  
  1709  			Diff: &terraform.InstanceDiff{
  1710  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1711  					"availability_zone": &terraform.ResourceAttrDiff{
  1712  						Old:         "",
  1713  						New:         "foo",
  1714  						RequiresNew: true,
  1715  					},
  1716  				},
  1717  			},
  1718  
  1719  			Result: &terraform.InstanceState{
  1720  				ID: "bar",
  1721  				Attributes: map[string]string{
  1722  					"id":                "bar",
  1723  					"availability_zone": "foo",
  1724  				},
  1725  			},
  1726  		},
  1727  
  1728  		// #9 Sets
  1729  		{
  1730  			Schema: map[string]*Schema{
  1731  				"ports": &Schema{
  1732  					Type:     TypeSet,
  1733  					Optional: true,
  1734  					Computed: true,
  1735  					Elem:     &Schema{Type: TypeInt},
  1736  					Set: func(a interface{}) int {
  1737  						return a.(int)
  1738  					},
  1739  				},
  1740  			},
  1741  
  1742  			State: &terraform.InstanceState{
  1743  				Attributes: map[string]string{
  1744  					"ports.#":   "3",
  1745  					"ports.100": "100",
  1746  					"ports.80":  "80",
  1747  					"ports.81":  "81",
  1748  				},
  1749  			},
  1750  
  1751  			Diff: nil,
  1752  
  1753  			Result: &terraform.InstanceState{
  1754  				Attributes: map[string]string{
  1755  					"ports.#":   "3",
  1756  					"ports.80":  "80",
  1757  					"ports.81":  "81",
  1758  					"ports.100": "100",
  1759  				},
  1760  			},
  1761  		},
  1762  
  1763  		// #10
  1764  		{
  1765  			Schema: map[string]*Schema{
  1766  				"ports": &Schema{
  1767  					Type:     TypeSet,
  1768  					Optional: true,
  1769  					Computed: true,
  1770  					Elem:     &Schema{Type: TypeInt},
  1771  					Set: func(a interface{}) int {
  1772  						return a.(int)
  1773  					},
  1774  				},
  1775  			},
  1776  
  1777  			State: nil,
  1778  
  1779  			Diff: nil,
  1780  
  1781  			Set: map[string]interface{}{
  1782  				"ports": []interface{}{100, 80},
  1783  			},
  1784  
  1785  			Result: &terraform.InstanceState{
  1786  				Attributes: map[string]string{
  1787  					"ports.#":   "2",
  1788  					"ports.80":  "80",
  1789  					"ports.100": "100",
  1790  				},
  1791  			},
  1792  		},
  1793  
  1794  		// #11
  1795  		{
  1796  			Schema: map[string]*Schema{
  1797  				"ports": &Schema{
  1798  					Type:     TypeSet,
  1799  					Optional: true,
  1800  					Computed: true,
  1801  					Elem: &Resource{
  1802  						Schema: map[string]*Schema{
  1803  							"order": &Schema{
  1804  								Type: TypeInt,
  1805  							},
  1806  
  1807  							"a": &Schema{
  1808  								Type: TypeList,
  1809  								Elem: &Schema{Type: TypeInt},
  1810  							},
  1811  
  1812  							"b": &Schema{
  1813  								Type: TypeList,
  1814  								Elem: &Schema{Type: TypeInt},
  1815  							},
  1816  						},
  1817  					},
  1818  					Set: func(a interface{}) int {
  1819  						m := a.(map[string]interface{})
  1820  						return m["order"].(int)
  1821  					},
  1822  				},
  1823  			},
  1824  
  1825  			State: &terraform.InstanceState{
  1826  				Attributes: map[string]string{
  1827  					"ports.#":        "2",
  1828  					"ports.10.order": "10",
  1829  					"ports.10.a.#":   "1",
  1830  					"ports.10.a.0":   "80",
  1831  					"ports.20.order": "20",
  1832  					"ports.20.b.#":   "1",
  1833  					"ports.20.b.0":   "100",
  1834  				},
  1835  			},
  1836  
  1837  			Set: map[string]interface{}{
  1838  				"ports": []interface{}{
  1839  					map[string]interface{}{
  1840  						"order": 20,
  1841  						"b":     []interface{}{100},
  1842  					},
  1843  					map[string]interface{}{
  1844  						"order": 10,
  1845  						"a":     []interface{}{80},
  1846  					},
  1847  				},
  1848  			},
  1849  
  1850  			Result: &terraform.InstanceState{
  1851  				Attributes: map[string]string{
  1852  					"ports.#":        "2",
  1853  					"ports.10.order": "10",
  1854  					"ports.10.a.#":   "1",
  1855  					"ports.10.a.0":   "80",
  1856  					"ports.10.b.#":   "0",
  1857  					"ports.20.order": "20",
  1858  					"ports.20.a.#":   "0",
  1859  					"ports.20.b.#":   "1",
  1860  					"ports.20.b.0":   "100",
  1861  				},
  1862  			},
  1863  		},
  1864  
  1865  		/*
  1866  		 * PARTIAL STATES
  1867  		 */
  1868  
  1869  		// #12 Basic primitive
  1870  		{
  1871  			Schema: map[string]*Schema{
  1872  				"availability_zone": &Schema{
  1873  					Type:     TypeString,
  1874  					Optional: true,
  1875  					Computed: true,
  1876  					ForceNew: true,
  1877  				},
  1878  			},
  1879  
  1880  			State: nil,
  1881  
  1882  			Diff: &terraform.InstanceDiff{
  1883  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1884  					"availability_zone": &terraform.ResourceAttrDiff{
  1885  						Old:         "",
  1886  						New:         "foo",
  1887  						RequiresNew: true,
  1888  					},
  1889  				},
  1890  			},
  1891  
  1892  			Partial: []string{},
  1893  
  1894  			Result: &terraform.InstanceState{
  1895  				Attributes: map[string]string{
  1896  					"availability_zone": "",
  1897  				},
  1898  			},
  1899  		},
  1900  
  1901  		// #13 List
  1902  		{
  1903  			Schema: map[string]*Schema{
  1904  				"ports": &Schema{
  1905  					Type:     TypeList,
  1906  					Required: true,
  1907  					Elem:     &Schema{Type: TypeInt},
  1908  				},
  1909  			},
  1910  
  1911  			State: &terraform.InstanceState{
  1912  				Attributes: map[string]string{
  1913  					"ports.#": "1",
  1914  					"ports.0": "80",
  1915  				},
  1916  			},
  1917  
  1918  			Diff: &terraform.InstanceDiff{
  1919  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1920  					"ports.#": &terraform.ResourceAttrDiff{
  1921  						Old: "1",
  1922  						New: "2",
  1923  					},
  1924  					"ports.1": &terraform.ResourceAttrDiff{
  1925  						Old: "",
  1926  						New: "100",
  1927  					},
  1928  				},
  1929  			},
  1930  
  1931  			Partial: []string{},
  1932  
  1933  			Result: &terraform.InstanceState{
  1934  				Attributes: map[string]string{
  1935  					"ports.#": "1",
  1936  					"ports.0": "80",
  1937  				},
  1938  			},
  1939  		},
  1940  
  1941  		// #14
  1942  		{
  1943  			Schema: map[string]*Schema{
  1944  				"ports": &Schema{
  1945  					Type:     TypeList,
  1946  					Optional: true,
  1947  					Computed: true,
  1948  					Elem:     &Schema{Type: TypeInt},
  1949  				},
  1950  			},
  1951  
  1952  			State: nil,
  1953  
  1954  			Diff: &terraform.InstanceDiff{
  1955  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1956  					"ports.#": &terraform.ResourceAttrDiff{
  1957  						Old:         "",
  1958  						NewComputed: true,
  1959  					},
  1960  				},
  1961  			},
  1962  
  1963  			Partial: []string{},
  1964  
  1965  			Set: map[string]interface{}{
  1966  				"ports": []interface{}{},
  1967  			},
  1968  
  1969  			Result: &terraform.InstanceState{
  1970  				Attributes: map[string]string{
  1971  					"ports.#": "0",
  1972  				},
  1973  			},
  1974  		},
  1975  
  1976  		// #15 List of resources
  1977  		{
  1978  			Schema: map[string]*Schema{
  1979  				"ingress": &Schema{
  1980  					Type:     TypeList,
  1981  					Required: true,
  1982  					Elem: &Resource{
  1983  						Schema: map[string]*Schema{
  1984  							"from": &Schema{
  1985  								Type:     TypeInt,
  1986  								Required: true,
  1987  							},
  1988  						},
  1989  					},
  1990  				},
  1991  			},
  1992  
  1993  			State: &terraform.InstanceState{
  1994  				Attributes: map[string]string{
  1995  					"ingress.#":      "1",
  1996  					"ingress.0.from": "80",
  1997  				},
  1998  			},
  1999  
  2000  			Diff: &terraform.InstanceDiff{
  2001  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2002  					"ingress.#": &terraform.ResourceAttrDiff{
  2003  						Old: "1",
  2004  						New: "2",
  2005  					},
  2006  					"ingress.0.from": &terraform.ResourceAttrDiff{
  2007  						Old: "80",
  2008  						New: "150",
  2009  					},
  2010  					"ingress.1.from": &terraform.ResourceAttrDiff{
  2011  						Old: "",
  2012  						New: "100",
  2013  					},
  2014  				},
  2015  			},
  2016  
  2017  			Partial: []string{},
  2018  
  2019  			Result: &terraform.InstanceState{
  2020  				Attributes: map[string]string{
  2021  					"ingress.#":      "1",
  2022  					"ingress.0.from": "80",
  2023  				},
  2024  			},
  2025  		},
  2026  
  2027  		// #16 List of maps
  2028  		{
  2029  			Schema: map[string]*Schema{
  2030  				"config_vars": &Schema{
  2031  					Type:     TypeList,
  2032  					Optional: true,
  2033  					Computed: true,
  2034  					Elem: &Schema{
  2035  						Type: TypeMap,
  2036  					},
  2037  				},
  2038  			},
  2039  
  2040  			State: &terraform.InstanceState{
  2041  				Attributes: map[string]string{
  2042  					"config_vars.#":     "2",
  2043  					"config_vars.0.foo": "bar",
  2044  					"config_vars.0.bar": "bar",
  2045  					"config_vars.1.bar": "baz",
  2046  				},
  2047  			},
  2048  
  2049  			Diff: &terraform.InstanceDiff{
  2050  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2051  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  2052  						NewRemoved: true,
  2053  					},
  2054  				},
  2055  			},
  2056  
  2057  			Set: map[string]interface{}{
  2058  				"config_vars": []map[string]interface{}{
  2059  					map[string]interface{}{
  2060  						"foo": "bar",
  2061  					},
  2062  					map[string]interface{}{
  2063  						"baz": "bang",
  2064  					},
  2065  				},
  2066  			},
  2067  
  2068  			Partial: []string{},
  2069  
  2070  			Result: &terraform.InstanceState{
  2071  				Attributes: map[string]string{
  2072  					// TODO: broken, shouldn't bar be removed?
  2073  					"config_vars.#":     "2",
  2074  					"config_vars.0.foo": "bar",
  2075  					"config_vars.0.bar": "bar",
  2076  					"config_vars.1.bar": "baz",
  2077  				},
  2078  			},
  2079  		},
  2080  
  2081  		// #17 Sets
  2082  		{
  2083  			Schema: map[string]*Schema{
  2084  				"ports": &Schema{
  2085  					Type:     TypeSet,
  2086  					Optional: true,
  2087  					Computed: true,
  2088  					Elem:     &Schema{Type: TypeInt},
  2089  					Set: func(a interface{}) int {
  2090  						return a.(int)
  2091  					},
  2092  				},
  2093  			},
  2094  
  2095  			State: &terraform.InstanceState{
  2096  				Attributes: map[string]string{
  2097  					"ports.#":   "3",
  2098  					"ports.100": "100",
  2099  					"ports.80":  "80",
  2100  					"ports.81":  "81",
  2101  				},
  2102  			},
  2103  
  2104  			Diff: &terraform.InstanceDiff{
  2105  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2106  					"ports.120": &terraform.ResourceAttrDiff{
  2107  						New: "120",
  2108  					},
  2109  				},
  2110  			},
  2111  
  2112  			Partial: []string{},
  2113  
  2114  			Result: &terraform.InstanceState{
  2115  				Attributes: map[string]string{
  2116  					"ports.#":   "3",
  2117  					"ports.80":  "80",
  2118  					"ports.81":  "81",
  2119  					"ports.100": "100",
  2120  				},
  2121  			},
  2122  		},
  2123  
  2124  		// #18
  2125  		{
  2126  			Schema: map[string]*Schema{
  2127  				"ports": &Schema{
  2128  					Type:     TypeSet,
  2129  					Optional: true,
  2130  					Computed: true,
  2131  					Elem:     &Schema{Type: TypeInt},
  2132  					Set: func(a interface{}) int {
  2133  						return a.(int)
  2134  					},
  2135  				},
  2136  			},
  2137  
  2138  			State: nil,
  2139  
  2140  			Diff: &terraform.InstanceDiff{
  2141  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2142  					"ports.#": &terraform.ResourceAttrDiff{
  2143  						Old:         "",
  2144  						NewComputed: true,
  2145  					},
  2146  				},
  2147  			},
  2148  
  2149  			Partial: []string{},
  2150  
  2151  			Result: &terraform.InstanceState{
  2152  				Attributes: map[string]string{
  2153  					"ports.#": "0",
  2154  				},
  2155  			},
  2156  		},
  2157  
  2158  		// #19 Maps
  2159  		{
  2160  			Schema: map[string]*Schema{
  2161  				"tags": &Schema{
  2162  					Type:     TypeMap,
  2163  					Optional: true,
  2164  					Computed: true,
  2165  				},
  2166  			},
  2167  
  2168  			State: nil,
  2169  
  2170  			Diff: &terraform.InstanceDiff{
  2171  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2172  					"tags.Name": &terraform.ResourceAttrDiff{
  2173  						Old: "",
  2174  						New: "foo",
  2175  					},
  2176  				},
  2177  			},
  2178  
  2179  			Result: &terraform.InstanceState{
  2180  				Attributes: map[string]string{
  2181  					"tags.Name": "foo",
  2182  				},
  2183  			},
  2184  		},
  2185  
  2186  		// #20
  2187  		{
  2188  			Schema: map[string]*Schema{
  2189  				"tags": &Schema{
  2190  					Type:     TypeMap,
  2191  					Optional: true,
  2192  					Computed: true,
  2193  				},
  2194  			},
  2195  
  2196  			State: nil,
  2197  
  2198  			Diff: &terraform.InstanceDiff{
  2199  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2200  					"tags.Name": &terraform.ResourceAttrDiff{
  2201  						Old: "",
  2202  						New: "foo",
  2203  					},
  2204  				},
  2205  			},
  2206  
  2207  			Set: map[string]interface{}{
  2208  				"tags": map[string]string{},
  2209  			},
  2210  
  2211  			Result: &terraform.InstanceState{
  2212  				Attributes: map[string]string{},
  2213  			},
  2214  		},
  2215  	}
  2216  
  2217  	for i, tc := range cases {
  2218  		d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff)
  2219  		if err != nil {
  2220  			t.Fatalf("err: %s", err)
  2221  		}
  2222  
  2223  		for k, v := range tc.Set {
  2224  			if err := d.Set(k, v); err != nil {
  2225  				t.Fatalf("%d err: %s", i, err)
  2226  			}
  2227  		}
  2228  
  2229  		// Set an ID so that the state returned is not nil
  2230  		idSet := false
  2231  		if d.Id() == "" {
  2232  			idSet = true
  2233  			d.SetId("foo")
  2234  		}
  2235  
  2236  		// If we have partial, then enable partial state mode.
  2237  		if tc.Partial != nil {
  2238  			d.Partial(true)
  2239  			for _, k := range tc.Partial {
  2240  				d.SetPartial(k)
  2241  			}
  2242  		}
  2243  
  2244  		actual := d.State()
  2245  
  2246  		// If we set an ID, then undo what we did so the comparison works
  2247  		if actual != nil && idSet {
  2248  			actual.ID = ""
  2249  			delete(actual.Attributes, "id")
  2250  		}
  2251  
  2252  		if !reflect.DeepEqual(actual, tc.Result) {
  2253  			t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result)
  2254  		}
  2255  	}
  2256  }
  2257  
  2258  func TestResourceDataSetConnInfo(t *testing.T) {
  2259  	d := &ResourceData{}
  2260  	d.SetId("foo")
  2261  	d.SetConnInfo(map[string]string{
  2262  		"foo": "bar",
  2263  	})
  2264  
  2265  	expected := map[string]string{
  2266  		"foo": "bar",
  2267  	}
  2268  
  2269  	actual := d.State()
  2270  	if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) {
  2271  		t.Fatalf("bad: %#v", actual)
  2272  	}
  2273  }
  2274  
  2275  func TestResourceDataSetId(t *testing.T) {
  2276  	d := &ResourceData{}
  2277  	d.SetId("foo")
  2278  
  2279  	actual := d.State()
  2280  	if actual.ID != "foo" {
  2281  		t.Fatalf("bad: %#v", actual)
  2282  	}
  2283  }
  2284  
  2285  func TestResourceDataSetId_clear(t *testing.T) {
  2286  	d := &ResourceData{
  2287  		state: &terraform.InstanceState{ID: "bar"},
  2288  	}
  2289  	d.SetId("")
  2290  
  2291  	actual := d.State()
  2292  	if actual != nil {
  2293  		t.Fatalf("bad: %#v", actual)
  2294  	}
  2295  }
  2296  
  2297  func TestResourceDataSetId_override(t *testing.T) {
  2298  	d := &ResourceData{
  2299  		state: &terraform.InstanceState{ID: "bar"},
  2300  	}
  2301  	d.SetId("foo")
  2302  
  2303  	actual := d.State()
  2304  	if actual.ID != "foo" {
  2305  		t.Fatalf("bad: %#v", actual)
  2306  	}
  2307  }