github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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_InternalValidate(t *testing.T) {
  2571  	cases := map[string]struct {
  2572  		In  map[string]*Schema
  2573  		Err bool
  2574  	}{
  2575  		"nothing": {
  2576  			nil,
  2577  			false,
  2578  		},
  2579  
  2580  		"Both optional and required": {
  2581  			map[string]*Schema{
  2582  				"foo": &Schema{
  2583  					Type:     TypeInt,
  2584  					Optional: true,
  2585  					Required: true,
  2586  				},
  2587  			},
  2588  			true,
  2589  		},
  2590  
  2591  		"No optional and no required": {
  2592  			map[string]*Schema{
  2593  				"foo": &Schema{
  2594  					Type: TypeInt,
  2595  				},
  2596  			},
  2597  			true,
  2598  		},
  2599  
  2600  		"Missing Type": {
  2601  			map[string]*Schema{
  2602  				"foo": &Schema{
  2603  					Required: true,
  2604  				},
  2605  			},
  2606  			true,
  2607  		},
  2608  
  2609  		"Required but computed": {
  2610  			map[string]*Schema{
  2611  				"foo": &Schema{
  2612  					Type:     TypeInt,
  2613  					Required: true,
  2614  					Computed: true,
  2615  				},
  2616  			},
  2617  			true,
  2618  		},
  2619  
  2620  		"Looks good": {
  2621  			map[string]*Schema{
  2622  				"foo": &Schema{
  2623  					Type:     TypeString,
  2624  					Required: true,
  2625  				},
  2626  			},
  2627  			false,
  2628  		},
  2629  
  2630  		"Computed but has default": {
  2631  			map[string]*Schema{
  2632  				"foo": &Schema{
  2633  					Type:     TypeInt,
  2634  					Optional: true,
  2635  					Computed: true,
  2636  					Default:  "foo",
  2637  				},
  2638  			},
  2639  			true,
  2640  		},
  2641  
  2642  		"Required but has default": {
  2643  			map[string]*Schema{
  2644  				"foo": &Schema{
  2645  					Type:     TypeInt,
  2646  					Optional: true,
  2647  					Required: true,
  2648  					Default:  "foo",
  2649  				},
  2650  			},
  2651  			true,
  2652  		},
  2653  
  2654  		"List element not set": {
  2655  			map[string]*Schema{
  2656  				"foo": &Schema{
  2657  					Type: TypeList,
  2658  				},
  2659  			},
  2660  			true,
  2661  		},
  2662  
  2663  		"List default": {
  2664  			map[string]*Schema{
  2665  				"foo": &Schema{
  2666  					Type:    TypeList,
  2667  					Elem:    &Schema{Type: TypeInt},
  2668  					Default: "foo",
  2669  				},
  2670  			},
  2671  			true,
  2672  		},
  2673  
  2674  		"List element computed": {
  2675  			map[string]*Schema{
  2676  				"foo": &Schema{
  2677  					Type:     TypeList,
  2678  					Optional: true,
  2679  					Elem: &Schema{
  2680  						Type:     TypeInt,
  2681  						Computed: true,
  2682  					},
  2683  				},
  2684  			},
  2685  			true,
  2686  		},
  2687  
  2688  		"List element with Set set": {
  2689  			map[string]*Schema{
  2690  				"foo": &Schema{
  2691  					Type:     TypeList,
  2692  					Elem:     &Schema{Type: TypeInt},
  2693  					Set:      func(interface{}) int { return 0 },
  2694  					Optional: true,
  2695  				},
  2696  			},
  2697  			true,
  2698  		},
  2699  
  2700  		"Set element with no Set set": {
  2701  			map[string]*Schema{
  2702  				"foo": &Schema{
  2703  					Type:     TypeSet,
  2704  					Elem:     &Schema{Type: TypeInt},
  2705  					Optional: true,
  2706  				},
  2707  			},
  2708  			false,
  2709  		},
  2710  
  2711  		"Required but computedWhen": {
  2712  			map[string]*Schema{
  2713  				"foo": &Schema{
  2714  					Type:         TypeInt,
  2715  					Required:     true,
  2716  					ComputedWhen: []string{"foo"},
  2717  				},
  2718  			},
  2719  			true,
  2720  		},
  2721  
  2722  		"Conflicting attributes cannot be required": {
  2723  			map[string]*Schema{
  2724  				"blacklist": &Schema{
  2725  					Type:     TypeBool,
  2726  					Required: true,
  2727  				},
  2728  				"whitelist": &Schema{
  2729  					Type:          TypeBool,
  2730  					Optional:      true,
  2731  					ConflictsWith: []string{"blacklist"},
  2732  				},
  2733  			},
  2734  			true,
  2735  		},
  2736  
  2737  		"Attribute with conflicts cannot be required": {
  2738  			map[string]*Schema{
  2739  				"whitelist": &Schema{
  2740  					Type:          TypeBool,
  2741  					Required:      true,
  2742  					ConflictsWith: []string{"blacklist"},
  2743  				},
  2744  			},
  2745  			true,
  2746  		},
  2747  
  2748  		"ConflictsWith cannot be used w/ Computed": {
  2749  			map[string]*Schema{
  2750  				"blacklist": &Schema{
  2751  					Type:     TypeBool,
  2752  					Computed: true,
  2753  				},
  2754  				"whitelist": &Schema{
  2755  					Type:          TypeBool,
  2756  					Optional:      true,
  2757  					ConflictsWith: []string{"blacklist"},
  2758  				},
  2759  			},
  2760  			true,
  2761  		},
  2762  
  2763  		"ConflictsWith cannot be used w/ ComputedWhen": {
  2764  			map[string]*Schema{
  2765  				"blacklist": &Schema{
  2766  					Type:         TypeBool,
  2767  					ComputedWhen: []string{"foor"},
  2768  				},
  2769  				"whitelist": &Schema{
  2770  					Type:          TypeBool,
  2771  					Required:      true,
  2772  					ConflictsWith: []string{"blacklist"},
  2773  				},
  2774  			},
  2775  			true,
  2776  		},
  2777  
  2778  		"Sub-resource invalid": {
  2779  			map[string]*Schema{
  2780  				"foo": &Schema{
  2781  					Type:     TypeList,
  2782  					Optional: true,
  2783  					Elem: &Resource{
  2784  						Schema: map[string]*Schema{
  2785  							"foo": new(Schema),
  2786  						},
  2787  					},
  2788  				},
  2789  			},
  2790  			true,
  2791  		},
  2792  
  2793  		"Sub-resource valid": {
  2794  			map[string]*Schema{
  2795  				"foo": &Schema{
  2796  					Type:     TypeList,
  2797  					Optional: true,
  2798  					Elem: &Resource{
  2799  						Schema: map[string]*Schema{
  2800  							"foo": &Schema{
  2801  								Type:     TypeInt,
  2802  								Optional: true,
  2803  							},
  2804  						},
  2805  					},
  2806  				},
  2807  			},
  2808  			false,
  2809  		},
  2810  
  2811  		"ValidateFunc on non-primitive": {
  2812  			map[string]*Schema{
  2813  				"foo": &Schema{
  2814  					Type:     TypeSet,
  2815  					Required: true,
  2816  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  2817  						return
  2818  					},
  2819  				},
  2820  			},
  2821  			true,
  2822  		},
  2823  	}
  2824  
  2825  	for tn, tc := range cases {
  2826  		err := schemaMap(tc.In).InternalValidate(schemaMap{})
  2827  		if err != nil != tc.Err {
  2828  			if tc.Err {
  2829  				t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In)
  2830  			}
  2831  			t.Fatalf("%q: Unexpected error occurred:\n\n%#v", tn, tc.In)
  2832  		}
  2833  	}
  2834  
  2835  }
  2836  
  2837  func TestSchemaMap_Validate(t *testing.T) {
  2838  	cases := map[string]struct {
  2839  		Schema   map[string]*Schema
  2840  		Config   map[string]interface{}
  2841  		Vars     map[string]string
  2842  		Err      bool
  2843  		Errors   []error
  2844  		Warnings []string
  2845  	}{
  2846  		"Good": {
  2847  			Schema: map[string]*Schema{
  2848  				"availability_zone": &Schema{
  2849  					Type:     TypeString,
  2850  					Optional: true,
  2851  					Computed: true,
  2852  					ForceNew: true,
  2853  				},
  2854  			},
  2855  
  2856  			Config: map[string]interface{}{
  2857  				"availability_zone": "foo",
  2858  			},
  2859  		},
  2860  
  2861  		"Good, because the var is not set and that error will come elsewhere": {
  2862  			Schema: map[string]*Schema{
  2863  				"size": &Schema{
  2864  					Type:     TypeInt,
  2865  					Required: true,
  2866  				},
  2867  			},
  2868  
  2869  			Config: map[string]interface{}{
  2870  				"size": "${var.foo}",
  2871  			},
  2872  
  2873  			Vars: map[string]string{
  2874  				"var.foo": config.UnknownVariableValue,
  2875  			},
  2876  		},
  2877  
  2878  		"Required field not set": {
  2879  			Schema: map[string]*Schema{
  2880  				"availability_zone": &Schema{
  2881  					Type:     TypeString,
  2882  					Required: true,
  2883  				},
  2884  			},
  2885  
  2886  			Config: map[string]interface{}{},
  2887  
  2888  			Err: true,
  2889  		},
  2890  
  2891  		"Invalid basic type": {
  2892  			Schema: map[string]*Schema{
  2893  				"port": &Schema{
  2894  					Type:     TypeInt,
  2895  					Required: true,
  2896  				},
  2897  			},
  2898  
  2899  			Config: map[string]interface{}{
  2900  				"port": "I am invalid",
  2901  			},
  2902  
  2903  			Err: true,
  2904  		},
  2905  
  2906  		"Invalid complex type": {
  2907  			Schema: map[string]*Schema{
  2908  				"user_data": &Schema{
  2909  					Type:     TypeString,
  2910  					Optional: true,
  2911  				},
  2912  			},
  2913  
  2914  			Config: map[string]interface{}{
  2915  				"user_data": []interface{}{
  2916  					map[string]interface{}{
  2917  						"foo": "bar",
  2918  					},
  2919  				},
  2920  			},
  2921  
  2922  			Err: true,
  2923  		},
  2924  
  2925  		"Bad type, interpolated": {
  2926  			Schema: map[string]*Schema{
  2927  				"size": &Schema{
  2928  					Type:     TypeInt,
  2929  					Required: true,
  2930  				},
  2931  			},
  2932  
  2933  			Config: map[string]interface{}{
  2934  				"size": "${var.foo}",
  2935  			},
  2936  
  2937  			Vars: map[string]string{
  2938  				"var.foo": "nope",
  2939  			},
  2940  
  2941  			Err: true,
  2942  		},
  2943  
  2944  		"Required but has DefaultFunc": {
  2945  			Schema: map[string]*Schema{
  2946  				"availability_zone": &Schema{
  2947  					Type:     TypeString,
  2948  					Required: true,
  2949  					DefaultFunc: func() (interface{}, error) {
  2950  						return "foo", nil
  2951  					},
  2952  				},
  2953  			},
  2954  
  2955  			Config: nil,
  2956  		},
  2957  
  2958  		"Required but has DefaultFunc return nil": {
  2959  			Schema: map[string]*Schema{
  2960  				"availability_zone": &Schema{
  2961  					Type:     TypeString,
  2962  					Required: true,
  2963  					DefaultFunc: func() (interface{}, error) {
  2964  						return nil, nil
  2965  					},
  2966  				},
  2967  			},
  2968  
  2969  			Config: nil,
  2970  
  2971  			Err: true,
  2972  		},
  2973  
  2974  		"Optional sub-resource": {
  2975  			Schema: map[string]*Schema{
  2976  				"ingress": &Schema{
  2977  					Type: TypeList,
  2978  					Elem: &Resource{
  2979  						Schema: map[string]*Schema{
  2980  							"from": &Schema{
  2981  								Type:     TypeInt,
  2982  								Required: true,
  2983  							},
  2984  						},
  2985  					},
  2986  				},
  2987  			},
  2988  
  2989  			Config: map[string]interface{}{},
  2990  
  2991  			Err: false,
  2992  		},
  2993  
  2994  		"Sub-resource is the wrong type": {
  2995  			Schema: map[string]*Schema{
  2996  				"ingress": &Schema{
  2997  					Type:     TypeList,
  2998  					Required: true,
  2999  					Elem: &Resource{
  3000  						Schema: map[string]*Schema{
  3001  							"from": &Schema{
  3002  								Type:     TypeInt,
  3003  								Required: true,
  3004  							},
  3005  						},
  3006  					},
  3007  				},
  3008  			},
  3009  
  3010  			Config: map[string]interface{}{
  3011  				"ingress": []interface{}{"foo"},
  3012  			},
  3013  
  3014  			Err: true,
  3015  		},
  3016  
  3017  		"Not a list": {
  3018  			Schema: map[string]*Schema{
  3019  				"ingress": &Schema{
  3020  					Type: TypeList,
  3021  					Elem: &Resource{
  3022  						Schema: map[string]*Schema{
  3023  							"from": &Schema{
  3024  								Type:     TypeInt,
  3025  								Required: true,
  3026  							},
  3027  						},
  3028  					},
  3029  				},
  3030  			},
  3031  
  3032  			Config: map[string]interface{}{
  3033  				"ingress": "foo",
  3034  			},
  3035  
  3036  			Err: true,
  3037  		},
  3038  
  3039  		"Required sub-resource field": {
  3040  			Schema: map[string]*Schema{
  3041  				"ingress": &Schema{
  3042  					Type: TypeList,
  3043  					Elem: &Resource{
  3044  						Schema: map[string]*Schema{
  3045  							"from": &Schema{
  3046  								Type:     TypeInt,
  3047  								Required: true,
  3048  							},
  3049  						},
  3050  					},
  3051  				},
  3052  			},
  3053  
  3054  			Config: map[string]interface{}{
  3055  				"ingress": []interface{}{
  3056  					map[string]interface{}{},
  3057  				},
  3058  			},
  3059  
  3060  			Err: true,
  3061  		},
  3062  
  3063  		"Good sub-resource": {
  3064  			Schema: map[string]*Schema{
  3065  				"ingress": &Schema{
  3066  					Type:     TypeList,
  3067  					Optional: true,
  3068  					Elem: &Resource{
  3069  						Schema: map[string]*Schema{
  3070  							"from": &Schema{
  3071  								Type:     TypeInt,
  3072  								Required: true,
  3073  							},
  3074  						},
  3075  					},
  3076  				},
  3077  			},
  3078  
  3079  			Config: map[string]interface{}{
  3080  				"ingress": []interface{}{
  3081  					map[string]interface{}{
  3082  						"from": 80,
  3083  					},
  3084  				},
  3085  			},
  3086  
  3087  			Err: false,
  3088  		},
  3089  
  3090  		"Invalid/unknown field": {
  3091  			Schema: map[string]*Schema{
  3092  				"availability_zone": &Schema{
  3093  					Type:     TypeString,
  3094  					Optional: true,
  3095  					Computed: true,
  3096  					ForceNew: true,
  3097  				},
  3098  			},
  3099  
  3100  			Config: map[string]interface{}{
  3101  				"foo": "bar",
  3102  			},
  3103  
  3104  			Err: true,
  3105  		},
  3106  
  3107  		"Invalid/unknown field with computed value": {
  3108  			Schema: map[string]*Schema{
  3109  				"availability_zone": &Schema{
  3110  					Type:     TypeString,
  3111  					Optional: true,
  3112  					Computed: true,
  3113  					ForceNew: true,
  3114  				},
  3115  			},
  3116  
  3117  			Config: map[string]interface{}{
  3118  				"foo": "${var.foo}",
  3119  			},
  3120  
  3121  			Vars: map[string]string{
  3122  				"var.foo": config.UnknownVariableValue,
  3123  			},
  3124  
  3125  			Err: true,
  3126  		},
  3127  
  3128  		"Computed field set": {
  3129  			Schema: map[string]*Schema{
  3130  				"availability_zone": &Schema{
  3131  					Type:     TypeString,
  3132  					Computed: true,
  3133  				},
  3134  			},
  3135  
  3136  			Config: map[string]interface{}{
  3137  				"availability_zone": "bar",
  3138  			},
  3139  
  3140  			Err: true,
  3141  		},
  3142  
  3143  		"Not a set": {
  3144  			Schema: map[string]*Schema{
  3145  				"ports": &Schema{
  3146  					Type:     TypeSet,
  3147  					Required: true,
  3148  					Elem:     &Schema{Type: TypeInt},
  3149  					Set: func(a interface{}) int {
  3150  						return a.(int)
  3151  					},
  3152  				},
  3153  			},
  3154  
  3155  			Config: map[string]interface{}{
  3156  				"ports": "foo",
  3157  			},
  3158  
  3159  			Err: true,
  3160  		},
  3161  
  3162  		"Maps": {
  3163  			Schema: map[string]*Schema{
  3164  				"user_data": &Schema{
  3165  					Type:     TypeMap,
  3166  					Optional: true,
  3167  				},
  3168  			},
  3169  
  3170  			Config: map[string]interface{}{
  3171  				"user_data": "foo",
  3172  			},
  3173  
  3174  			Err: true,
  3175  		},
  3176  
  3177  		"Good map: data surrounded by extra slice": {
  3178  			Schema: map[string]*Schema{
  3179  				"user_data": &Schema{
  3180  					Type:     TypeMap,
  3181  					Optional: true,
  3182  				},
  3183  			},
  3184  
  3185  			Config: map[string]interface{}{
  3186  				"user_data": []interface{}{
  3187  					map[string]interface{}{
  3188  						"foo": "bar",
  3189  					},
  3190  				},
  3191  			},
  3192  		},
  3193  
  3194  		"Good map": {
  3195  			Schema: map[string]*Schema{
  3196  				"user_data": &Schema{
  3197  					Type:     TypeMap,
  3198  					Optional: true,
  3199  				},
  3200  			},
  3201  
  3202  			Config: map[string]interface{}{
  3203  				"user_data": map[string]interface{}{
  3204  					"foo": "bar",
  3205  				},
  3206  			},
  3207  		},
  3208  
  3209  		"Bad map: just a slice": {
  3210  			Schema: map[string]*Schema{
  3211  				"user_data": &Schema{
  3212  					Type:     TypeMap,
  3213  					Optional: true,
  3214  				},
  3215  			},
  3216  
  3217  			Config: map[string]interface{}{
  3218  				"user_data": []interface{}{
  3219  					"foo",
  3220  				},
  3221  			},
  3222  
  3223  			Err: true,
  3224  		},
  3225  
  3226  		"Good set: config has slice with single interpolated value": {
  3227  			Schema: map[string]*Schema{
  3228  				"security_groups": &Schema{
  3229  					Type:     TypeSet,
  3230  					Optional: true,
  3231  					Computed: true,
  3232  					ForceNew: true,
  3233  					Elem:     &Schema{Type: TypeString},
  3234  					Set: func(v interface{}) int {
  3235  						return len(v.(string))
  3236  					},
  3237  				},
  3238  			},
  3239  
  3240  			Config: map[string]interface{}{
  3241  				"security_groups": []interface{}{"${var.foo}"},
  3242  			},
  3243  
  3244  			Err: false,
  3245  		},
  3246  
  3247  		"Bad set: config has single interpolated value": {
  3248  			Schema: map[string]*Schema{
  3249  				"security_groups": &Schema{
  3250  					Type:     TypeSet,
  3251  					Optional: true,
  3252  					Computed: true,
  3253  					ForceNew: true,
  3254  					Elem:     &Schema{Type: TypeString},
  3255  				},
  3256  			},
  3257  
  3258  			Config: map[string]interface{}{
  3259  				"security_groups": "${var.foo}",
  3260  			},
  3261  
  3262  			Err: true,
  3263  		},
  3264  
  3265  		"Bad, subresource should not allow unknown elements": {
  3266  			Schema: map[string]*Schema{
  3267  				"ingress": &Schema{
  3268  					Type:     TypeList,
  3269  					Optional: true,
  3270  					Elem: &Resource{
  3271  						Schema: map[string]*Schema{
  3272  							"port": &Schema{
  3273  								Type:     TypeInt,
  3274  								Required: true,
  3275  							},
  3276  						},
  3277  					},
  3278  				},
  3279  			},
  3280  
  3281  			Config: map[string]interface{}{
  3282  				"ingress": []interface{}{
  3283  					map[string]interface{}{
  3284  						"port":  80,
  3285  						"other": "yes",
  3286  					},
  3287  				},
  3288  			},
  3289  
  3290  			Err: true,
  3291  		},
  3292  
  3293  		"Bad, subresource should not allow invalid types": {
  3294  			Schema: map[string]*Schema{
  3295  				"ingress": &Schema{
  3296  					Type:     TypeList,
  3297  					Optional: true,
  3298  					Elem: &Resource{
  3299  						Schema: map[string]*Schema{
  3300  							"port": &Schema{
  3301  								Type:     TypeInt,
  3302  								Required: true,
  3303  							},
  3304  						},
  3305  					},
  3306  				},
  3307  			},
  3308  
  3309  			Config: map[string]interface{}{
  3310  				"ingress": []interface{}{
  3311  					map[string]interface{}{
  3312  						"port": "bad",
  3313  					},
  3314  				},
  3315  			},
  3316  
  3317  			Err: true,
  3318  		},
  3319  
  3320  		"Bad, should not allow lists to be assigned to string attributes": {
  3321  			Schema: map[string]*Schema{
  3322  				"availability_zone": &Schema{
  3323  					Type:     TypeString,
  3324  					Required: true,
  3325  				},
  3326  			},
  3327  
  3328  			Config: map[string]interface{}{
  3329  				"availability_zone": []interface{}{"foo", "bar", "baz"},
  3330  			},
  3331  
  3332  			Err: true,
  3333  		},
  3334  
  3335  		"Bad, should not allow maps to be assigned to string attributes": {
  3336  			Schema: map[string]*Schema{
  3337  				"availability_zone": &Schema{
  3338  					Type:     TypeString,
  3339  					Required: true,
  3340  				},
  3341  			},
  3342  
  3343  			Config: map[string]interface{}{
  3344  				"availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"},
  3345  			},
  3346  
  3347  			Err: true,
  3348  		},
  3349  
  3350  		"Deprecated attribute usage generates warning, but not error": {
  3351  			Schema: map[string]*Schema{
  3352  				"old_news": &Schema{
  3353  					Type:       TypeString,
  3354  					Optional:   true,
  3355  					Deprecated: "please use 'new_news' instead",
  3356  				},
  3357  			},
  3358  
  3359  			Config: map[string]interface{}{
  3360  				"old_news": "extra extra!",
  3361  			},
  3362  
  3363  			Err: false,
  3364  
  3365  			Warnings: []string{
  3366  				"\"old_news\": [DEPRECATED] please use 'new_news' instead",
  3367  			},
  3368  		},
  3369  
  3370  		"Deprecated generates no warnings if attr not used": {
  3371  			Schema: map[string]*Schema{
  3372  				"old_news": &Schema{
  3373  					Type:       TypeString,
  3374  					Optional:   true,
  3375  					Deprecated: "please use 'new_news' instead",
  3376  				},
  3377  			},
  3378  
  3379  			Err: false,
  3380  
  3381  			Warnings: nil,
  3382  		},
  3383  
  3384  		"Removed attribute usage generates error": {
  3385  			Schema: map[string]*Schema{
  3386  				"long_gone": &Schema{
  3387  					Type:     TypeString,
  3388  					Optional: true,
  3389  					Removed:  "no longer supported by Cloud API",
  3390  				},
  3391  			},
  3392  
  3393  			Config: map[string]interface{}{
  3394  				"long_gone": "still here!",
  3395  			},
  3396  
  3397  			Err: true,
  3398  			Errors: []error{
  3399  				fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"),
  3400  			},
  3401  		},
  3402  
  3403  		"Removed generates no errors if attr not used": {
  3404  			Schema: map[string]*Schema{
  3405  				"long_gone": &Schema{
  3406  					Type:     TypeString,
  3407  					Optional: true,
  3408  					Removed:  "no longer supported by Cloud API",
  3409  				},
  3410  			},
  3411  
  3412  			Err: false,
  3413  		},
  3414  
  3415  		"Conflicting attributes generate error": {
  3416  			Schema: map[string]*Schema{
  3417  				"whitelist": &Schema{
  3418  					Type:     TypeString,
  3419  					Optional: true,
  3420  				},
  3421  				"blacklist": &Schema{
  3422  					Type:          TypeString,
  3423  					Optional:      true,
  3424  					ConflictsWith: []string{"whitelist"},
  3425  				},
  3426  			},
  3427  
  3428  			Config: map[string]interface{}{
  3429  				"whitelist": "white-val",
  3430  				"blacklist": "black-val",
  3431  			},
  3432  
  3433  			Err: true,
  3434  			Errors: []error{
  3435  				fmt.Errorf("\"blacklist\": conflicts with whitelist (\"white-val\")"),
  3436  			},
  3437  		},
  3438  
  3439  		"Required attribute & undefined conflicting optional are good": {
  3440  			Schema: map[string]*Schema{
  3441  				"required_att": &Schema{
  3442  					Type:     TypeString,
  3443  					Required: true,
  3444  				},
  3445  				"optional_att": &Schema{
  3446  					Type:          TypeString,
  3447  					Optional:      true,
  3448  					ConflictsWith: []string{"required_att"},
  3449  				},
  3450  			},
  3451  
  3452  			Config: map[string]interface{}{
  3453  				"required_att": "required-val",
  3454  			},
  3455  
  3456  			Err: false,
  3457  		},
  3458  
  3459  		"Required conflicting attribute & defined optional generate error": {
  3460  			Schema: map[string]*Schema{
  3461  				"required_att": &Schema{
  3462  					Type:     TypeString,
  3463  					Required: true,
  3464  				},
  3465  				"optional_att": &Schema{
  3466  					Type:          TypeString,
  3467  					Optional:      true,
  3468  					ConflictsWith: []string{"required_att"},
  3469  				},
  3470  			},
  3471  
  3472  			Config: map[string]interface{}{
  3473  				"required_att": "required-val",
  3474  				"optional_att": "optional-val",
  3475  			},
  3476  
  3477  			Err: true,
  3478  			Errors: []error{
  3479  				fmt.Errorf(`"optional_att": conflicts with required_att ("required-val")`),
  3480  			},
  3481  		},
  3482  
  3483  		"Good with ValidateFunc": {
  3484  			Schema: map[string]*Schema{
  3485  				"validate_me": &Schema{
  3486  					Type:     TypeString,
  3487  					Required: true,
  3488  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3489  						return
  3490  					},
  3491  				},
  3492  			},
  3493  			Config: map[string]interface{}{
  3494  				"validate_me": "valid",
  3495  			},
  3496  			Err: false,
  3497  		},
  3498  
  3499  		"Bad with ValidateFunc": {
  3500  			Schema: map[string]*Schema{
  3501  				"validate_me": &Schema{
  3502  					Type:     TypeString,
  3503  					Required: true,
  3504  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3505  						es = append(es, fmt.Errorf("something is not right here"))
  3506  						return
  3507  					},
  3508  				},
  3509  			},
  3510  			Config: map[string]interface{}{
  3511  				"validate_me": "invalid",
  3512  			},
  3513  			Err: true,
  3514  			Errors: []error{
  3515  				fmt.Errorf(`something is not right here`),
  3516  			},
  3517  		},
  3518  
  3519  		"ValidateFunc not called when type does not match": {
  3520  			Schema: map[string]*Schema{
  3521  				"number": &Schema{
  3522  					Type:     TypeInt,
  3523  					Required: true,
  3524  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3525  						t.Fatalf("Should not have gotten validate call")
  3526  						return
  3527  					},
  3528  				},
  3529  			},
  3530  			Config: map[string]interface{}{
  3531  				"number": "NaN",
  3532  			},
  3533  			Err: true,
  3534  		},
  3535  
  3536  		"ValidateFunc gets decoded type": {
  3537  			Schema: map[string]*Schema{
  3538  				"maybe": &Schema{
  3539  					Type:     TypeBool,
  3540  					Required: true,
  3541  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3542  						if _, ok := v.(bool); !ok {
  3543  							t.Fatalf("Expected bool, got: %#v", v)
  3544  						}
  3545  						return
  3546  					},
  3547  				},
  3548  			},
  3549  			Config: map[string]interface{}{
  3550  				"maybe": "true",
  3551  			},
  3552  		},
  3553  
  3554  		"ValidateFunc is not called with a computed value": {
  3555  			Schema: map[string]*Schema{
  3556  				"validate_me": &Schema{
  3557  					Type:     TypeString,
  3558  					Required: true,
  3559  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3560  						es = append(es, fmt.Errorf("something is not right here"))
  3561  						return
  3562  					},
  3563  				},
  3564  			},
  3565  			Config: map[string]interface{}{
  3566  				"validate_me": "${var.foo}",
  3567  			},
  3568  			Vars: map[string]string{
  3569  				"var.foo": config.UnknownVariableValue,
  3570  			},
  3571  
  3572  			Err: false,
  3573  		},
  3574  	}
  3575  
  3576  	for tn, tc := range cases {
  3577  		c, err := config.NewRawConfig(tc.Config)
  3578  		if err != nil {
  3579  			t.Fatalf("err: %s", err)
  3580  		}
  3581  		if tc.Vars != nil {
  3582  			vars := make(map[string]ast.Variable)
  3583  			for k, v := range tc.Vars {
  3584  				vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
  3585  			}
  3586  
  3587  			if err := c.Interpolate(vars); err != nil {
  3588  				t.Fatalf("err: %s", err)
  3589  			}
  3590  		}
  3591  
  3592  		ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  3593  		if len(es) > 0 != tc.Err {
  3594  			if len(es) == 0 {
  3595  				t.Errorf("%q: no errors", tn)
  3596  			}
  3597  
  3598  			for _, e := range es {
  3599  				t.Errorf("%q: err: %s", tn, e)
  3600  			}
  3601  
  3602  			t.FailNow()
  3603  		}
  3604  
  3605  		if !reflect.DeepEqual(ws, tc.Warnings) {
  3606  			t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws)
  3607  		}
  3608  
  3609  		if tc.Errors != nil {
  3610  			if !reflect.DeepEqual(es, tc.Errors) {
  3611  				t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es)
  3612  			}
  3613  		}
  3614  	}
  3615  }