github.com/emc-cmd/terraform@v0.7.8-0.20161101145618-f16309630e7c/helper/schema/schema_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"strconv"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/hil"
    12  	"github.com/hashicorp/hil/ast"
    13  	"github.com/hashicorp/terraform/config"
    14  	"github.com/hashicorp/terraform/helper/hashcode"
    15  	"github.com/hashicorp/terraform/terraform"
    16  )
    17  
    18  func TestEnvDefaultFunc(t *testing.T) {
    19  	key := "TF_TEST_ENV_DEFAULT_FUNC"
    20  	defer os.Unsetenv(key)
    21  
    22  	f := EnvDefaultFunc(key, "42")
    23  	if err := os.Setenv(key, "foo"); err != nil {
    24  		t.Fatalf("err: %s", err)
    25  	}
    26  
    27  	actual, err := f()
    28  	if err != nil {
    29  		t.Fatalf("err: %s", err)
    30  	}
    31  	if actual != "foo" {
    32  		t.Fatalf("bad: %#v", actual)
    33  	}
    34  
    35  	if err := os.Unsetenv(key); err != nil {
    36  		t.Fatalf("err: %s", err)
    37  	}
    38  
    39  	actual, err = f()
    40  	if err != nil {
    41  		t.Fatalf("err: %s", err)
    42  	}
    43  	if actual != "42" {
    44  		t.Fatalf("bad: %#v", actual)
    45  	}
    46  }
    47  
    48  func TestMultiEnvDefaultFunc(t *testing.T) {
    49  	keys := []string{
    50  		"TF_TEST_MULTI_ENV_DEFAULT_FUNC1",
    51  		"TF_TEST_MULTI_ENV_DEFAULT_FUNC2",
    52  	}
    53  	defer func() {
    54  		for _, k := range keys {
    55  			os.Unsetenv(k)
    56  		}
    57  	}()
    58  
    59  	// Test that the first key is returned first
    60  	f := MultiEnvDefaultFunc(keys, "42")
    61  	if err := os.Setenv(keys[0], "foo"); err != nil {
    62  		t.Fatalf("err: %s", err)
    63  	}
    64  
    65  	actual, err := f()
    66  	if err != nil {
    67  		t.Fatalf("err: %s", err)
    68  	}
    69  	if actual != "foo" {
    70  		t.Fatalf("bad: %#v", actual)
    71  	}
    72  
    73  	if err := os.Unsetenv(keys[0]); err != nil {
    74  		t.Fatalf("err: %s", err)
    75  	}
    76  
    77  	// Test that the second key is returned if the first one is empty
    78  	f = MultiEnvDefaultFunc(keys, "42")
    79  	if err := os.Setenv(keys[1], "foo"); err != nil {
    80  		t.Fatalf("err: %s", err)
    81  	}
    82  
    83  	actual, err = f()
    84  	if err != nil {
    85  		t.Fatalf("err: %s", err)
    86  	}
    87  	if actual != "foo" {
    88  		t.Fatalf("bad: %#v", actual)
    89  	}
    90  
    91  	if err := os.Unsetenv(keys[1]); err != nil {
    92  		t.Fatalf("err: %s", err)
    93  	}
    94  
    95  	// Test that the default value is returned when no keys are set
    96  	actual, err = f()
    97  	if err != nil {
    98  		t.Fatalf("err: %s", err)
    99  	}
   100  	if actual != "42" {
   101  		t.Fatalf("bad: %#v", actual)
   102  	}
   103  }
   104  
   105  func TestValueType_Zero(t *testing.T) {
   106  	cases := []struct {
   107  		Type  ValueType
   108  		Value interface{}
   109  	}{
   110  		{TypeBool, false},
   111  		{TypeInt, 0},
   112  		{TypeFloat, 0.0},
   113  		{TypeString, ""},
   114  		{TypeList, []interface{}{}},
   115  		{TypeMap, map[string]interface{}{}},
   116  		{TypeSet, new(Set)},
   117  	}
   118  
   119  	for i, tc := range cases {
   120  		actual := tc.Type.Zero()
   121  		if !reflect.DeepEqual(actual, tc.Value) {
   122  			t.Fatalf("%d: %#v != %#v", i, actual, tc.Value)
   123  		}
   124  	}
   125  }
   126  
   127  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
   128  	variable, _ := hil.InterfaceToVariable(input)
   129  	return variable
   130  }
   131  
   132  func TestSchemaMap_Diff(t *testing.T) {
   133  	cases := map[string]struct {
   134  		Schema          map[string]*Schema
   135  		State           *terraform.InstanceState
   136  		Config          map[string]interface{}
   137  		ConfigVariables map[string]ast.Variable
   138  		Diff            *terraform.InstanceDiff
   139  		Err             bool
   140  	}{
   141  		"#0": {
   142  			Schema: map[string]*Schema{
   143  				"availability_zone": &Schema{
   144  					Type:     TypeString,
   145  					Optional: true,
   146  					Computed: true,
   147  					ForceNew: true,
   148  				},
   149  			},
   150  
   151  			State: nil,
   152  
   153  			Config: map[string]interface{}{
   154  				"availability_zone": "foo",
   155  			},
   156  
   157  			Diff: &terraform.InstanceDiff{
   158  				Attributes: map[string]*terraform.ResourceAttrDiff{
   159  					"availability_zone": &terraform.ResourceAttrDiff{
   160  						Old:         "",
   161  						New:         "foo",
   162  						RequiresNew: true,
   163  					},
   164  				},
   165  			},
   166  
   167  			Err: false,
   168  		},
   169  
   170  		"#1": {
   171  			Schema: map[string]*Schema{
   172  				"availability_zone": &Schema{
   173  					Type:     TypeString,
   174  					Optional: true,
   175  					Computed: true,
   176  					ForceNew: true,
   177  				},
   178  			},
   179  
   180  			State: nil,
   181  
   182  			Config: map[string]interface{}{},
   183  
   184  			Diff: &terraform.InstanceDiff{
   185  				Attributes: map[string]*terraform.ResourceAttrDiff{
   186  					"availability_zone": &terraform.ResourceAttrDiff{
   187  						Old:         "",
   188  						NewComputed: true,
   189  						RequiresNew: true,
   190  					},
   191  				},
   192  			},
   193  
   194  			Err: false,
   195  		},
   196  
   197  		"#2": {
   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  			Schema: map[string]*Schema{
   220  				"availability_zone": &Schema{
   221  					Type:     TypeString,
   222  					Optional: true,
   223  					Computed: true,
   224  				},
   225  			},
   226  
   227  			State: &terraform.InstanceState{
   228  				Attributes: map[string]string{
   229  					"availability_zone": "foo",
   230  				},
   231  			},
   232  
   233  			Config: map[string]interface{}{
   234  				"availability_zone": "bar",
   235  			},
   236  
   237  			Diff: &terraform.InstanceDiff{
   238  				Attributes: map[string]*terraform.ResourceAttrDiff{
   239  					"availability_zone": &terraform.ResourceAttrDiff{
   240  						Old: "foo",
   241  						New: "bar",
   242  					},
   243  				},
   244  			},
   245  
   246  			Err: false,
   247  		},
   248  
   249  		"#4 Default": {
   250  			Schema: map[string]*Schema{
   251  				"availability_zone": &Schema{
   252  					Type:     TypeString,
   253  					Optional: true,
   254  					Default:  "foo",
   255  				},
   256  			},
   257  
   258  			State: nil,
   259  
   260  			Config: nil,
   261  
   262  			Diff: &terraform.InstanceDiff{
   263  				Attributes: map[string]*terraform.ResourceAttrDiff{
   264  					"availability_zone": &terraform.ResourceAttrDiff{
   265  						Old: "",
   266  						New: "foo",
   267  					},
   268  				},
   269  			},
   270  
   271  			Err: false,
   272  		},
   273  
   274  		"#5 DefaultFunc, value": {
   275  			Schema: map[string]*Schema{
   276  				"availability_zone": &Schema{
   277  					Type:     TypeString,
   278  					Optional: true,
   279  					DefaultFunc: func() (interface{}, error) {
   280  						return "foo", nil
   281  					},
   282  				},
   283  			},
   284  
   285  			State: nil,
   286  
   287  			Config: nil,
   288  
   289  			Diff: &terraform.InstanceDiff{
   290  				Attributes: map[string]*terraform.ResourceAttrDiff{
   291  					"availability_zone": &terraform.ResourceAttrDiff{
   292  						Old: "",
   293  						New: "foo",
   294  					},
   295  				},
   296  			},
   297  
   298  			Err: false,
   299  		},
   300  
   301  		"#6 DefaultFunc, configuration set": {
   302  			Schema: map[string]*Schema{
   303  				"availability_zone": &Schema{
   304  					Type:     TypeString,
   305  					Optional: true,
   306  					DefaultFunc: func() (interface{}, error) {
   307  						return "foo", nil
   308  					},
   309  				},
   310  			},
   311  
   312  			State: nil,
   313  
   314  			Config: map[string]interface{}{
   315  				"availability_zone": "bar",
   316  			},
   317  
   318  			Diff: &terraform.InstanceDiff{
   319  				Attributes: map[string]*terraform.ResourceAttrDiff{
   320  					"availability_zone": &terraform.ResourceAttrDiff{
   321  						Old: "",
   322  						New: "bar",
   323  					},
   324  				},
   325  			},
   326  
   327  			Err: false,
   328  		},
   329  
   330  		"String with StateFunc": {
   331  			Schema: map[string]*Schema{
   332  				"availability_zone": &Schema{
   333  					Type:     TypeString,
   334  					Optional: true,
   335  					Computed: true,
   336  					StateFunc: func(a interface{}) string {
   337  						return a.(string) + "!"
   338  					},
   339  				},
   340  			},
   341  
   342  			State: nil,
   343  
   344  			Config: map[string]interface{}{
   345  				"availability_zone": "foo",
   346  			},
   347  
   348  			Diff: &terraform.InstanceDiff{
   349  				Attributes: map[string]*terraform.ResourceAttrDiff{
   350  					"availability_zone": &terraform.ResourceAttrDiff{
   351  						Old:      "",
   352  						New:      "foo!",
   353  						NewExtra: "foo",
   354  					},
   355  				},
   356  			},
   357  
   358  			Err: false,
   359  		},
   360  
   361  		"StateFunc not called with nil value": {
   362  			Schema: map[string]*Schema{
   363  				"availability_zone": &Schema{
   364  					Type:     TypeString,
   365  					Optional: true,
   366  					Computed: true,
   367  					StateFunc: func(a interface{}) string {
   368  						t.Fatalf("should not get here!")
   369  						return ""
   370  					},
   371  				},
   372  			},
   373  
   374  			State: nil,
   375  
   376  			Config: map[string]interface{}{},
   377  
   378  			Diff: &terraform.InstanceDiff{
   379  				Attributes: map[string]*terraform.ResourceAttrDiff{
   380  					"availability_zone": &terraform.ResourceAttrDiff{
   381  						Old:         "",
   382  						New:         "",
   383  						NewComputed: true,
   384  					},
   385  				},
   386  			},
   387  
   388  			Err: false,
   389  		},
   390  
   391  		"#8 Variable (just checking)": {
   392  			Schema: map[string]*Schema{
   393  				"availability_zone": &Schema{
   394  					Type:     TypeString,
   395  					Optional: true,
   396  				},
   397  			},
   398  
   399  			State: nil,
   400  
   401  			Config: map[string]interface{}{
   402  				"availability_zone": "${var.foo}",
   403  			},
   404  
   405  			ConfigVariables: map[string]ast.Variable{
   406  				"var.foo": interfaceToVariableSwallowError("bar"),
   407  			},
   408  
   409  			Diff: &terraform.InstanceDiff{
   410  				Attributes: map[string]*terraform.ResourceAttrDiff{
   411  					"availability_zone": &terraform.ResourceAttrDiff{
   412  						Old: "",
   413  						New: "bar",
   414  					},
   415  				},
   416  			},
   417  
   418  			Err: false,
   419  		},
   420  
   421  		"#9 Variable computed": {
   422  			Schema: map[string]*Schema{
   423  				"availability_zone": &Schema{
   424  					Type:     TypeString,
   425  					Optional: true,
   426  				},
   427  			},
   428  
   429  			State: nil,
   430  
   431  			Config: map[string]interface{}{
   432  				"availability_zone": "${var.foo}",
   433  			},
   434  
   435  			ConfigVariables: map[string]ast.Variable{
   436  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
   437  			},
   438  
   439  			Diff: &terraform.InstanceDiff{
   440  				Attributes: map[string]*terraform.ResourceAttrDiff{
   441  					"availability_zone": &terraform.ResourceAttrDiff{
   442  						Old:         "",
   443  						New:         "${var.foo}",
   444  						NewComputed: true,
   445  					},
   446  				},
   447  			},
   448  
   449  			Err: false,
   450  		},
   451  
   452  		"#10 Int decode": {
   453  			Schema: map[string]*Schema{
   454  				"port": &Schema{
   455  					Type:     TypeInt,
   456  					Optional: true,
   457  					Computed: true,
   458  					ForceNew: true,
   459  				},
   460  			},
   461  
   462  			State: nil,
   463  
   464  			Config: map[string]interface{}{
   465  				"port": 27,
   466  			},
   467  
   468  			Diff: &terraform.InstanceDiff{
   469  				Attributes: map[string]*terraform.ResourceAttrDiff{
   470  					"port": &terraform.ResourceAttrDiff{
   471  						Old:         "",
   472  						New:         "27",
   473  						RequiresNew: true,
   474  					},
   475  				},
   476  			},
   477  
   478  			Err: false,
   479  		},
   480  
   481  		"#11 bool decode": {
   482  			Schema: map[string]*Schema{
   483  				"port": &Schema{
   484  					Type:     TypeBool,
   485  					Optional: true,
   486  					Computed: true,
   487  					ForceNew: true,
   488  				},
   489  			},
   490  
   491  			State: nil,
   492  
   493  			Config: map[string]interface{}{
   494  				"port": false,
   495  			},
   496  
   497  			Diff: &terraform.InstanceDiff{
   498  				Attributes: map[string]*terraform.ResourceAttrDiff{
   499  					"port": &terraform.ResourceAttrDiff{
   500  						Old:         "",
   501  						New:         "false",
   502  						RequiresNew: true,
   503  					},
   504  				},
   505  			},
   506  
   507  			Err: false,
   508  		},
   509  
   510  		"#12 Bool": {
   511  			Schema: map[string]*Schema{
   512  				"delete": &Schema{
   513  					Type:     TypeBool,
   514  					Optional: true,
   515  					Default:  false,
   516  				},
   517  			},
   518  
   519  			State: &terraform.InstanceState{
   520  				Attributes: map[string]string{
   521  					"delete": "false",
   522  				},
   523  			},
   524  
   525  			Config: nil,
   526  
   527  			Diff: nil,
   528  
   529  			Err: false,
   530  		},
   531  
   532  		"#13 List decode": {
   533  			Schema: map[string]*Schema{
   534  				"ports": &Schema{
   535  					Type:     TypeList,
   536  					Required: true,
   537  					Elem:     &Schema{Type: TypeInt},
   538  				},
   539  			},
   540  
   541  			State: nil,
   542  
   543  			Config: map[string]interface{}{
   544  				"ports": []interface{}{1, 2, 5},
   545  			},
   546  
   547  			Diff: &terraform.InstanceDiff{
   548  				Attributes: map[string]*terraform.ResourceAttrDiff{
   549  					"ports.#": &terraform.ResourceAttrDiff{
   550  						Old: "0",
   551  						New: "3",
   552  					},
   553  					"ports.0": &terraform.ResourceAttrDiff{
   554  						Old: "",
   555  						New: "1",
   556  					},
   557  					"ports.1": &terraform.ResourceAttrDiff{
   558  						Old: "",
   559  						New: "2",
   560  					},
   561  					"ports.2": &terraform.ResourceAttrDiff{
   562  						Old: "",
   563  						New: "5",
   564  					},
   565  				},
   566  			},
   567  
   568  			Err: false,
   569  		},
   570  
   571  		"#14": {
   572  			Schema: map[string]*Schema{
   573  				"ports": &Schema{
   574  					Type:     TypeList,
   575  					Required: true,
   576  					Elem:     &Schema{Type: TypeInt},
   577  				},
   578  			},
   579  
   580  			State: nil,
   581  
   582  			Config: map[string]interface{}{
   583  				"ports": []interface{}{1, "${var.foo}"},
   584  			},
   585  
   586  			ConfigVariables: map[string]ast.Variable{
   587  				"var.foo": interfaceToVariableSwallowError([]interface{}{"2", "5"}),
   588  			},
   589  
   590  			Diff: &terraform.InstanceDiff{
   591  				Attributes: map[string]*terraform.ResourceAttrDiff{
   592  					"ports.#": &terraform.ResourceAttrDiff{
   593  						Old: "0",
   594  						New: "3",
   595  					},
   596  					"ports.0": &terraform.ResourceAttrDiff{
   597  						Old: "",
   598  						New: "1",
   599  					},
   600  					"ports.1": &terraform.ResourceAttrDiff{
   601  						Old: "",
   602  						New: "2",
   603  					},
   604  					"ports.2": &terraform.ResourceAttrDiff{
   605  						Old: "",
   606  						New: "5",
   607  					},
   608  				},
   609  			},
   610  
   611  			Err: false,
   612  		},
   613  
   614  		"#15": {
   615  			Schema: map[string]*Schema{
   616  				"ports": &Schema{
   617  					Type:     TypeList,
   618  					Required: true,
   619  					Elem:     &Schema{Type: TypeInt},
   620  				},
   621  			},
   622  
   623  			State: nil,
   624  
   625  			Config: map[string]interface{}{
   626  				"ports": []interface{}{1, "${var.foo}"},
   627  			},
   628  
   629  			ConfigVariables: map[string]ast.Variable{
   630  				"var.foo": interfaceToVariableSwallowError([]interface{}{
   631  					config.UnknownVariableValue, "5"}),
   632  			},
   633  
   634  			Diff: &terraform.InstanceDiff{
   635  				Attributes: map[string]*terraform.ResourceAttrDiff{
   636  					"ports.#": &terraform.ResourceAttrDiff{
   637  						Old:         "0",
   638  						New:         "",
   639  						NewComputed: true,
   640  					},
   641  				},
   642  			},
   643  
   644  			Err: false,
   645  		},
   646  
   647  		"#16": {
   648  			Schema: map[string]*Schema{
   649  				"ports": &Schema{
   650  					Type:     TypeList,
   651  					Required: true,
   652  					Elem:     &Schema{Type: TypeInt},
   653  				},
   654  			},
   655  
   656  			State: &terraform.InstanceState{
   657  				Attributes: map[string]string{
   658  					"ports.#": "3",
   659  					"ports.0": "1",
   660  					"ports.1": "2",
   661  					"ports.2": "5",
   662  				},
   663  			},
   664  
   665  			Config: map[string]interface{}{
   666  				"ports": []interface{}{1, 2, 5},
   667  			},
   668  
   669  			Diff: nil,
   670  
   671  			Err: false,
   672  		},
   673  
   674  		"#17": {
   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  			Schema: map[string]*Schema{
   713  				"ports": &Schema{
   714  					Type:     TypeList,
   715  					Required: true,
   716  					Elem:     &Schema{Type: TypeInt},
   717  					ForceNew: true,
   718  				},
   719  			},
   720  
   721  			State: nil,
   722  
   723  			Config: map[string]interface{}{
   724  				"ports": []interface{}{1, 2, 5},
   725  			},
   726  
   727  			Diff: &terraform.InstanceDiff{
   728  				Attributes: map[string]*terraform.ResourceAttrDiff{
   729  					"ports.#": &terraform.ResourceAttrDiff{
   730  						Old:         "0",
   731  						New:         "3",
   732  						RequiresNew: true,
   733  					},
   734  					"ports.0": &terraform.ResourceAttrDiff{
   735  						Old:         "",
   736  						New:         "1",
   737  						RequiresNew: true,
   738  					},
   739  					"ports.1": &terraform.ResourceAttrDiff{
   740  						Old:         "",
   741  						New:         "2",
   742  						RequiresNew: true,
   743  					},
   744  					"ports.2": &terraform.ResourceAttrDiff{
   745  						Old:         "",
   746  						New:         "5",
   747  						RequiresNew: true,
   748  					},
   749  				},
   750  			},
   751  
   752  			Err: false,
   753  		},
   754  
   755  		"#19": {
   756  			Schema: map[string]*Schema{
   757  				"ports": &Schema{
   758  					Type:     TypeList,
   759  					Optional: true,
   760  					Computed: true,
   761  					Elem:     &Schema{Type: TypeInt},
   762  				},
   763  			},
   764  
   765  			State: nil,
   766  
   767  			Config: map[string]interface{}{},
   768  
   769  			Diff: &terraform.InstanceDiff{
   770  				Attributes: map[string]*terraform.ResourceAttrDiff{
   771  					"ports.#": &terraform.ResourceAttrDiff{
   772  						Old:         "",
   773  						NewComputed: true,
   774  					},
   775  				},
   776  			},
   777  
   778  			Err: false,
   779  		},
   780  
   781  		"#20 Set": {
   782  			Schema: map[string]*Schema{
   783  				"ports": &Schema{
   784  					Type:     TypeSet,
   785  					Required: true,
   786  					Elem:     &Schema{Type: TypeInt},
   787  					Set: func(a interface{}) int {
   788  						return a.(int)
   789  					},
   790  				},
   791  			},
   792  
   793  			State: nil,
   794  
   795  			Config: map[string]interface{}{
   796  				"ports": []interface{}{5, 2, 1},
   797  			},
   798  
   799  			Diff: &terraform.InstanceDiff{
   800  				Attributes: map[string]*terraform.ResourceAttrDiff{
   801  					"ports.#": &terraform.ResourceAttrDiff{
   802  						Old: "0",
   803  						New: "3",
   804  					},
   805  					"ports.1": &terraform.ResourceAttrDiff{
   806  						Old: "",
   807  						New: "1",
   808  					},
   809  					"ports.2": &terraform.ResourceAttrDiff{
   810  						Old: "",
   811  						New: "2",
   812  					},
   813  					"ports.5": &terraform.ResourceAttrDiff{
   814  						Old: "",
   815  						New: "5",
   816  					},
   817  				},
   818  			},
   819  
   820  			Err: false,
   821  		},
   822  
   823  		"#21 Set": {
   824  			Schema: map[string]*Schema{
   825  				"ports": &Schema{
   826  					Type:     TypeSet,
   827  					Computed: true,
   828  					Required: true,
   829  					Elem:     &Schema{Type: TypeInt},
   830  					Set: func(a interface{}) int {
   831  						return a.(int)
   832  					},
   833  				},
   834  			},
   835  
   836  			State: &terraform.InstanceState{
   837  				Attributes: map[string]string{
   838  					"ports.#": "0",
   839  				},
   840  			},
   841  
   842  			Config: nil,
   843  
   844  			Diff: nil,
   845  
   846  			Err: false,
   847  		},
   848  
   849  		"#22 Set": {
   850  			Schema: map[string]*Schema{
   851  				"ports": &Schema{
   852  					Type:     TypeSet,
   853  					Optional: true,
   854  					Computed: true,
   855  					Elem:     &Schema{Type: TypeInt},
   856  					Set: func(a interface{}) int {
   857  						return a.(int)
   858  					},
   859  				},
   860  			},
   861  
   862  			State: nil,
   863  
   864  			Config: nil,
   865  
   866  			Diff: &terraform.InstanceDiff{
   867  				Attributes: map[string]*terraform.ResourceAttrDiff{
   868  					"ports.#": &terraform.ResourceAttrDiff{
   869  						Old:         "",
   870  						NewComputed: true,
   871  					},
   872  				},
   873  			},
   874  
   875  			Err: false,
   876  		},
   877  
   878  		"#23 Set": {
   879  			Schema: map[string]*Schema{
   880  				"ports": &Schema{
   881  					Type:     TypeSet,
   882  					Required: true,
   883  					Elem:     &Schema{Type: TypeInt},
   884  					Set: func(a interface{}) int {
   885  						return a.(int)
   886  					},
   887  				},
   888  			},
   889  
   890  			State: nil,
   891  
   892  			Config: map[string]interface{}{
   893  				"ports": []interface{}{"${var.foo}", 1},
   894  			},
   895  
   896  			ConfigVariables: map[string]ast.Variable{
   897  				"var.foo": interfaceToVariableSwallowError([]interface{}{"2", "5"}),
   898  			},
   899  
   900  			Diff: &terraform.InstanceDiff{
   901  				Attributes: map[string]*terraform.ResourceAttrDiff{
   902  					"ports.#": &terraform.ResourceAttrDiff{
   903  						Old: "0",
   904  						New: "3",
   905  					},
   906  					"ports.1": &terraform.ResourceAttrDiff{
   907  						Old: "",
   908  						New: "1",
   909  					},
   910  					"ports.2": &terraform.ResourceAttrDiff{
   911  						Old: "",
   912  						New: "2",
   913  					},
   914  					"ports.5": &terraform.ResourceAttrDiff{
   915  						Old: "",
   916  						New: "5",
   917  					},
   918  				},
   919  			},
   920  
   921  			Err: false,
   922  		},
   923  
   924  		"#24 Set": {
   925  			Schema: map[string]*Schema{
   926  				"ports": &Schema{
   927  					Type:     TypeSet,
   928  					Required: true,
   929  					Elem:     &Schema{Type: TypeInt},
   930  					Set: func(a interface{}) int {
   931  						return a.(int)
   932  					},
   933  				},
   934  			},
   935  
   936  			State: nil,
   937  
   938  			Config: map[string]interface{}{
   939  				"ports": []interface{}{1, "${var.foo}"},
   940  			},
   941  
   942  			ConfigVariables: map[string]ast.Variable{
   943  				"var.foo": interfaceToVariableSwallowError([]interface{}{
   944  					config.UnknownVariableValue, "5"}),
   945  			},
   946  
   947  			Diff: &terraform.InstanceDiff{
   948  				Attributes: map[string]*terraform.ResourceAttrDiff{
   949  					"ports.#": &terraform.ResourceAttrDiff{
   950  						Old:         "",
   951  						New:         "",
   952  						NewComputed: true,
   953  					},
   954  				},
   955  			},
   956  
   957  			Err: false,
   958  		},
   959  
   960  		"#25 Set": {
   961  			Schema: map[string]*Schema{
   962  				"ports": &Schema{
   963  					Type:     TypeSet,
   964  					Required: true,
   965  					Elem:     &Schema{Type: TypeInt},
   966  					Set: func(a interface{}) int {
   967  						return a.(int)
   968  					},
   969  				},
   970  			},
   971  
   972  			State: &terraform.InstanceState{
   973  				Attributes: map[string]string{
   974  					"ports.#": "2",
   975  					"ports.1": "1",
   976  					"ports.2": "2",
   977  				},
   978  			},
   979  
   980  			Config: map[string]interface{}{
   981  				"ports": []interface{}{5, 2, 1},
   982  			},
   983  
   984  			Diff: &terraform.InstanceDiff{
   985  				Attributes: map[string]*terraform.ResourceAttrDiff{
   986  					"ports.#": &terraform.ResourceAttrDiff{
   987  						Old: "2",
   988  						New: "3",
   989  					},
   990  					"ports.1": &terraform.ResourceAttrDiff{
   991  						Old: "1",
   992  						New: "1",
   993  					},
   994  					"ports.2": &terraform.ResourceAttrDiff{
   995  						Old: "2",
   996  						New: "2",
   997  					},
   998  					"ports.5": &terraform.ResourceAttrDiff{
   999  						Old: "",
  1000  						New: "5",
  1001  					},
  1002  				},
  1003  			},
  1004  
  1005  			Err: false,
  1006  		},
  1007  
  1008  		"#26 Set": {
  1009  			Schema: map[string]*Schema{
  1010  				"ports": &Schema{
  1011  					Type:     TypeSet,
  1012  					Required: true,
  1013  					Elem:     &Schema{Type: TypeInt},
  1014  					Set: func(a interface{}) int {
  1015  						return a.(int)
  1016  					},
  1017  				},
  1018  			},
  1019  
  1020  			State: &terraform.InstanceState{
  1021  				Attributes: map[string]string{
  1022  					"ports.#": "2",
  1023  					"ports.1": "1",
  1024  					"ports.2": "2",
  1025  				},
  1026  			},
  1027  
  1028  			Config: map[string]interface{}{},
  1029  
  1030  			Diff: &terraform.InstanceDiff{
  1031  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1032  					"ports.#": &terraform.ResourceAttrDiff{
  1033  						Old: "2",
  1034  						New: "0",
  1035  					},
  1036  					"ports.1": &terraform.ResourceAttrDiff{
  1037  						Old:        "1",
  1038  						New:        "0",
  1039  						NewRemoved: true,
  1040  					},
  1041  					"ports.2": &terraform.ResourceAttrDiff{
  1042  						Old:        "2",
  1043  						New:        "0",
  1044  						NewRemoved: true,
  1045  					},
  1046  				},
  1047  			},
  1048  
  1049  			Err: false,
  1050  		},
  1051  
  1052  		"#27 Set": {
  1053  			Schema: map[string]*Schema{
  1054  				"ports": &Schema{
  1055  					Type:     TypeSet,
  1056  					Optional: true,
  1057  					Computed: true,
  1058  					Elem:     &Schema{Type: TypeInt},
  1059  					Set: func(a interface{}) int {
  1060  						return a.(int)
  1061  					},
  1062  				},
  1063  			},
  1064  
  1065  			State: &terraform.InstanceState{
  1066  				Attributes: map[string]string{
  1067  					"availability_zone": "bar",
  1068  					"ports.#":           "1",
  1069  					"ports.80":          "80",
  1070  				},
  1071  			},
  1072  
  1073  			Config: map[string]interface{}{},
  1074  
  1075  			Diff: nil,
  1076  
  1077  			Err: false,
  1078  		},
  1079  
  1080  		"#28 Set": {
  1081  			Schema: map[string]*Schema{
  1082  				"ingress": &Schema{
  1083  					Type:     TypeSet,
  1084  					Required: true,
  1085  					Elem: &Resource{
  1086  						Schema: map[string]*Schema{
  1087  							"ports": &Schema{
  1088  								Type:     TypeList,
  1089  								Optional: true,
  1090  								Elem:     &Schema{Type: TypeInt},
  1091  							},
  1092  						},
  1093  					},
  1094  					Set: func(v interface{}) int {
  1095  						m := v.(map[string]interface{})
  1096  						ps := m["ports"].([]interface{})
  1097  						result := 0
  1098  						for _, p := range ps {
  1099  							result += p.(int)
  1100  						}
  1101  						return result
  1102  					},
  1103  				},
  1104  			},
  1105  
  1106  			State: &terraform.InstanceState{
  1107  				Attributes: map[string]string{
  1108  					"ingress.#":           "2",
  1109  					"ingress.80.ports.#":  "1",
  1110  					"ingress.80.ports.0":  "80",
  1111  					"ingress.443.ports.#": "1",
  1112  					"ingress.443.ports.0": "443",
  1113  				},
  1114  			},
  1115  
  1116  			Config: map[string]interface{}{
  1117  				"ingress": []map[string]interface{}{
  1118  					map[string]interface{}{
  1119  						"ports": []interface{}{443},
  1120  					},
  1121  					map[string]interface{}{
  1122  						"ports": []interface{}{80},
  1123  					},
  1124  				},
  1125  			},
  1126  
  1127  			Diff: nil,
  1128  
  1129  			Err: false,
  1130  		},
  1131  
  1132  		"#29 List of structure decode": {
  1133  			Schema: map[string]*Schema{
  1134  				"ingress": &Schema{
  1135  					Type:     TypeList,
  1136  					Required: true,
  1137  					Elem: &Resource{
  1138  						Schema: map[string]*Schema{
  1139  							"from": &Schema{
  1140  								Type:     TypeInt,
  1141  								Required: true,
  1142  							},
  1143  						},
  1144  					},
  1145  				},
  1146  			},
  1147  
  1148  			State: nil,
  1149  
  1150  			Config: map[string]interface{}{
  1151  				"ingress": []interface{}{
  1152  					map[string]interface{}{
  1153  						"from": 8080,
  1154  					},
  1155  				},
  1156  			},
  1157  
  1158  			Diff: &terraform.InstanceDiff{
  1159  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1160  					"ingress.#": &terraform.ResourceAttrDiff{
  1161  						Old: "0",
  1162  						New: "1",
  1163  					},
  1164  					"ingress.0.from": &terraform.ResourceAttrDiff{
  1165  						Old: "",
  1166  						New: "8080",
  1167  					},
  1168  				},
  1169  			},
  1170  
  1171  			Err: false,
  1172  		},
  1173  
  1174  		"#30 ComputedWhen": {
  1175  			Schema: map[string]*Schema{
  1176  				"availability_zone": &Schema{
  1177  					Type:         TypeString,
  1178  					Computed:     true,
  1179  					ComputedWhen: []string{"port"},
  1180  				},
  1181  
  1182  				"port": &Schema{
  1183  					Type:     TypeInt,
  1184  					Optional: true,
  1185  				},
  1186  			},
  1187  
  1188  			State: &terraform.InstanceState{
  1189  				Attributes: map[string]string{
  1190  					"availability_zone": "foo",
  1191  					"port":              "80",
  1192  				},
  1193  			},
  1194  
  1195  			Config: map[string]interface{}{
  1196  				"port": 80,
  1197  			},
  1198  
  1199  			Diff: nil,
  1200  
  1201  			Err: false,
  1202  		},
  1203  
  1204  		"#31": {
  1205  			Schema: map[string]*Schema{
  1206  				"availability_zone": &Schema{
  1207  					Type:         TypeString,
  1208  					Computed:     true,
  1209  					ComputedWhen: []string{"port"},
  1210  				},
  1211  
  1212  				"port": &Schema{
  1213  					Type:     TypeInt,
  1214  					Optional: true,
  1215  				},
  1216  			},
  1217  
  1218  			State: &terraform.InstanceState{
  1219  				Attributes: map[string]string{
  1220  					"port": "80",
  1221  				},
  1222  			},
  1223  
  1224  			Config: map[string]interface{}{
  1225  				"port": 80,
  1226  			},
  1227  
  1228  			Diff: &terraform.InstanceDiff{
  1229  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1230  					"availability_zone": &terraform.ResourceAttrDiff{
  1231  						NewComputed: true,
  1232  					},
  1233  				},
  1234  			},
  1235  
  1236  			Err: false,
  1237  		},
  1238  
  1239  		/* TODO
  1240  		{
  1241  			Schema: map[string]*Schema{
  1242  				"availability_zone": &Schema{
  1243  					Type:         TypeString,
  1244  					Computed:     true,
  1245  					ComputedWhen: []string{"port"},
  1246  				},
  1247  
  1248  				"port": &Schema{
  1249  					Type:     TypeInt,
  1250  					Optional: true,
  1251  				},
  1252  			},
  1253  
  1254  			State: &terraform.InstanceState{
  1255  				Attributes: map[string]string{
  1256  					"availability_zone": "foo",
  1257  					"port":              "80",
  1258  				},
  1259  			},
  1260  
  1261  			Config: map[string]interface{}{
  1262  				"port": 8080,
  1263  			},
  1264  
  1265  			Diff: &terraform.ResourceDiff{
  1266  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1267  					"availability_zone": &terraform.ResourceAttrDiff{
  1268  						Old:         "foo",
  1269  						NewComputed: true,
  1270  					},
  1271  					"port": &terraform.ResourceAttrDiff{
  1272  						Old: "80",
  1273  						New: "8080",
  1274  					},
  1275  				},
  1276  			},
  1277  
  1278  			Err: false,
  1279  		},
  1280  		*/
  1281  
  1282  		"#32 Maps": {
  1283  			Schema: map[string]*Schema{
  1284  				"config_vars": &Schema{
  1285  					Type: TypeMap,
  1286  				},
  1287  			},
  1288  
  1289  			State: nil,
  1290  
  1291  			Config: map[string]interface{}{
  1292  				"config_vars": []interface{}{
  1293  					map[string]interface{}{
  1294  						"bar": "baz",
  1295  					},
  1296  				},
  1297  			},
  1298  
  1299  			Diff: &terraform.InstanceDiff{
  1300  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1301  					"config_vars.%": &terraform.ResourceAttrDiff{
  1302  						Old: "0",
  1303  						New: "1",
  1304  					},
  1305  
  1306  					"config_vars.bar": &terraform.ResourceAttrDiff{
  1307  						Old: "",
  1308  						New: "baz",
  1309  					},
  1310  				},
  1311  			},
  1312  
  1313  			Err: false,
  1314  		},
  1315  
  1316  		"#33 Maps": {
  1317  			Schema: map[string]*Schema{
  1318  				"config_vars": &Schema{
  1319  					Type: TypeMap,
  1320  				},
  1321  			},
  1322  
  1323  			State: &terraform.InstanceState{
  1324  				Attributes: map[string]string{
  1325  					"config_vars.foo": "bar",
  1326  				},
  1327  			},
  1328  
  1329  			Config: map[string]interface{}{
  1330  				"config_vars": []interface{}{
  1331  					map[string]interface{}{
  1332  						"bar": "baz",
  1333  					},
  1334  				},
  1335  			},
  1336  
  1337  			Diff: &terraform.InstanceDiff{
  1338  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1339  					"config_vars.foo": &terraform.ResourceAttrDiff{
  1340  						Old:        "bar",
  1341  						NewRemoved: true,
  1342  					},
  1343  					"config_vars.bar": &terraform.ResourceAttrDiff{
  1344  						Old: "",
  1345  						New: "baz",
  1346  					},
  1347  				},
  1348  			},
  1349  
  1350  			Err: false,
  1351  		},
  1352  
  1353  		"#34 Maps": {
  1354  			Schema: map[string]*Schema{
  1355  				"vars": &Schema{
  1356  					Type:     TypeMap,
  1357  					Optional: true,
  1358  					Computed: true,
  1359  				},
  1360  			},
  1361  
  1362  			State: &terraform.InstanceState{
  1363  				Attributes: map[string]string{
  1364  					"vars.foo": "bar",
  1365  				},
  1366  			},
  1367  
  1368  			Config: map[string]interface{}{
  1369  				"vars": []interface{}{
  1370  					map[string]interface{}{
  1371  						"bar": "baz",
  1372  					},
  1373  				},
  1374  			},
  1375  
  1376  			Diff: &terraform.InstanceDiff{
  1377  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1378  					"vars.foo": &terraform.ResourceAttrDiff{
  1379  						Old:        "bar",
  1380  						New:        "",
  1381  						NewRemoved: true,
  1382  					},
  1383  					"vars.bar": &terraform.ResourceAttrDiff{
  1384  						Old: "",
  1385  						New: "baz",
  1386  					},
  1387  				},
  1388  			},
  1389  
  1390  			Err: false,
  1391  		},
  1392  
  1393  		"#35 Maps": {
  1394  			Schema: map[string]*Schema{
  1395  				"vars": &Schema{
  1396  					Type:     TypeMap,
  1397  					Computed: true,
  1398  				},
  1399  			},
  1400  
  1401  			State: &terraform.InstanceState{
  1402  				Attributes: map[string]string{
  1403  					"vars.foo": "bar",
  1404  				},
  1405  			},
  1406  
  1407  			Config: nil,
  1408  
  1409  			Diff: nil,
  1410  
  1411  			Err: false,
  1412  		},
  1413  
  1414  		"#36 Maps": {
  1415  			Schema: map[string]*Schema{
  1416  				"config_vars": &Schema{
  1417  					Type: TypeList,
  1418  					Elem: &Schema{Type: TypeMap},
  1419  				},
  1420  			},
  1421  
  1422  			State: &terraform.InstanceState{
  1423  				Attributes: map[string]string{
  1424  					"config_vars.#":     "1",
  1425  					"config_vars.0.foo": "bar",
  1426  				},
  1427  			},
  1428  
  1429  			Config: map[string]interface{}{
  1430  				"config_vars": []interface{}{
  1431  					map[string]interface{}{
  1432  						"bar": "baz",
  1433  					},
  1434  				},
  1435  			},
  1436  
  1437  			Diff: &terraform.InstanceDiff{
  1438  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1439  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
  1440  						Old:        "bar",
  1441  						NewRemoved: true,
  1442  					},
  1443  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1444  						Old: "",
  1445  						New: "baz",
  1446  					},
  1447  				},
  1448  			},
  1449  
  1450  			Err: false,
  1451  		},
  1452  
  1453  		"#37 Maps": {
  1454  			Schema: map[string]*Schema{
  1455  				"config_vars": &Schema{
  1456  					Type: TypeList,
  1457  					Elem: &Schema{Type: TypeMap},
  1458  				},
  1459  			},
  1460  
  1461  			State: &terraform.InstanceState{
  1462  				Attributes: map[string]string{
  1463  					"config_vars.#":     "1",
  1464  					"config_vars.0.foo": "bar",
  1465  					"config_vars.0.bar": "baz",
  1466  				},
  1467  			},
  1468  
  1469  			Config: map[string]interface{}{},
  1470  
  1471  			Diff: &terraform.InstanceDiff{
  1472  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1473  					"config_vars.#": &terraform.ResourceAttrDiff{
  1474  						Old: "1",
  1475  						New: "0",
  1476  					},
  1477  					"config_vars.0.%": &terraform.ResourceAttrDiff{
  1478  						Old: "2",
  1479  						New: "0",
  1480  					},
  1481  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
  1482  						Old:        "bar",
  1483  						NewRemoved: true,
  1484  					},
  1485  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1486  						Old:        "baz",
  1487  						NewRemoved: true,
  1488  					},
  1489  				},
  1490  			},
  1491  
  1492  			Err: false,
  1493  		},
  1494  
  1495  		"#38 ForceNews": {
  1496  			Schema: map[string]*Schema{
  1497  				"availability_zone": &Schema{
  1498  					Type:     TypeString,
  1499  					Optional: true,
  1500  					ForceNew: true,
  1501  				},
  1502  
  1503  				"address": &Schema{
  1504  					Type:     TypeString,
  1505  					Optional: true,
  1506  					Computed: true,
  1507  				},
  1508  			},
  1509  
  1510  			State: &terraform.InstanceState{
  1511  				Attributes: map[string]string{
  1512  					"availability_zone": "bar",
  1513  					"address":           "foo",
  1514  				},
  1515  			},
  1516  
  1517  			Config: map[string]interface{}{
  1518  				"availability_zone": "foo",
  1519  			},
  1520  
  1521  			Diff: &terraform.InstanceDiff{
  1522  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1523  					"availability_zone": &terraform.ResourceAttrDiff{
  1524  						Old:         "bar",
  1525  						New:         "foo",
  1526  						RequiresNew: true,
  1527  					},
  1528  
  1529  					"address": &terraform.ResourceAttrDiff{
  1530  						Old:         "foo",
  1531  						New:         "",
  1532  						NewComputed: true,
  1533  					},
  1534  				},
  1535  			},
  1536  
  1537  			Err: false,
  1538  		},
  1539  
  1540  		"#39 Set": {
  1541  			Schema: map[string]*Schema{
  1542  				"availability_zone": &Schema{
  1543  					Type:     TypeString,
  1544  					Optional: true,
  1545  					ForceNew: true,
  1546  				},
  1547  
  1548  				"ports": &Schema{
  1549  					Type:     TypeSet,
  1550  					Optional: true,
  1551  					Computed: true,
  1552  					Elem:     &Schema{Type: TypeInt},
  1553  					Set: func(a interface{}) int {
  1554  						return a.(int)
  1555  					},
  1556  				},
  1557  			},
  1558  
  1559  			State: &terraform.InstanceState{
  1560  				Attributes: map[string]string{
  1561  					"availability_zone": "bar",
  1562  					"ports.#":           "1",
  1563  					"ports.80":          "80",
  1564  				},
  1565  			},
  1566  
  1567  			Config: map[string]interface{}{
  1568  				"availability_zone": "foo",
  1569  			},
  1570  
  1571  			Diff: &terraform.InstanceDiff{
  1572  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1573  					"availability_zone": &terraform.ResourceAttrDiff{
  1574  						Old:         "bar",
  1575  						New:         "foo",
  1576  						RequiresNew: true,
  1577  					},
  1578  
  1579  					"ports.#": &terraform.ResourceAttrDiff{
  1580  						Old:         "1",
  1581  						New:         "",
  1582  						NewComputed: true,
  1583  					},
  1584  				},
  1585  			},
  1586  
  1587  			Err: false,
  1588  		},
  1589  
  1590  		"#40 Set": {
  1591  			Schema: map[string]*Schema{
  1592  				"instances": &Schema{
  1593  					Type:     TypeSet,
  1594  					Elem:     &Schema{Type: TypeString},
  1595  					Optional: true,
  1596  					Computed: true,
  1597  					Set: func(v interface{}) int {
  1598  						return len(v.(string))
  1599  					},
  1600  				},
  1601  			},
  1602  
  1603  			State: &terraform.InstanceState{
  1604  				Attributes: map[string]string{
  1605  					"instances.#": "0",
  1606  				},
  1607  			},
  1608  
  1609  			Config: map[string]interface{}{
  1610  				"instances": []interface{}{"${var.foo}"},
  1611  			},
  1612  
  1613  			ConfigVariables: map[string]ast.Variable{
  1614  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1615  			},
  1616  
  1617  			Diff: &terraform.InstanceDiff{
  1618  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1619  					"instances.#": &terraform.ResourceAttrDiff{
  1620  						NewComputed: true,
  1621  					},
  1622  				},
  1623  			},
  1624  
  1625  			Err: false,
  1626  		},
  1627  
  1628  		"#41 Set": {
  1629  			Schema: map[string]*Schema{
  1630  				"route": &Schema{
  1631  					Type:     TypeSet,
  1632  					Optional: true,
  1633  					Elem: &Resource{
  1634  						Schema: map[string]*Schema{
  1635  							"index": &Schema{
  1636  								Type:     TypeInt,
  1637  								Required: true,
  1638  							},
  1639  
  1640  							"gateway": &Schema{
  1641  								Type:     TypeString,
  1642  								Optional: true,
  1643  							},
  1644  						},
  1645  					},
  1646  					Set: func(v interface{}) int {
  1647  						m := v.(map[string]interface{})
  1648  						return m["index"].(int)
  1649  					},
  1650  				},
  1651  			},
  1652  
  1653  			State: nil,
  1654  
  1655  			Config: map[string]interface{}{
  1656  				"route": []map[string]interface{}{
  1657  					map[string]interface{}{
  1658  						"index":   "1",
  1659  						"gateway": "${var.foo}",
  1660  					},
  1661  				},
  1662  			},
  1663  
  1664  			ConfigVariables: map[string]ast.Variable{
  1665  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1666  			},
  1667  
  1668  			Diff: &terraform.InstanceDiff{
  1669  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1670  					"route.#": &terraform.ResourceAttrDiff{
  1671  						Old: "0",
  1672  						New: "1",
  1673  					},
  1674  					"route.~1.index": &terraform.ResourceAttrDiff{
  1675  						Old: "",
  1676  						New: "1",
  1677  					},
  1678  					"route.~1.gateway": &terraform.ResourceAttrDiff{
  1679  						Old:         "",
  1680  						New:         "${var.foo}",
  1681  						NewComputed: true,
  1682  					},
  1683  				},
  1684  			},
  1685  
  1686  			Err: false,
  1687  		},
  1688  
  1689  		"#42 Set": {
  1690  			Schema: map[string]*Schema{
  1691  				"route": &Schema{
  1692  					Type:     TypeSet,
  1693  					Optional: true,
  1694  					Elem: &Resource{
  1695  						Schema: map[string]*Schema{
  1696  							"index": &Schema{
  1697  								Type:     TypeInt,
  1698  								Required: true,
  1699  							},
  1700  
  1701  							"gateway": &Schema{
  1702  								Type:     TypeSet,
  1703  								Optional: true,
  1704  								Elem:     &Schema{Type: TypeInt},
  1705  								Set: func(a interface{}) int {
  1706  									return a.(int)
  1707  								},
  1708  							},
  1709  						},
  1710  					},
  1711  					Set: func(v interface{}) int {
  1712  						m := v.(map[string]interface{})
  1713  						return m["index"].(int)
  1714  					},
  1715  				},
  1716  			},
  1717  
  1718  			State: nil,
  1719  
  1720  			Config: map[string]interface{}{
  1721  				"route": []map[string]interface{}{
  1722  					map[string]interface{}{
  1723  						"index": "1",
  1724  						"gateway": []interface{}{
  1725  							"${var.foo}",
  1726  						},
  1727  					},
  1728  				},
  1729  			},
  1730  
  1731  			ConfigVariables: map[string]ast.Variable{
  1732  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1733  			},
  1734  
  1735  			Diff: &terraform.InstanceDiff{
  1736  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1737  					"route.#": &terraform.ResourceAttrDiff{
  1738  						Old: "0",
  1739  						New: "1",
  1740  					},
  1741  					"route.~1.index": &terraform.ResourceAttrDiff{
  1742  						Old: "",
  1743  						New: "1",
  1744  					},
  1745  					"route.~1.gateway.#": &terraform.ResourceAttrDiff{
  1746  						NewComputed: true,
  1747  					},
  1748  				},
  1749  			},
  1750  
  1751  			Err: false,
  1752  		},
  1753  
  1754  		"#43 - Computed maps": {
  1755  			Schema: map[string]*Schema{
  1756  				"vars": &Schema{
  1757  					Type:     TypeMap,
  1758  					Computed: true,
  1759  				},
  1760  			},
  1761  
  1762  			State: nil,
  1763  
  1764  			Config: nil,
  1765  
  1766  			Diff: &terraform.InstanceDiff{
  1767  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1768  					"vars.%": &terraform.ResourceAttrDiff{
  1769  						Old:         "",
  1770  						NewComputed: true,
  1771  					},
  1772  				},
  1773  			},
  1774  
  1775  			Err: false,
  1776  		},
  1777  
  1778  		"#44 - Computed maps": {
  1779  			Schema: map[string]*Schema{
  1780  				"vars": &Schema{
  1781  					Type:     TypeMap,
  1782  					Computed: true,
  1783  				},
  1784  			},
  1785  
  1786  			State: &terraform.InstanceState{
  1787  				Attributes: map[string]string{
  1788  					"vars.%": "0",
  1789  				},
  1790  			},
  1791  
  1792  			Config: map[string]interface{}{
  1793  				"vars": map[string]interface{}{
  1794  					"bar": "${var.foo}",
  1795  				},
  1796  			},
  1797  
  1798  			ConfigVariables: map[string]ast.Variable{
  1799  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  1800  			},
  1801  
  1802  			Diff: &terraform.InstanceDiff{
  1803  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1804  					"vars.%": &terraform.ResourceAttrDiff{
  1805  						Old:         "",
  1806  						NewComputed: true,
  1807  					},
  1808  				},
  1809  			},
  1810  
  1811  			Err: false,
  1812  		},
  1813  
  1814  		"#45 - Empty": {
  1815  			Schema: map[string]*Schema{},
  1816  
  1817  			State: &terraform.InstanceState{},
  1818  
  1819  			Config: map[string]interface{}{},
  1820  
  1821  			Diff: nil,
  1822  
  1823  			Err: false,
  1824  		},
  1825  
  1826  		"#46 - Float": {
  1827  			Schema: map[string]*Schema{
  1828  				"some_threshold": &Schema{
  1829  					Type: TypeFloat,
  1830  				},
  1831  			},
  1832  
  1833  			State: &terraform.InstanceState{
  1834  				Attributes: map[string]string{
  1835  					"some_threshold": "567.8",
  1836  				},
  1837  			},
  1838  
  1839  			Config: map[string]interface{}{
  1840  				"some_threshold": 12.34,
  1841  			},
  1842  
  1843  			Diff: &terraform.InstanceDiff{
  1844  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1845  					"some_threshold": &terraform.ResourceAttrDiff{
  1846  						Old: "567.8",
  1847  						New: "12.34",
  1848  					},
  1849  				},
  1850  			},
  1851  
  1852  			Err: false,
  1853  		},
  1854  
  1855  		"#47 - https://github.com/hashicorp/terraform/issues/824": {
  1856  			Schema: map[string]*Schema{
  1857  				"block_device": &Schema{
  1858  					Type:     TypeSet,
  1859  					Optional: true,
  1860  					Computed: true,
  1861  					Elem: &Resource{
  1862  						Schema: map[string]*Schema{
  1863  							"device_name": &Schema{
  1864  								Type:     TypeString,
  1865  								Required: true,
  1866  							},
  1867  							"delete_on_termination": &Schema{
  1868  								Type:     TypeBool,
  1869  								Optional: true,
  1870  								Default:  true,
  1871  							},
  1872  						},
  1873  					},
  1874  					Set: func(v interface{}) int {
  1875  						var buf bytes.Buffer
  1876  						m := v.(map[string]interface{})
  1877  						buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
  1878  						buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool)))
  1879  						return hashcode.String(buf.String())
  1880  					},
  1881  				},
  1882  			},
  1883  
  1884  			State: &terraform.InstanceState{
  1885  				Attributes: map[string]string{
  1886  					"block_device.#":                                "2",
  1887  					"block_device.616397234.delete_on_termination":  "true",
  1888  					"block_device.616397234.device_name":            "/dev/sda1",
  1889  					"block_device.2801811477.delete_on_termination": "true",
  1890  					"block_device.2801811477.device_name":           "/dev/sdx",
  1891  				},
  1892  			},
  1893  
  1894  			Config: map[string]interface{}{
  1895  				"block_device": []map[string]interface{}{
  1896  					map[string]interface{}{
  1897  						"device_name": "/dev/sda1",
  1898  					},
  1899  					map[string]interface{}{
  1900  						"device_name": "/dev/sdx",
  1901  					},
  1902  				},
  1903  			},
  1904  			Diff: nil,
  1905  			Err:  false,
  1906  		},
  1907  
  1908  		"#48 - Zero value in state shouldn't result in diff": {
  1909  			Schema: map[string]*Schema{
  1910  				"port": &Schema{
  1911  					Type:     TypeBool,
  1912  					Optional: true,
  1913  					ForceNew: true,
  1914  				},
  1915  			},
  1916  
  1917  			State: &terraform.InstanceState{
  1918  				Attributes: map[string]string{
  1919  					"port": "false",
  1920  				},
  1921  			},
  1922  
  1923  			Config: map[string]interface{}{},
  1924  
  1925  			Diff: nil,
  1926  
  1927  			Err: false,
  1928  		},
  1929  
  1930  		"#49 Set - Same as #48 but for sets": {
  1931  			Schema: map[string]*Schema{
  1932  				"route": &Schema{
  1933  					Type:     TypeSet,
  1934  					Optional: true,
  1935  					Elem: &Resource{
  1936  						Schema: map[string]*Schema{
  1937  							"index": &Schema{
  1938  								Type:     TypeInt,
  1939  								Required: true,
  1940  							},
  1941  
  1942  							"gateway": &Schema{
  1943  								Type:     TypeSet,
  1944  								Optional: true,
  1945  								Elem:     &Schema{Type: TypeInt},
  1946  								Set: func(a interface{}) int {
  1947  									return a.(int)
  1948  								},
  1949  							},
  1950  						},
  1951  					},
  1952  					Set: func(v interface{}) int {
  1953  						m := v.(map[string]interface{})
  1954  						return m["index"].(int)
  1955  					},
  1956  				},
  1957  			},
  1958  
  1959  			State: &terraform.InstanceState{
  1960  				Attributes: map[string]string{
  1961  					"route.#": "0",
  1962  				},
  1963  			},
  1964  
  1965  			Config: map[string]interface{}{},
  1966  
  1967  			Diff: nil,
  1968  
  1969  			Err: false,
  1970  		},
  1971  
  1972  		"#50 - A set computed element shouldn't cause a diff": {
  1973  			Schema: map[string]*Schema{
  1974  				"active": &Schema{
  1975  					Type:     TypeBool,
  1976  					Computed: true,
  1977  					ForceNew: true,
  1978  				},
  1979  			},
  1980  
  1981  			State: &terraform.InstanceState{
  1982  				Attributes: map[string]string{
  1983  					"active": "true",
  1984  				},
  1985  			},
  1986  
  1987  			Config: map[string]interface{}{},
  1988  
  1989  			Diff: nil,
  1990  
  1991  			Err: false,
  1992  		},
  1993  
  1994  		"#51 - An empty set should show up in the diff": {
  1995  			Schema: map[string]*Schema{
  1996  				"instances": &Schema{
  1997  					Type:     TypeSet,
  1998  					Elem:     &Schema{Type: TypeString},
  1999  					Optional: true,
  2000  					ForceNew: true,
  2001  					Set: func(v interface{}) int {
  2002  						return len(v.(string))
  2003  					},
  2004  				},
  2005  			},
  2006  
  2007  			State: &terraform.InstanceState{
  2008  				Attributes: map[string]string{
  2009  					"instances.#": "1",
  2010  					"instances.3": "foo",
  2011  				},
  2012  			},
  2013  
  2014  			Config: map[string]interface{}{},
  2015  
  2016  			Diff: &terraform.InstanceDiff{
  2017  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2018  					"instances.#": &terraform.ResourceAttrDiff{
  2019  						Old:         "1",
  2020  						New:         "0",
  2021  						RequiresNew: true,
  2022  					},
  2023  					"instances.3": &terraform.ResourceAttrDiff{
  2024  						Old:         "foo",
  2025  						New:         "",
  2026  						NewRemoved:  true,
  2027  						RequiresNew: true,
  2028  					},
  2029  				},
  2030  			},
  2031  
  2032  			Err: false,
  2033  		},
  2034  
  2035  		"#52 - Map with empty value": {
  2036  			Schema: map[string]*Schema{
  2037  				"vars": &Schema{
  2038  					Type: TypeMap,
  2039  				},
  2040  			},
  2041  
  2042  			State: nil,
  2043  
  2044  			Config: map[string]interface{}{
  2045  				"vars": map[string]interface{}{
  2046  					"foo": "",
  2047  				},
  2048  			},
  2049  
  2050  			Diff: &terraform.InstanceDiff{
  2051  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2052  					"vars.%": &terraform.ResourceAttrDiff{
  2053  						Old: "0",
  2054  						New: "1",
  2055  					},
  2056  					"vars.foo": &terraform.ResourceAttrDiff{
  2057  						Old: "",
  2058  						New: "",
  2059  					},
  2060  				},
  2061  			},
  2062  
  2063  			Err: false,
  2064  		},
  2065  
  2066  		"#53 - Unset bool, not in state": {
  2067  			Schema: map[string]*Schema{
  2068  				"force": &Schema{
  2069  					Type:     TypeBool,
  2070  					Optional: true,
  2071  					ForceNew: true,
  2072  				},
  2073  			},
  2074  
  2075  			State: nil,
  2076  
  2077  			Config: map[string]interface{}{},
  2078  
  2079  			Diff: nil,
  2080  
  2081  			Err: false,
  2082  		},
  2083  
  2084  		"#54 - Unset set, not in state": {
  2085  			Schema: map[string]*Schema{
  2086  				"metadata_keys": &Schema{
  2087  					Type:     TypeSet,
  2088  					Optional: true,
  2089  					ForceNew: true,
  2090  					Elem:     &Schema{Type: TypeInt},
  2091  					Set:      func(interface{}) int { return 0 },
  2092  				},
  2093  			},
  2094  
  2095  			State: nil,
  2096  
  2097  			Config: map[string]interface{}{},
  2098  
  2099  			Diff: nil,
  2100  
  2101  			Err: false,
  2102  		},
  2103  
  2104  		"#55 - Unset list in state, should not show up computed": {
  2105  			Schema: map[string]*Schema{
  2106  				"metadata_keys": &Schema{
  2107  					Type:     TypeList,
  2108  					Optional: true,
  2109  					Computed: true,
  2110  					ForceNew: true,
  2111  					Elem:     &Schema{Type: TypeInt},
  2112  				},
  2113  			},
  2114  
  2115  			State: &terraform.InstanceState{
  2116  				Attributes: map[string]string{
  2117  					"metadata_keys.#": "0",
  2118  				},
  2119  			},
  2120  
  2121  			Config: map[string]interface{}{},
  2122  
  2123  			Diff: nil,
  2124  
  2125  			Err: false,
  2126  		},
  2127  
  2128  		"#56 - Set element computed substring": {
  2129  			Schema: map[string]*Schema{
  2130  				"ports": &Schema{
  2131  					Type:     TypeSet,
  2132  					Required: true,
  2133  					Elem:     &Schema{Type: TypeInt},
  2134  					Set: func(a interface{}) int {
  2135  						return a.(int)
  2136  					},
  2137  				},
  2138  			},
  2139  
  2140  			State: nil,
  2141  
  2142  			Config: map[string]interface{}{
  2143  				"ports": []interface{}{1, "${var.foo}32"},
  2144  			},
  2145  
  2146  			ConfigVariables: map[string]ast.Variable{
  2147  				"var.foo": interfaceToVariableSwallowError(config.UnknownVariableValue),
  2148  			},
  2149  
  2150  			Diff: &terraform.InstanceDiff{
  2151  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2152  					"ports.#": &terraform.ResourceAttrDiff{
  2153  						Old:         "",
  2154  						New:         "",
  2155  						NewComputed: true,
  2156  					},
  2157  				},
  2158  			},
  2159  
  2160  			Err: false,
  2161  		},
  2162  
  2163  		"#57 Computed map without config that's known to be empty does not generate diff": {
  2164  			Schema: map[string]*Schema{
  2165  				"tags": &Schema{
  2166  					Type:     TypeMap,
  2167  					Computed: true,
  2168  				},
  2169  			},
  2170  
  2171  			Config: nil,
  2172  
  2173  			State: &terraform.InstanceState{
  2174  				Attributes: map[string]string{
  2175  					"tags.%": "0",
  2176  				},
  2177  			},
  2178  
  2179  			Diff: nil,
  2180  
  2181  			Err: false,
  2182  		},
  2183  
  2184  		"#58 Set with hyphen keys": {
  2185  			Schema: map[string]*Schema{
  2186  				"route": &Schema{
  2187  					Type:     TypeSet,
  2188  					Optional: true,
  2189  					Elem: &Resource{
  2190  						Schema: map[string]*Schema{
  2191  							"index": &Schema{
  2192  								Type:     TypeInt,
  2193  								Required: true,
  2194  							},
  2195  
  2196  							"gateway-name": &Schema{
  2197  								Type:     TypeString,
  2198  								Optional: true,
  2199  							},
  2200  						},
  2201  					},
  2202  					Set: func(v interface{}) int {
  2203  						m := v.(map[string]interface{})
  2204  						return m["index"].(int)
  2205  					},
  2206  				},
  2207  			},
  2208  
  2209  			State: nil,
  2210  
  2211  			Config: map[string]interface{}{
  2212  				"route": []map[string]interface{}{
  2213  					map[string]interface{}{
  2214  						"index":        "1",
  2215  						"gateway-name": "hello",
  2216  					},
  2217  				},
  2218  			},
  2219  
  2220  			Diff: &terraform.InstanceDiff{
  2221  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2222  					"route.#": &terraform.ResourceAttrDiff{
  2223  						Old: "0",
  2224  						New: "1",
  2225  					},
  2226  					"route.1.index": &terraform.ResourceAttrDiff{
  2227  						Old: "",
  2228  						New: "1",
  2229  					},
  2230  					"route.1.gateway-name": &terraform.ResourceAttrDiff{
  2231  						Old: "",
  2232  						New: "hello",
  2233  					},
  2234  				},
  2235  			},
  2236  
  2237  			Err: false,
  2238  		},
  2239  
  2240  		"#59: StateFunc in nested set (#1759)": {
  2241  			Schema: map[string]*Schema{
  2242  				"service_account": &Schema{
  2243  					Type:     TypeList,
  2244  					Optional: true,
  2245  					ForceNew: true,
  2246  					Elem: &Resource{
  2247  						Schema: map[string]*Schema{
  2248  							"scopes": &Schema{
  2249  								Type:     TypeSet,
  2250  								Required: true,
  2251  								ForceNew: true,
  2252  								Elem: &Schema{
  2253  									Type: TypeString,
  2254  									StateFunc: func(v interface{}) string {
  2255  										return v.(string) + "!"
  2256  									},
  2257  								},
  2258  								Set: func(v interface{}) int {
  2259  									i, err := strconv.Atoi(v.(string))
  2260  									if err != nil {
  2261  										t.Fatalf("err: %s", err)
  2262  									}
  2263  									return i
  2264  								},
  2265  							},
  2266  						},
  2267  					},
  2268  				},
  2269  			},
  2270  
  2271  			State: nil,
  2272  
  2273  			Config: map[string]interface{}{
  2274  				"service_account": []map[string]interface{}{
  2275  					{
  2276  						"scopes": []interface{}{"123"},
  2277  					},
  2278  				},
  2279  			},
  2280  
  2281  			Diff: &terraform.InstanceDiff{
  2282  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2283  					"service_account.#": &terraform.ResourceAttrDiff{
  2284  						Old:         "0",
  2285  						New:         "1",
  2286  						RequiresNew: true,
  2287  					},
  2288  					"service_account.0.scopes.#": &terraform.ResourceAttrDiff{
  2289  						Old:         "0",
  2290  						New:         "1",
  2291  						RequiresNew: true,
  2292  					},
  2293  					"service_account.0.scopes.123": &terraform.ResourceAttrDiff{
  2294  						Old:         "",
  2295  						New:         "123!",
  2296  						NewExtra:    "123",
  2297  						RequiresNew: true,
  2298  					},
  2299  				},
  2300  			},
  2301  
  2302  			Err: false,
  2303  		},
  2304  
  2305  		"#60 - Removing set elements": {
  2306  			Schema: map[string]*Schema{
  2307  				"instances": &Schema{
  2308  					Type:     TypeSet,
  2309  					Elem:     &Schema{Type: TypeString},
  2310  					Optional: true,
  2311  					ForceNew: true,
  2312  					Set: func(v interface{}) int {
  2313  						return len(v.(string))
  2314  					},
  2315  				},
  2316  			},
  2317  
  2318  			State: &terraform.InstanceState{
  2319  				Attributes: map[string]string{
  2320  					"instances.#": "2",
  2321  					"instances.3": "333",
  2322  					"instances.2": "22",
  2323  				},
  2324  			},
  2325  
  2326  			Config: map[string]interface{}{
  2327  				"instances": []interface{}{"333", "4444"},
  2328  			},
  2329  
  2330  			Diff: &terraform.InstanceDiff{
  2331  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2332  					"instances.#": &terraform.ResourceAttrDiff{
  2333  						Old: "2",
  2334  						New: "2",
  2335  					},
  2336  					"instances.2": &terraform.ResourceAttrDiff{
  2337  						Old:         "22",
  2338  						New:         "",
  2339  						NewRemoved:  true,
  2340  						RequiresNew: true,
  2341  					},
  2342  					"instances.3": &terraform.ResourceAttrDiff{
  2343  						Old:         "333",
  2344  						New:         "333",
  2345  						RequiresNew: true,
  2346  					},
  2347  					"instances.4": &terraform.ResourceAttrDiff{
  2348  						Old:         "",
  2349  						New:         "4444",
  2350  						RequiresNew: true,
  2351  					},
  2352  				},
  2353  			},
  2354  
  2355  			Err: false,
  2356  		},
  2357  
  2358  		"Bools can be set with 0/1 in config, still get true/false": {
  2359  			Schema: map[string]*Schema{
  2360  				"one": &Schema{
  2361  					Type:     TypeBool,
  2362  					Optional: true,
  2363  				},
  2364  				"two": &Schema{
  2365  					Type:     TypeBool,
  2366  					Optional: true,
  2367  				},
  2368  				"three": &Schema{
  2369  					Type:     TypeBool,
  2370  					Optional: true,
  2371  				},
  2372  			},
  2373  
  2374  			State: &terraform.InstanceState{
  2375  				Attributes: map[string]string{
  2376  					"one":   "false",
  2377  					"two":   "true",
  2378  					"three": "true",
  2379  				},
  2380  			},
  2381  
  2382  			Config: map[string]interface{}{
  2383  				"one": "1",
  2384  				"two": "0",
  2385  			},
  2386  
  2387  			Diff: &terraform.InstanceDiff{
  2388  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2389  					"one": &terraform.ResourceAttrDiff{
  2390  						Old: "false",
  2391  						New: "true",
  2392  					},
  2393  					"two": &terraform.ResourceAttrDiff{
  2394  						Old: "true",
  2395  						New: "false",
  2396  					},
  2397  					"three": &terraform.ResourceAttrDiff{
  2398  						Old:        "true",
  2399  						New:        "false",
  2400  						NewRemoved: true,
  2401  					},
  2402  				},
  2403  			},
  2404  
  2405  			Err: false,
  2406  		},
  2407  
  2408  		"tainted in state w/ no attr changes is still a replacement": {
  2409  			Schema: map[string]*Schema{},
  2410  
  2411  			State: &terraform.InstanceState{
  2412  				Attributes: map[string]string{
  2413  					"id": "someid",
  2414  				},
  2415  				Tainted: true,
  2416  			},
  2417  
  2418  			Config: map[string]interface{}{},
  2419  
  2420  			Diff: &terraform.InstanceDiff{
  2421  				Attributes:     map[string]*terraform.ResourceAttrDiff{},
  2422  				DestroyTainted: true,
  2423  			},
  2424  
  2425  			Err: false,
  2426  		},
  2427  
  2428  		"removed optional items should trigger ForceNew": {
  2429  			Schema: map[string]*Schema{
  2430  				"description": &Schema{
  2431  					Type:     TypeString,
  2432  					ForceNew: true,
  2433  					Optional: true,
  2434  				},
  2435  			},
  2436  
  2437  			State: &terraform.InstanceState{
  2438  				Attributes: map[string]string{
  2439  					"description": "foo",
  2440  				},
  2441  			},
  2442  
  2443  			Config: map[string]interface{}{},
  2444  
  2445  			Diff: &terraform.InstanceDiff{
  2446  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2447  					"description": &terraform.ResourceAttrDiff{
  2448  						Old:         "foo",
  2449  						New:         "",
  2450  						RequiresNew: true,
  2451  						NewRemoved:  true,
  2452  					},
  2453  				},
  2454  			},
  2455  
  2456  			Err: false,
  2457  		},
  2458  	}
  2459  
  2460  	for tn, tc := range cases {
  2461  		c, err := config.NewRawConfig(tc.Config)
  2462  		if err != nil {
  2463  			t.Fatalf("#%q err: %s", tn, err)
  2464  		}
  2465  
  2466  		if len(tc.ConfigVariables) > 0 {
  2467  			if err := c.Interpolate(tc.ConfigVariables); err != nil {
  2468  				t.Fatalf("#%q err: %s", tn, err)
  2469  			}
  2470  		}
  2471  
  2472  		d, err := schemaMap(tc.Schema).Diff(
  2473  			tc.State, terraform.NewResourceConfig(c))
  2474  		if err != nil != tc.Err {
  2475  			t.Fatalf("#%q err: %s", tn, err)
  2476  		}
  2477  
  2478  		if !reflect.DeepEqual(tc.Diff, d) {
  2479  			t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Diff, d)
  2480  		}
  2481  	}
  2482  }
  2483  
  2484  func TestSchemaMap_Input(t *testing.T) {
  2485  	cases := map[string]struct {
  2486  		Schema map[string]*Schema
  2487  		Config map[string]interface{}
  2488  		Input  map[string]string
  2489  		Result map[string]interface{}
  2490  		Err    bool
  2491  	}{
  2492  		/*
  2493  		 * String decode
  2494  		 */
  2495  
  2496  		"uses input on optional field with no config": {
  2497  			Schema: map[string]*Schema{
  2498  				"availability_zone": &Schema{
  2499  					Type:     TypeString,
  2500  					Optional: true,
  2501  				},
  2502  			},
  2503  
  2504  			Input: map[string]string{
  2505  				"availability_zone": "foo",
  2506  			},
  2507  
  2508  			Result: map[string]interface{}{
  2509  				"availability_zone": "foo",
  2510  			},
  2511  
  2512  			Err: false,
  2513  		},
  2514  
  2515  		"input ignored when config has a value": {
  2516  			Schema: map[string]*Schema{
  2517  				"availability_zone": &Schema{
  2518  					Type:     TypeString,
  2519  					Optional: true,
  2520  				},
  2521  			},
  2522  
  2523  			Config: map[string]interface{}{
  2524  				"availability_zone": "bar",
  2525  			},
  2526  
  2527  			Input: map[string]string{
  2528  				"availability_zone": "foo",
  2529  			},
  2530  
  2531  			Result: map[string]interface{}{},
  2532  
  2533  			Err: false,
  2534  		},
  2535  
  2536  		"input ignored when schema has a default": {
  2537  			Schema: map[string]*Schema{
  2538  				"availability_zone": &Schema{
  2539  					Type:     TypeString,
  2540  					Default:  "foo",
  2541  					Optional: true,
  2542  				},
  2543  			},
  2544  
  2545  			Input: map[string]string{
  2546  				"availability_zone": "bar",
  2547  			},
  2548  
  2549  			Result: map[string]interface{}{},
  2550  
  2551  			Err: false,
  2552  		},
  2553  
  2554  		"input ignored when default function returns a value": {
  2555  			Schema: map[string]*Schema{
  2556  				"availability_zone": &Schema{
  2557  					Type: TypeString,
  2558  					DefaultFunc: func() (interface{}, error) {
  2559  						return "foo", nil
  2560  					},
  2561  					Optional: true,
  2562  				},
  2563  			},
  2564  
  2565  			Input: map[string]string{
  2566  				"availability_zone": "bar",
  2567  			},
  2568  
  2569  			Result: map[string]interface{}{},
  2570  
  2571  			Err: false,
  2572  		},
  2573  
  2574  		"input ignored when default function returns an empty string": {
  2575  			Schema: map[string]*Schema{
  2576  				"availability_zone": &Schema{
  2577  					Type:     TypeString,
  2578  					Default:  "",
  2579  					Optional: true,
  2580  				},
  2581  			},
  2582  
  2583  			Input: map[string]string{
  2584  				"availability_zone": "bar",
  2585  			},
  2586  
  2587  			Result: map[string]interface{}{},
  2588  
  2589  			Err: false,
  2590  		},
  2591  
  2592  		"input used when default function returns nil": {
  2593  			Schema: map[string]*Schema{
  2594  				"availability_zone": &Schema{
  2595  					Type: TypeString,
  2596  					DefaultFunc: func() (interface{}, error) {
  2597  						return nil, nil
  2598  					},
  2599  					Optional: true,
  2600  				},
  2601  			},
  2602  
  2603  			Input: map[string]string{
  2604  				"availability_zone": "bar",
  2605  			},
  2606  
  2607  			Result: map[string]interface{}{
  2608  				"availability_zone": "bar",
  2609  			},
  2610  
  2611  			Err: false,
  2612  		},
  2613  	}
  2614  
  2615  	for i, tc := range cases {
  2616  		if tc.Config == nil {
  2617  			tc.Config = make(map[string]interface{})
  2618  		}
  2619  
  2620  		c, err := config.NewRawConfig(tc.Config)
  2621  		if err != nil {
  2622  			t.Fatalf("err: %s", err)
  2623  		}
  2624  
  2625  		input := new(terraform.MockUIInput)
  2626  		input.InputReturnMap = tc.Input
  2627  
  2628  		rc := terraform.NewResourceConfig(c)
  2629  		rc.Config = make(map[string]interface{})
  2630  
  2631  		actual, err := schemaMap(tc.Schema).Input(input, rc)
  2632  		if err != nil != tc.Err {
  2633  			t.Fatalf("#%v err: %s", i, err)
  2634  		}
  2635  
  2636  		if !reflect.DeepEqual(tc.Result, actual.Config) {
  2637  			t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result)
  2638  		}
  2639  	}
  2640  }
  2641  
  2642  func TestSchemaMap_InputDefault(t *testing.T) {
  2643  	emptyConfig := make(map[string]interface{})
  2644  	c, err := config.NewRawConfig(emptyConfig)
  2645  	if err != nil {
  2646  		t.Fatalf("err: %s", err)
  2647  	}
  2648  	rc := terraform.NewResourceConfig(c)
  2649  	rc.Config = make(map[string]interface{})
  2650  
  2651  	input := new(terraform.MockUIInput)
  2652  	input.InputFn = func(opts *terraform.InputOpts) (string, error) {
  2653  		t.Fatalf("InputFn should not be called on: %#v", opts)
  2654  		return "", nil
  2655  	}
  2656  
  2657  	schema := map[string]*Schema{
  2658  		"availability_zone": &Schema{
  2659  			Type:     TypeString,
  2660  			Default:  "foo",
  2661  			Optional: true,
  2662  		},
  2663  	}
  2664  	actual, err := schemaMap(schema).Input(input, rc)
  2665  	if err != nil {
  2666  		t.Fatalf("err: %s", err)
  2667  	}
  2668  
  2669  	expected := map[string]interface{}{}
  2670  
  2671  	if !reflect.DeepEqual(expected, actual.Config) {
  2672  		t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected)
  2673  	}
  2674  }
  2675  
  2676  func TestSchemaMap_InputDeprecated(t *testing.T) {
  2677  	emptyConfig := make(map[string]interface{})
  2678  	c, err := config.NewRawConfig(emptyConfig)
  2679  	if err != nil {
  2680  		t.Fatalf("err: %s", err)
  2681  	}
  2682  	rc := terraform.NewResourceConfig(c)
  2683  	rc.Config = make(map[string]interface{})
  2684  
  2685  	input := new(terraform.MockUIInput)
  2686  	input.InputFn = func(opts *terraform.InputOpts) (string, error) {
  2687  		t.Fatalf("InputFn should not be called on: %#v", opts)
  2688  		return "", nil
  2689  	}
  2690  
  2691  	schema := map[string]*Schema{
  2692  		"availability_zone": &Schema{
  2693  			Type:       TypeString,
  2694  			Deprecated: "long gone",
  2695  			Optional:   true,
  2696  		},
  2697  	}
  2698  	actual, err := schemaMap(schema).Input(input, rc)
  2699  	if err != nil {
  2700  		t.Fatalf("err: %s", err)
  2701  	}
  2702  
  2703  	expected := map[string]interface{}{}
  2704  
  2705  	if !reflect.DeepEqual(expected, actual.Config) {
  2706  		t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected)
  2707  	}
  2708  }
  2709  
  2710  func TestSchemaMap_InternalValidate(t *testing.T) {
  2711  	cases := map[string]struct {
  2712  		In  map[string]*Schema
  2713  		Err bool
  2714  	}{
  2715  		"nothing": {
  2716  			nil,
  2717  			false,
  2718  		},
  2719  
  2720  		"Both optional and required": {
  2721  			map[string]*Schema{
  2722  				"foo": &Schema{
  2723  					Type:     TypeInt,
  2724  					Optional: true,
  2725  					Required: true,
  2726  				},
  2727  			},
  2728  			true,
  2729  		},
  2730  
  2731  		"No optional and no required": {
  2732  			map[string]*Schema{
  2733  				"foo": &Schema{
  2734  					Type: TypeInt,
  2735  				},
  2736  			},
  2737  			true,
  2738  		},
  2739  
  2740  		"Missing Type": {
  2741  			map[string]*Schema{
  2742  				"foo": &Schema{
  2743  					Required: true,
  2744  				},
  2745  			},
  2746  			true,
  2747  		},
  2748  
  2749  		"Required but computed": {
  2750  			map[string]*Schema{
  2751  				"foo": &Schema{
  2752  					Type:     TypeInt,
  2753  					Required: true,
  2754  					Computed: true,
  2755  				},
  2756  			},
  2757  			true,
  2758  		},
  2759  
  2760  		"Looks good": {
  2761  			map[string]*Schema{
  2762  				"foo": &Schema{
  2763  					Type:     TypeString,
  2764  					Required: true,
  2765  				},
  2766  			},
  2767  			false,
  2768  		},
  2769  
  2770  		"Computed but has default": {
  2771  			map[string]*Schema{
  2772  				"foo": &Schema{
  2773  					Type:     TypeInt,
  2774  					Optional: true,
  2775  					Computed: true,
  2776  					Default:  "foo",
  2777  				},
  2778  			},
  2779  			true,
  2780  		},
  2781  
  2782  		"Required but has default": {
  2783  			map[string]*Schema{
  2784  				"foo": &Schema{
  2785  					Type:     TypeInt,
  2786  					Optional: true,
  2787  					Required: true,
  2788  					Default:  "foo",
  2789  				},
  2790  			},
  2791  			true,
  2792  		},
  2793  
  2794  		"List element not set": {
  2795  			map[string]*Schema{
  2796  				"foo": &Schema{
  2797  					Type: TypeList,
  2798  				},
  2799  			},
  2800  			true,
  2801  		},
  2802  
  2803  		"List default": {
  2804  			map[string]*Schema{
  2805  				"foo": &Schema{
  2806  					Type:    TypeList,
  2807  					Elem:    &Schema{Type: TypeInt},
  2808  					Default: "foo",
  2809  				},
  2810  			},
  2811  			true,
  2812  		},
  2813  
  2814  		"List element computed": {
  2815  			map[string]*Schema{
  2816  				"foo": &Schema{
  2817  					Type:     TypeList,
  2818  					Optional: true,
  2819  					Elem: &Schema{
  2820  						Type:     TypeInt,
  2821  						Computed: true,
  2822  					},
  2823  				},
  2824  			},
  2825  			true,
  2826  		},
  2827  
  2828  		"List element with Set set": {
  2829  			map[string]*Schema{
  2830  				"foo": &Schema{
  2831  					Type:     TypeList,
  2832  					Elem:     &Schema{Type: TypeInt},
  2833  					Set:      func(interface{}) int { return 0 },
  2834  					Optional: true,
  2835  				},
  2836  			},
  2837  			true,
  2838  		},
  2839  
  2840  		"Set element with no Set set": {
  2841  			map[string]*Schema{
  2842  				"foo": &Schema{
  2843  					Type:     TypeSet,
  2844  					Elem:     &Schema{Type: TypeInt},
  2845  					Optional: true,
  2846  				},
  2847  			},
  2848  			false,
  2849  		},
  2850  
  2851  		"Required but computedWhen": {
  2852  			map[string]*Schema{
  2853  				"foo": &Schema{
  2854  					Type:         TypeInt,
  2855  					Required:     true,
  2856  					ComputedWhen: []string{"foo"},
  2857  				},
  2858  			},
  2859  			true,
  2860  		},
  2861  
  2862  		"Conflicting attributes cannot be required": {
  2863  			map[string]*Schema{
  2864  				"blacklist": &Schema{
  2865  					Type:     TypeBool,
  2866  					Required: true,
  2867  				},
  2868  				"whitelist": &Schema{
  2869  					Type:          TypeBool,
  2870  					Optional:      true,
  2871  					ConflictsWith: []string{"blacklist"},
  2872  				},
  2873  			},
  2874  			true,
  2875  		},
  2876  
  2877  		"Attribute with conflicts cannot be required": {
  2878  			map[string]*Schema{
  2879  				"whitelist": &Schema{
  2880  					Type:          TypeBool,
  2881  					Required:      true,
  2882  					ConflictsWith: []string{"blacklist"},
  2883  				},
  2884  			},
  2885  			true,
  2886  		},
  2887  
  2888  		"ConflictsWith cannot be used w/ Computed": {
  2889  			map[string]*Schema{
  2890  				"blacklist": &Schema{
  2891  					Type:     TypeBool,
  2892  					Computed: true,
  2893  				},
  2894  				"whitelist": &Schema{
  2895  					Type:          TypeBool,
  2896  					Optional:      true,
  2897  					ConflictsWith: []string{"blacklist"},
  2898  				},
  2899  			},
  2900  			true,
  2901  		},
  2902  
  2903  		"ConflictsWith cannot be used w/ ComputedWhen": {
  2904  			map[string]*Schema{
  2905  				"blacklist": &Schema{
  2906  					Type:         TypeBool,
  2907  					ComputedWhen: []string{"foor"},
  2908  				},
  2909  				"whitelist": &Schema{
  2910  					Type:          TypeBool,
  2911  					Required:      true,
  2912  					ConflictsWith: []string{"blacklist"},
  2913  				},
  2914  			},
  2915  			true,
  2916  		},
  2917  
  2918  		"Sub-resource invalid": {
  2919  			map[string]*Schema{
  2920  				"foo": &Schema{
  2921  					Type:     TypeList,
  2922  					Optional: true,
  2923  					Elem: &Resource{
  2924  						Schema: map[string]*Schema{
  2925  							"foo": new(Schema),
  2926  						},
  2927  					},
  2928  				},
  2929  			},
  2930  			true,
  2931  		},
  2932  
  2933  		"Sub-resource valid": {
  2934  			map[string]*Schema{
  2935  				"foo": &Schema{
  2936  					Type:     TypeList,
  2937  					Optional: true,
  2938  					Elem: &Resource{
  2939  						Schema: map[string]*Schema{
  2940  							"foo": &Schema{
  2941  								Type:     TypeInt,
  2942  								Optional: true,
  2943  							},
  2944  						},
  2945  					},
  2946  				},
  2947  			},
  2948  			false,
  2949  		},
  2950  
  2951  		"ValidateFunc on non-primitive": {
  2952  			map[string]*Schema{
  2953  				"foo": &Schema{
  2954  					Type:     TypeSet,
  2955  					Required: true,
  2956  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  2957  						return
  2958  					},
  2959  				},
  2960  			},
  2961  			true,
  2962  		},
  2963  	}
  2964  
  2965  	for tn, tc := range cases {
  2966  		err := schemaMap(tc.In).InternalValidate(schemaMap{})
  2967  		if err != nil != tc.Err {
  2968  			if tc.Err {
  2969  				t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In)
  2970  			}
  2971  			t.Fatalf("%q: Unexpected error occurred:\n\n%#v", tn, tc.In)
  2972  		}
  2973  	}
  2974  
  2975  }
  2976  
  2977  func TestSchemaMap_DiffSuppress(t *testing.T) {
  2978  	cases := map[string]struct {
  2979  		Schema          map[string]*Schema
  2980  		State           *terraform.InstanceState
  2981  		Config          map[string]interface{}
  2982  		ConfigVariables map[string]ast.Variable
  2983  		ExpectedDiff    *terraform.InstanceDiff
  2984  		Err             bool
  2985  	}{
  2986  		"#0 - Suppress otherwise valid diff by returning true": {
  2987  			Schema: map[string]*Schema{
  2988  				"availability_zone": {
  2989  					Type:     TypeString,
  2990  					Optional: true,
  2991  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  2992  						// Always suppress any diff
  2993  						return true
  2994  					},
  2995  				},
  2996  			},
  2997  
  2998  			State: nil,
  2999  
  3000  			Config: map[string]interface{}{
  3001  				"availability_zone": "foo",
  3002  			},
  3003  
  3004  			ExpectedDiff: nil,
  3005  
  3006  			Err: false,
  3007  		},
  3008  
  3009  		"#1 - Don't suppress diff by returning false": {
  3010  			Schema: map[string]*Schema{
  3011  				"availability_zone": {
  3012  					Type:     TypeString,
  3013  					Optional: true,
  3014  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  3015  						// Always suppress any diff
  3016  						return false
  3017  					},
  3018  				},
  3019  			},
  3020  
  3021  			State: nil,
  3022  
  3023  			Config: map[string]interface{}{
  3024  				"availability_zone": "foo",
  3025  			},
  3026  
  3027  			ExpectedDiff: &terraform.InstanceDiff{
  3028  				Attributes: map[string]*terraform.ResourceAttrDiff{
  3029  					"availability_zone": {
  3030  						Old: "",
  3031  						New: "foo",
  3032  					},
  3033  				},
  3034  			},
  3035  
  3036  			Err: false,
  3037  		},
  3038  
  3039  		"Default with suppress makes no diff": {
  3040  			Schema: map[string]*Schema{
  3041  				"availability_zone": {
  3042  					Type:     TypeString,
  3043  					Optional: true,
  3044  					Default:  "foo",
  3045  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  3046  						return true
  3047  					},
  3048  				},
  3049  			},
  3050  
  3051  			State: nil,
  3052  
  3053  			Config: map[string]interface{}{},
  3054  
  3055  			ExpectedDiff: nil,
  3056  
  3057  			Err: false,
  3058  		},
  3059  
  3060  		"Default with false suppress makes diff": {
  3061  			Schema: map[string]*Schema{
  3062  				"availability_zone": {
  3063  					Type:     TypeString,
  3064  					Optional: true,
  3065  					Default:  "foo",
  3066  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  3067  						return false
  3068  					},
  3069  				},
  3070  			},
  3071  
  3072  			State: nil,
  3073  
  3074  			Config: map[string]interface{}{},
  3075  
  3076  			ExpectedDiff: &terraform.InstanceDiff{
  3077  				Attributes: map[string]*terraform.ResourceAttrDiff{
  3078  					"availability_zone": {
  3079  						Old: "",
  3080  						New: "foo",
  3081  					},
  3082  				},
  3083  			},
  3084  
  3085  			Err: false,
  3086  		},
  3087  	}
  3088  
  3089  	for tn, tc := range cases {
  3090  		t.Run(tn, func(t *testing.T) {
  3091  			c, err := config.NewRawConfig(tc.Config)
  3092  			if err != nil {
  3093  				t.Fatalf("#%q err: %s", tn, err)
  3094  			}
  3095  
  3096  			if len(tc.ConfigVariables) > 0 {
  3097  				if err := c.Interpolate(tc.ConfigVariables); err != nil {
  3098  					t.Fatalf("#%q err: %s", tn, err)
  3099  				}
  3100  			}
  3101  
  3102  			d, err := schemaMap(tc.Schema).Diff(
  3103  				tc.State, terraform.NewResourceConfig(c))
  3104  			if err != nil != tc.Err {
  3105  				t.Fatalf("#%q err: %s", tn, err)
  3106  			}
  3107  
  3108  			if !reflect.DeepEqual(tc.ExpectedDiff, d) {
  3109  				t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.ExpectedDiff, d)
  3110  			}
  3111  		})
  3112  	}
  3113  }
  3114  
  3115  func TestSchemaMap_Validate(t *testing.T) {
  3116  	cases := map[string]struct {
  3117  		Schema   map[string]*Schema
  3118  		Config   map[string]interface{}
  3119  		Vars     map[string]string
  3120  		Err      bool
  3121  		Errors   []error
  3122  		Warnings []string
  3123  	}{
  3124  		"Good": {
  3125  			Schema: map[string]*Schema{
  3126  				"availability_zone": &Schema{
  3127  					Type:     TypeString,
  3128  					Optional: true,
  3129  					Computed: true,
  3130  					ForceNew: true,
  3131  				},
  3132  			},
  3133  
  3134  			Config: map[string]interface{}{
  3135  				"availability_zone": "foo",
  3136  			},
  3137  		},
  3138  
  3139  		"Good, because the var is not set and that error will come elsewhere": {
  3140  			Schema: map[string]*Schema{
  3141  				"size": &Schema{
  3142  					Type:     TypeInt,
  3143  					Required: true,
  3144  				},
  3145  			},
  3146  
  3147  			Config: map[string]interface{}{
  3148  				"size": "${var.foo}",
  3149  			},
  3150  
  3151  			Vars: map[string]string{
  3152  				"var.foo": config.UnknownVariableValue,
  3153  			},
  3154  		},
  3155  
  3156  		"Required field not set": {
  3157  			Schema: map[string]*Schema{
  3158  				"availability_zone": &Schema{
  3159  					Type:     TypeString,
  3160  					Required: true,
  3161  				},
  3162  			},
  3163  
  3164  			Config: map[string]interface{}{},
  3165  
  3166  			Err: true,
  3167  		},
  3168  
  3169  		"Invalid basic type": {
  3170  			Schema: map[string]*Schema{
  3171  				"port": &Schema{
  3172  					Type:     TypeInt,
  3173  					Required: true,
  3174  				},
  3175  			},
  3176  
  3177  			Config: map[string]interface{}{
  3178  				"port": "I am invalid",
  3179  			},
  3180  
  3181  			Err: true,
  3182  		},
  3183  
  3184  		"Invalid complex type": {
  3185  			Schema: map[string]*Schema{
  3186  				"user_data": &Schema{
  3187  					Type:     TypeString,
  3188  					Optional: true,
  3189  				},
  3190  			},
  3191  
  3192  			Config: map[string]interface{}{
  3193  				"user_data": []interface{}{
  3194  					map[string]interface{}{
  3195  						"foo": "bar",
  3196  					},
  3197  				},
  3198  			},
  3199  
  3200  			Err: true,
  3201  		},
  3202  
  3203  		"Bad type, interpolated": {
  3204  			Schema: map[string]*Schema{
  3205  				"size": &Schema{
  3206  					Type:     TypeInt,
  3207  					Required: true,
  3208  				},
  3209  			},
  3210  
  3211  			Config: map[string]interface{}{
  3212  				"size": "${var.foo}",
  3213  			},
  3214  
  3215  			Vars: map[string]string{
  3216  				"var.foo": "nope",
  3217  			},
  3218  
  3219  			Err: true,
  3220  		},
  3221  
  3222  		"Required but has DefaultFunc": {
  3223  			Schema: map[string]*Schema{
  3224  				"availability_zone": &Schema{
  3225  					Type:     TypeString,
  3226  					Required: true,
  3227  					DefaultFunc: func() (interface{}, error) {
  3228  						return "foo", nil
  3229  					},
  3230  				},
  3231  			},
  3232  
  3233  			Config: nil,
  3234  		},
  3235  
  3236  		"Required but has DefaultFunc return nil": {
  3237  			Schema: map[string]*Schema{
  3238  				"availability_zone": &Schema{
  3239  					Type:     TypeString,
  3240  					Required: true,
  3241  					DefaultFunc: func() (interface{}, error) {
  3242  						return nil, nil
  3243  					},
  3244  				},
  3245  			},
  3246  
  3247  			Config: nil,
  3248  
  3249  			Err: true,
  3250  		},
  3251  
  3252  		"Optional sub-resource": {
  3253  			Schema: map[string]*Schema{
  3254  				"ingress": &Schema{
  3255  					Type: TypeList,
  3256  					Elem: &Resource{
  3257  						Schema: map[string]*Schema{
  3258  							"from": &Schema{
  3259  								Type:     TypeInt,
  3260  								Required: true,
  3261  							},
  3262  						},
  3263  					},
  3264  				},
  3265  			},
  3266  
  3267  			Config: map[string]interface{}{},
  3268  
  3269  			Err: false,
  3270  		},
  3271  
  3272  		"Sub-resource is the wrong type": {
  3273  			Schema: map[string]*Schema{
  3274  				"ingress": &Schema{
  3275  					Type:     TypeList,
  3276  					Required: true,
  3277  					Elem: &Resource{
  3278  						Schema: map[string]*Schema{
  3279  							"from": &Schema{
  3280  								Type:     TypeInt,
  3281  								Required: true,
  3282  							},
  3283  						},
  3284  					},
  3285  				},
  3286  			},
  3287  
  3288  			Config: map[string]interface{}{
  3289  				"ingress": []interface{}{"foo"},
  3290  			},
  3291  
  3292  			Err: true,
  3293  		},
  3294  
  3295  		"Not a list": {
  3296  			Schema: map[string]*Schema{
  3297  				"ingress": &Schema{
  3298  					Type: TypeList,
  3299  					Elem: &Resource{
  3300  						Schema: map[string]*Schema{
  3301  							"from": &Schema{
  3302  								Type:     TypeInt,
  3303  								Required: true,
  3304  							},
  3305  						},
  3306  					},
  3307  				},
  3308  			},
  3309  
  3310  			Config: map[string]interface{}{
  3311  				"ingress": "foo",
  3312  			},
  3313  
  3314  			Err: true,
  3315  		},
  3316  
  3317  		"Required sub-resource field": {
  3318  			Schema: map[string]*Schema{
  3319  				"ingress": &Schema{
  3320  					Type: TypeList,
  3321  					Elem: &Resource{
  3322  						Schema: map[string]*Schema{
  3323  							"from": &Schema{
  3324  								Type:     TypeInt,
  3325  								Required: true,
  3326  							},
  3327  						},
  3328  					},
  3329  				},
  3330  			},
  3331  
  3332  			Config: map[string]interface{}{
  3333  				"ingress": []interface{}{
  3334  					map[string]interface{}{},
  3335  				},
  3336  			},
  3337  
  3338  			Err: true,
  3339  		},
  3340  
  3341  		"Good sub-resource": {
  3342  			Schema: map[string]*Schema{
  3343  				"ingress": &Schema{
  3344  					Type:     TypeList,
  3345  					Optional: true,
  3346  					Elem: &Resource{
  3347  						Schema: map[string]*Schema{
  3348  							"from": &Schema{
  3349  								Type:     TypeInt,
  3350  								Required: true,
  3351  							},
  3352  						},
  3353  					},
  3354  				},
  3355  			},
  3356  
  3357  			Config: map[string]interface{}{
  3358  				"ingress": []interface{}{
  3359  					map[string]interface{}{
  3360  						"from": 80,
  3361  					},
  3362  				},
  3363  			},
  3364  
  3365  			Err: false,
  3366  		},
  3367  
  3368  		"Invalid/unknown field": {
  3369  			Schema: map[string]*Schema{
  3370  				"availability_zone": &Schema{
  3371  					Type:     TypeString,
  3372  					Optional: true,
  3373  					Computed: true,
  3374  					ForceNew: true,
  3375  				},
  3376  			},
  3377  
  3378  			Config: map[string]interface{}{
  3379  				"foo": "bar",
  3380  			},
  3381  
  3382  			Err: true,
  3383  		},
  3384  
  3385  		"Invalid/unknown field with computed value": {
  3386  			Schema: map[string]*Schema{
  3387  				"availability_zone": &Schema{
  3388  					Type:     TypeString,
  3389  					Optional: true,
  3390  					Computed: true,
  3391  					ForceNew: true,
  3392  				},
  3393  			},
  3394  
  3395  			Config: map[string]interface{}{
  3396  				"foo": "${var.foo}",
  3397  			},
  3398  
  3399  			Vars: map[string]string{
  3400  				"var.foo": config.UnknownVariableValue,
  3401  			},
  3402  
  3403  			Err: true,
  3404  		},
  3405  
  3406  		"Computed field set": {
  3407  			Schema: map[string]*Schema{
  3408  				"availability_zone": &Schema{
  3409  					Type:     TypeString,
  3410  					Computed: true,
  3411  				},
  3412  			},
  3413  
  3414  			Config: map[string]interface{}{
  3415  				"availability_zone": "bar",
  3416  			},
  3417  
  3418  			Err: true,
  3419  		},
  3420  
  3421  		"Not a set": {
  3422  			Schema: map[string]*Schema{
  3423  				"ports": &Schema{
  3424  					Type:     TypeSet,
  3425  					Required: true,
  3426  					Elem:     &Schema{Type: TypeInt},
  3427  					Set: func(a interface{}) int {
  3428  						return a.(int)
  3429  					},
  3430  				},
  3431  			},
  3432  
  3433  			Config: map[string]interface{}{
  3434  				"ports": "foo",
  3435  			},
  3436  
  3437  			Err: true,
  3438  		},
  3439  
  3440  		"Maps": {
  3441  			Schema: map[string]*Schema{
  3442  				"user_data": &Schema{
  3443  					Type:     TypeMap,
  3444  					Optional: true,
  3445  				},
  3446  			},
  3447  
  3448  			Config: map[string]interface{}{
  3449  				"user_data": "foo",
  3450  			},
  3451  
  3452  			Err: true,
  3453  		},
  3454  
  3455  		"Good map: data surrounded by extra slice": {
  3456  			Schema: map[string]*Schema{
  3457  				"user_data": &Schema{
  3458  					Type:     TypeMap,
  3459  					Optional: true,
  3460  				},
  3461  			},
  3462  
  3463  			Config: map[string]interface{}{
  3464  				"user_data": []interface{}{
  3465  					map[string]interface{}{
  3466  						"foo": "bar",
  3467  					},
  3468  				},
  3469  			},
  3470  		},
  3471  
  3472  		"Good map": {
  3473  			Schema: map[string]*Schema{
  3474  				"user_data": &Schema{
  3475  					Type:     TypeMap,
  3476  					Optional: true,
  3477  				},
  3478  			},
  3479  
  3480  			Config: map[string]interface{}{
  3481  				"user_data": map[string]interface{}{
  3482  					"foo": "bar",
  3483  				},
  3484  			},
  3485  		},
  3486  
  3487  		"Bad map: just a slice": {
  3488  			Schema: map[string]*Schema{
  3489  				"user_data": &Schema{
  3490  					Type:     TypeMap,
  3491  					Optional: true,
  3492  				},
  3493  			},
  3494  
  3495  			Config: map[string]interface{}{
  3496  				"user_data": []interface{}{
  3497  					"foo",
  3498  				},
  3499  			},
  3500  
  3501  			Err: true,
  3502  		},
  3503  
  3504  		"Good set: config has slice with single interpolated value": {
  3505  			Schema: map[string]*Schema{
  3506  				"security_groups": &Schema{
  3507  					Type:     TypeSet,
  3508  					Optional: true,
  3509  					Computed: true,
  3510  					ForceNew: true,
  3511  					Elem:     &Schema{Type: TypeString},
  3512  					Set: func(v interface{}) int {
  3513  						return len(v.(string))
  3514  					},
  3515  				},
  3516  			},
  3517  
  3518  			Config: map[string]interface{}{
  3519  				"security_groups": []interface{}{"${var.foo}"},
  3520  			},
  3521  
  3522  			Err: false,
  3523  		},
  3524  
  3525  		"Bad set: config has single interpolated value": {
  3526  			Schema: map[string]*Schema{
  3527  				"security_groups": &Schema{
  3528  					Type:     TypeSet,
  3529  					Optional: true,
  3530  					Computed: true,
  3531  					ForceNew: true,
  3532  					Elem:     &Schema{Type: TypeString},
  3533  				},
  3534  			},
  3535  
  3536  			Config: map[string]interface{}{
  3537  				"security_groups": "${var.foo}",
  3538  			},
  3539  
  3540  			Err: true,
  3541  		},
  3542  
  3543  		"Bad, subresource should not allow unknown elements": {
  3544  			Schema: map[string]*Schema{
  3545  				"ingress": &Schema{
  3546  					Type:     TypeList,
  3547  					Optional: true,
  3548  					Elem: &Resource{
  3549  						Schema: map[string]*Schema{
  3550  							"port": &Schema{
  3551  								Type:     TypeInt,
  3552  								Required: true,
  3553  							},
  3554  						},
  3555  					},
  3556  				},
  3557  			},
  3558  
  3559  			Config: map[string]interface{}{
  3560  				"ingress": []interface{}{
  3561  					map[string]interface{}{
  3562  						"port":  80,
  3563  						"other": "yes",
  3564  					},
  3565  				},
  3566  			},
  3567  
  3568  			Err: true,
  3569  		},
  3570  
  3571  		"Bad, subresource should not allow invalid types": {
  3572  			Schema: map[string]*Schema{
  3573  				"ingress": &Schema{
  3574  					Type:     TypeList,
  3575  					Optional: true,
  3576  					Elem: &Resource{
  3577  						Schema: map[string]*Schema{
  3578  							"port": &Schema{
  3579  								Type:     TypeInt,
  3580  								Required: true,
  3581  							},
  3582  						},
  3583  					},
  3584  				},
  3585  			},
  3586  
  3587  			Config: map[string]interface{}{
  3588  				"ingress": []interface{}{
  3589  					map[string]interface{}{
  3590  						"port": "bad",
  3591  					},
  3592  				},
  3593  			},
  3594  
  3595  			Err: true,
  3596  		},
  3597  
  3598  		"Bad, should not allow lists to be assigned to string attributes": {
  3599  			Schema: map[string]*Schema{
  3600  				"availability_zone": &Schema{
  3601  					Type:     TypeString,
  3602  					Required: true,
  3603  				},
  3604  			},
  3605  
  3606  			Config: map[string]interface{}{
  3607  				"availability_zone": []interface{}{"foo", "bar", "baz"},
  3608  			},
  3609  
  3610  			Err: true,
  3611  		},
  3612  
  3613  		"Bad, should not allow maps to be assigned to string attributes": {
  3614  			Schema: map[string]*Schema{
  3615  				"availability_zone": &Schema{
  3616  					Type:     TypeString,
  3617  					Required: true,
  3618  				},
  3619  			},
  3620  
  3621  			Config: map[string]interface{}{
  3622  				"availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"},
  3623  			},
  3624  
  3625  			Err: true,
  3626  		},
  3627  
  3628  		"Deprecated attribute usage generates warning, but not error": {
  3629  			Schema: map[string]*Schema{
  3630  				"old_news": &Schema{
  3631  					Type:       TypeString,
  3632  					Optional:   true,
  3633  					Deprecated: "please use 'new_news' instead",
  3634  				},
  3635  			},
  3636  
  3637  			Config: map[string]interface{}{
  3638  				"old_news": "extra extra!",
  3639  			},
  3640  
  3641  			Err: false,
  3642  
  3643  			Warnings: []string{
  3644  				"\"old_news\": [DEPRECATED] please use 'new_news' instead",
  3645  			},
  3646  		},
  3647  
  3648  		"Deprecated generates no warnings if attr not used": {
  3649  			Schema: map[string]*Schema{
  3650  				"old_news": &Schema{
  3651  					Type:       TypeString,
  3652  					Optional:   true,
  3653  					Deprecated: "please use 'new_news' instead",
  3654  				},
  3655  			},
  3656  
  3657  			Err: false,
  3658  
  3659  			Warnings: nil,
  3660  		},
  3661  
  3662  		"Removed attribute usage generates error": {
  3663  			Schema: map[string]*Schema{
  3664  				"long_gone": &Schema{
  3665  					Type:     TypeString,
  3666  					Optional: true,
  3667  					Removed:  "no longer supported by Cloud API",
  3668  				},
  3669  			},
  3670  
  3671  			Config: map[string]interface{}{
  3672  				"long_gone": "still here!",
  3673  			},
  3674  
  3675  			Err: true,
  3676  			Errors: []error{
  3677  				fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"),
  3678  			},
  3679  		},
  3680  
  3681  		"Removed generates no errors if attr not used": {
  3682  			Schema: map[string]*Schema{
  3683  				"long_gone": &Schema{
  3684  					Type:     TypeString,
  3685  					Optional: true,
  3686  					Removed:  "no longer supported by Cloud API",
  3687  				},
  3688  			},
  3689  
  3690  			Err: false,
  3691  		},
  3692  
  3693  		"Conflicting attributes generate error": {
  3694  			Schema: map[string]*Schema{
  3695  				"whitelist": &Schema{
  3696  					Type:     TypeString,
  3697  					Optional: true,
  3698  				},
  3699  				"blacklist": &Schema{
  3700  					Type:          TypeString,
  3701  					Optional:      true,
  3702  					ConflictsWith: []string{"whitelist"},
  3703  				},
  3704  			},
  3705  
  3706  			Config: map[string]interface{}{
  3707  				"whitelist": "white-val",
  3708  				"blacklist": "black-val",
  3709  			},
  3710  
  3711  			Err: true,
  3712  			Errors: []error{
  3713  				fmt.Errorf("\"blacklist\": conflicts with whitelist (\"white-val\")"),
  3714  			},
  3715  		},
  3716  
  3717  		"Required attribute & undefined conflicting optional are good": {
  3718  			Schema: map[string]*Schema{
  3719  				"required_att": &Schema{
  3720  					Type:     TypeString,
  3721  					Required: true,
  3722  				},
  3723  				"optional_att": &Schema{
  3724  					Type:          TypeString,
  3725  					Optional:      true,
  3726  					ConflictsWith: []string{"required_att"},
  3727  				},
  3728  			},
  3729  
  3730  			Config: map[string]interface{}{
  3731  				"required_att": "required-val",
  3732  			},
  3733  
  3734  			Err: false,
  3735  		},
  3736  
  3737  		"Required conflicting attribute & defined optional generate error": {
  3738  			Schema: map[string]*Schema{
  3739  				"required_att": &Schema{
  3740  					Type:     TypeString,
  3741  					Required: true,
  3742  				},
  3743  				"optional_att": &Schema{
  3744  					Type:          TypeString,
  3745  					Optional:      true,
  3746  					ConflictsWith: []string{"required_att"},
  3747  				},
  3748  			},
  3749  
  3750  			Config: map[string]interface{}{
  3751  				"required_att": "required-val",
  3752  				"optional_att": "optional-val",
  3753  			},
  3754  
  3755  			Err: true,
  3756  			Errors: []error{
  3757  				fmt.Errorf(`"optional_att": conflicts with required_att ("required-val")`),
  3758  			},
  3759  		},
  3760  
  3761  		"Good with ValidateFunc": {
  3762  			Schema: map[string]*Schema{
  3763  				"validate_me": &Schema{
  3764  					Type:     TypeString,
  3765  					Required: true,
  3766  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3767  						return
  3768  					},
  3769  				},
  3770  			},
  3771  			Config: map[string]interface{}{
  3772  				"validate_me": "valid",
  3773  			},
  3774  			Err: false,
  3775  		},
  3776  
  3777  		"Bad with ValidateFunc": {
  3778  			Schema: map[string]*Schema{
  3779  				"validate_me": &Schema{
  3780  					Type:     TypeString,
  3781  					Required: true,
  3782  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3783  						es = append(es, fmt.Errorf("something is not right here"))
  3784  						return
  3785  					},
  3786  				},
  3787  			},
  3788  			Config: map[string]interface{}{
  3789  				"validate_me": "invalid",
  3790  			},
  3791  			Err: true,
  3792  			Errors: []error{
  3793  				fmt.Errorf(`something is not right here`),
  3794  			},
  3795  		},
  3796  
  3797  		"ValidateFunc not called when type does not match": {
  3798  			Schema: map[string]*Schema{
  3799  				"number": &Schema{
  3800  					Type:     TypeInt,
  3801  					Required: true,
  3802  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3803  						t.Fatalf("Should not have gotten validate call")
  3804  						return
  3805  					},
  3806  				},
  3807  			},
  3808  			Config: map[string]interface{}{
  3809  				"number": "NaN",
  3810  			},
  3811  			Err: true,
  3812  		},
  3813  
  3814  		"ValidateFunc gets decoded type": {
  3815  			Schema: map[string]*Schema{
  3816  				"maybe": &Schema{
  3817  					Type:     TypeBool,
  3818  					Required: true,
  3819  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3820  						if _, ok := v.(bool); !ok {
  3821  							t.Fatalf("Expected bool, got: %#v", v)
  3822  						}
  3823  						return
  3824  					},
  3825  				},
  3826  			},
  3827  			Config: map[string]interface{}{
  3828  				"maybe": "true",
  3829  			},
  3830  		},
  3831  
  3832  		"ValidateFunc is not called with a computed value": {
  3833  			Schema: map[string]*Schema{
  3834  				"validate_me": &Schema{
  3835  					Type:     TypeString,
  3836  					Required: true,
  3837  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3838  						es = append(es, fmt.Errorf("something is not right here"))
  3839  						return
  3840  					},
  3841  				},
  3842  			},
  3843  			Config: map[string]interface{}{
  3844  				"validate_me": "${var.foo}",
  3845  			},
  3846  			Vars: map[string]string{
  3847  				"var.foo": config.UnknownVariableValue,
  3848  			},
  3849  
  3850  			Err: false,
  3851  		},
  3852  	}
  3853  
  3854  	for tn, tc := range cases {
  3855  		c, err := config.NewRawConfig(tc.Config)
  3856  		if err != nil {
  3857  			t.Fatalf("err: %s", err)
  3858  		}
  3859  		if tc.Vars != nil {
  3860  			vars := make(map[string]ast.Variable)
  3861  			for k, v := range tc.Vars {
  3862  				vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
  3863  			}
  3864  
  3865  			if err := c.Interpolate(vars); err != nil {
  3866  				t.Fatalf("err: %s", err)
  3867  			}
  3868  		}
  3869  
  3870  		ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  3871  		if len(es) > 0 != tc.Err {
  3872  			if len(es) == 0 {
  3873  				t.Errorf("%q: no errors", tn)
  3874  			}
  3875  
  3876  			for _, e := range es {
  3877  				t.Errorf("%q: err: %s", tn, e)
  3878  			}
  3879  
  3880  			t.FailNow()
  3881  		}
  3882  
  3883  		if !reflect.DeepEqual(ws, tc.Warnings) {
  3884  			t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws)
  3885  		}
  3886  
  3887  		if tc.Errors != nil {
  3888  			if !reflect.DeepEqual(es, tc.Errors) {
  3889  				t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es)
  3890  			}
  3891  		}
  3892  	}
  3893  }
  3894  
  3895  func TestSchemaSet_ValidateMaxItems(t *testing.T) {
  3896  	cases := map[string]struct {
  3897  		Schema          map[string]*Schema
  3898  		State           *terraform.InstanceState
  3899  		Config          map[string]interface{}
  3900  		ConfigVariables map[string]string
  3901  		Diff            *terraform.InstanceDiff
  3902  		Err             bool
  3903  		Errors          []error
  3904  	}{
  3905  		"#0": {
  3906  			Schema: map[string]*Schema{
  3907  				"aliases": &Schema{
  3908  					Type:     TypeSet,
  3909  					Optional: true,
  3910  					MaxItems: 1,
  3911  					Elem:     &Schema{Type: TypeString},
  3912  				},
  3913  			},
  3914  			State: nil,
  3915  			Config: map[string]interface{}{
  3916  				"aliases": []interface{}{"foo", "bar"},
  3917  			},
  3918  			Diff: nil,
  3919  			Err:  true,
  3920  			Errors: []error{
  3921  				fmt.Errorf("aliases: attribute supports 1 item maximum, config has 2 declared"),
  3922  			},
  3923  		},
  3924  		"#1": {
  3925  			Schema: map[string]*Schema{
  3926  				"aliases": &Schema{
  3927  					Type:     TypeSet,
  3928  					Optional: true,
  3929  					Elem:     &Schema{Type: TypeString},
  3930  				},
  3931  			},
  3932  			State: nil,
  3933  			Config: map[string]interface{}{
  3934  				"aliases": []interface{}{"foo", "bar"},
  3935  			},
  3936  			Diff:   nil,
  3937  			Err:    false,
  3938  			Errors: nil,
  3939  		},
  3940  		"#2": {
  3941  			Schema: map[string]*Schema{
  3942  				"aliases": &Schema{
  3943  					Type:     TypeSet,
  3944  					Optional: true,
  3945  					MaxItems: 1,
  3946  					Elem:     &Schema{Type: TypeString},
  3947  				},
  3948  			},
  3949  			State: nil,
  3950  			Config: map[string]interface{}{
  3951  				"aliases": []interface{}{"foo"},
  3952  			},
  3953  			Diff:   nil,
  3954  			Err:    false,
  3955  			Errors: nil,
  3956  		},
  3957  	}
  3958  
  3959  	for tn, tc := range cases {
  3960  		c, err := config.NewRawConfig(tc.Config)
  3961  		if err != nil {
  3962  			t.Fatalf("%q: err: %s", tn, err)
  3963  		}
  3964  		_, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  3965  
  3966  		if len(es) > 0 != tc.Err {
  3967  			if len(es) == 0 {
  3968  				t.Errorf("%q: no errors", tn)
  3969  			}
  3970  
  3971  			for _, e := range es {
  3972  				t.Errorf("%q: err: %s", tn, e)
  3973  			}
  3974  
  3975  			t.FailNow()
  3976  		}
  3977  
  3978  		if tc.Errors != nil {
  3979  			if !reflect.DeepEqual(es, tc.Errors) {
  3980  				t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es)
  3981  			}
  3982  		}
  3983  	}
  3984  }
  3985  
  3986  func TestSchemaSet_ValidateMinItems(t *testing.T) {
  3987  	cases := map[string]struct {
  3988  		Schema          map[string]*Schema
  3989  		State           *terraform.InstanceState
  3990  		Config          map[string]interface{}
  3991  		ConfigVariables map[string]string
  3992  		Diff            *terraform.InstanceDiff
  3993  		Err             bool
  3994  		Errors          []error
  3995  	}{
  3996  		"#0": {
  3997  			Schema: map[string]*Schema{
  3998  				"aliases": &Schema{
  3999  					Type:     TypeSet,
  4000  					Optional: true,
  4001  					MinItems: 2,
  4002  					Elem:     &Schema{Type: TypeString},
  4003  				},
  4004  			},
  4005  			State: nil,
  4006  			Config: map[string]interface{}{
  4007  				"aliases": []interface{}{"foo", "bar"},
  4008  			},
  4009  			Diff:   nil,
  4010  			Err:    false,
  4011  			Errors: nil,
  4012  		},
  4013  		"#1": {
  4014  			Schema: map[string]*Schema{
  4015  				"aliases": &Schema{
  4016  					Type:     TypeSet,
  4017  					Optional: true,
  4018  					Elem:     &Schema{Type: TypeString},
  4019  				},
  4020  			},
  4021  			State: nil,
  4022  			Config: map[string]interface{}{
  4023  				"aliases": []interface{}{"foo", "bar"},
  4024  			},
  4025  			Diff:   nil,
  4026  			Err:    false,
  4027  			Errors: nil,
  4028  		},
  4029  		"#2": {
  4030  			Schema: map[string]*Schema{
  4031  				"aliases": &Schema{
  4032  					Type:     TypeSet,
  4033  					Optional: true,
  4034  					MinItems: 2,
  4035  					Elem:     &Schema{Type: TypeString},
  4036  				},
  4037  			},
  4038  			State: nil,
  4039  			Config: map[string]interface{}{
  4040  				"aliases": []interface{}{"foo"},
  4041  			},
  4042  			Diff: nil,
  4043  			Err:  true,
  4044  			Errors: []error{
  4045  				fmt.Errorf("aliases: attribute supports 2 item as a minimum, config has 1 declared"),
  4046  			},
  4047  		},
  4048  	}
  4049  
  4050  	for tn, tc := range cases {
  4051  		c, err := config.NewRawConfig(tc.Config)
  4052  		if err != nil {
  4053  			t.Fatalf("%q: err: %s", tn, err)
  4054  		}
  4055  		_, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  4056  
  4057  		if len(es) > 0 != tc.Err {
  4058  			if len(es) == 0 {
  4059  				t.Errorf("%q: no errors", tn)
  4060  			}
  4061  
  4062  			for _, e := range es {
  4063  				t.Errorf("%q: err: %s", tn, e)
  4064  			}
  4065  
  4066  			t.FailNow()
  4067  		}
  4068  
  4069  		if tc.Errors != nil {
  4070  			if !reflect.DeepEqual(es, tc.Errors) {
  4071  				t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es)
  4072  			}
  4073  		}
  4074  	}
  4075  }