github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/helper/schema/schema_test.go (about)

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