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