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