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