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