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