github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/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, nil},
   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  
  1948  	for i, tc := range cases {
  1949  		c, err := config.NewRawConfig(tc.Config)
  1950  		if err != nil {
  1951  			t.Fatalf("#%d err: %s", i, err)
  1952  		}
  1953  
  1954  		if len(tc.ConfigVariables) > 0 {
  1955  			vars := make(map[string]ast.Variable)
  1956  			for k, v := range tc.ConfigVariables {
  1957  				vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
  1958  			}
  1959  
  1960  			if err := c.Interpolate(vars); err != nil {
  1961  				t.Fatalf("#%d err: %s", i, err)
  1962  			}
  1963  		}
  1964  
  1965  		d, err := schemaMap(tc.Schema).Diff(
  1966  			tc.State, terraform.NewResourceConfig(c))
  1967  		if (err != nil) != tc.Err {
  1968  			t.Fatalf("#%d err: %s", i, err)
  1969  		}
  1970  
  1971  		if !reflect.DeepEqual(tc.Diff, d) {
  1972  			t.Fatalf("#%d: bad:\n\n%#v", i, d)
  1973  		}
  1974  	}
  1975  }
  1976  
  1977  func TestSchemaMap_Input(t *testing.T) {
  1978  	cases := map[string]struct {
  1979  		Schema map[string]*Schema
  1980  		Config map[string]interface{}
  1981  		Input  map[string]string
  1982  		Result map[string]interface{}
  1983  		Err    bool
  1984  	}{
  1985  		/*
  1986  		 * String decode
  1987  		 */
  1988  
  1989  		"uses input on optional field with no config": {
  1990  			Schema: map[string]*Schema{
  1991  				"availability_zone": &Schema{
  1992  					Type:     TypeString,
  1993  					Optional: true,
  1994  				},
  1995  			},
  1996  
  1997  			Input: map[string]string{
  1998  				"availability_zone": "foo",
  1999  			},
  2000  
  2001  			Result: map[string]interface{}{
  2002  				"availability_zone": "foo",
  2003  			},
  2004  
  2005  			Err: false,
  2006  		},
  2007  
  2008  		"input ignored when config has a value": {
  2009  			Schema: map[string]*Schema{
  2010  				"availability_zone": &Schema{
  2011  					Type:     TypeString,
  2012  					Optional: true,
  2013  				},
  2014  			},
  2015  
  2016  			Config: map[string]interface{}{
  2017  				"availability_zone": "bar",
  2018  			},
  2019  
  2020  			Input: map[string]string{
  2021  				"availability_zone": "foo",
  2022  			},
  2023  
  2024  			Result: map[string]interface{}{},
  2025  
  2026  			Err: false,
  2027  		},
  2028  
  2029  		"input ignored when schema has a default": {
  2030  			Schema: map[string]*Schema{
  2031  				"availability_zone": &Schema{
  2032  					Type:     TypeString,
  2033  					Default:  "foo",
  2034  					Optional: true,
  2035  				},
  2036  			},
  2037  
  2038  			Input: map[string]string{
  2039  				"availability_zone": "bar",
  2040  			},
  2041  
  2042  			Result: map[string]interface{}{},
  2043  
  2044  			Err: false,
  2045  		},
  2046  
  2047  		"input ignored when default function returns a value": {
  2048  			Schema: map[string]*Schema{
  2049  				"availability_zone": &Schema{
  2050  					Type: TypeString,
  2051  					DefaultFunc: func() (interface{}, error) {
  2052  						return "foo", nil
  2053  					},
  2054  					Optional: true,
  2055  				},
  2056  			},
  2057  
  2058  			Input: map[string]string{
  2059  				"availability_zone": "bar",
  2060  			},
  2061  
  2062  			Result: map[string]interface{}{},
  2063  
  2064  			Err: false,
  2065  		},
  2066  
  2067  		"input used when default function returns nil": {
  2068  			Schema: map[string]*Schema{
  2069  				"availability_zone": &Schema{
  2070  					Type: TypeString,
  2071  					DefaultFunc: func() (interface{}, error) {
  2072  						return nil, nil
  2073  					},
  2074  					Optional: true,
  2075  				},
  2076  			},
  2077  
  2078  			Input: map[string]string{
  2079  				"availability_zone": "bar",
  2080  			},
  2081  
  2082  			Result: map[string]interface{}{
  2083  				"availability_zone": "bar",
  2084  			},
  2085  
  2086  			Err: false,
  2087  		},
  2088  	}
  2089  
  2090  	for i, tc := range cases {
  2091  		if tc.Config == nil {
  2092  			tc.Config = make(map[string]interface{})
  2093  		}
  2094  
  2095  		c, err := config.NewRawConfig(tc.Config)
  2096  		if err != nil {
  2097  			t.Fatalf("err: %s", err)
  2098  		}
  2099  
  2100  		input := new(terraform.MockUIInput)
  2101  		input.InputReturnMap = tc.Input
  2102  
  2103  		rc := terraform.NewResourceConfig(c)
  2104  		rc.Config = make(map[string]interface{})
  2105  
  2106  		actual, err := schemaMap(tc.Schema).Input(input, rc)
  2107  		if (err != nil) != tc.Err {
  2108  			t.Fatalf("#%v err: %s", i, err)
  2109  		}
  2110  
  2111  		if !reflect.DeepEqual(tc.Result, actual.Config) {
  2112  			t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result)
  2113  		}
  2114  	}
  2115  }
  2116  
  2117  func TestSchemaMap_InternalValidate(t *testing.T) {
  2118  	cases := []struct {
  2119  		In  map[string]*Schema
  2120  		Err bool
  2121  	}{
  2122  		{
  2123  			nil,
  2124  			false,
  2125  		},
  2126  
  2127  		// No optional and no required
  2128  		{
  2129  			map[string]*Schema{
  2130  				"foo": &Schema{
  2131  					Type:     TypeInt,
  2132  					Optional: true,
  2133  					Required: true,
  2134  				},
  2135  			},
  2136  			true,
  2137  		},
  2138  
  2139  		// No optional and no required
  2140  		{
  2141  			map[string]*Schema{
  2142  				"foo": &Schema{
  2143  					Type: TypeInt,
  2144  				},
  2145  			},
  2146  			true,
  2147  		},
  2148  
  2149  		// Missing Type
  2150  		{
  2151  			map[string]*Schema{
  2152  				"foo": &Schema{
  2153  					Required: true,
  2154  				},
  2155  			},
  2156  			true,
  2157  		},
  2158  
  2159  		// Required but computed
  2160  		{
  2161  			map[string]*Schema{
  2162  				"foo": &Schema{
  2163  					Type:     TypeInt,
  2164  					Required: true,
  2165  					Computed: true,
  2166  				},
  2167  			},
  2168  			true,
  2169  		},
  2170  
  2171  		// Looks good
  2172  		{
  2173  			map[string]*Schema{
  2174  				"foo": &Schema{
  2175  					Type:     TypeString,
  2176  					Required: true,
  2177  				},
  2178  			},
  2179  			false,
  2180  		},
  2181  
  2182  		// Computed but has default
  2183  		{
  2184  			map[string]*Schema{
  2185  				"foo": &Schema{
  2186  					Type:     TypeInt,
  2187  					Optional: true,
  2188  					Computed: true,
  2189  					Default:  "foo",
  2190  				},
  2191  			},
  2192  			true,
  2193  		},
  2194  
  2195  		// Required but has default
  2196  		{
  2197  			map[string]*Schema{
  2198  				"foo": &Schema{
  2199  					Type:     TypeInt,
  2200  					Optional: true,
  2201  					Required: true,
  2202  					Default:  "foo",
  2203  				},
  2204  			},
  2205  			true,
  2206  		},
  2207  
  2208  		// List element not set
  2209  		{
  2210  			map[string]*Schema{
  2211  				"foo": &Schema{
  2212  					Type: TypeList,
  2213  				},
  2214  			},
  2215  			true,
  2216  		},
  2217  
  2218  		// List default
  2219  		{
  2220  			map[string]*Schema{
  2221  				"foo": &Schema{
  2222  					Type:    TypeList,
  2223  					Elem:    &Schema{Type: TypeInt},
  2224  					Default: "foo",
  2225  				},
  2226  			},
  2227  			true,
  2228  		},
  2229  
  2230  		// List element computed
  2231  		{
  2232  			map[string]*Schema{
  2233  				"foo": &Schema{
  2234  					Type:     TypeList,
  2235  					Optional: true,
  2236  					Elem: &Schema{
  2237  						Type:     TypeInt,
  2238  						Computed: true,
  2239  					},
  2240  				},
  2241  			},
  2242  			true,
  2243  		},
  2244  
  2245  		// List element with Set set
  2246  		{
  2247  			map[string]*Schema{
  2248  				"foo": &Schema{
  2249  					Type:     TypeList,
  2250  					Elem:     &Schema{Type: TypeInt},
  2251  					Set:      func(interface{}) int { return 0 },
  2252  					Optional: true,
  2253  				},
  2254  			},
  2255  			true,
  2256  		},
  2257  
  2258  		// Set element with no Set set
  2259  		{
  2260  			map[string]*Schema{
  2261  				"foo": &Schema{
  2262  					Type:     TypeSet,
  2263  					Elem:     &Schema{Type: TypeInt},
  2264  					Optional: true,
  2265  				},
  2266  			},
  2267  			true,
  2268  		},
  2269  
  2270  		// Required but computed
  2271  		{
  2272  			map[string]*Schema{
  2273  				"foo": &Schema{
  2274  					Type:         TypeInt,
  2275  					Required:     true,
  2276  					ComputedWhen: []string{"foo"},
  2277  				},
  2278  			},
  2279  			true,
  2280  		},
  2281  
  2282  		// Sub-resource invalid
  2283  		{
  2284  			map[string]*Schema{
  2285  				"foo": &Schema{
  2286  					Type:     TypeList,
  2287  					Optional: true,
  2288  					Elem: &Resource{
  2289  						Schema: map[string]*Schema{
  2290  							"foo": new(Schema),
  2291  						},
  2292  					},
  2293  				},
  2294  			},
  2295  			true,
  2296  		},
  2297  
  2298  		// Sub-resource valid
  2299  		{
  2300  			map[string]*Schema{
  2301  				"foo": &Schema{
  2302  					Type:     TypeList,
  2303  					Optional: true,
  2304  					Elem: &Resource{
  2305  						Schema: map[string]*Schema{
  2306  							"foo": &Schema{
  2307  								Type:     TypeInt,
  2308  								Optional: true,
  2309  							},
  2310  						},
  2311  					},
  2312  				},
  2313  			},
  2314  			false,
  2315  		},
  2316  	}
  2317  
  2318  	for i, tc := range cases {
  2319  		err := schemaMap(tc.In).InternalValidate()
  2320  		if (err != nil) != tc.Err {
  2321  			t.Fatalf("%d: bad: %s\n\n%#v", i, err, tc.In)
  2322  		}
  2323  	}
  2324  
  2325  }
  2326  
  2327  func TestSchemaMap_Validate(t *testing.T) {
  2328  	cases := []struct {
  2329  		Schema map[string]*Schema
  2330  		Config map[string]interface{}
  2331  		Vars   map[string]string
  2332  		Warn   bool
  2333  		Err    bool
  2334  	}{
  2335  		// Good
  2336  		{
  2337  			Schema: map[string]*Schema{
  2338  				"availability_zone": &Schema{
  2339  					Type:     TypeString,
  2340  					Optional: true,
  2341  					Computed: true,
  2342  					ForceNew: true,
  2343  				},
  2344  			},
  2345  
  2346  			Config: map[string]interface{}{
  2347  				"availability_zone": "foo",
  2348  			},
  2349  		},
  2350  
  2351  		// Good, because the var is not set and that error will come elsewhere
  2352  		{
  2353  			Schema: map[string]*Schema{
  2354  				"size": &Schema{
  2355  					Type:     TypeInt,
  2356  					Required: true,
  2357  				},
  2358  			},
  2359  
  2360  			Config: map[string]interface{}{
  2361  				"size": "${var.foo}",
  2362  			},
  2363  
  2364  			Vars: map[string]string{
  2365  				"var.foo": config.UnknownVariableValue,
  2366  			},
  2367  		},
  2368  
  2369  		// Required field not set
  2370  		{
  2371  			Schema: map[string]*Schema{
  2372  				"availability_zone": &Schema{
  2373  					Type:     TypeString,
  2374  					Required: true,
  2375  				},
  2376  			},
  2377  
  2378  			Config: map[string]interface{}{},
  2379  
  2380  			Err: true,
  2381  		},
  2382  
  2383  		// Invalid type
  2384  		{
  2385  			Schema: map[string]*Schema{
  2386  				"port": &Schema{
  2387  					Type:     TypeInt,
  2388  					Required: true,
  2389  				},
  2390  			},
  2391  
  2392  			Config: map[string]interface{}{
  2393  				"port": "I am invalid",
  2394  			},
  2395  
  2396  			Err: true,
  2397  		},
  2398  
  2399  		{
  2400  			Schema: map[string]*Schema{
  2401  				"user_data": &Schema{
  2402  					Type:     TypeString,
  2403  					Optional: true,
  2404  				},
  2405  			},
  2406  
  2407  			Config: map[string]interface{}{
  2408  				"user_data": []interface{}{
  2409  					map[string]interface{}{
  2410  						"foo": "bar",
  2411  					},
  2412  				},
  2413  			},
  2414  
  2415  			Err: true,
  2416  		},
  2417  
  2418  		// Bad type, interpolated
  2419  		{
  2420  			Schema: map[string]*Schema{
  2421  				"size": &Schema{
  2422  					Type:     TypeInt,
  2423  					Required: true,
  2424  				},
  2425  			},
  2426  
  2427  			Config: map[string]interface{}{
  2428  				"size": "${var.foo}",
  2429  			},
  2430  
  2431  			Vars: map[string]string{
  2432  				"var.foo": "nope",
  2433  			},
  2434  
  2435  			Err: true,
  2436  		},
  2437  
  2438  		// Required but has DefaultFunc
  2439  		{
  2440  			Schema: map[string]*Schema{
  2441  				"availability_zone": &Schema{
  2442  					Type:     TypeString,
  2443  					Required: true,
  2444  					DefaultFunc: func() (interface{}, error) {
  2445  						return "foo", nil
  2446  					},
  2447  				},
  2448  			},
  2449  
  2450  			Config: nil,
  2451  		},
  2452  
  2453  		// Required but has DefaultFunc return nil
  2454  		{
  2455  			Schema: map[string]*Schema{
  2456  				"availability_zone": &Schema{
  2457  					Type:     TypeString,
  2458  					Required: true,
  2459  					DefaultFunc: func() (interface{}, error) {
  2460  						return nil, nil
  2461  					},
  2462  				},
  2463  			},
  2464  
  2465  			Config: nil,
  2466  
  2467  			Err: true,
  2468  		},
  2469  
  2470  		// Optional sub-resource
  2471  		{
  2472  			Schema: map[string]*Schema{
  2473  				"ingress": &Schema{
  2474  					Type: TypeList,
  2475  					Elem: &Resource{
  2476  						Schema: map[string]*Schema{
  2477  							"from": &Schema{
  2478  								Type:     TypeInt,
  2479  								Required: true,
  2480  							},
  2481  						},
  2482  					},
  2483  				},
  2484  			},
  2485  
  2486  			Config: map[string]interface{}{},
  2487  
  2488  			Err: false,
  2489  		},
  2490  
  2491  		// Not a list
  2492  		{
  2493  			Schema: map[string]*Schema{
  2494  				"ingress": &Schema{
  2495  					Type: TypeList,
  2496  					Elem: &Resource{
  2497  						Schema: map[string]*Schema{
  2498  							"from": &Schema{
  2499  								Type:     TypeInt,
  2500  								Required: true,
  2501  							},
  2502  						},
  2503  					},
  2504  				},
  2505  			},
  2506  
  2507  			Config: map[string]interface{}{
  2508  				"ingress": "foo",
  2509  			},
  2510  
  2511  			Err: true,
  2512  		},
  2513  
  2514  		// Required sub-resource field
  2515  		{
  2516  			Schema: map[string]*Schema{
  2517  				"ingress": &Schema{
  2518  					Type: TypeList,
  2519  					Elem: &Resource{
  2520  						Schema: map[string]*Schema{
  2521  							"from": &Schema{
  2522  								Type:     TypeInt,
  2523  								Required: true,
  2524  							},
  2525  						},
  2526  					},
  2527  				},
  2528  			},
  2529  
  2530  			Config: map[string]interface{}{
  2531  				"ingress": []interface{}{
  2532  					map[string]interface{}{},
  2533  				},
  2534  			},
  2535  
  2536  			Err: true,
  2537  		},
  2538  
  2539  		// Good sub-resource
  2540  		{
  2541  			Schema: map[string]*Schema{
  2542  				"ingress": &Schema{
  2543  					Type:     TypeList,
  2544  					Optional: true,
  2545  					Elem: &Resource{
  2546  						Schema: map[string]*Schema{
  2547  							"from": &Schema{
  2548  								Type:     TypeInt,
  2549  								Required: true,
  2550  							},
  2551  						},
  2552  					},
  2553  				},
  2554  			},
  2555  
  2556  			Config: map[string]interface{}{
  2557  				"ingress": []interface{}{
  2558  					map[string]interface{}{
  2559  						"from": 80,
  2560  					},
  2561  				},
  2562  			},
  2563  
  2564  			Err: false,
  2565  		},
  2566  
  2567  		// Invalid/unknown field
  2568  		{
  2569  			Schema: map[string]*Schema{
  2570  				"availability_zone": &Schema{
  2571  					Type:     TypeString,
  2572  					Optional: true,
  2573  					Computed: true,
  2574  					ForceNew: true,
  2575  				},
  2576  			},
  2577  
  2578  			Config: map[string]interface{}{
  2579  				"foo": "bar",
  2580  			},
  2581  
  2582  			Err: true,
  2583  		},
  2584  
  2585  		// Computed field set
  2586  		{
  2587  			Schema: map[string]*Schema{
  2588  				"availability_zone": &Schema{
  2589  					Type:     TypeString,
  2590  					Computed: true,
  2591  				},
  2592  			},
  2593  
  2594  			Config: map[string]interface{}{
  2595  				"availability_zone": "bar",
  2596  			},
  2597  
  2598  			Err: true,
  2599  		},
  2600  
  2601  		// Not a set
  2602  		{
  2603  			Schema: map[string]*Schema{
  2604  				"ports": &Schema{
  2605  					Type:     TypeSet,
  2606  					Required: true,
  2607  					Elem:     &Schema{Type: TypeInt},
  2608  					Set: func(a interface{}) int {
  2609  						return a.(int)
  2610  					},
  2611  				},
  2612  			},
  2613  
  2614  			Config: map[string]interface{}{
  2615  				"ports": "foo",
  2616  			},
  2617  
  2618  			Err: true,
  2619  		},
  2620  
  2621  		// Maps
  2622  		{
  2623  			Schema: map[string]*Schema{
  2624  				"user_data": &Schema{
  2625  					Type:     TypeMap,
  2626  					Optional: true,
  2627  				},
  2628  			},
  2629  
  2630  			Config: map[string]interface{}{
  2631  				"user_data": "foo",
  2632  			},
  2633  
  2634  			Err: true,
  2635  		},
  2636  
  2637  		{
  2638  			Schema: map[string]*Schema{
  2639  				"user_data": &Schema{
  2640  					Type:     TypeMap,
  2641  					Optional: true,
  2642  				},
  2643  			},
  2644  
  2645  			Config: map[string]interface{}{
  2646  				"user_data": []interface{}{
  2647  					map[string]interface{}{
  2648  						"foo": "bar",
  2649  					},
  2650  				},
  2651  			},
  2652  		},
  2653  
  2654  		{
  2655  			Schema: map[string]*Schema{
  2656  				"user_data": &Schema{
  2657  					Type:     TypeMap,
  2658  					Optional: true,
  2659  				},
  2660  			},
  2661  
  2662  			Config: map[string]interface{}{
  2663  				"user_data": map[string]interface{}{
  2664  					"foo": "bar",
  2665  				},
  2666  			},
  2667  		},
  2668  
  2669  		{
  2670  			Schema: map[string]*Schema{
  2671  				"user_data": &Schema{
  2672  					Type:     TypeMap,
  2673  					Optional: true,
  2674  				},
  2675  			},
  2676  
  2677  			Config: map[string]interface{}{
  2678  				"user_data": []interface{}{
  2679  					"foo",
  2680  				},
  2681  			},
  2682  
  2683  			Err: true,
  2684  		},
  2685  
  2686  		{
  2687  			Schema: map[string]*Schema{
  2688  				"security_groups": &Schema{
  2689  					Type:     TypeSet,
  2690  					Optional: true,
  2691  					Computed: true,
  2692  					ForceNew: true,
  2693  					Elem:     &Schema{Type: TypeString},
  2694  					Set: func(v interface{}) int {
  2695  						return len(v.(string))
  2696  					},
  2697  				},
  2698  			},
  2699  
  2700  			Config: map[string]interface{}{
  2701  				"security_groups": []interface{}{"${var.foo}"},
  2702  			},
  2703  
  2704  			Err: false,
  2705  		},
  2706  
  2707  		{
  2708  			Schema: map[string]*Schema{
  2709  				"security_groups": &Schema{
  2710  					Type:     TypeSet,
  2711  					Optional: true,
  2712  					Computed: true,
  2713  					ForceNew: true,
  2714  					Elem:     &Schema{Type: TypeString},
  2715  				},
  2716  			},
  2717  
  2718  			Config: map[string]interface{}{
  2719  				"security_groups": "${var.foo}",
  2720  			},
  2721  
  2722  			Err: true,
  2723  		},
  2724  	}
  2725  
  2726  	for i, tc := range cases {
  2727  		c, err := config.NewRawConfig(tc.Config)
  2728  		if err != nil {
  2729  			t.Fatalf("err: %s", err)
  2730  		}
  2731  		if tc.Vars != nil {
  2732  			vars := make(map[string]ast.Variable)
  2733  			for k, v := range tc.Vars {
  2734  				vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
  2735  			}
  2736  
  2737  			if err := c.Interpolate(vars); err != nil {
  2738  				t.Fatalf("err: %s", err)
  2739  			}
  2740  		}
  2741  
  2742  		ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  2743  		if (len(es) > 0) != tc.Err {
  2744  			if len(es) == 0 {
  2745  				t.Errorf("%d: no errors", i)
  2746  			}
  2747  
  2748  			for _, e := range es {
  2749  				t.Errorf("%d: err: %s", i, e)
  2750  			}
  2751  
  2752  			t.FailNow()
  2753  		}
  2754  
  2755  		if (len(ws) > 0) != tc.Warn {
  2756  			t.Fatalf("%d: ws: %#v", i, ws)
  2757  		}
  2758  	}
  2759  }