github.com/acm1/terraform@v0.6.2-0.20150729164239-1f314444f45c/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": config.NewStringList([]string{"2", "5"}).String(),
   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.NewStringList([]string{
   630  					config.UnknownVariableValue, "5"}).String(),
   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": config.NewStringList([]string{"2", "5"}).String(),
   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.NewStringList([]string{
   956  					config.UnknownVariableValue, "5"}).String(),
   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  					"ports.1": &terraform.ResourceAttrDiff{
  1051  						Old:        "1",
  1052  						New:        "0",
  1053  						NewRemoved: true,
  1054  					},
  1055  					"ports.2": &terraform.ResourceAttrDiff{
  1056  						Old:        "2",
  1057  						New:        "0",
  1058  						NewRemoved: true,
  1059  					},
  1060  				},
  1061  			},
  1062  
  1063  			Err: false,
  1064  		},
  1065  
  1066  		// #27
  1067  		{
  1068  			Schema: map[string]*Schema{
  1069  				"ports": &Schema{
  1070  					Type:     TypeSet,
  1071  					Optional: true,
  1072  					Computed: true,
  1073  					Elem:     &Schema{Type: TypeInt},
  1074  					Set: func(a interface{}) int {
  1075  						return a.(int)
  1076  					},
  1077  				},
  1078  			},
  1079  
  1080  			State: &terraform.InstanceState{
  1081  				Attributes: map[string]string{
  1082  					"availability_zone": "bar",
  1083  					"ports.#":           "1",
  1084  					"ports.80":          "80",
  1085  				},
  1086  			},
  1087  
  1088  			Config: map[string]interface{}{},
  1089  
  1090  			Diff: nil,
  1091  
  1092  			Err: false,
  1093  		},
  1094  
  1095  		// #28
  1096  		{
  1097  			Schema: map[string]*Schema{
  1098  				"ingress": &Schema{
  1099  					Type:     TypeSet,
  1100  					Required: true,
  1101  					Elem: &Resource{
  1102  						Schema: map[string]*Schema{
  1103  							"ports": &Schema{
  1104  								Type:     TypeList,
  1105  								Optional: true,
  1106  								Elem:     &Schema{Type: TypeInt},
  1107  							},
  1108  						},
  1109  					},
  1110  					Set: func(v interface{}) int {
  1111  						m := v.(map[string]interface{})
  1112  						ps := m["ports"].([]interface{})
  1113  						result := 0
  1114  						for _, p := range ps {
  1115  							result += p.(int)
  1116  						}
  1117  						return result
  1118  					},
  1119  				},
  1120  			},
  1121  
  1122  			State: &terraform.InstanceState{
  1123  				Attributes: map[string]string{
  1124  					"ingress.#":           "2",
  1125  					"ingress.80.ports.#":  "1",
  1126  					"ingress.80.ports.0":  "80",
  1127  					"ingress.443.ports.#": "1",
  1128  					"ingress.443.ports.0": "443",
  1129  				},
  1130  			},
  1131  
  1132  			Config: map[string]interface{}{
  1133  				"ingress": []map[string]interface{}{
  1134  					map[string]interface{}{
  1135  						"ports": []interface{}{443},
  1136  					},
  1137  					map[string]interface{}{
  1138  						"ports": []interface{}{80},
  1139  					},
  1140  				},
  1141  			},
  1142  
  1143  			Diff: nil,
  1144  
  1145  			Err: false,
  1146  		},
  1147  
  1148  		/*
  1149  		 * List of structure decode
  1150  		 */
  1151  
  1152  		// #29
  1153  		{
  1154  			Schema: map[string]*Schema{
  1155  				"ingress": &Schema{
  1156  					Type:     TypeList,
  1157  					Required: true,
  1158  					Elem: &Resource{
  1159  						Schema: map[string]*Schema{
  1160  							"from": &Schema{
  1161  								Type:     TypeInt,
  1162  								Required: true,
  1163  							},
  1164  						},
  1165  					},
  1166  				},
  1167  			},
  1168  
  1169  			State: nil,
  1170  
  1171  			Config: map[string]interface{}{
  1172  				"ingress": []interface{}{
  1173  					map[string]interface{}{
  1174  						"from": 8080,
  1175  					},
  1176  				},
  1177  			},
  1178  
  1179  			Diff: &terraform.InstanceDiff{
  1180  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1181  					"ingress.#": &terraform.ResourceAttrDiff{
  1182  						Old: "0",
  1183  						New: "1",
  1184  					},
  1185  					"ingress.0.from": &terraform.ResourceAttrDiff{
  1186  						Old: "",
  1187  						New: "8080",
  1188  					},
  1189  				},
  1190  			},
  1191  
  1192  			Err: false,
  1193  		},
  1194  
  1195  		/*
  1196  		 * ComputedWhen
  1197  		 */
  1198  
  1199  		// #30
  1200  		{
  1201  			Schema: map[string]*Schema{
  1202  				"availability_zone": &Schema{
  1203  					Type:         TypeString,
  1204  					Computed:     true,
  1205  					ComputedWhen: []string{"port"},
  1206  				},
  1207  
  1208  				"port": &Schema{
  1209  					Type:     TypeInt,
  1210  					Optional: true,
  1211  				},
  1212  			},
  1213  
  1214  			State: &terraform.InstanceState{
  1215  				Attributes: map[string]string{
  1216  					"availability_zone": "foo",
  1217  					"port":              "80",
  1218  				},
  1219  			},
  1220  
  1221  			Config: map[string]interface{}{
  1222  				"port": 80,
  1223  			},
  1224  
  1225  			Diff: nil,
  1226  
  1227  			Err: false,
  1228  		},
  1229  
  1230  		// #31
  1231  		{
  1232  			Schema: map[string]*Schema{
  1233  				"availability_zone": &Schema{
  1234  					Type:         TypeString,
  1235  					Computed:     true,
  1236  					ComputedWhen: []string{"port"},
  1237  				},
  1238  
  1239  				"port": &Schema{
  1240  					Type:     TypeInt,
  1241  					Optional: true,
  1242  				},
  1243  			},
  1244  
  1245  			State: &terraform.InstanceState{
  1246  				Attributes: map[string]string{
  1247  					"port": "80",
  1248  				},
  1249  			},
  1250  
  1251  			Config: map[string]interface{}{
  1252  				"port": 80,
  1253  			},
  1254  
  1255  			Diff: &terraform.InstanceDiff{
  1256  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1257  					"availability_zone": &terraform.ResourceAttrDiff{
  1258  						NewComputed: true,
  1259  					},
  1260  				},
  1261  			},
  1262  
  1263  			Err: false,
  1264  		},
  1265  
  1266  		/* TODO
  1267  		{
  1268  			Schema: map[string]*Schema{
  1269  				"availability_zone": &Schema{
  1270  					Type:         TypeString,
  1271  					Computed:     true,
  1272  					ComputedWhen: []string{"port"},
  1273  				},
  1274  
  1275  				"port": &Schema{
  1276  					Type:     TypeInt,
  1277  					Optional: true,
  1278  				},
  1279  			},
  1280  
  1281  			State: &terraform.InstanceState{
  1282  				Attributes: map[string]string{
  1283  					"availability_zone": "foo",
  1284  					"port":              "80",
  1285  				},
  1286  			},
  1287  
  1288  			Config: map[string]interface{}{
  1289  				"port": 8080,
  1290  			},
  1291  
  1292  			Diff: &terraform.ResourceDiff{
  1293  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1294  					"availability_zone": &terraform.ResourceAttrDiff{
  1295  						Old:         "foo",
  1296  						NewComputed: true,
  1297  					},
  1298  					"port": &terraform.ResourceAttrDiff{
  1299  						Old: "80",
  1300  						New: "8080",
  1301  					},
  1302  				},
  1303  			},
  1304  
  1305  			Err: false,
  1306  		},
  1307  		*/
  1308  
  1309  		/*
  1310  		 * Maps
  1311  		 */
  1312  
  1313  		// #32
  1314  		{
  1315  			Schema: map[string]*Schema{
  1316  				"config_vars": &Schema{
  1317  					Type: TypeMap,
  1318  				},
  1319  			},
  1320  
  1321  			State: nil,
  1322  
  1323  			Config: map[string]interface{}{
  1324  				"config_vars": []interface{}{
  1325  					map[string]interface{}{
  1326  						"bar": "baz",
  1327  					},
  1328  				},
  1329  			},
  1330  
  1331  			Diff: &terraform.InstanceDiff{
  1332  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1333  					"config_vars.#": &terraform.ResourceAttrDiff{
  1334  						Old: "0",
  1335  						New: "1",
  1336  					},
  1337  
  1338  					"config_vars.bar": &terraform.ResourceAttrDiff{
  1339  						Old: "",
  1340  						New: "baz",
  1341  					},
  1342  				},
  1343  			},
  1344  
  1345  			Err: false,
  1346  		},
  1347  
  1348  		// #33
  1349  		{
  1350  			Schema: map[string]*Schema{
  1351  				"config_vars": &Schema{
  1352  					Type: TypeMap,
  1353  				},
  1354  			},
  1355  
  1356  			State: &terraform.InstanceState{
  1357  				Attributes: map[string]string{
  1358  					"config_vars.foo": "bar",
  1359  				},
  1360  			},
  1361  
  1362  			Config: map[string]interface{}{
  1363  				"config_vars": []interface{}{
  1364  					map[string]interface{}{
  1365  						"bar": "baz",
  1366  					},
  1367  				},
  1368  			},
  1369  
  1370  			Diff: &terraform.InstanceDiff{
  1371  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1372  					"config_vars.foo": &terraform.ResourceAttrDiff{
  1373  						Old:        "bar",
  1374  						NewRemoved: true,
  1375  					},
  1376  					"config_vars.bar": &terraform.ResourceAttrDiff{
  1377  						Old: "",
  1378  						New: "baz",
  1379  					},
  1380  				},
  1381  			},
  1382  
  1383  			Err: false,
  1384  		},
  1385  
  1386  		// #34
  1387  		{
  1388  			Schema: map[string]*Schema{
  1389  				"vars": &Schema{
  1390  					Type:     TypeMap,
  1391  					Optional: true,
  1392  					Computed: true,
  1393  				},
  1394  			},
  1395  
  1396  			State: &terraform.InstanceState{
  1397  				Attributes: map[string]string{
  1398  					"vars.foo": "bar",
  1399  				},
  1400  			},
  1401  
  1402  			Config: map[string]interface{}{
  1403  				"vars": []interface{}{
  1404  					map[string]interface{}{
  1405  						"bar": "baz",
  1406  					},
  1407  				},
  1408  			},
  1409  
  1410  			Diff: &terraform.InstanceDiff{
  1411  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1412  					"vars.foo": &terraform.ResourceAttrDiff{
  1413  						Old:        "bar",
  1414  						New:        "",
  1415  						NewRemoved: true,
  1416  					},
  1417  					"vars.bar": &terraform.ResourceAttrDiff{
  1418  						Old: "",
  1419  						New: "baz",
  1420  					},
  1421  				},
  1422  			},
  1423  
  1424  			Err: false,
  1425  		},
  1426  
  1427  		// #35
  1428  		{
  1429  			Schema: map[string]*Schema{
  1430  				"vars": &Schema{
  1431  					Type:     TypeMap,
  1432  					Computed: true,
  1433  				},
  1434  			},
  1435  
  1436  			State: &terraform.InstanceState{
  1437  				Attributes: map[string]string{
  1438  					"vars.foo": "bar",
  1439  				},
  1440  			},
  1441  
  1442  			Config: nil,
  1443  
  1444  			Diff: nil,
  1445  
  1446  			Err: false,
  1447  		},
  1448  
  1449  		// #36
  1450  		{
  1451  			Schema: map[string]*Schema{
  1452  				"config_vars": &Schema{
  1453  					Type: TypeList,
  1454  					Elem: &Schema{Type: TypeMap},
  1455  				},
  1456  			},
  1457  
  1458  			State: &terraform.InstanceState{
  1459  				Attributes: map[string]string{
  1460  					"config_vars.#":     "1",
  1461  					"config_vars.0.foo": "bar",
  1462  				},
  1463  			},
  1464  
  1465  			Config: map[string]interface{}{
  1466  				"config_vars": []interface{}{
  1467  					map[string]interface{}{
  1468  						"bar": "baz",
  1469  					},
  1470  				},
  1471  			},
  1472  
  1473  			Diff: &terraform.InstanceDiff{
  1474  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1475  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
  1476  						Old:        "bar",
  1477  						NewRemoved: true,
  1478  					},
  1479  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1480  						Old: "",
  1481  						New: "baz",
  1482  					},
  1483  				},
  1484  			},
  1485  
  1486  			Err: false,
  1487  		},
  1488  
  1489  		// #37
  1490  		{
  1491  			Schema: map[string]*Schema{
  1492  				"config_vars": &Schema{
  1493  					Type: TypeList,
  1494  					Elem: &Schema{Type: TypeMap},
  1495  				},
  1496  			},
  1497  
  1498  			State: &terraform.InstanceState{
  1499  				Attributes: map[string]string{
  1500  					"config_vars.#":     "1",
  1501  					"config_vars.0.foo": "bar",
  1502  					"config_vars.0.bar": "baz",
  1503  				},
  1504  			},
  1505  
  1506  			Config: map[string]interface{}{},
  1507  
  1508  			Diff: &terraform.InstanceDiff{
  1509  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1510  					"config_vars.#": &terraform.ResourceAttrDiff{
  1511  						Old: "1",
  1512  						New: "0",
  1513  					},
  1514  					"config_vars.0.#": &terraform.ResourceAttrDiff{
  1515  						Old: "2",
  1516  						New: "0",
  1517  					},
  1518  					"config_vars.0.foo": &terraform.ResourceAttrDiff{
  1519  						Old:        "bar",
  1520  						NewRemoved: true,
  1521  					},
  1522  					"config_vars.0.bar": &terraform.ResourceAttrDiff{
  1523  						Old:        "baz",
  1524  						NewRemoved: true,
  1525  					},
  1526  				},
  1527  			},
  1528  
  1529  			Err: false,
  1530  		},
  1531  
  1532  		/*
  1533  		 * ForceNews
  1534  		 */
  1535  
  1536  		// #38
  1537  		{
  1538  			Schema: map[string]*Schema{
  1539  				"availability_zone": &Schema{
  1540  					Type:     TypeString,
  1541  					Optional: true,
  1542  					ForceNew: true,
  1543  				},
  1544  
  1545  				"address": &Schema{
  1546  					Type:     TypeString,
  1547  					Optional: true,
  1548  					Computed: true,
  1549  				},
  1550  			},
  1551  
  1552  			State: &terraform.InstanceState{
  1553  				Attributes: map[string]string{
  1554  					"availability_zone": "bar",
  1555  					"address":           "foo",
  1556  				},
  1557  			},
  1558  
  1559  			Config: map[string]interface{}{
  1560  				"availability_zone": "foo",
  1561  			},
  1562  
  1563  			Diff: &terraform.InstanceDiff{
  1564  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1565  					"availability_zone": &terraform.ResourceAttrDiff{
  1566  						Old:         "bar",
  1567  						New:         "foo",
  1568  						RequiresNew: true,
  1569  					},
  1570  
  1571  					"address": &terraform.ResourceAttrDiff{
  1572  						Old:         "foo",
  1573  						New:         "",
  1574  						NewComputed: true,
  1575  					},
  1576  				},
  1577  			},
  1578  
  1579  			Err: false,
  1580  		},
  1581  
  1582  		// #39 Set
  1583  		{
  1584  			Schema: map[string]*Schema{
  1585  				"availability_zone": &Schema{
  1586  					Type:     TypeString,
  1587  					Optional: true,
  1588  					ForceNew: true,
  1589  				},
  1590  
  1591  				"ports": &Schema{
  1592  					Type:     TypeSet,
  1593  					Optional: true,
  1594  					Computed: true,
  1595  					Elem:     &Schema{Type: TypeInt},
  1596  					Set: func(a interface{}) int {
  1597  						return a.(int)
  1598  					},
  1599  				},
  1600  			},
  1601  
  1602  			State: &terraform.InstanceState{
  1603  				Attributes: map[string]string{
  1604  					"availability_zone": "bar",
  1605  					"ports.#":           "1",
  1606  					"ports.80":          "80",
  1607  				},
  1608  			},
  1609  
  1610  			Config: map[string]interface{}{
  1611  				"availability_zone": "foo",
  1612  			},
  1613  
  1614  			Diff: &terraform.InstanceDiff{
  1615  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1616  					"availability_zone": &terraform.ResourceAttrDiff{
  1617  						Old:         "bar",
  1618  						New:         "foo",
  1619  						RequiresNew: true,
  1620  					},
  1621  
  1622  					"ports.#": &terraform.ResourceAttrDiff{
  1623  						Old:         "1",
  1624  						New:         "",
  1625  						NewComputed: true,
  1626  					},
  1627  				},
  1628  			},
  1629  
  1630  			Err: false,
  1631  		},
  1632  
  1633  		// #40 Set
  1634  		{
  1635  			Schema: map[string]*Schema{
  1636  				"instances": &Schema{
  1637  					Type:     TypeSet,
  1638  					Elem:     &Schema{Type: TypeString},
  1639  					Optional: true,
  1640  					Computed: true,
  1641  					Set: func(v interface{}) int {
  1642  						return len(v.(string))
  1643  					},
  1644  				},
  1645  			},
  1646  
  1647  			State: &terraform.InstanceState{
  1648  				Attributes: map[string]string{
  1649  					"instances.#": "0",
  1650  				},
  1651  			},
  1652  
  1653  			Config: map[string]interface{}{
  1654  				"instances": []interface{}{"${var.foo}"},
  1655  			},
  1656  
  1657  			ConfigVariables: map[string]string{
  1658  				"var.foo": config.UnknownVariableValue,
  1659  			},
  1660  
  1661  			Diff: &terraform.InstanceDiff{
  1662  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1663  					"instances.#": &terraform.ResourceAttrDiff{
  1664  						NewComputed: true,
  1665  					},
  1666  				},
  1667  			},
  1668  
  1669  			Err: false,
  1670  		},
  1671  
  1672  		// #41 Set
  1673  		{
  1674  			Schema: map[string]*Schema{
  1675  				"route": &Schema{
  1676  					Type:     TypeSet,
  1677  					Optional: true,
  1678  					Elem: &Resource{
  1679  						Schema: map[string]*Schema{
  1680  							"index": &Schema{
  1681  								Type:     TypeInt,
  1682  								Required: true,
  1683  							},
  1684  
  1685  							"gateway": &Schema{
  1686  								Type:     TypeString,
  1687  								Optional: true,
  1688  							},
  1689  						},
  1690  					},
  1691  					Set: func(v interface{}) int {
  1692  						m := v.(map[string]interface{})
  1693  						return m["index"].(int)
  1694  					},
  1695  				},
  1696  			},
  1697  
  1698  			State: nil,
  1699  
  1700  			Config: map[string]interface{}{
  1701  				"route": []map[string]interface{}{
  1702  					map[string]interface{}{
  1703  						"index":   "1",
  1704  						"gateway": "${var.foo}",
  1705  					},
  1706  				},
  1707  			},
  1708  
  1709  			ConfigVariables: map[string]string{
  1710  				"var.foo": config.UnknownVariableValue,
  1711  			},
  1712  
  1713  			Diff: &terraform.InstanceDiff{
  1714  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1715  					"route.#": &terraform.ResourceAttrDiff{
  1716  						Old: "0",
  1717  						New: "1",
  1718  					},
  1719  					"route.~1.index": &terraform.ResourceAttrDiff{
  1720  						Old: "",
  1721  						New: "1",
  1722  					},
  1723  					"route.~1.gateway": &terraform.ResourceAttrDiff{
  1724  						Old: "",
  1725  						New: "${var.foo}",
  1726  					},
  1727  				},
  1728  			},
  1729  
  1730  			Err: false,
  1731  		},
  1732  
  1733  		// #42 Set
  1734  		{
  1735  			Schema: map[string]*Schema{
  1736  				"route": &Schema{
  1737  					Type:     TypeSet,
  1738  					Optional: true,
  1739  					Elem: &Resource{
  1740  						Schema: map[string]*Schema{
  1741  							"index": &Schema{
  1742  								Type:     TypeInt,
  1743  								Required: true,
  1744  							},
  1745  
  1746  							"gateway": &Schema{
  1747  								Type:     TypeSet,
  1748  								Optional: true,
  1749  								Elem:     &Schema{Type: TypeInt},
  1750  								Set: func(a interface{}) int {
  1751  									return a.(int)
  1752  								},
  1753  							},
  1754  						},
  1755  					},
  1756  					Set: func(v interface{}) int {
  1757  						m := v.(map[string]interface{})
  1758  						return m["index"].(int)
  1759  					},
  1760  				},
  1761  			},
  1762  
  1763  			State: nil,
  1764  
  1765  			Config: map[string]interface{}{
  1766  				"route": []map[string]interface{}{
  1767  					map[string]interface{}{
  1768  						"index": "1",
  1769  						"gateway": []interface{}{
  1770  							"${var.foo}",
  1771  						},
  1772  					},
  1773  				},
  1774  			},
  1775  
  1776  			ConfigVariables: map[string]string{
  1777  				"var.foo": config.UnknownVariableValue,
  1778  			},
  1779  
  1780  			Diff: &terraform.InstanceDiff{
  1781  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1782  					"route.#": &terraform.ResourceAttrDiff{
  1783  						Old: "0",
  1784  						New: "1",
  1785  					},
  1786  					"route.~1.index": &terraform.ResourceAttrDiff{
  1787  						Old: "",
  1788  						New: "1",
  1789  					},
  1790  					"route.~1.gateway.#": &terraform.ResourceAttrDiff{
  1791  						NewComputed: true,
  1792  					},
  1793  				},
  1794  			},
  1795  
  1796  			Err: false,
  1797  		},
  1798  
  1799  		// #43 - Computed maps
  1800  		{
  1801  			Schema: map[string]*Schema{
  1802  				"vars": &Schema{
  1803  					Type:     TypeMap,
  1804  					Computed: true,
  1805  				},
  1806  			},
  1807  
  1808  			State: nil,
  1809  
  1810  			Config: nil,
  1811  
  1812  			Diff: &terraform.InstanceDiff{
  1813  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1814  					"vars.#": &terraform.ResourceAttrDiff{
  1815  						Old:         "",
  1816  						NewComputed: true,
  1817  					},
  1818  				},
  1819  			},
  1820  
  1821  			Err: false,
  1822  		},
  1823  
  1824  		// #44 - Computed maps
  1825  		{
  1826  			Schema: map[string]*Schema{
  1827  				"vars": &Schema{
  1828  					Type:     TypeMap,
  1829  					Computed: true,
  1830  				},
  1831  			},
  1832  
  1833  			State: &terraform.InstanceState{
  1834  				Attributes: map[string]string{
  1835  					"vars.#": "0",
  1836  				},
  1837  			},
  1838  
  1839  			Config: map[string]interface{}{
  1840  				"vars": map[string]interface{}{
  1841  					"bar": "${var.foo}",
  1842  				},
  1843  			},
  1844  
  1845  			ConfigVariables: map[string]string{
  1846  				"var.foo": config.UnknownVariableValue,
  1847  			},
  1848  
  1849  			Diff: &terraform.InstanceDiff{
  1850  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1851  					"vars.#": &terraform.ResourceAttrDiff{
  1852  						Old:         "",
  1853  						NewComputed: true,
  1854  					},
  1855  				},
  1856  			},
  1857  
  1858  			Err: false,
  1859  		},
  1860  
  1861  		// #45 - Empty
  1862  		{
  1863  			Schema: map[string]*Schema{},
  1864  
  1865  			State: &terraform.InstanceState{},
  1866  
  1867  			Config: map[string]interface{}{},
  1868  
  1869  			Diff: nil,
  1870  
  1871  			Err: false,
  1872  		},
  1873  
  1874  		// #46 - Float
  1875  		{
  1876  			Schema: map[string]*Schema{
  1877  				"some_threshold": &Schema{
  1878  					Type: TypeFloat,
  1879  				},
  1880  			},
  1881  
  1882  			State: &terraform.InstanceState{
  1883  				Attributes: map[string]string{
  1884  					"some_threshold": "567.8",
  1885  				},
  1886  			},
  1887  
  1888  			Config: map[string]interface{}{
  1889  				"some_threshold": 12.34,
  1890  			},
  1891  
  1892  			Diff: &terraform.InstanceDiff{
  1893  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1894  					"some_threshold": &terraform.ResourceAttrDiff{
  1895  						Old: "567.8",
  1896  						New: "12.34",
  1897  					},
  1898  				},
  1899  			},
  1900  
  1901  			Err: false,
  1902  		},
  1903  
  1904  		// #47 - https://github.com/hashicorp/terraform/issues/824
  1905  		{
  1906  			Schema: map[string]*Schema{
  1907  				"block_device": &Schema{
  1908  					Type:     TypeSet,
  1909  					Optional: true,
  1910  					Computed: true,
  1911  					Elem: &Resource{
  1912  						Schema: map[string]*Schema{
  1913  							"device_name": &Schema{
  1914  								Type:     TypeString,
  1915  								Required: true,
  1916  							},
  1917  							"delete_on_termination": &Schema{
  1918  								Type:     TypeBool,
  1919  								Optional: true,
  1920  								Default:  true,
  1921  							},
  1922  						},
  1923  					},
  1924  					Set: func(v interface{}) int {
  1925  						var buf bytes.Buffer
  1926  						m := v.(map[string]interface{})
  1927  						buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
  1928  						buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool)))
  1929  						return hashcode.String(buf.String())
  1930  					},
  1931  				},
  1932  			},
  1933  
  1934  			State: &terraform.InstanceState{
  1935  				Attributes: map[string]string{
  1936  					"block_device.#":                                "2",
  1937  					"block_device.616397234.delete_on_termination":  "true",
  1938  					"block_device.616397234.device_name":            "/dev/sda1",
  1939  					"block_device.2801811477.delete_on_termination": "true",
  1940  					"block_device.2801811477.device_name":           "/dev/sdx",
  1941  				},
  1942  			},
  1943  
  1944  			Config: map[string]interface{}{
  1945  				"block_device": []map[string]interface{}{
  1946  					map[string]interface{}{
  1947  						"device_name": "/dev/sda1",
  1948  					},
  1949  					map[string]interface{}{
  1950  						"device_name": "/dev/sdx",
  1951  					},
  1952  				},
  1953  			},
  1954  			Diff: nil,
  1955  			Err:  false,
  1956  		},
  1957  
  1958  		// #48 - Zero value in state shouldn't result in diff
  1959  		{
  1960  			Schema: map[string]*Schema{
  1961  				"port": &Schema{
  1962  					Type:     TypeBool,
  1963  					Optional: true,
  1964  					ForceNew: true,
  1965  				},
  1966  			},
  1967  
  1968  			State: &terraform.InstanceState{
  1969  				Attributes: map[string]string{
  1970  					"port": "false",
  1971  				},
  1972  			},
  1973  
  1974  			Config: map[string]interface{}{},
  1975  
  1976  			Diff: nil,
  1977  
  1978  			Err: false,
  1979  		},
  1980  
  1981  		// #49 Set - Same as #48 but for sets
  1982  		{
  1983  			Schema: map[string]*Schema{
  1984  				"route": &Schema{
  1985  					Type:     TypeSet,
  1986  					Optional: true,
  1987  					Elem: &Resource{
  1988  						Schema: map[string]*Schema{
  1989  							"index": &Schema{
  1990  								Type:     TypeInt,
  1991  								Required: true,
  1992  							},
  1993  
  1994  							"gateway": &Schema{
  1995  								Type:     TypeSet,
  1996  								Optional: true,
  1997  								Elem:     &Schema{Type: TypeInt},
  1998  								Set: func(a interface{}) int {
  1999  									return a.(int)
  2000  								},
  2001  							},
  2002  						},
  2003  					},
  2004  					Set: func(v interface{}) int {
  2005  						m := v.(map[string]interface{})
  2006  						return m["index"].(int)
  2007  					},
  2008  				},
  2009  			},
  2010  
  2011  			State: &terraform.InstanceState{
  2012  				Attributes: map[string]string{
  2013  					"route.#": "0",
  2014  				},
  2015  			},
  2016  
  2017  			Config: map[string]interface{}{},
  2018  
  2019  			Diff: nil,
  2020  
  2021  			Err: false,
  2022  		},
  2023  
  2024  		// #50 - A set computed element shouldn't cause a diff
  2025  		{
  2026  			Schema: map[string]*Schema{
  2027  				"active": &Schema{
  2028  					Type:     TypeBool,
  2029  					Computed: true,
  2030  					ForceNew: true,
  2031  				},
  2032  			},
  2033  
  2034  			State: &terraform.InstanceState{
  2035  				Attributes: map[string]string{
  2036  					"active": "true",
  2037  				},
  2038  			},
  2039  
  2040  			Config: map[string]interface{}{},
  2041  
  2042  			Diff: nil,
  2043  
  2044  			Err: false,
  2045  		},
  2046  
  2047  		// #51 - An empty set should show up in the diff
  2048  		{
  2049  			Schema: map[string]*Schema{
  2050  				"instances": &Schema{
  2051  					Type:     TypeSet,
  2052  					Elem:     &Schema{Type: TypeString},
  2053  					Optional: true,
  2054  					ForceNew: true,
  2055  					Set: func(v interface{}) int {
  2056  						return len(v.(string))
  2057  					},
  2058  				},
  2059  			},
  2060  
  2061  			State: &terraform.InstanceState{
  2062  				Attributes: map[string]string{
  2063  					"instances.#": "1",
  2064  					"instances.3": "foo",
  2065  				},
  2066  			},
  2067  
  2068  			Config: map[string]interface{}{},
  2069  
  2070  			Diff: &terraform.InstanceDiff{
  2071  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2072  					"instances.#": &terraform.ResourceAttrDiff{
  2073  						Old:         "1",
  2074  						New:         "0",
  2075  						RequiresNew: true,
  2076  					},
  2077  					"instances.3": &terraform.ResourceAttrDiff{
  2078  						Old:        "foo",
  2079  						New:        "",
  2080  						NewRemoved: true,
  2081  					},
  2082  				},
  2083  			},
  2084  
  2085  			Err: false,
  2086  		},
  2087  
  2088  		// #52 - Map with empty value
  2089  		{
  2090  			Schema: map[string]*Schema{
  2091  				"vars": &Schema{
  2092  					Type: TypeMap,
  2093  				},
  2094  			},
  2095  
  2096  			State: nil,
  2097  
  2098  			Config: map[string]interface{}{
  2099  				"vars": map[string]interface{}{
  2100  					"foo": "",
  2101  				},
  2102  			},
  2103  
  2104  			Diff: &terraform.InstanceDiff{
  2105  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2106  					"vars.#": &terraform.ResourceAttrDiff{
  2107  						Old: "0",
  2108  						New: "1",
  2109  					},
  2110  					"vars.foo": &terraform.ResourceAttrDiff{
  2111  						Old: "",
  2112  						New: "",
  2113  					},
  2114  				},
  2115  			},
  2116  
  2117  			Err: false,
  2118  		},
  2119  
  2120  		// #53 - Unset bool, not in state
  2121  		{
  2122  			Schema: map[string]*Schema{
  2123  				"force": &Schema{
  2124  					Type:     TypeBool,
  2125  					Optional: true,
  2126  					ForceNew: true,
  2127  				},
  2128  			},
  2129  
  2130  			State: nil,
  2131  
  2132  			Config: map[string]interface{}{},
  2133  
  2134  			Diff: nil,
  2135  
  2136  			Err: false,
  2137  		},
  2138  
  2139  		// #54 - Unset set, not in state
  2140  		{
  2141  			Schema: map[string]*Schema{
  2142  				"metadata_keys": &Schema{
  2143  					Type:     TypeSet,
  2144  					Optional: true,
  2145  					ForceNew: true,
  2146  					Elem:     &Schema{Type: TypeInt},
  2147  					Set:      func(interface{}) int { return 0 },
  2148  				},
  2149  			},
  2150  
  2151  			State: nil,
  2152  
  2153  			Config: map[string]interface{}{},
  2154  
  2155  			Diff: nil,
  2156  
  2157  			Err: false,
  2158  		},
  2159  
  2160  		// #55 - Unset list in state, should not show up computed
  2161  		{
  2162  			Schema: map[string]*Schema{
  2163  				"metadata_keys": &Schema{
  2164  					Type:     TypeList,
  2165  					Optional: true,
  2166  					Computed: true,
  2167  					ForceNew: true,
  2168  					Elem:     &Schema{Type: TypeInt},
  2169  				},
  2170  			},
  2171  
  2172  			State: &terraform.InstanceState{
  2173  				Attributes: map[string]string{
  2174  					"metadata_keys.#": "0",
  2175  				},
  2176  			},
  2177  
  2178  			Config: map[string]interface{}{},
  2179  
  2180  			Diff: nil,
  2181  
  2182  			Err: false,
  2183  		},
  2184  
  2185  		// #56 - Set element computed substring
  2186  		{
  2187  			Schema: map[string]*Schema{
  2188  				"ports": &Schema{
  2189  					Type:     TypeSet,
  2190  					Required: true,
  2191  					Elem:     &Schema{Type: TypeInt},
  2192  					Set: func(a interface{}) int {
  2193  						return a.(int)
  2194  					},
  2195  				},
  2196  			},
  2197  
  2198  			State: nil,
  2199  
  2200  			Config: map[string]interface{}{
  2201  				"ports": []interface{}{1, "${var.foo}32"},
  2202  			},
  2203  
  2204  			ConfigVariables: map[string]string{
  2205  				"var.foo": config.UnknownVariableValue,
  2206  			},
  2207  
  2208  			Diff: &terraform.InstanceDiff{
  2209  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2210  					"ports.#": &terraform.ResourceAttrDiff{
  2211  						Old:         "",
  2212  						New:         "",
  2213  						NewComputed: true,
  2214  					},
  2215  				},
  2216  			},
  2217  
  2218  			Err: false,
  2219  		},
  2220  
  2221  		// #57 - Computed map without config that's known to be empty does not
  2222  		//       generate diff
  2223  		{
  2224  			Schema: map[string]*Schema{
  2225  				"tags": &Schema{
  2226  					Type:     TypeMap,
  2227  					Computed: true,
  2228  				},
  2229  			},
  2230  
  2231  			Config: nil,
  2232  
  2233  			State: &terraform.InstanceState{
  2234  				Attributes: map[string]string{
  2235  					"tags.#": "0",
  2236  				},
  2237  			},
  2238  
  2239  			Diff: nil,
  2240  
  2241  			Err: false,
  2242  		},
  2243  
  2244  		// #58 Set with hyphen keys
  2245  		{
  2246  			Schema: map[string]*Schema{
  2247  				"route": &Schema{
  2248  					Type:     TypeSet,
  2249  					Optional: true,
  2250  					Elem: &Resource{
  2251  						Schema: map[string]*Schema{
  2252  							"index": &Schema{
  2253  								Type:     TypeInt,
  2254  								Required: true,
  2255  							},
  2256  
  2257  							"gateway-name": &Schema{
  2258  								Type:     TypeString,
  2259  								Optional: true,
  2260  							},
  2261  						},
  2262  					},
  2263  					Set: func(v interface{}) int {
  2264  						m := v.(map[string]interface{})
  2265  						return m["index"].(int)
  2266  					},
  2267  				},
  2268  			},
  2269  
  2270  			State: nil,
  2271  
  2272  			Config: map[string]interface{}{
  2273  				"route": []map[string]interface{}{
  2274  					map[string]interface{}{
  2275  						"index":        "1",
  2276  						"gateway-name": "hello",
  2277  					},
  2278  				},
  2279  			},
  2280  
  2281  			Diff: &terraform.InstanceDiff{
  2282  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2283  					"route.#": &terraform.ResourceAttrDiff{
  2284  						Old: "0",
  2285  						New: "1",
  2286  					},
  2287  					"route.1.index": &terraform.ResourceAttrDiff{
  2288  						Old: "",
  2289  						New: "1",
  2290  					},
  2291  					"route.1.gateway-name": &terraform.ResourceAttrDiff{
  2292  						Old: "",
  2293  						New: "hello",
  2294  					},
  2295  				},
  2296  			},
  2297  
  2298  			Err: false,
  2299  		},
  2300  
  2301  		// #59: StateFunc in nested set (#1759)
  2302  		{
  2303  			Schema: map[string]*Schema{
  2304  				"service_account": &Schema{
  2305  					Type:     TypeList,
  2306  					Optional: true,
  2307  					ForceNew: true,
  2308  					Elem: &Resource{
  2309  						Schema: map[string]*Schema{
  2310  							"scopes": &Schema{
  2311  								Type:     TypeSet,
  2312  								Required: true,
  2313  								ForceNew: true,
  2314  								Elem: &Schema{
  2315  									Type: TypeString,
  2316  									StateFunc: func(v interface{}) string {
  2317  										return v.(string) + "!"
  2318  									},
  2319  								},
  2320  								Set: func(v interface{}) int {
  2321  									i, err := strconv.Atoi(v.(string))
  2322  									if err != nil {
  2323  										t.Fatalf("err: %s", err)
  2324  									}
  2325  									return i
  2326  								},
  2327  							},
  2328  						},
  2329  					},
  2330  				},
  2331  			},
  2332  
  2333  			State: nil,
  2334  
  2335  			Config: map[string]interface{}{
  2336  				"service_account": []map[string]interface{}{
  2337  					{
  2338  						"scopes": []interface{}{"123"},
  2339  					},
  2340  				},
  2341  			},
  2342  
  2343  			Diff: &terraform.InstanceDiff{
  2344  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2345  					"service_account.#": &terraform.ResourceAttrDiff{
  2346  						Old:         "0",
  2347  						New:         "1",
  2348  						RequiresNew: true,
  2349  					},
  2350  					"service_account.0.scopes.#": &terraform.ResourceAttrDiff{
  2351  						Old:         "0",
  2352  						New:         "1",
  2353  						RequiresNew: true,
  2354  					},
  2355  					"service_account.0.scopes.123": &terraform.ResourceAttrDiff{
  2356  						Old:         "",
  2357  						New:         "123!",
  2358  						NewExtra:    "123",
  2359  						RequiresNew: true,
  2360  					},
  2361  				},
  2362  			},
  2363  
  2364  			Err: false,
  2365  		},
  2366  
  2367  		// #60 - Removing set elements
  2368  		{
  2369  			Schema: map[string]*Schema{
  2370  				"instances": &Schema{
  2371  					Type:     TypeSet,
  2372  					Elem:     &Schema{Type: TypeString},
  2373  					Optional: true,
  2374  					ForceNew: true,
  2375  					Set: func(v interface{}) int {
  2376  						return len(v.(string))
  2377  					},
  2378  				},
  2379  			},
  2380  
  2381  			State: &terraform.InstanceState{
  2382  				Attributes: map[string]string{
  2383  					"instances.#": "2",
  2384  					"instances.3": "333",
  2385  					"instances.2": "22",
  2386  				},
  2387  			},
  2388  
  2389  			Config: map[string]interface{}{
  2390  				"instances": []interface{}{"333", "4444"},
  2391  			},
  2392  
  2393  			Diff: &terraform.InstanceDiff{
  2394  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2395  					"instances.#": &terraform.ResourceAttrDiff{
  2396  						Old: "2",
  2397  						New: "2",
  2398  					},
  2399  					"instances.2": &terraform.ResourceAttrDiff{
  2400  						Old:        "22",
  2401  						New:        "",
  2402  						NewRemoved: true,
  2403  					},
  2404  					"instances.3": &terraform.ResourceAttrDiff{
  2405  						Old:         "333",
  2406  						New:         "333",
  2407  						RequiresNew: true,
  2408  					},
  2409  					"instances.4": &terraform.ResourceAttrDiff{
  2410  						Old:         "",
  2411  						New:         "4444",
  2412  						RequiresNew: true,
  2413  					},
  2414  				},
  2415  			},
  2416  
  2417  			Err: false,
  2418  		},
  2419  	}
  2420  
  2421  	for i, tc := range cases {
  2422  		c, err := config.NewRawConfig(tc.Config)
  2423  		if err != nil {
  2424  			t.Fatalf("#%d err: %s", i, err)
  2425  		}
  2426  
  2427  		if len(tc.ConfigVariables) > 0 {
  2428  			vars := make(map[string]ast.Variable)
  2429  			for k, v := range tc.ConfigVariables {
  2430  				vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
  2431  			}
  2432  
  2433  			if err := c.Interpolate(vars); err != nil {
  2434  				t.Fatalf("#%d err: %s", i, err)
  2435  			}
  2436  		}
  2437  
  2438  		d, err := schemaMap(tc.Schema).Diff(
  2439  			tc.State, terraform.NewResourceConfig(c))
  2440  		if (err != nil) != tc.Err {
  2441  			t.Fatalf("#%d err: %s", i, err)
  2442  		}
  2443  
  2444  		if !reflect.DeepEqual(tc.Diff, d) {
  2445  			t.Fatalf("#%d:\n\nexpected: %#v\n\ngot:\n\n%#v", i, tc.Diff, d)
  2446  		}
  2447  	}
  2448  }
  2449  
  2450  func TestSchemaMap_Input(t *testing.T) {
  2451  	cases := map[string]struct {
  2452  		Schema map[string]*Schema
  2453  		Config map[string]interface{}
  2454  		Input  map[string]string
  2455  		Result map[string]interface{}
  2456  		Err    bool
  2457  	}{
  2458  		/*
  2459  		 * String decode
  2460  		 */
  2461  
  2462  		"uses input on optional field with no config": {
  2463  			Schema: map[string]*Schema{
  2464  				"availability_zone": &Schema{
  2465  					Type:     TypeString,
  2466  					Optional: true,
  2467  				},
  2468  			},
  2469  
  2470  			Input: map[string]string{
  2471  				"availability_zone": "foo",
  2472  			},
  2473  
  2474  			Result: map[string]interface{}{
  2475  				"availability_zone": "foo",
  2476  			},
  2477  
  2478  			Err: false,
  2479  		},
  2480  
  2481  		"input ignored when config has a value": {
  2482  			Schema: map[string]*Schema{
  2483  				"availability_zone": &Schema{
  2484  					Type:     TypeString,
  2485  					Optional: true,
  2486  				},
  2487  			},
  2488  
  2489  			Config: map[string]interface{}{
  2490  				"availability_zone": "bar",
  2491  			},
  2492  
  2493  			Input: map[string]string{
  2494  				"availability_zone": "foo",
  2495  			},
  2496  
  2497  			Result: map[string]interface{}{},
  2498  
  2499  			Err: false,
  2500  		},
  2501  
  2502  		"input ignored when schema has a default": {
  2503  			Schema: map[string]*Schema{
  2504  				"availability_zone": &Schema{
  2505  					Type:     TypeString,
  2506  					Default:  "foo",
  2507  					Optional: true,
  2508  				},
  2509  			},
  2510  
  2511  			Input: map[string]string{
  2512  				"availability_zone": "bar",
  2513  			},
  2514  
  2515  			Result: map[string]interface{}{},
  2516  
  2517  			Err: false,
  2518  		},
  2519  
  2520  		"input ignored when default function returns a value": {
  2521  			Schema: map[string]*Schema{
  2522  				"availability_zone": &Schema{
  2523  					Type: TypeString,
  2524  					DefaultFunc: func() (interface{}, error) {
  2525  						return "foo", nil
  2526  					},
  2527  					Optional: true,
  2528  				},
  2529  			},
  2530  
  2531  			Input: map[string]string{
  2532  				"availability_zone": "bar",
  2533  			},
  2534  
  2535  			Result: map[string]interface{}{},
  2536  
  2537  			Err: false,
  2538  		},
  2539  
  2540  		"input ignored when default function returns an empty string": {
  2541  			Schema: map[string]*Schema{
  2542  				"availability_zone": &Schema{
  2543  					Type:     TypeString,
  2544  					Default:  "",
  2545  					Optional: true,
  2546  				},
  2547  			},
  2548  
  2549  			Input: map[string]string{
  2550  				"availability_zone": "bar",
  2551  			},
  2552  
  2553  			Result: map[string]interface{}{},
  2554  
  2555  			Err: false,
  2556  		},
  2557  
  2558  		"input used when default function returns nil": {
  2559  			Schema: map[string]*Schema{
  2560  				"availability_zone": &Schema{
  2561  					Type: TypeString,
  2562  					DefaultFunc: func() (interface{}, error) {
  2563  						return nil, nil
  2564  					},
  2565  					Optional: true,
  2566  				},
  2567  			},
  2568  
  2569  			Input: map[string]string{
  2570  				"availability_zone": "bar",
  2571  			},
  2572  
  2573  			Result: map[string]interface{}{
  2574  				"availability_zone": "bar",
  2575  			},
  2576  
  2577  			Err: false,
  2578  		},
  2579  	}
  2580  
  2581  	for i, tc := range cases {
  2582  		if tc.Config == nil {
  2583  			tc.Config = make(map[string]interface{})
  2584  		}
  2585  
  2586  		c, err := config.NewRawConfig(tc.Config)
  2587  		if err != nil {
  2588  			t.Fatalf("err: %s", err)
  2589  		}
  2590  
  2591  		input := new(terraform.MockUIInput)
  2592  		input.InputReturnMap = tc.Input
  2593  
  2594  		rc := terraform.NewResourceConfig(c)
  2595  		rc.Config = make(map[string]interface{})
  2596  
  2597  		actual, err := schemaMap(tc.Schema).Input(input, rc)
  2598  		if (err != nil) != tc.Err {
  2599  			t.Fatalf("#%v err: %s", i, err)
  2600  		}
  2601  
  2602  		if !reflect.DeepEqual(tc.Result, actual.Config) {
  2603  			t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result)
  2604  		}
  2605  	}
  2606  }
  2607  
  2608  func TestSchemaMap_InputDefault(t *testing.T) {
  2609  	emptyConfig := make(map[string]interface{})
  2610  	c, err := config.NewRawConfig(emptyConfig)
  2611  	if err != nil {
  2612  		t.Fatalf("err: %s", err)
  2613  	}
  2614  	rc := terraform.NewResourceConfig(c)
  2615  	rc.Config = make(map[string]interface{})
  2616  
  2617  	input := new(terraform.MockUIInput)
  2618  	input.InputFn = func(opts *terraform.InputOpts) (string, error) {
  2619  		t.Fatalf("InputFn should not be called on: %#v", opts)
  2620  		return "", nil
  2621  	}
  2622  
  2623  	schema := map[string]*Schema{
  2624  		"availability_zone": &Schema{
  2625  			Type:     TypeString,
  2626  			Default:  "foo",
  2627  			Optional: true,
  2628  		},
  2629  	}
  2630  	actual, err := schemaMap(schema).Input(input, rc)
  2631  	if err != nil {
  2632  		t.Fatalf("err: %s", err)
  2633  	}
  2634  
  2635  	expected := map[string]interface{}{}
  2636  
  2637  	if !reflect.DeepEqual(expected, actual.Config) {
  2638  		t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected)
  2639  	}
  2640  }
  2641  
  2642  func TestSchemaMap_InternalValidate(t *testing.T) {
  2643  	cases := []struct {
  2644  		In  map[string]*Schema
  2645  		Err bool
  2646  	}{
  2647  		{
  2648  			nil,
  2649  			false,
  2650  		},
  2651  
  2652  		// No optional and no required
  2653  		{
  2654  			map[string]*Schema{
  2655  				"foo": &Schema{
  2656  					Type:     TypeInt,
  2657  					Optional: true,
  2658  					Required: true,
  2659  				},
  2660  			},
  2661  			true,
  2662  		},
  2663  
  2664  		// No optional and no required
  2665  		{
  2666  			map[string]*Schema{
  2667  				"foo": &Schema{
  2668  					Type: TypeInt,
  2669  				},
  2670  			},
  2671  			true,
  2672  		},
  2673  
  2674  		// Missing Type
  2675  		{
  2676  			map[string]*Schema{
  2677  				"foo": &Schema{
  2678  					Required: true,
  2679  				},
  2680  			},
  2681  			true,
  2682  		},
  2683  
  2684  		// Required but computed
  2685  		{
  2686  			map[string]*Schema{
  2687  				"foo": &Schema{
  2688  					Type:     TypeInt,
  2689  					Required: true,
  2690  					Computed: true,
  2691  				},
  2692  			},
  2693  			true,
  2694  		},
  2695  
  2696  		// Looks good
  2697  		{
  2698  			map[string]*Schema{
  2699  				"foo": &Schema{
  2700  					Type:     TypeString,
  2701  					Required: true,
  2702  				},
  2703  			},
  2704  			false,
  2705  		},
  2706  
  2707  		// Computed but has default
  2708  		{
  2709  			map[string]*Schema{
  2710  				"foo": &Schema{
  2711  					Type:     TypeInt,
  2712  					Optional: true,
  2713  					Computed: true,
  2714  					Default:  "foo",
  2715  				},
  2716  			},
  2717  			true,
  2718  		},
  2719  
  2720  		// Required but has default
  2721  		{
  2722  			map[string]*Schema{
  2723  				"foo": &Schema{
  2724  					Type:     TypeInt,
  2725  					Optional: true,
  2726  					Required: true,
  2727  					Default:  "foo",
  2728  				},
  2729  			},
  2730  			true,
  2731  		},
  2732  
  2733  		// List element not set
  2734  		{
  2735  			map[string]*Schema{
  2736  				"foo": &Schema{
  2737  					Type: TypeList,
  2738  				},
  2739  			},
  2740  			true,
  2741  		},
  2742  
  2743  		// List default
  2744  		{
  2745  			map[string]*Schema{
  2746  				"foo": &Schema{
  2747  					Type:    TypeList,
  2748  					Elem:    &Schema{Type: TypeInt},
  2749  					Default: "foo",
  2750  				},
  2751  			},
  2752  			true,
  2753  		},
  2754  
  2755  		// List element computed
  2756  		{
  2757  			map[string]*Schema{
  2758  				"foo": &Schema{
  2759  					Type:     TypeList,
  2760  					Optional: true,
  2761  					Elem: &Schema{
  2762  						Type:     TypeInt,
  2763  						Computed: true,
  2764  					},
  2765  				},
  2766  			},
  2767  			true,
  2768  		},
  2769  
  2770  		// List element with Set set
  2771  		{
  2772  			map[string]*Schema{
  2773  				"foo": &Schema{
  2774  					Type:     TypeList,
  2775  					Elem:     &Schema{Type: TypeInt},
  2776  					Set:      func(interface{}) int { return 0 },
  2777  					Optional: true,
  2778  				},
  2779  			},
  2780  			true,
  2781  		},
  2782  
  2783  		// Set element with no Set set
  2784  		{
  2785  			map[string]*Schema{
  2786  				"foo": &Schema{
  2787  					Type:     TypeSet,
  2788  					Elem:     &Schema{Type: TypeInt},
  2789  					Optional: true,
  2790  				},
  2791  			},
  2792  			true,
  2793  		},
  2794  
  2795  		// Required but computed
  2796  		{
  2797  			map[string]*Schema{
  2798  				"foo": &Schema{
  2799  					Type:         TypeInt,
  2800  					Required:     true,
  2801  					ComputedWhen: []string{"foo"},
  2802  				},
  2803  			},
  2804  			true,
  2805  		},
  2806  
  2807  		// Conflicting attributes cannot be required
  2808  		{
  2809  			map[string]*Schema{
  2810  				"blacklist": &Schema{
  2811  					Type:     TypeBool,
  2812  					Required: true,
  2813  				},
  2814  				"whitelist": &Schema{
  2815  					Type:          TypeBool,
  2816  					Optional:      true,
  2817  					ConflictsWith: []string{"blacklist"},
  2818  				},
  2819  			},
  2820  			true,
  2821  		},
  2822  
  2823  		// Attribute with conflicts cannot be required
  2824  		{
  2825  			map[string]*Schema{
  2826  				"whitelist": &Schema{
  2827  					Type:          TypeBool,
  2828  					Required:      true,
  2829  					ConflictsWith: []string{"blacklist"},
  2830  				},
  2831  			},
  2832  			true,
  2833  		},
  2834  
  2835  		// ConflictsWith cannot be used w/ Computed
  2836  		{
  2837  			map[string]*Schema{
  2838  				"blacklist": &Schema{
  2839  					Type:     TypeBool,
  2840  					Computed: true,
  2841  				},
  2842  				"whitelist": &Schema{
  2843  					Type:          TypeBool,
  2844  					Optional:      true,
  2845  					ConflictsWith: []string{"blacklist"},
  2846  				},
  2847  			},
  2848  			true,
  2849  		},
  2850  
  2851  		// ConflictsWith cannot be used w/ ComputedWhen
  2852  		{
  2853  			map[string]*Schema{
  2854  				"blacklist": &Schema{
  2855  					Type:         TypeBool,
  2856  					ComputedWhen: []string{"foor"},
  2857  				},
  2858  				"whitelist": &Schema{
  2859  					Type:          TypeBool,
  2860  					Required:      true,
  2861  					ConflictsWith: []string{"blacklist"},
  2862  				},
  2863  			},
  2864  			true,
  2865  		},
  2866  
  2867  		// Sub-resource invalid
  2868  		{
  2869  			map[string]*Schema{
  2870  				"foo": &Schema{
  2871  					Type:     TypeList,
  2872  					Optional: true,
  2873  					Elem: &Resource{
  2874  						Schema: map[string]*Schema{
  2875  							"foo": new(Schema),
  2876  						},
  2877  					},
  2878  				},
  2879  			},
  2880  			true,
  2881  		},
  2882  
  2883  		// Sub-resource valid
  2884  		{
  2885  			map[string]*Schema{
  2886  				"foo": &Schema{
  2887  					Type:     TypeList,
  2888  					Optional: true,
  2889  					Elem: &Resource{
  2890  						Schema: map[string]*Schema{
  2891  							"foo": &Schema{
  2892  								Type:     TypeInt,
  2893  								Optional: true,
  2894  							},
  2895  						},
  2896  					},
  2897  				},
  2898  			},
  2899  			false,
  2900  		},
  2901  
  2902  		// ValidateFunc on non-primitive
  2903  		{
  2904  			map[string]*Schema{
  2905  				"foo": &Schema{
  2906  					Type:     TypeMap,
  2907  					Required: true,
  2908  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  2909  						return
  2910  					},
  2911  				},
  2912  			},
  2913  			true,
  2914  		},
  2915  	}
  2916  
  2917  	for i, tc := range cases {
  2918  		err := schemaMap(tc.In).InternalValidate(schemaMap{})
  2919  		if (err != nil) != tc.Err {
  2920  			if tc.Err {
  2921  				t.Fatalf("%d: Expected error did not occur:\n\n%#v", i, tc.In)
  2922  			}
  2923  			t.Fatalf("%d: Unexpected error occured:\n\n%#v", i, tc.In)
  2924  		}
  2925  	}
  2926  
  2927  }
  2928  
  2929  func TestSchemaMap_Validate(t *testing.T) {
  2930  	cases := map[string]struct {
  2931  		Schema   map[string]*Schema
  2932  		Config   map[string]interface{}
  2933  		Vars     map[string]string
  2934  		Err      bool
  2935  		Errors   []error
  2936  		Warnings []string
  2937  	}{
  2938  		"Good": {
  2939  			Schema: map[string]*Schema{
  2940  				"availability_zone": &Schema{
  2941  					Type:     TypeString,
  2942  					Optional: true,
  2943  					Computed: true,
  2944  					ForceNew: true,
  2945  				},
  2946  			},
  2947  
  2948  			Config: map[string]interface{}{
  2949  				"availability_zone": "foo",
  2950  			},
  2951  		},
  2952  
  2953  		"Good, because the var is not set and that error will come elsewhere": {
  2954  			Schema: map[string]*Schema{
  2955  				"size": &Schema{
  2956  					Type:     TypeInt,
  2957  					Required: true,
  2958  				},
  2959  			},
  2960  
  2961  			Config: map[string]interface{}{
  2962  				"size": "${var.foo}",
  2963  			},
  2964  
  2965  			Vars: map[string]string{
  2966  				"var.foo": config.UnknownVariableValue,
  2967  			},
  2968  		},
  2969  
  2970  		"Required field not set": {
  2971  			Schema: map[string]*Schema{
  2972  				"availability_zone": &Schema{
  2973  					Type:     TypeString,
  2974  					Required: true,
  2975  				},
  2976  			},
  2977  
  2978  			Config: map[string]interface{}{},
  2979  
  2980  			Err: true,
  2981  		},
  2982  
  2983  		"Invalid basic type": {
  2984  			Schema: map[string]*Schema{
  2985  				"port": &Schema{
  2986  					Type:     TypeInt,
  2987  					Required: true,
  2988  				},
  2989  			},
  2990  
  2991  			Config: map[string]interface{}{
  2992  				"port": "I am invalid",
  2993  			},
  2994  
  2995  			Err: true,
  2996  		},
  2997  
  2998  		"Invalid complex type": {
  2999  			Schema: map[string]*Schema{
  3000  				"user_data": &Schema{
  3001  					Type:     TypeString,
  3002  					Optional: true,
  3003  				},
  3004  			},
  3005  
  3006  			Config: map[string]interface{}{
  3007  				"user_data": []interface{}{
  3008  					map[string]interface{}{
  3009  						"foo": "bar",
  3010  					},
  3011  				},
  3012  			},
  3013  
  3014  			Err: true,
  3015  		},
  3016  
  3017  		"Bad type, interpolated": {
  3018  			Schema: map[string]*Schema{
  3019  				"size": &Schema{
  3020  					Type:     TypeInt,
  3021  					Required: true,
  3022  				},
  3023  			},
  3024  
  3025  			Config: map[string]interface{}{
  3026  				"size": "${var.foo}",
  3027  			},
  3028  
  3029  			Vars: map[string]string{
  3030  				"var.foo": "nope",
  3031  			},
  3032  
  3033  			Err: true,
  3034  		},
  3035  
  3036  		"Required but has DefaultFunc": {
  3037  			Schema: map[string]*Schema{
  3038  				"availability_zone": &Schema{
  3039  					Type:     TypeString,
  3040  					Required: true,
  3041  					DefaultFunc: func() (interface{}, error) {
  3042  						return "foo", nil
  3043  					},
  3044  				},
  3045  			},
  3046  
  3047  			Config: nil,
  3048  		},
  3049  
  3050  		"Required but has DefaultFunc return nil": {
  3051  			Schema: map[string]*Schema{
  3052  				"availability_zone": &Schema{
  3053  					Type:     TypeString,
  3054  					Required: true,
  3055  					DefaultFunc: func() (interface{}, error) {
  3056  						return nil, nil
  3057  					},
  3058  				},
  3059  			},
  3060  
  3061  			Config: nil,
  3062  
  3063  			Err: true,
  3064  		},
  3065  
  3066  		"Optional sub-resource": {
  3067  			Schema: map[string]*Schema{
  3068  				"ingress": &Schema{
  3069  					Type: TypeList,
  3070  					Elem: &Resource{
  3071  						Schema: map[string]*Schema{
  3072  							"from": &Schema{
  3073  								Type:     TypeInt,
  3074  								Required: true,
  3075  							},
  3076  						},
  3077  					},
  3078  				},
  3079  			},
  3080  
  3081  			Config: map[string]interface{}{},
  3082  
  3083  			Err: false,
  3084  		},
  3085  
  3086  		"Sub-resource is the wrong type": {
  3087  			Schema: map[string]*Schema{
  3088  				"ingress": &Schema{
  3089  					Type:     TypeList,
  3090  					Required: true,
  3091  					Elem: &Resource{
  3092  						Schema: map[string]*Schema{
  3093  							"from": &Schema{
  3094  								Type:     TypeInt,
  3095  								Required: true,
  3096  							},
  3097  						},
  3098  					},
  3099  				},
  3100  			},
  3101  
  3102  			Config: map[string]interface{}{
  3103  				"ingress": []interface{}{"foo"},
  3104  			},
  3105  
  3106  			Err: true,
  3107  		},
  3108  
  3109  		"Not a list": {
  3110  			Schema: map[string]*Schema{
  3111  				"ingress": &Schema{
  3112  					Type: TypeList,
  3113  					Elem: &Resource{
  3114  						Schema: map[string]*Schema{
  3115  							"from": &Schema{
  3116  								Type:     TypeInt,
  3117  								Required: true,
  3118  							},
  3119  						},
  3120  					},
  3121  				},
  3122  			},
  3123  
  3124  			Config: map[string]interface{}{
  3125  				"ingress": "foo",
  3126  			},
  3127  
  3128  			Err: true,
  3129  		},
  3130  
  3131  		"Required sub-resource field": {
  3132  			Schema: map[string]*Schema{
  3133  				"ingress": &Schema{
  3134  					Type: TypeList,
  3135  					Elem: &Resource{
  3136  						Schema: map[string]*Schema{
  3137  							"from": &Schema{
  3138  								Type:     TypeInt,
  3139  								Required: true,
  3140  							},
  3141  						},
  3142  					},
  3143  				},
  3144  			},
  3145  
  3146  			Config: map[string]interface{}{
  3147  				"ingress": []interface{}{
  3148  					map[string]interface{}{},
  3149  				},
  3150  			},
  3151  
  3152  			Err: true,
  3153  		},
  3154  
  3155  		"Good sub-resource": {
  3156  			Schema: map[string]*Schema{
  3157  				"ingress": &Schema{
  3158  					Type:     TypeList,
  3159  					Optional: true,
  3160  					Elem: &Resource{
  3161  						Schema: map[string]*Schema{
  3162  							"from": &Schema{
  3163  								Type:     TypeInt,
  3164  								Required: true,
  3165  							},
  3166  						},
  3167  					},
  3168  				},
  3169  			},
  3170  
  3171  			Config: map[string]interface{}{
  3172  				"ingress": []interface{}{
  3173  					map[string]interface{}{
  3174  						"from": 80,
  3175  					},
  3176  				},
  3177  			},
  3178  
  3179  			Err: false,
  3180  		},
  3181  
  3182  		"Invalid/unknown field": {
  3183  			Schema: map[string]*Schema{
  3184  				"availability_zone": &Schema{
  3185  					Type:     TypeString,
  3186  					Optional: true,
  3187  					Computed: true,
  3188  					ForceNew: true,
  3189  				},
  3190  			},
  3191  
  3192  			Config: map[string]interface{}{
  3193  				"foo": "bar",
  3194  			},
  3195  
  3196  			Err: true,
  3197  		},
  3198  
  3199  		"Invalid/unknown field with computed value": {
  3200  			Schema: map[string]*Schema{
  3201  				"availability_zone": &Schema{
  3202  					Type:     TypeString,
  3203  					Optional: true,
  3204  					Computed: true,
  3205  					ForceNew: true,
  3206  				},
  3207  			},
  3208  
  3209  			Config: map[string]interface{}{
  3210  				"foo": "${var.foo}",
  3211  			},
  3212  
  3213  			Vars: map[string]string{
  3214  				"var.foo": config.UnknownVariableValue,
  3215  			},
  3216  
  3217  			Err: true,
  3218  		},
  3219  
  3220  		"Computed field set": {
  3221  			Schema: map[string]*Schema{
  3222  				"availability_zone": &Schema{
  3223  					Type:     TypeString,
  3224  					Computed: true,
  3225  				},
  3226  			},
  3227  
  3228  			Config: map[string]interface{}{
  3229  				"availability_zone": "bar",
  3230  			},
  3231  
  3232  			Err: true,
  3233  		},
  3234  
  3235  		"Not a set": {
  3236  			Schema: map[string]*Schema{
  3237  				"ports": &Schema{
  3238  					Type:     TypeSet,
  3239  					Required: true,
  3240  					Elem:     &Schema{Type: TypeInt},
  3241  					Set: func(a interface{}) int {
  3242  						return a.(int)
  3243  					},
  3244  				},
  3245  			},
  3246  
  3247  			Config: map[string]interface{}{
  3248  				"ports": "foo",
  3249  			},
  3250  
  3251  			Err: true,
  3252  		},
  3253  
  3254  		"Maps": {
  3255  			Schema: map[string]*Schema{
  3256  				"user_data": &Schema{
  3257  					Type:     TypeMap,
  3258  					Optional: true,
  3259  				},
  3260  			},
  3261  
  3262  			Config: map[string]interface{}{
  3263  				"user_data": "foo",
  3264  			},
  3265  
  3266  			Err: true,
  3267  		},
  3268  
  3269  		"Good map: data surrounded by extra slice": {
  3270  			Schema: map[string]*Schema{
  3271  				"user_data": &Schema{
  3272  					Type:     TypeMap,
  3273  					Optional: true,
  3274  				},
  3275  			},
  3276  
  3277  			Config: map[string]interface{}{
  3278  				"user_data": []interface{}{
  3279  					map[string]interface{}{
  3280  						"foo": "bar",
  3281  					},
  3282  				},
  3283  			},
  3284  		},
  3285  
  3286  		"Good map": {
  3287  			Schema: map[string]*Schema{
  3288  				"user_data": &Schema{
  3289  					Type:     TypeMap,
  3290  					Optional: true,
  3291  				},
  3292  			},
  3293  
  3294  			Config: map[string]interface{}{
  3295  				"user_data": map[string]interface{}{
  3296  					"foo": "bar",
  3297  				},
  3298  			},
  3299  		},
  3300  
  3301  		"Bad map: just a slice": {
  3302  			Schema: map[string]*Schema{
  3303  				"user_data": &Schema{
  3304  					Type:     TypeMap,
  3305  					Optional: true,
  3306  				},
  3307  			},
  3308  
  3309  			Config: map[string]interface{}{
  3310  				"user_data": []interface{}{
  3311  					"foo",
  3312  				},
  3313  			},
  3314  
  3315  			Err: true,
  3316  		},
  3317  
  3318  		"Good set: config has slice with single interpolated value": {
  3319  			Schema: map[string]*Schema{
  3320  				"security_groups": &Schema{
  3321  					Type:     TypeSet,
  3322  					Optional: true,
  3323  					Computed: true,
  3324  					ForceNew: true,
  3325  					Elem:     &Schema{Type: TypeString},
  3326  					Set: func(v interface{}) int {
  3327  						return len(v.(string))
  3328  					},
  3329  				},
  3330  			},
  3331  
  3332  			Config: map[string]interface{}{
  3333  				"security_groups": []interface{}{"${var.foo}"},
  3334  			},
  3335  
  3336  			Err: false,
  3337  		},
  3338  
  3339  		"Bad set: config has single interpolated value": {
  3340  			Schema: map[string]*Schema{
  3341  				"security_groups": &Schema{
  3342  					Type:     TypeSet,
  3343  					Optional: true,
  3344  					Computed: true,
  3345  					ForceNew: true,
  3346  					Elem:     &Schema{Type: TypeString},
  3347  				},
  3348  			},
  3349  
  3350  			Config: map[string]interface{}{
  3351  				"security_groups": "${var.foo}",
  3352  			},
  3353  
  3354  			Err: true,
  3355  		},
  3356  
  3357  		"Bad, subresource should not allow unknown elements": {
  3358  			Schema: map[string]*Schema{
  3359  				"ingress": &Schema{
  3360  					Type:     TypeList,
  3361  					Optional: true,
  3362  					Elem: &Resource{
  3363  						Schema: map[string]*Schema{
  3364  							"port": &Schema{
  3365  								Type:     TypeInt,
  3366  								Required: true,
  3367  							},
  3368  						},
  3369  					},
  3370  				},
  3371  			},
  3372  
  3373  			Config: map[string]interface{}{
  3374  				"ingress": []interface{}{
  3375  					map[string]interface{}{
  3376  						"port":  80,
  3377  						"other": "yes",
  3378  					},
  3379  				},
  3380  			},
  3381  
  3382  			Err: true,
  3383  		},
  3384  
  3385  		"Bad, subresource should not allow invalid types": {
  3386  			Schema: map[string]*Schema{
  3387  				"ingress": &Schema{
  3388  					Type:     TypeList,
  3389  					Optional: true,
  3390  					Elem: &Resource{
  3391  						Schema: map[string]*Schema{
  3392  							"port": &Schema{
  3393  								Type:     TypeInt,
  3394  								Required: true,
  3395  							},
  3396  						},
  3397  					},
  3398  				},
  3399  			},
  3400  
  3401  			Config: map[string]interface{}{
  3402  				"ingress": []interface{}{
  3403  					map[string]interface{}{
  3404  						"port": "bad",
  3405  					},
  3406  				},
  3407  			},
  3408  
  3409  			Err: true,
  3410  		},
  3411  
  3412  		"Deprecated attribute usage generates warning, but not error": {
  3413  			Schema: map[string]*Schema{
  3414  				"old_news": &Schema{
  3415  					Type:       TypeString,
  3416  					Optional:   true,
  3417  					Deprecated: "please use 'new_news' instead",
  3418  				},
  3419  			},
  3420  
  3421  			Config: map[string]interface{}{
  3422  				"old_news": "extra extra!",
  3423  			},
  3424  
  3425  			Err: false,
  3426  
  3427  			Warnings: []string{
  3428  				"\"old_news\": [DEPRECATED] please use 'new_news' instead",
  3429  			},
  3430  		},
  3431  
  3432  		"Deprecated generates no warnings if attr not used": {
  3433  			Schema: map[string]*Schema{
  3434  				"old_news": &Schema{
  3435  					Type:       TypeString,
  3436  					Optional:   true,
  3437  					Deprecated: "please use 'new_news' instead",
  3438  				},
  3439  			},
  3440  
  3441  			Err: false,
  3442  
  3443  			Warnings: nil,
  3444  		},
  3445  
  3446  		"Removed attribute usage generates error": {
  3447  			Schema: map[string]*Schema{
  3448  				"long_gone": &Schema{
  3449  					Type:     TypeString,
  3450  					Optional: true,
  3451  					Removed:  "no longer supported by Cloud API",
  3452  				},
  3453  			},
  3454  
  3455  			Config: map[string]interface{}{
  3456  				"long_gone": "still here!",
  3457  			},
  3458  
  3459  			Err: true,
  3460  			Errors: []error{
  3461  				fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"),
  3462  			},
  3463  		},
  3464  
  3465  		"Removed generates no errors if attr not used": {
  3466  			Schema: map[string]*Schema{
  3467  				"long_gone": &Schema{
  3468  					Type:     TypeString,
  3469  					Optional: true,
  3470  					Removed:  "no longer supported by Cloud API",
  3471  				},
  3472  			},
  3473  
  3474  			Err: false,
  3475  		},
  3476  
  3477  		"Conflicting attributes generate error": {
  3478  			Schema: map[string]*Schema{
  3479  				"whitelist": &Schema{
  3480  					Type:     TypeString,
  3481  					Optional: true,
  3482  				},
  3483  				"blacklist": &Schema{
  3484  					Type:          TypeString,
  3485  					Optional:      true,
  3486  					ConflictsWith: []string{"whitelist"},
  3487  				},
  3488  			},
  3489  
  3490  			Config: map[string]interface{}{
  3491  				"whitelist": "white-val",
  3492  				"blacklist": "black-val",
  3493  			},
  3494  
  3495  			Err: true,
  3496  			Errors: []error{
  3497  				fmt.Errorf("\"blacklist\": conflicts with whitelist (\"white-val\")"),
  3498  			},
  3499  		},
  3500  
  3501  		"Required attribute & undefined conflicting optional are good": {
  3502  			Schema: map[string]*Schema{
  3503  				"required_att": &Schema{
  3504  					Type:     TypeString,
  3505  					Required: true,
  3506  				},
  3507  				"optional_att": &Schema{
  3508  					Type:          TypeString,
  3509  					Optional:      true,
  3510  					ConflictsWith: []string{"required_att"},
  3511  				},
  3512  			},
  3513  
  3514  			Config: map[string]interface{}{
  3515  				"required_att": "required-val",
  3516  			},
  3517  
  3518  			Err: false,
  3519  		},
  3520  
  3521  		"Required conflicting attribute & defined optional generate error": {
  3522  			Schema: map[string]*Schema{
  3523  				"required_att": &Schema{
  3524  					Type:     TypeString,
  3525  					Required: true,
  3526  				},
  3527  				"optional_att": &Schema{
  3528  					Type:          TypeString,
  3529  					Optional:      true,
  3530  					ConflictsWith: []string{"required_att"},
  3531  				},
  3532  			},
  3533  
  3534  			Config: map[string]interface{}{
  3535  				"required_att": "required-val",
  3536  				"optional_att": "optional-val",
  3537  			},
  3538  
  3539  			Err: true,
  3540  			Errors: []error{
  3541  				fmt.Errorf(`"optional_att": conflicts with required_att ("required-val")`),
  3542  			},
  3543  		},
  3544  
  3545  		"Good with ValidateFunc": {
  3546  			Schema: map[string]*Schema{
  3547  				"validate_me": &Schema{
  3548  					Type:     TypeString,
  3549  					Required: true,
  3550  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3551  						return
  3552  					},
  3553  				},
  3554  			},
  3555  			Config: map[string]interface{}{
  3556  				"validate_me": "valid",
  3557  			},
  3558  			Err: false,
  3559  		},
  3560  
  3561  		"Bad with ValidateFunc": {
  3562  			Schema: map[string]*Schema{
  3563  				"validate_me": &Schema{
  3564  					Type:     TypeString,
  3565  					Required: true,
  3566  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3567  						es = append(es, fmt.Errorf("something is not right here"))
  3568  						return
  3569  					},
  3570  				},
  3571  			},
  3572  			Config: map[string]interface{}{
  3573  				"validate_me": "invalid",
  3574  			},
  3575  			Err: true,
  3576  			Errors: []error{
  3577  				fmt.Errorf(`something is not right here`),
  3578  			},
  3579  		},
  3580  
  3581  		"ValidateFunc not called when type does not match": {
  3582  			Schema: map[string]*Schema{
  3583  				"number": &Schema{
  3584  					Type:     TypeInt,
  3585  					Required: true,
  3586  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3587  						t.Fatalf("Should not have gotten validate call")
  3588  						return
  3589  					},
  3590  				},
  3591  			},
  3592  			Config: map[string]interface{}{
  3593  				"number": "NaN",
  3594  			},
  3595  			Err: true,
  3596  		},
  3597  
  3598  		"ValidateFunc gets decoded type": {
  3599  			Schema: map[string]*Schema{
  3600  				"maybe": &Schema{
  3601  					Type:     TypeBool,
  3602  					Required: true,
  3603  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3604  						if _, ok := v.(bool); !ok {
  3605  							t.Fatalf("Expected bool, got: %#v", v)
  3606  						}
  3607  						return
  3608  					},
  3609  				},
  3610  			},
  3611  			Config: map[string]interface{}{
  3612  				"maybe": "true",
  3613  			},
  3614  		},
  3615  
  3616  		"ValidateFunc is not called with a computed value": {
  3617  			Schema: map[string]*Schema{
  3618  				"validate_me": &Schema{
  3619  					Type:     TypeString,
  3620  					Required: true,
  3621  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  3622  						es = append(es, fmt.Errorf("something is not right here"))
  3623  						return
  3624  					},
  3625  				},
  3626  			},
  3627  			Config: map[string]interface{}{
  3628  				"validate_me": "${var.foo}",
  3629  			},
  3630  			Vars: map[string]string{
  3631  				"var.foo": config.UnknownVariableValue,
  3632  			},
  3633  
  3634  			Err: false,
  3635  		},
  3636  	}
  3637  
  3638  	for tn, tc := range cases {
  3639  		c, err := config.NewRawConfig(tc.Config)
  3640  		if err != nil {
  3641  			t.Fatalf("err: %s", err)
  3642  		}
  3643  		if tc.Vars != nil {
  3644  			vars := make(map[string]ast.Variable)
  3645  			for k, v := range tc.Vars {
  3646  				vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
  3647  			}
  3648  
  3649  			if err := c.Interpolate(vars); err != nil {
  3650  				t.Fatalf("err: %s", err)
  3651  			}
  3652  		}
  3653  
  3654  		ws, es := schemaMap(tc.Schema).Validate(terraform.NewResourceConfig(c))
  3655  		if (len(es) > 0) != tc.Err {
  3656  			if len(es) == 0 {
  3657  				t.Errorf("%q: no errors", tn)
  3658  			}
  3659  
  3660  			for _, e := range es {
  3661  				t.Errorf("%q: err: %s", tn, e)
  3662  			}
  3663  
  3664  			t.FailNow()
  3665  		}
  3666  
  3667  		if !reflect.DeepEqual(ws, tc.Warnings) {
  3668  			t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws)
  3669  		}
  3670  
  3671  		if tc.Errors != nil {
  3672  			if !reflect.DeepEqual(es, tc.Errors) {
  3673  				t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es)
  3674  			}
  3675  		}
  3676  	}
  3677  }