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

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