github.com/rmenn/terraform@v0.3.8-0.20150225065417-fc84b3a78802/helper/schema/schema_test.go (about)

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