github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/schema/schema_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"reflect"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
    15  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim"
    16  	"github.com/hashicorp/terraform-plugin-sdk/terraform"
    17  )
    18  
    19  func TestEnvDefaultFunc(t *testing.T) {
    20  	key := "TF_TEST_ENV_DEFAULT_FUNC"
    21  	defer os.Unsetenv(key)
    22  
    23  	f := EnvDefaultFunc(key, "42")
    24  	if err := os.Setenv(key, "foo"); err != nil {
    25  		t.Fatalf("err: %s", err)
    26  	}
    27  
    28  	actual, err := f()
    29  	if err != nil {
    30  		t.Fatalf("err: %s", err)
    31  	}
    32  	if actual != "foo" {
    33  		t.Fatalf("bad: %#v", actual)
    34  	}
    35  
    36  	if err := os.Unsetenv(key); err != nil {
    37  		t.Fatalf("err: %s", err)
    38  	}
    39  
    40  	actual, err = f()
    41  	if err != nil {
    42  		t.Fatalf("err: %s", err)
    43  	}
    44  	if actual != "42" {
    45  		t.Fatalf("bad: %#v", actual)
    46  	}
    47  }
    48  
    49  func TestMultiEnvDefaultFunc(t *testing.T) {
    50  	keys := []string{
    51  		"TF_TEST_MULTI_ENV_DEFAULT_FUNC1",
    52  		"TF_TEST_MULTI_ENV_DEFAULT_FUNC2",
    53  	}
    54  	defer func() {
    55  		for _, k := range keys {
    56  			os.Unsetenv(k)
    57  		}
    58  	}()
    59  
    60  	// Test that the first key is returned first
    61  	f := MultiEnvDefaultFunc(keys, "42")
    62  	if err := os.Setenv(keys[0], "foo"); err != nil {
    63  		t.Fatalf("err: %s", err)
    64  	}
    65  
    66  	actual, err := f()
    67  	if err != nil {
    68  		t.Fatalf("err: %s", err)
    69  	}
    70  	if actual != "foo" {
    71  		t.Fatalf("bad: %#v", actual)
    72  	}
    73  
    74  	if err := os.Unsetenv(keys[0]); err != nil {
    75  		t.Fatalf("err: %s", err)
    76  	}
    77  
    78  	// Test that the second key is returned if the first one is empty
    79  	f = MultiEnvDefaultFunc(keys, "42")
    80  	if err := os.Setenv(keys[1], "foo"); err != nil {
    81  		t.Fatalf("err: %s", err)
    82  	}
    83  
    84  	actual, err = f()
    85  	if err != nil {
    86  		t.Fatalf("err: %s", err)
    87  	}
    88  	if actual != "foo" {
    89  		t.Fatalf("bad: %#v", actual)
    90  	}
    91  
    92  	if err := os.Unsetenv(keys[1]); err != nil {
    93  		t.Fatalf("err: %s", err)
    94  	}
    95  
    96  	// Test that the default value is returned when no keys are set
    97  	actual, err = f()
    98  	if err != nil {
    99  		t.Fatalf("err: %s", err)
   100  	}
   101  	if actual != "42" {
   102  		t.Fatalf("bad: %#v", actual)
   103  	}
   104  }
   105  
   106  func TestValueType_Zero(t *testing.T) {
   107  	cases := []struct {
   108  		Type  ValueType
   109  		Value interface{}
   110  	}{
   111  		{TypeBool, false},
   112  		{TypeInt, 0},
   113  		{TypeFloat, 0.0},
   114  		{TypeString, ""},
   115  		{TypeList, []interface{}{}},
   116  		{TypeMap, map[string]interface{}{}},
   117  		{TypeSet, new(Set)},
   118  	}
   119  
   120  	for i, tc := range cases {
   121  		actual := tc.Type.Zero()
   122  		if !reflect.DeepEqual(actual, tc.Value) {
   123  			t.Fatalf("%d: %#v != %#v", i, actual, tc.Value)
   124  		}
   125  	}
   126  }
   127  
   128  func TestSchemaMap_Diff(t *testing.T) {
   129  	cases := []struct {
   130  		Name          string
   131  		Schema        map[string]*Schema
   132  		State         *terraform.InstanceState
   133  		Config        map[string]interface{}
   134  		CustomizeDiff CustomizeDiffFunc
   135  		Diff          *terraform.InstanceDiff
   136  		Err           bool
   137  	}{
   138  		{
   139  			Schema: map[string]*Schema{
   140  				"availability_zone": {
   141  					Type:     TypeString,
   142  					Optional: true,
   143  					Computed: true,
   144  					ForceNew: true,
   145  				},
   146  			},
   147  
   148  			State: nil,
   149  
   150  			Config: map[string]interface{}{
   151  				"availability_zone": "foo",
   152  			},
   153  
   154  			Diff: &terraform.InstanceDiff{
   155  				Attributes: map[string]*terraform.ResourceAttrDiff{
   156  					"availability_zone": {
   157  						Old:         "",
   158  						New:         "foo",
   159  						RequiresNew: true,
   160  					},
   161  				},
   162  			},
   163  
   164  			Err: false,
   165  		},
   166  
   167  		{
   168  			Schema: map[string]*Schema{
   169  				"availability_zone": {
   170  					Type:     TypeString,
   171  					Optional: true,
   172  					Computed: true,
   173  					ForceNew: true,
   174  				},
   175  			},
   176  
   177  			State: nil,
   178  
   179  			Config: map[string]interface{}{},
   180  
   181  			Diff: &terraform.InstanceDiff{
   182  				Attributes: map[string]*terraform.ResourceAttrDiff{
   183  					"availability_zone": {
   184  						Old:         "",
   185  						NewComputed: true,
   186  						RequiresNew: true,
   187  					},
   188  				},
   189  			},
   190  
   191  			Err: false,
   192  		},
   193  
   194  		{
   195  			Schema: map[string]*Schema{
   196  				"availability_zone": {
   197  					Type:     TypeString,
   198  					Optional: true,
   199  					Computed: true,
   200  					ForceNew: true,
   201  				},
   202  			},
   203  
   204  			State: &terraform.InstanceState{
   205  				ID: "foo",
   206  			},
   207  
   208  			Config: map[string]interface{}{},
   209  
   210  			Diff: nil,
   211  
   212  			Err: false,
   213  		},
   214  
   215  		{
   216  			Name: "Computed, but set in config",
   217  			Schema: map[string]*Schema{
   218  				"availability_zone": {
   219  					Type:     TypeString,
   220  					Optional: true,
   221  					Computed: true,
   222  				},
   223  			},
   224  
   225  			State: &terraform.InstanceState{
   226  				Attributes: map[string]string{
   227  					"availability_zone": "foo",
   228  				},
   229  			},
   230  
   231  			Config: map[string]interface{}{
   232  				"availability_zone": "bar",
   233  			},
   234  
   235  			Diff: &terraform.InstanceDiff{
   236  				Attributes: map[string]*terraform.ResourceAttrDiff{
   237  					"availability_zone": {
   238  						Old: "foo",
   239  						New: "bar",
   240  					},
   241  				},
   242  			},
   243  
   244  			Err: false,
   245  		},
   246  
   247  		{
   248  			Name: "Default",
   249  			Schema: map[string]*Schema{
   250  				"availability_zone": {
   251  					Type:     TypeString,
   252  					Optional: true,
   253  					Default:  "foo",
   254  				},
   255  			},
   256  
   257  			State: nil,
   258  
   259  			Config: nil,
   260  
   261  			Diff: &terraform.InstanceDiff{
   262  				Attributes: map[string]*terraform.ResourceAttrDiff{
   263  					"availability_zone": {
   264  						Old: "",
   265  						New: "foo",
   266  					},
   267  				},
   268  			},
   269  
   270  			Err: false,
   271  		},
   272  
   273  		{
   274  			Name: "DefaultFunc, value",
   275  			Schema: map[string]*Schema{
   276  				"availability_zone": {
   277  					Type:     TypeString,
   278  					Optional: true,
   279  					DefaultFunc: func() (interface{}, error) {
   280  						return "foo", nil
   281  					},
   282  				},
   283  			},
   284  
   285  			State: nil,
   286  
   287  			Config: nil,
   288  
   289  			Diff: &terraform.InstanceDiff{
   290  				Attributes: map[string]*terraform.ResourceAttrDiff{
   291  					"availability_zone": {
   292  						Old: "",
   293  						New: "foo",
   294  					},
   295  				},
   296  			},
   297  
   298  			Err: false,
   299  		},
   300  
   301  		{
   302  			Name: "DefaultFunc, configuration set",
   303  			Schema: map[string]*Schema{
   304  				"availability_zone": {
   305  					Type:     TypeString,
   306  					Optional: true,
   307  					DefaultFunc: func() (interface{}, error) {
   308  						return "foo", nil
   309  					},
   310  				},
   311  			},
   312  
   313  			State: nil,
   314  
   315  			Config: map[string]interface{}{
   316  				"availability_zone": "bar",
   317  			},
   318  
   319  			Diff: &terraform.InstanceDiff{
   320  				Attributes: map[string]*terraform.ResourceAttrDiff{
   321  					"availability_zone": {
   322  						Old: "",
   323  						New: "bar",
   324  					},
   325  				},
   326  			},
   327  
   328  			Err: false,
   329  		},
   330  
   331  		{
   332  			Name: "String with StateFunc",
   333  			Schema: map[string]*Schema{
   334  				"availability_zone": {
   335  					Type:     TypeString,
   336  					Optional: true,
   337  					Computed: true,
   338  					StateFunc: func(a interface{}) string {
   339  						return a.(string) + "!"
   340  					},
   341  				},
   342  			},
   343  
   344  			State: nil,
   345  
   346  			Config: map[string]interface{}{
   347  				"availability_zone": "foo",
   348  			},
   349  
   350  			Diff: &terraform.InstanceDiff{
   351  				Attributes: map[string]*terraform.ResourceAttrDiff{
   352  					"availability_zone": {
   353  						Old:      "",
   354  						New:      "foo!",
   355  						NewExtra: "foo",
   356  					},
   357  				},
   358  			},
   359  
   360  			Err: false,
   361  		},
   362  
   363  		{
   364  			Name: "StateFunc not called with nil value",
   365  			Schema: map[string]*Schema{
   366  				"availability_zone": {
   367  					Type:     TypeString,
   368  					Optional: true,
   369  					Computed: true,
   370  					StateFunc: func(a interface{}) string {
   371  						t.Fatalf("should not get here!")
   372  						return ""
   373  					},
   374  				},
   375  			},
   376  
   377  			State: nil,
   378  
   379  			Config: map[string]interface{}{},
   380  
   381  			Diff: &terraform.InstanceDiff{
   382  				Attributes: map[string]*terraform.ResourceAttrDiff{
   383  					"availability_zone": {
   384  						Old:         "",
   385  						New:         "",
   386  						NewComputed: true,
   387  					},
   388  				},
   389  			},
   390  
   391  			Err: false,
   392  		},
   393  
   394  		{
   395  			Name: "Variable computed",
   396  			Schema: map[string]*Schema{
   397  				"availability_zone": {
   398  					Type:     TypeString,
   399  					Optional: true,
   400  				},
   401  			},
   402  
   403  			State: nil,
   404  
   405  			Config: map[string]interface{}{
   406  				"availability_zone": hcl2shim.UnknownVariableValue,
   407  			},
   408  
   409  			Diff: &terraform.InstanceDiff{
   410  				Attributes: map[string]*terraform.ResourceAttrDiff{
   411  					"availability_zone": {
   412  						Old:         "",
   413  						New:         hcl2shim.UnknownVariableValue,
   414  						NewComputed: true,
   415  					},
   416  				},
   417  			},
   418  
   419  			Err: false,
   420  		},
   421  
   422  		{
   423  			Name: "Int decode",
   424  			Schema: map[string]*Schema{
   425  				"port": {
   426  					Type:     TypeInt,
   427  					Optional: true,
   428  					Computed: true,
   429  					ForceNew: true,
   430  				},
   431  			},
   432  
   433  			State: nil,
   434  
   435  			Config: map[string]interface{}{
   436  				"port": 27,
   437  			},
   438  
   439  			Diff: &terraform.InstanceDiff{
   440  				Attributes: map[string]*terraform.ResourceAttrDiff{
   441  					"port": {
   442  						Old:         "",
   443  						New:         "27",
   444  						RequiresNew: true,
   445  					},
   446  				},
   447  			},
   448  
   449  			Err: false,
   450  		},
   451  
   452  		{
   453  			Name: "bool decode",
   454  			Schema: map[string]*Schema{
   455  				"port": {
   456  					Type:     TypeBool,
   457  					Optional: true,
   458  					Computed: true,
   459  					ForceNew: true,
   460  				},
   461  			},
   462  
   463  			State: nil,
   464  
   465  			Config: map[string]interface{}{
   466  				"port": false,
   467  			},
   468  
   469  			Diff: &terraform.InstanceDiff{
   470  				Attributes: map[string]*terraform.ResourceAttrDiff{
   471  					"port": {
   472  						Old:         "",
   473  						New:         "false",
   474  						RequiresNew: true,
   475  					},
   476  				},
   477  			},
   478  
   479  			Err: false,
   480  		},
   481  
   482  		{
   483  			Name: "Bool",
   484  			Schema: map[string]*Schema{
   485  				"delete": {
   486  					Type:     TypeBool,
   487  					Optional: true,
   488  					Default:  false,
   489  				},
   490  			},
   491  
   492  			State: &terraform.InstanceState{
   493  				Attributes: map[string]string{
   494  					"delete": "false",
   495  				},
   496  			},
   497  
   498  			Config: nil,
   499  
   500  			Diff: nil,
   501  
   502  			Err: false,
   503  		},
   504  
   505  		{
   506  			Name: "List decode",
   507  			Schema: map[string]*Schema{
   508  				"ports": {
   509  					Type:     TypeList,
   510  					Required: true,
   511  					Elem:     &Schema{Type: TypeInt},
   512  				},
   513  			},
   514  
   515  			State: nil,
   516  
   517  			Config: map[string]interface{}{
   518  				"ports": []interface{}{1, 2, 5},
   519  			},
   520  
   521  			Diff: &terraform.InstanceDiff{
   522  				Attributes: map[string]*terraform.ResourceAttrDiff{
   523  					"ports.#": {
   524  						Old: "0",
   525  						New: "3",
   526  					},
   527  					"ports.0": {
   528  						Old: "",
   529  						New: "1",
   530  					},
   531  					"ports.1": {
   532  						Old: "",
   533  						New: "2",
   534  					},
   535  					"ports.2": {
   536  						Old: "",
   537  						New: "5",
   538  					},
   539  				},
   540  			},
   541  
   542  			Err: false,
   543  		},
   544  
   545  		{
   546  			Name: "List decode with promotion",
   547  			Schema: map[string]*Schema{
   548  				"ports": {
   549  					Type:          TypeList,
   550  					Required:      true,
   551  					Elem:          &Schema{Type: TypeInt},
   552  					PromoteSingle: true,
   553  				},
   554  			},
   555  
   556  			State: nil,
   557  
   558  			Config: map[string]interface{}{
   559  				"ports": "5",
   560  			},
   561  
   562  			Diff: &terraform.InstanceDiff{
   563  				Attributes: map[string]*terraform.ResourceAttrDiff{
   564  					"ports.#": {
   565  						Old: "0",
   566  						New: "1",
   567  					},
   568  					"ports.0": {
   569  						Old: "",
   570  						New: "5",
   571  					},
   572  				},
   573  			},
   574  
   575  			Err: false,
   576  		},
   577  
   578  		{
   579  			Name: "List decode with promotion with list",
   580  			Schema: map[string]*Schema{
   581  				"ports": {
   582  					Type:          TypeList,
   583  					Required:      true,
   584  					Elem:          &Schema{Type: TypeInt},
   585  					PromoteSingle: true,
   586  				},
   587  			},
   588  
   589  			State: nil,
   590  
   591  			Config: map[string]interface{}{
   592  				"ports": []interface{}{"5"},
   593  			},
   594  
   595  			Diff: &terraform.InstanceDiff{
   596  				Attributes: map[string]*terraform.ResourceAttrDiff{
   597  					"ports.#": {
   598  						Old: "0",
   599  						New: "1",
   600  					},
   601  					"ports.0": {
   602  						Old: "",
   603  						New: "5",
   604  					},
   605  				},
   606  			},
   607  
   608  			Err: false,
   609  		},
   610  
   611  		{
   612  			Schema: map[string]*Schema{
   613  				"ports": {
   614  					Type:     TypeList,
   615  					Required: true,
   616  					Elem:     &Schema{Type: TypeInt},
   617  				},
   618  			},
   619  
   620  			State: nil,
   621  
   622  			Config: map[string]interface{}{
   623  				"ports": []interface{}{1, 2, 5},
   624  			},
   625  
   626  			Diff: &terraform.InstanceDiff{
   627  				Attributes: map[string]*terraform.ResourceAttrDiff{
   628  					"ports.#": {
   629  						Old: "0",
   630  						New: "3",
   631  					},
   632  					"ports.0": {
   633  						Old: "",
   634  						New: "1",
   635  					},
   636  					"ports.1": {
   637  						Old: "",
   638  						New: "2",
   639  					},
   640  					"ports.2": {
   641  						Old: "",
   642  						New: "5",
   643  					},
   644  				},
   645  			},
   646  
   647  			Err: false,
   648  		},
   649  
   650  		{
   651  			Schema: map[string]*Schema{
   652  				"ports": {
   653  					Type:     TypeList,
   654  					Required: true,
   655  					Elem:     &Schema{Type: TypeInt},
   656  				},
   657  			},
   658  
   659  			State: nil,
   660  
   661  			Config: map[string]interface{}{
   662  				"ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5},
   663  			},
   664  
   665  			Diff: &terraform.InstanceDiff{
   666  				Attributes: map[string]*terraform.ResourceAttrDiff{
   667  					"ports.#": {
   668  						Old:         "0",
   669  						New:         "",
   670  						NewComputed: true,
   671  					},
   672  				},
   673  			},
   674  
   675  			Err: false,
   676  		},
   677  
   678  		{
   679  			Schema: map[string]*Schema{
   680  				"ports": {
   681  					Type:     TypeList,
   682  					Required: true,
   683  					Elem:     &Schema{Type: TypeInt},
   684  				},
   685  			},
   686  
   687  			State: &terraform.InstanceState{
   688  				Attributes: map[string]string{
   689  					"ports.#": "3",
   690  					"ports.0": "1",
   691  					"ports.1": "2",
   692  					"ports.2": "5",
   693  				},
   694  			},
   695  
   696  			Config: map[string]interface{}{
   697  				"ports": []interface{}{1, 2, 5},
   698  			},
   699  
   700  			Diff: nil,
   701  
   702  			Err: false,
   703  		},
   704  
   705  		{
   706  			Name: "",
   707  			Schema: map[string]*Schema{
   708  				"ports": {
   709  					Type:     TypeList,
   710  					Required: true,
   711  					Elem:     &Schema{Type: TypeInt},
   712  				},
   713  			},
   714  
   715  			State: &terraform.InstanceState{
   716  				Attributes: map[string]string{
   717  					"ports.#": "2",
   718  					"ports.0": "1",
   719  					"ports.1": "2",
   720  				},
   721  			},
   722  
   723  			Config: map[string]interface{}{
   724  				"ports": []interface{}{1, 2, 5},
   725  			},
   726  
   727  			Diff: &terraform.InstanceDiff{
   728  				Attributes: map[string]*terraform.ResourceAttrDiff{
   729  					"ports.#": {
   730  						Old: "2",
   731  						New: "3",
   732  					},
   733  					"ports.2": {
   734  						Old: "",
   735  						New: "5",
   736  					},
   737  				},
   738  			},
   739  
   740  			Err: false,
   741  		},
   742  
   743  		{
   744  			Name: "",
   745  			Schema: map[string]*Schema{
   746  				"ports": {
   747  					Type:     TypeList,
   748  					Required: true,
   749  					Elem:     &Schema{Type: TypeInt},
   750  					ForceNew: true,
   751  				},
   752  			},
   753  
   754  			State: nil,
   755  
   756  			Config: map[string]interface{}{
   757  				"ports": []interface{}{1, 2, 5},
   758  			},
   759  
   760  			Diff: &terraform.InstanceDiff{
   761  				Attributes: map[string]*terraform.ResourceAttrDiff{
   762  					"ports.#": {
   763  						Old:         "0",
   764  						New:         "3",
   765  						RequiresNew: true,
   766  					},
   767  					"ports.0": {
   768  						Old:         "",
   769  						New:         "1",
   770  						RequiresNew: true,
   771  					},
   772  					"ports.1": {
   773  						Old:         "",
   774  						New:         "2",
   775  						RequiresNew: true,
   776  					},
   777  					"ports.2": {
   778  						Old:         "",
   779  						New:         "5",
   780  						RequiresNew: true,
   781  					},
   782  				},
   783  			},
   784  
   785  			Err: false,
   786  		},
   787  
   788  		{
   789  			Name: "",
   790  			Schema: map[string]*Schema{
   791  				"ports": {
   792  					Type:     TypeList,
   793  					Optional: true,
   794  					Computed: true,
   795  					Elem:     &Schema{Type: TypeInt},
   796  				},
   797  			},
   798  
   799  			State: nil,
   800  
   801  			Config: map[string]interface{}{},
   802  
   803  			Diff: &terraform.InstanceDiff{
   804  				Attributes: map[string]*terraform.ResourceAttrDiff{
   805  					"ports.#": {
   806  						Old:         "",
   807  						NewComputed: true,
   808  					},
   809  				},
   810  			},
   811  
   812  			Err: false,
   813  		},
   814  
   815  		{
   816  			Name: "List with computed set",
   817  			Schema: map[string]*Schema{
   818  				"config": {
   819  					Type:     TypeList,
   820  					Optional: true,
   821  					ForceNew: true,
   822  					MinItems: 1,
   823  					Elem: &Resource{
   824  						Schema: map[string]*Schema{
   825  							"name": {
   826  								Type:     TypeString,
   827  								Required: true,
   828  							},
   829  
   830  							"rules": {
   831  								Type:     TypeSet,
   832  								Computed: true,
   833  								Elem:     &Schema{Type: TypeString},
   834  								Set:      HashString,
   835  							},
   836  						},
   837  					},
   838  				},
   839  			},
   840  
   841  			State: nil,
   842  
   843  			Config: map[string]interface{}{
   844  				"config": []interface{}{
   845  					map[string]interface{}{
   846  						"name": "hello",
   847  					},
   848  				},
   849  			},
   850  
   851  			Diff: &terraform.InstanceDiff{
   852  				Attributes: map[string]*terraform.ResourceAttrDiff{
   853  					"config.#": {
   854  						Old:         "0",
   855  						New:         "1",
   856  						RequiresNew: true,
   857  					},
   858  
   859  					"config.0.name": {
   860  						Old: "",
   861  						New: "hello",
   862  					},
   863  
   864  					"config.0.rules.#": {
   865  						Old:         "",
   866  						NewComputed: true,
   867  					},
   868  				},
   869  			},
   870  
   871  			Err: false,
   872  		},
   873  
   874  		{
   875  			Name: "Set",
   876  			Schema: map[string]*Schema{
   877  				"ports": {
   878  					Type:     TypeSet,
   879  					Required: true,
   880  					Elem:     &Schema{Type: TypeInt},
   881  					Set: func(a interface{}) int {
   882  						return a.(int)
   883  					},
   884  				},
   885  			},
   886  
   887  			State: nil,
   888  
   889  			Config: map[string]interface{}{
   890  				"ports": []interface{}{5, 2, 1},
   891  			},
   892  
   893  			Diff: &terraform.InstanceDiff{
   894  				Attributes: map[string]*terraform.ResourceAttrDiff{
   895  					"ports.#": {
   896  						Old: "0",
   897  						New: "3",
   898  					},
   899  					"ports.1": {
   900  						Old: "",
   901  						New: "1",
   902  					},
   903  					"ports.2": {
   904  						Old: "",
   905  						New: "2",
   906  					},
   907  					"ports.5": {
   908  						Old: "",
   909  						New: "5",
   910  					},
   911  				},
   912  			},
   913  
   914  			Err: false,
   915  		},
   916  
   917  		{
   918  			Name: "Set",
   919  			Schema: map[string]*Schema{
   920  				"ports": {
   921  					Type:     TypeSet,
   922  					Computed: true,
   923  					Required: true,
   924  					Elem:     &Schema{Type: TypeInt},
   925  					Set: func(a interface{}) int {
   926  						return a.(int)
   927  					},
   928  				},
   929  			},
   930  
   931  			State: &terraform.InstanceState{
   932  				Attributes: map[string]string{
   933  					"ports.#": "0",
   934  				},
   935  			},
   936  
   937  			Config: nil,
   938  
   939  			Diff: nil,
   940  
   941  			Err: false,
   942  		},
   943  
   944  		{
   945  			Name: "Set",
   946  			Schema: map[string]*Schema{
   947  				"ports": {
   948  					Type:     TypeSet,
   949  					Optional: true,
   950  					Computed: true,
   951  					Elem:     &Schema{Type: TypeInt},
   952  					Set: func(a interface{}) int {
   953  						return a.(int)
   954  					},
   955  				},
   956  			},
   957  
   958  			State: nil,
   959  
   960  			Config: nil,
   961  
   962  			Diff: &terraform.InstanceDiff{
   963  				Attributes: map[string]*terraform.ResourceAttrDiff{
   964  					"ports.#": {
   965  						Old:         "",
   966  						NewComputed: true,
   967  					},
   968  				},
   969  			},
   970  
   971  			Err: false,
   972  		},
   973  
   974  		{
   975  			Name: "Set",
   976  			Schema: map[string]*Schema{
   977  				"ports": {
   978  					Type:     TypeSet,
   979  					Required: true,
   980  					Elem:     &Schema{Type: TypeInt},
   981  					Set: func(a interface{}) int {
   982  						return a.(int)
   983  					},
   984  				},
   985  			},
   986  
   987  			State: nil,
   988  
   989  			Config: map[string]interface{}{
   990  				"ports": []interface{}{"2", "5", 1},
   991  			},
   992  
   993  			Diff: &terraform.InstanceDiff{
   994  				Attributes: map[string]*terraform.ResourceAttrDiff{
   995  					"ports.#": {
   996  						Old: "0",
   997  						New: "3",
   998  					},
   999  					"ports.1": {
  1000  						Old: "",
  1001  						New: "1",
  1002  					},
  1003  					"ports.2": {
  1004  						Old: "",
  1005  						New: "2",
  1006  					},
  1007  					"ports.5": {
  1008  						Old: "",
  1009  						New: "5",
  1010  					},
  1011  				},
  1012  			},
  1013  
  1014  			Err: false,
  1015  		},
  1016  
  1017  		{
  1018  			Name: "Set",
  1019  			Schema: map[string]*Schema{
  1020  				"ports": {
  1021  					Type:     TypeSet,
  1022  					Required: true,
  1023  					Elem:     &Schema{Type: TypeInt},
  1024  					Set: func(a interface{}) int {
  1025  						return a.(int)
  1026  					},
  1027  				},
  1028  			},
  1029  
  1030  			State: nil,
  1031  
  1032  			Config: map[string]interface{}{
  1033  				"ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"},
  1034  			},
  1035  
  1036  			Diff: &terraform.InstanceDiff{
  1037  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1038  					"ports.#": {
  1039  						Old:         "",
  1040  						New:         "",
  1041  						NewComputed: true,
  1042  					},
  1043  				},
  1044  			},
  1045  
  1046  			Err: false,
  1047  		},
  1048  
  1049  		{
  1050  			Name: "Set",
  1051  			Schema: map[string]*Schema{
  1052  				"ports": {
  1053  					Type:     TypeSet,
  1054  					Required: true,
  1055  					Elem:     &Schema{Type: TypeInt},
  1056  					Set: func(a interface{}) int {
  1057  						return a.(int)
  1058  					},
  1059  				},
  1060  			},
  1061  
  1062  			State: &terraform.InstanceState{
  1063  				Attributes: map[string]string{
  1064  					"ports.#": "2",
  1065  					"ports.1": "1",
  1066  					"ports.2": "2",
  1067  				},
  1068  			},
  1069  
  1070  			Config: map[string]interface{}{
  1071  				"ports": []interface{}{5, 2, 1},
  1072  			},
  1073  
  1074  			Diff: &terraform.InstanceDiff{
  1075  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1076  					"ports.#": {
  1077  						Old: "2",
  1078  						New: "3",
  1079  					},
  1080  					"ports.1": {
  1081  						Old: "1",
  1082  						New: "1",
  1083  					},
  1084  					"ports.2": {
  1085  						Old: "2",
  1086  						New: "2",
  1087  					},
  1088  					"ports.5": {
  1089  						Old: "",
  1090  						New: "5",
  1091  					},
  1092  				},
  1093  			},
  1094  
  1095  			Err: false,
  1096  		},
  1097  
  1098  		{
  1099  			Name: "Set",
  1100  			Schema: map[string]*Schema{
  1101  				"ports": {
  1102  					Type:     TypeSet,
  1103  					Required: true,
  1104  					Elem:     &Schema{Type: TypeInt},
  1105  					Set: func(a interface{}) int {
  1106  						return a.(int)
  1107  					},
  1108  				},
  1109  			},
  1110  
  1111  			State: &terraform.InstanceState{
  1112  				Attributes: map[string]string{
  1113  					"ports.#": "2",
  1114  					"ports.1": "1",
  1115  					"ports.2": "2",
  1116  				},
  1117  			},
  1118  
  1119  			Config: map[string]interface{}{},
  1120  
  1121  			Diff: &terraform.InstanceDiff{
  1122  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1123  					"ports.#": {
  1124  						Old: "2",
  1125  						New: "0",
  1126  					},
  1127  					"ports.1": {
  1128  						Old:        "1",
  1129  						New:        "0",
  1130  						NewRemoved: true,
  1131  					},
  1132  					"ports.2": {
  1133  						Old:        "2",
  1134  						New:        "0",
  1135  						NewRemoved: true,
  1136  					},
  1137  				},
  1138  			},
  1139  
  1140  			Err: false,
  1141  		},
  1142  
  1143  		{
  1144  			Name: "Set",
  1145  			Schema: map[string]*Schema{
  1146  				"ports": {
  1147  					Type:     TypeSet,
  1148  					Optional: true,
  1149  					Computed: true,
  1150  					Elem:     &Schema{Type: TypeInt},
  1151  					Set: func(a interface{}) int {
  1152  						return a.(int)
  1153  					},
  1154  				},
  1155  			},
  1156  
  1157  			State: &terraform.InstanceState{
  1158  				Attributes: map[string]string{
  1159  					"availability_zone": "bar",
  1160  					"ports.#":           "1",
  1161  					"ports.80":          "80",
  1162  				},
  1163  			},
  1164  
  1165  			Config: map[string]interface{}{},
  1166  
  1167  			Diff: nil,
  1168  
  1169  			Err: false,
  1170  		},
  1171  
  1172  		{
  1173  			Name: "Set",
  1174  			Schema: map[string]*Schema{
  1175  				"ingress": {
  1176  					Type:     TypeSet,
  1177  					Required: true,
  1178  					Elem: &Resource{
  1179  						Schema: map[string]*Schema{
  1180  							"ports": {
  1181  								Type:     TypeList,
  1182  								Optional: true,
  1183  								Elem:     &Schema{Type: TypeInt},
  1184  							},
  1185  						},
  1186  					},
  1187  					Set: func(v interface{}) int {
  1188  						m := v.(map[string]interface{})
  1189  						ps := m["ports"].([]interface{})
  1190  						result := 0
  1191  						for _, p := range ps {
  1192  							result += p.(int)
  1193  						}
  1194  						return result
  1195  					},
  1196  				},
  1197  			},
  1198  
  1199  			State: &terraform.InstanceState{
  1200  				Attributes: map[string]string{
  1201  					"ingress.#":           "2",
  1202  					"ingress.80.ports.#":  "1",
  1203  					"ingress.80.ports.0":  "80",
  1204  					"ingress.443.ports.#": "1",
  1205  					"ingress.443.ports.0": "443",
  1206  				},
  1207  			},
  1208  
  1209  			Config: map[string]interface{}{
  1210  				"ingress": []interface{}{
  1211  					map[string]interface{}{
  1212  						"ports": []interface{}{443},
  1213  					},
  1214  					map[string]interface{}{
  1215  						"ports": []interface{}{80},
  1216  					},
  1217  				},
  1218  			},
  1219  
  1220  			Diff: nil,
  1221  
  1222  			Err: false,
  1223  		},
  1224  
  1225  		{
  1226  			Name: "List of structure decode",
  1227  			Schema: map[string]*Schema{
  1228  				"ingress": {
  1229  					Type:     TypeList,
  1230  					Required: true,
  1231  					Elem: &Resource{
  1232  						Schema: map[string]*Schema{
  1233  							"from": {
  1234  								Type:     TypeInt,
  1235  								Required: true,
  1236  							},
  1237  						},
  1238  					},
  1239  				},
  1240  			},
  1241  
  1242  			State: nil,
  1243  
  1244  			Config: map[string]interface{}{
  1245  				"ingress": []interface{}{
  1246  					map[string]interface{}{
  1247  						"from": 8080,
  1248  					},
  1249  				},
  1250  			},
  1251  
  1252  			Diff: &terraform.InstanceDiff{
  1253  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1254  					"ingress.#": {
  1255  						Old: "0",
  1256  						New: "1",
  1257  					},
  1258  					"ingress.0.from": {
  1259  						Old: "",
  1260  						New: "8080",
  1261  					},
  1262  				},
  1263  			},
  1264  
  1265  			Err: false,
  1266  		},
  1267  
  1268  		{
  1269  			Name: "ComputedWhen",
  1270  			Schema: map[string]*Schema{
  1271  				"availability_zone": {
  1272  					Type:         TypeString,
  1273  					Computed:     true,
  1274  					ComputedWhen: []string{"port"},
  1275  				},
  1276  
  1277  				"port": {
  1278  					Type:     TypeInt,
  1279  					Optional: true,
  1280  				},
  1281  			},
  1282  
  1283  			State: &terraform.InstanceState{
  1284  				Attributes: map[string]string{
  1285  					"availability_zone": "foo",
  1286  					"port":              "80",
  1287  				},
  1288  			},
  1289  
  1290  			Config: map[string]interface{}{
  1291  				"port": 80,
  1292  			},
  1293  
  1294  			Diff: nil,
  1295  
  1296  			Err: false,
  1297  		},
  1298  
  1299  		{
  1300  			Name: "",
  1301  			Schema: map[string]*Schema{
  1302  				"availability_zone": {
  1303  					Type:         TypeString,
  1304  					Computed:     true,
  1305  					ComputedWhen: []string{"port"},
  1306  				},
  1307  
  1308  				"port": {
  1309  					Type:     TypeInt,
  1310  					Optional: true,
  1311  				},
  1312  			},
  1313  
  1314  			State: &terraform.InstanceState{
  1315  				Attributes: map[string]string{
  1316  					"port": "80",
  1317  				},
  1318  			},
  1319  
  1320  			Config: map[string]interface{}{
  1321  				"port": 80,
  1322  			},
  1323  
  1324  			Diff: &terraform.InstanceDiff{
  1325  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1326  					"availability_zone": {
  1327  						NewComputed: true,
  1328  					},
  1329  				},
  1330  			},
  1331  
  1332  			Err: false,
  1333  		},
  1334  
  1335  		/* TODO
  1336  		{
  1337  			Schema: map[string]*Schema{
  1338  				"availability_zone": &Schema{
  1339  					Type:         TypeString,
  1340  					Computed:     true,
  1341  					ComputedWhen: []string{"port"},
  1342  				},
  1343  
  1344  				"port": &Schema{
  1345  					Type:     TypeInt,
  1346  					Optional: true,
  1347  				},
  1348  			},
  1349  
  1350  			State: &terraform.InstanceState{
  1351  				Attributes: map[string]string{
  1352  					"availability_zone": "foo",
  1353  					"port":              "80",
  1354  				},
  1355  			},
  1356  
  1357  			Config: map[string]interface{}{
  1358  				"port": 8080,
  1359  			},
  1360  
  1361  			Diff: &terraform.ResourceDiff{
  1362  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1363  					"availability_zone": &terraform.ResourceAttrDiff{
  1364  						Old:         "foo",
  1365  						NewComputed: true,
  1366  					},
  1367  					"port": &terraform.ResourceAttrDiff{
  1368  						Old: "80",
  1369  						New: "8080",
  1370  					},
  1371  				},
  1372  			},
  1373  
  1374  			Err: false,
  1375  		},
  1376  		*/
  1377  
  1378  		{
  1379  			Name: "Maps",
  1380  			Schema: map[string]*Schema{
  1381  				"config_vars": {
  1382  					Type: TypeMap,
  1383  				},
  1384  			},
  1385  
  1386  			State: nil,
  1387  
  1388  			Config: map[string]interface{}{
  1389  				"config_vars": []interface{}{
  1390  					map[string]interface{}{
  1391  						"bar": "baz",
  1392  					},
  1393  				},
  1394  			},
  1395  
  1396  			Diff: &terraform.InstanceDiff{
  1397  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1398  					"config_vars.%": {
  1399  						Old: "0",
  1400  						New: "1",
  1401  					},
  1402  
  1403  					"config_vars.bar": {
  1404  						Old: "",
  1405  						New: "baz",
  1406  					},
  1407  				},
  1408  			},
  1409  
  1410  			Err: false,
  1411  		},
  1412  
  1413  		{
  1414  			Name: "Maps",
  1415  			Schema: map[string]*Schema{
  1416  				"config_vars": {
  1417  					Type: TypeMap,
  1418  				},
  1419  			},
  1420  
  1421  			State: &terraform.InstanceState{
  1422  				Attributes: map[string]string{
  1423  					"config_vars.foo": "bar",
  1424  				},
  1425  			},
  1426  
  1427  			Config: map[string]interface{}{
  1428  				"config_vars": []interface{}{
  1429  					map[string]interface{}{
  1430  						"bar": "baz",
  1431  					},
  1432  				},
  1433  			},
  1434  
  1435  			Diff: &terraform.InstanceDiff{
  1436  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1437  					"config_vars.foo": {
  1438  						Old:        "bar",
  1439  						NewRemoved: true,
  1440  					},
  1441  					"config_vars.bar": {
  1442  						Old: "",
  1443  						New: "baz",
  1444  					},
  1445  				},
  1446  			},
  1447  
  1448  			Err: false,
  1449  		},
  1450  
  1451  		{
  1452  			Name: "Maps",
  1453  			Schema: map[string]*Schema{
  1454  				"vars": {
  1455  					Type:     TypeMap,
  1456  					Optional: true,
  1457  					Computed: true,
  1458  				},
  1459  			},
  1460  
  1461  			State: &terraform.InstanceState{
  1462  				Attributes: map[string]string{
  1463  					"vars.foo": "bar",
  1464  				},
  1465  			},
  1466  
  1467  			Config: map[string]interface{}{
  1468  				"vars": []interface{}{
  1469  					map[string]interface{}{
  1470  						"bar": "baz",
  1471  					},
  1472  				},
  1473  			},
  1474  
  1475  			Diff: &terraform.InstanceDiff{
  1476  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1477  					"vars.foo": {
  1478  						Old:        "bar",
  1479  						New:        "",
  1480  						NewRemoved: true,
  1481  					},
  1482  					"vars.bar": {
  1483  						Old: "",
  1484  						New: "baz",
  1485  					},
  1486  				},
  1487  			},
  1488  
  1489  			Err: false,
  1490  		},
  1491  
  1492  		{
  1493  			Name: "Maps",
  1494  			Schema: map[string]*Schema{
  1495  				"vars": {
  1496  					Type:     TypeMap,
  1497  					Computed: true,
  1498  				},
  1499  			},
  1500  
  1501  			State: &terraform.InstanceState{
  1502  				Attributes: map[string]string{
  1503  					"vars.foo": "bar",
  1504  				},
  1505  			},
  1506  
  1507  			Config: nil,
  1508  
  1509  			Diff: nil,
  1510  
  1511  			Err: false,
  1512  		},
  1513  
  1514  		{
  1515  			Name: "Maps",
  1516  			Schema: map[string]*Schema{
  1517  				"config_vars": {
  1518  					Type: TypeList,
  1519  					Elem: &Schema{Type: TypeMap},
  1520  				},
  1521  			},
  1522  
  1523  			State: &terraform.InstanceState{
  1524  				Attributes: map[string]string{
  1525  					"config_vars.#":     "1",
  1526  					"config_vars.0.foo": "bar",
  1527  				},
  1528  			},
  1529  
  1530  			Config: map[string]interface{}{
  1531  				"config_vars": []interface{}{
  1532  					map[string]interface{}{
  1533  						"bar": "baz",
  1534  					},
  1535  				},
  1536  			},
  1537  
  1538  			Diff: &terraform.InstanceDiff{
  1539  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1540  					"config_vars.0.foo": {
  1541  						Old:        "bar",
  1542  						NewRemoved: true,
  1543  					},
  1544  					"config_vars.0.bar": {
  1545  						Old: "",
  1546  						New: "baz",
  1547  					},
  1548  				},
  1549  			},
  1550  
  1551  			Err: false,
  1552  		},
  1553  
  1554  		{
  1555  			Name: "Maps",
  1556  			Schema: map[string]*Schema{
  1557  				"config_vars": {
  1558  					Type: TypeList,
  1559  					Elem: &Schema{Type: TypeMap},
  1560  				},
  1561  			},
  1562  
  1563  			State: &terraform.InstanceState{
  1564  				Attributes: map[string]string{
  1565  					"config_vars.#":     "1",
  1566  					"config_vars.0.foo": "bar",
  1567  					"config_vars.0.bar": "baz",
  1568  				},
  1569  			},
  1570  
  1571  			Config: map[string]interface{}{},
  1572  
  1573  			Diff: &terraform.InstanceDiff{
  1574  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1575  					"config_vars.#": {
  1576  						Old: "1",
  1577  						New: "0",
  1578  					},
  1579  					"config_vars.0.%": {
  1580  						Old: "2",
  1581  						New: "0",
  1582  					},
  1583  					"config_vars.0.foo": {
  1584  						Old:        "bar",
  1585  						NewRemoved: true,
  1586  					},
  1587  					"config_vars.0.bar": {
  1588  						Old:        "baz",
  1589  						NewRemoved: true,
  1590  					},
  1591  				},
  1592  			},
  1593  
  1594  			Err: false,
  1595  		},
  1596  
  1597  		{
  1598  			Name: "ForceNews",
  1599  			Schema: map[string]*Schema{
  1600  				"availability_zone": {
  1601  					Type:     TypeString,
  1602  					Optional: true,
  1603  					ForceNew: true,
  1604  				},
  1605  
  1606  				"address": {
  1607  					Type:     TypeString,
  1608  					Optional: true,
  1609  					Computed: true,
  1610  				},
  1611  			},
  1612  
  1613  			State: &terraform.InstanceState{
  1614  				Attributes: map[string]string{
  1615  					"availability_zone": "bar",
  1616  					"address":           "foo",
  1617  				},
  1618  			},
  1619  
  1620  			Config: map[string]interface{}{
  1621  				"availability_zone": "foo",
  1622  			},
  1623  
  1624  			Diff: &terraform.InstanceDiff{
  1625  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1626  					"availability_zone": {
  1627  						Old:         "bar",
  1628  						New:         "foo",
  1629  						RequiresNew: true,
  1630  					},
  1631  
  1632  					"address": {
  1633  						Old:         "foo",
  1634  						New:         "",
  1635  						NewComputed: true,
  1636  					},
  1637  				},
  1638  			},
  1639  
  1640  			Err: false,
  1641  		},
  1642  
  1643  		{
  1644  			Name: "Set",
  1645  			Schema: map[string]*Schema{
  1646  				"availability_zone": {
  1647  					Type:     TypeString,
  1648  					Optional: true,
  1649  					ForceNew: true,
  1650  				},
  1651  
  1652  				"ports": {
  1653  					Type:     TypeSet,
  1654  					Optional: true,
  1655  					Computed: true,
  1656  					Elem:     &Schema{Type: TypeInt},
  1657  					Set: func(a interface{}) int {
  1658  						return a.(int)
  1659  					},
  1660  				},
  1661  			},
  1662  
  1663  			State: &terraform.InstanceState{
  1664  				Attributes: map[string]string{
  1665  					"availability_zone": "bar",
  1666  					"ports.#":           "1",
  1667  					"ports.80":          "80",
  1668  				},
  1669  			},
  1670  
  1671  			Config: map[string]interface{}{
  1672  				"availability_zone": "foo",
  1673  			},
  1674  
  1675  			Diff: &terraform.InstanceDiff{
  1676  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1677  					"availability_zone": {
  1678  						Old:         "bar",
  1679  						New:         "foo",
  1680  						RequiresNew: true,
  1681  					},
  1682  
  1683  					"ports.#": {
  1684  						Old:         "1",
  1685  						New:         "",
  1686  						NewComputed: true,
  1687  					},
  1688  				},
  1689  			},
  1690  
  1691  			Err: false,
  1692  		},
  1693  
  1694  		{
  1695  			Name: "Set",
  1696  			Schema: map[string]*Schema{
  1697  				"instances": {
  1698  					Type:     TypeSet,
  1699  					Elem:     &Schema{Type: TypeString},
  1700  					Optional: true,
  1701  					Computed: true,
  1702  					Set: func(v interface{}) int {
  1703  						return len(v.(string))
  1704  					},
  1705  				},
  1706  			},
  1707  
  1708  			State: &terraform.InstanceState{
  1709  				Attributes: map[string]string{
  1710  					"instances.#": "0",
  1711  				},
  1712  			},
  1713  
  1714  			Config: map[string]interface{}{
  1715  				"instances": []interface{}{hcl2shim.UnknownVariableValue},
  1716  			},
  1717  
  1718  			Diff: &terraform.InstanceDiff{
  1719  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1720  					"instances.#": {
  1721  						NewComputed: true,
  1722  					},
  1723  				},
  1724  			},
  1725  
  1726  			Err: false,
  1727  		},
  1728  
  1729  		{
  1730  			Name: "Set",
  1731  			Schema: map[string]*Schema{
  1732  				"route": {
  1733  					Type:     TypeSet,
  1734  					Optional: true,
  1735  					Elem: &Resource{
  1736  						Schema: map[string]*Schema{
  1737  							"index": {
  1738  								Type:     TypeInt,
  1739  								Required: true,
  1740  							},
  1741  
  1742  							"gateway": {
  1743  								Type:     TypeString,
  1744  								Optional: true,
  1745  							},
  1746  						},
  1747  					},
  1748  					Set: func(v interface{}) int {
  1749  						m := v.(map[string]interface{})
  1750  						return m["index"].(int)
  1751  					},
  1752  				},
  1753  			},
  1754  
  1755  			State: nil,
  1756  
  1757  			Config: map[string]interface{}{
  1758  				"route": []interface{}{
  1759  					map[string]interface{}{
  1760  						"index":   "1",
  1761  						"gateway": hcl2shim.UnknownVariableValue,
  1762  					},
  1763  				},
  1764  			},
  1765  
  1766  			Diff: &terraform.InstanceDiff{
  1767  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1768  					"route.#": {
  1769  						Old: "0",
  1770  						New: "1",
  1771  					},
  1772  					"route.~1.index": {
  1773  						Old: "",
  1774  						New: "1",
  1775  					},
  1776  					"route.~1.gateway": {
  1777  						Old:         "",
  1778  						New:         hcl2shim.UnknownVariableValue,
  1779  						NewComputed: true,
  1780  					},
  1781  				},
  1782  			},
  1783  
  1784  			Err: false,
  1785  		},
  1786  
  1787  		{
  1788  			Name: "Set",
  1789  			Schema: map[string]*Schema{
  1790  				"route": {
  1791  					Type:     TypeSet,
  1792  					Optional: true,
  1793  					Elem: &Resource{
  1794  						Schema: map[string]*Schema{
  1795  							"index": {
  1796  								Type:     TypeInt,
  1797  								Required: true,
  1798  							},
  1799  
  1800  							"gateway": {
  1801  								Type:     TypeSet,
  1802  								Optional: true,
  1803  								Elem:     &Schema{Type: TypeInt},
  1804  								Set: func(a interface{}) int {
  1805  									return a.(int)
  1806  								},
  1807  							},
  1808  						},
  1809  					},
  1810  					Set: func(v interface{}) int {
  1811  						m := v.(map[string]interface{})
  1812  						return m["index"].(int)
  1813  					},
  1814  				},
  1815  			},
  1816  
  1817  			State: nil,
  1818  
  1819  			Config: map[string]interface{}{
  1820  				"route": []interface{}{
  1821  					map[string]interface{}{
  1822  						"index": "1",
  1823  						"gateway": []interface{}{
  1824  							hcl2shim.UnknownVariableValue,
  1825  						},
  1826  					},
  1827  				},
  1828  			},
  1829  
  1830  			Diff: &terraform.InstanceDiff{
  1831  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1832  					"route.#": {
  1833  						Old: "0",
  1834  						New: "1",
  1835  					},
  1836  					"route.~1.index": {
  1837  						Old: "",
  1838  						New: "1",
  1839  					},
  1840  					"route.~1.gateway.#": {
  1841  						NewComputed: true,
  1842  					},
  1843  				},
  1844  			},
  1845  
  1846  			Err: false,
  1847  		},
  1848  
  1849  		{
  1850  			Name: "Computed maps",
  1851  			Schema: map[string]*Schema{
  1852  				"vars": {
  1853  					Type:     TypeMap,
  1854  					Computed: true,
  1855  				},
  1856  			},
  1857  
  1858  			State: nil,
  1859  
  1860  			Config: nil,
  1861  
  1862  			Diff: &terraform.InstanceDiff{
  1863  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1864  					"vars.%": {
  1865  						Old:         "",
  1866  						NewComputed: true,
  1867  					},
  1868  				},
  1869  			},
  1870  
  1871  			Err: false,
  1872  		},
  1873  
  1874  		{
  1875  			Name: "Computed maps",
  1876  			Schema: map[string]*Schema{
  1877  				"vars": {
  1878  					Type:     TypeMap,
  1879  					Computed: true,
  1880  				},
  1881  			},
  1882  
  1883  			State: &terraform.InstanceState{
  1884  				Attributes: map[string]string{
  1885  					"vars.%": "0",
  1886  				},
  1887  			},
  1888  
  1889  			Config: map[string]interface{}{
  1890  				"vars": map[string]interface{}{
  1891  					"bar": hcl2shim.UnknownVariableValue,
  1892  				},
  1893  			},
  1894  
  1895  			Diff: &terraform.InstanceDiff{
  1896  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1897  					"vars.%": {
  1898  						Old:         "",
  1899  						NewComputed: true,
  1900  					},
  1901  				},
  1902  			},
  1903  
  1904  			Err: false,
  1905  		},
  1906  
  1907  		{
  1908  			Name:   " - Empty",
  1909  			Schema: map[string]*Schema{},
  1910  
  1911  			State: &terraform.InstanceState{},
  1912  
  1913  			Config: map[string]interface{}{},
  1914  
  1915  			Diff: nil,
  1916  
  1917  			Err: false,
  1918  		},
  1919  
  1920  		{
  1921  			Name: "Float",
  1922  			Schema: map[string]*Schema{
  1923  				"some_threshold": {
  1924  					Type: TypeFloat,
  1925  				},
  1926  			},
  1927  
  1928  			State: &terraform.InstanceState{
  1929  				Attributes: map[string]string{
  1930  					"some_threshold": "567.8",
  1931  				},
  1932  			},
  1933  
  1934  			Config: map[string]interface{}{
  1935  				"some_threshold": 12.34,
  1936  			},
  1937  
  1938  			Diff: &terraform.InstanceDiff{
  1939  				Attributes: map[string]*terraform.ResourceAttrDiff{
  1940  					"some_threshold": {
  1941  						Old: "567.8",
  1942  						New: "12.34",
  1943  					},
  1944  				},
  1945  			},
  1946  
  1947  			Err: false,
  1948  		},
  1949  
  1950  		{
  1951  			Name: "https://github.com/hashicorp/terraform-plugin-sdk/issues/824",
  1952  			Schema: map[string]*Schema{
  1953  				"block_device": {
  1954  					Type:     TypeSet,
  1955  					Optional: true,
  1956  					Computed: true,
  1957  					Elem: &Resource{
  1958  						Schema: map[string]*Schema{
  1959  							"device_name": {
  1960  								Type:     TypeString,
  1961  								Required: true,
  1962  							},
  1963  							"delete_on_termination": {
  1964  								Type:     TypeBool,
  1965  								Optional: true,
  1966  								Default:  true,
  1967  							},
  1968  						},
  1969  					},
  1970  					Set: func(v interface{}) int {
  1971  						var buf bytes.Buffer
  1972  						m := v.(map[string]interface{})
  1973  						buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
  1974  						buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool)))
  1975  						return hashcode.String(buf.String())
  1976  					},
  1977  				},
  1978  			},
  1979  
  1980  			State: &terraform.InstanceState{
  1981  				Attributes: map[string]string{
  1982  					"block_device.#": "2",
  1983  					"block_device.616397234.delete_on_termination":  "true",
  1984  					"block_device.616397234.device_name":            "/dev/sda1",
  1985  					"block_device.2801811477.delete_on_termination": "true",
  1986  					"block_device.2801811477.device_name":           "/dev/sdx",
  1987  				},
  1988  			},
  1989  
  1990  			Config: map[string]interface{}{
  1991  				"block_device": []interface{}{
  1992  					map[string]interface{}{
  1993  						"device_name": "/dev/sda1",
  1994  					},
  1995  					map[string]interface{}{
  1996  						"device_name": "/dev/sdx",
  1997  					},
  1998  				},
  1999  			},
  2000  			Diff: nil,
  2001  			Err:  false,
  2002  		},
  2003  
  2004  		{
  2005  			Name: "Zero value in state shouldn't result in diff",
  2006  			Schema: map[string]*Schema{
  2007  				"port": {
  2008  					Type:     TypeBool,
  2009  					Optional: true,
  2010  					ForceNew: true,
  2011  				},
  2012  			},
  2013  
  2014  			State: &terraform.InstanceState{
  2015  				Attributes: map[string]string{
  2016  					"port": "false",
  2017  				},
  2018  			},
  2019  
  2020  			Config: map[string]interface{}{},
  2021  
  2022  			Diff: nil,
  2023  
  2024  			Err: false,
  2025  		},
  2026  
  2027  		{
  2028  			Name: "Same as prev, but for sets",
  2029  			Schema: map[string]*Schema{
  2030  				"route": {
  2031  					Type:     TypeSet,
  2032  					Optional: true,
  2033  					Elem: &Resource{
  2034  						Schema: map[string]*Schema{
  2035  							"index": {
  2036  								Type:     TypeInt,
  2037  								Required: true,
  2038  							},
  2039  
  2040  							"gateway": {
  2041  								Type:     TypeSet,
  2042  								Optional: true,
  2043  								Elem:     &Schema{Type: TypeInt},
  2044  								Set: func(a interface{}) int {
  2045  									return a.(int)
  2046  								},
  2047  							},
  2048  						},
  2049  					},
  2050  					Set: func(v interface{}) int {
  2051  						m := v.(map[string]interface{})
  2052  						return m["index"].(int)
  2053  					},
  2054  				},
  2055  			},
  2056  
  2057  			State: &terraform.InstanceState{
  2058  				Attributes: map[string]string{
  2059  					"route.#": "0",
  2060  				},
  2061  			},
  2062  
  2063  			Config: map[string]interface{}{},
  2064  
  2065  			Diff: nil,
  2066  
  2067  			Err: false,
  2068  		},
  2069  
  2070  		{
  2071  			Name: "A set computed element shouldn't cause a diff",
  2072  			Schema: map[string]*Schema{
  2073  				"active": {
  2074  					Type:     TypeBool,
  2075  					Computed: true,
  2076  					ForceNew: true,
  2077  				},
  2078  			},
  2079  
  2080  			State: &terraform.InstanceState{
  2081  				Attributes: map[string]string{
  2082  					"active": "true",
  2083  				},
  2084  			},
  2085  
  2086  			Config: map[string]interface{}{},
  2087  
  2088  			Diff: nil,
  2089  
  2090  			Err: false,
  2091  		},
  2092  
  2093  		{
  2094  			Name: "An empty set should show up in the diff",
  2095  			Schema: map[string]*Schema{
  2096  				"instances": {
  2097  					Type:     TypeSet,
  2098  					Elem:     &Schema{Type: TypeString},
  2099  					Optional: true,
  2100  					ForceNew: true,
  2101  					Set: func(v interface{}) int {
  2102  						return len(v.(string))
  2103  					},
  2104  				},
  2105  			},
  2106  
  2107  			State: &terraform.InstanceState{
  2108  				Attributes: map[string]string{
  2109  					"instances.#": "1",
  2110  					"instances.3": "foo",
  2111  				},
  2112  			},
  2113  
  2114  			Config: map[string]interface{}{},
  2115  
  2116  			Diff: &terraform.InstanceDiff{
  2117  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2118  					"instances.#": {
  2119  						Old:         "1",
  2120  						New:         "0",
  2121  						RequiresNew: true,
  2122  					},
  2123  					"instances.3": {
  2124  						Old:         "foo",
  2125  						New:         "",
  2126  						NewRemoved:  true,
  2127  						RequiresNew: true,
  2128  					},
  2129  				},
  2130  			},
  2131  
  2132  			Err: false,
  2133  		},
  2134  
  2135  		{
  2136  			Name: "Map with empty value",
  2137  			Schema: map[string]*Schema{
  2138  				"vars": {
  2139  					Type: TypeMap,
  2140  				},
  2141  			},
  2142  
  2143  			State: nil,
  2144  
  2145  			Config: map[string]interface{}{
  2146  				"vars": map[string]interface{}{
  2147  					"foo": "",
  2148  				},
  2149  			},
  2150  
  2151  			Diff: &terraform.InstanceDiff{
  2152  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2153  					"vars.%": {
  2154  						Old: "0",
  2155  						New: "1",
  2156  					},
  2157  					"vars.foo": {
  2158  						Old: "",
  2159  						New: "",
  2160  					},
  2161  				},
  2162  			},
  2163  
  2164  			Err: false,
  2165  		},
  2166  
  2167  		{
  2168  			Name: "Unset bool, not in state",
  2169  			Schema: map[string]*Schema{
  2170  				"force": {
  2171  					Type:     TypeBool,
  2172  					Optional: true,
  2173  					ForceNew: true,
  2174  				},
  2175  			},
  2176  
  2177  			State: nil,
  2178  
  2179  			Config: map[string]interface{}{},
  2180  
  2181  			Diff: nil,
  2182  
  2183  			Err: false,
  2184  		},
  2185  
  2186  		{
  2187  			Name: "Unset set, not in state",
  2188  			Schema: map[string]*Schema{
  2189  				"metadata_keys": {
  2190  					Type:     TypeSet,
  2191  					Optional: true,
  2192  					ForceNew: true,
  2193  					Elem:     &Schema{Type: TypeInt},
  2194  					Set:      func(interface{}) int { return 0 },
  2195  				},
  2196  			},
  2197  
  2198  			State: nil,
  2199  
  2200  			Config: map[string]interface{}{},
  2201  
  2202  			Diff: nil,
  2203  
  2204  			Err: false,
  2205  		},
  2206  
  2207  		{
  2208  			Name: "Unset list in state, should not show up computed",
  2209  			Schema: map[string]*Schema{
  2210  				"metadata_keys": {
  2211  					Type:     TypeList,
  2212  					Optional: true,
  2213  					Computed: true,
  2214  					ForceNew: true,
  2215  					Elem:     &Schema{Type: TypeInt},
  2216  				},
  2217  			},
  2218  
  2219  			State: &terraform.InstanceState{
  2220  				Attributes: map[string]string{
  2221  					"metadata_keys.#": "0",
  2222  				},
  2223  			},
  2224  
  2225  			Config: map[string]interface{}{},
  2226  
  2227  			Diff: nil,
  2228  
  2229  			Err: false,
  2230  		},
  2231  
  2232  		{
  2233  			Name: "Set element computed element",
  2234  			Schema: map[string]*Schema{
  2235  				"ports": {
  2236  					Type:     TypeSet,
  2237  					Required: true,
  2238  					Elem:     &Schema{Type: TypeInt},
  2239  					Set: func(a interface{}) int {
  2240  						return a.(int)
  2241  					},
  2242  				},
  2243  			},
  2244  
  2245  			State: nil,
  2246  
  2247  			Config: map[string]interface{}{
  2248  				"ports": []interface{}{1, hcl2shim.UnknownVariableValue},
  2249  			},
  2250  
  2251  			Diff: &terraform.InstanceDiff{
  2252  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2253  					"ports.#": {
  2254  						Old:         "",
  2255  						New:         "",
  2256  						NewComputed: true,
  2257  					},
  2258  				},
  2259  			},
  2260  
  2261  			Err: false,
  2262  		},
  2263  
  2264  		{
  2265  			Name: "Computed map without config that's known to be empty does not generate diff",
  2266  			Schema: map[string]*Schema{
  2267  				"tags": {
  2268  					Type:     TypeMap,
  2269  					Computed: true,
  2270  				},
  2271  			},
  2272  
  2273  			Config: nil,
  2274  
  2275  			State: &terraform.InstanceState{
  2276  				Attributes: map[string]string{
  2277  					"tags.%": "0",
  2278  				},
  2279  			},
  2280  
  2281  			Diff: nil,
  2282  
  2283  			Err: false,
  2284  		},
  2285  
  2286  		{
  2287  			Name: "Set with hyphen keys",
  2288  			Schema: map[string]*Schema{
  2289  				"route": {
  2290  					Type:     TypeSet,
  2291  					Optional: true,
  2292  					Elem: &Resource{
  2293  						Schema: map[string]*Schema{
  2294  							"index": {
  2295  								Type:     TypeInt,
  2296  								Required: true,
  2297  							},
  2298  
  2299  							"gateway-name": {
  2300  								Type:     TypeString,
  2301  								Optional: true,
  2302  							},
  2303  						},
  2304  					},
  2305  					Set: func(v interface{}) int {
  2306  						m := v.(map[string]interface{})
  2307  						return m["index"].(int)
  2308  					},
  2309  				},
  2310  			},
  2311  
  2312  			State: nil,
  2313  
  2314  			Config: map[string]interface{}{
  2315  				"route": []interface{}{
  2316  					map[string]interface{}{
  2317  						"index":        "1",
  2318  						"gateway-name": "hello",
  2319  					},
  2320  				},
  2321  			},
  2322  
  2323  			Diff: &terraform.InstanceDiff{
  2324  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2325  					"route.#": {
  2326  						Old: "0",
  2327  						New: "1",
  2328  					},
  2329  					"route.1.index": {
  2330  						Old: "",
  2331  						New: "1",
  2332  					},
  2333  					"route.1.gateway-name": {
  2334  						Old: "",
  2335  						New: "hello",
  2336  					},
  2337  				},
  2338  			},
  2339  
  2340  			Err: false,
  2341  		},
  2342  
  2343  		{
  2344  			Name: ": StateFunc in nested set (#1759)",
  2345  			Schema: map[string]*Schema{
  2346  				"service_account": {
  2347  					Type:     TypeList,
  2348  					Optional: true,
  2349  					ForceNew: true,
  2350  					Elem: &Resource{
  2351  						Schema: map[string]*Schema{
  2352  							"scopes": {
  2353  								Type:     TypeSet,
  2354  								Required: true,
  2355  								ForceNew: true,
  2356  								Elem: &Schema{
  2357  									Type: TypeString,
  2358  									StateFunc: func(v interface{}) string {
  2359  										return v.(string) + "!"
  2360  									},
  2361  								},
  2362  								Set: func(v interface{}) int {
  2363  									i, err := strconv.Atoi(v.(string))
  2364  									if err != nil {
  2365  										t.Fatalf("err: %s", err)
  2366  									}
  2367  									return i
  2368  								},
  2369  							},
  2370  						},
  2371  					},
  2372  				},
  2373  			},
  2374  
  2375  			State: nil,
  2376  
  2377  			Config: map[string]interface{}{
  2378  				"service_account": []interface{}{
  2379  					map[string]interface{}{
  2380  						"scopes": []interface{}{"123"},
  2381  					},
  2382  				},
  2383  			},
  2384  
  2385  			Diff: &terraform.InstanceDiff{
  2386  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2387  					"service_account.#": {
  2388  						Old:         "0",
  2389  						New:         "1",
  2390  						RequiresNew: true,
  2391  					},
  2392  					"service_account.0.scopes.#": {
  2393  						Old:         "0",
  2394  						New:         "1",
  2395  						RequiresNew: true,
  2396  					},
  2397  					"service_account.0.scopes.123": {
  2398  						Old:         "",
  2399  						New:         "123!",
  2400  						NewExtra:    "123",
  2401  						RequiresNew: true,
  2402  					},
  2403  				},
  2404  			},
  2405  
  2406  			Err: false,
  2407  		},
  2408  
  2409  		{
  2410  			Name: "Removing set elements",
  2411  			Schema: map[string]*Schema{
  2412  				"instances": {
  2413  					Type:     TypeSet,
  2414  					Elem:     &Schema{Type: TypeString},
  2415  					Optional: true,
  2416  					ForceNew: true,
  2417  					Set: func(v interface{}) int {
  2418  						return len(v.(string))
  2419  					},
  2420  				},
  2421  			},
  2422  
  2423  			State: &terraform.InstanceState{
  2424  				Attributes: map[string]string{
  2425  					"instances.#": "2",
  2426  					"instances.3": "333",
  2427  					"instances.2": "22",
  2428  				},
  2429  			},
  2430  
  2431  			Config: map[string]interface{}{
  2432  				"instances": []interface{}{"333", "4444"},
  2433  			},
  2434  
  2435  			Diff: &terraform.InstanceDiff{
  2436  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2437  					"instances.#": {
  2438  						Old: "2",
  2439  						New: "2",
  2440  					},
  2441  					"instances.2": {
  2442  						Old:         "22",
  2443  						New:         "",
  2444  						NewRemoved:  true,
  2445  						RequiresNew: true,
  2446  					},
  2447  					"instances.3": {
  2448  						Old: "333",
  2449  						New: "333",
  2450  					},
  2451  					"instances.4": {
  2452  						Old:         "",
  2453  						New:         "4444",
  2454  						RequiresNew: true,
  2455  					},
  2456  				},
  2457  			},
  2458  
  2459  			Err: false,
  2460  		},
  2461  
  2462  		{
  2463  			Name: "Bools can be set with 0/1 in config, still get true/false",
  2464  			Schema: map[string]*Schema{
  2465  				"one": {
  2466  					Type:     TypeBool,
  2467  					Optional: true,
  2468  				},
  2469  				"two": {
  2470  					Type:     TypeBool,
  2471  					Optional: true,
  2472  				},
  2473  				"three": {
  2474  					Type:     TypeBool,
  2475  					Optional: true,
  2476  				},
  2477  			},
  2478  
  2479  			State: &terraform.InstanceState{
  2480  				Attributes: map[string]string{
  2481  					"one":   "false",
  2482  					"two":   "true",
  2483  					"three": "true",
  2484  				},
  2485  			},
  2486  
  2487  			Config: map[string]interface{}{
  2488  				"one": "1",
  2489  				"two": "0",
  2490  			},
  2491  
  2492  			Diff: &terraform.InstanceDiff{
  2493  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2494  					"one": {
  2495  						Old: "false",
  2496  						New: "true",
  2497  					},
  2498  					"two": {
  2499  						Old: "true",
  2500  						New: "false",
  2501  					},
  2502  					"three": {
  2503  						Old:        "true",
  2504  						New:        "false",
  2505  						NewRemoved: true,
  2506  					},
  2507  				},
  2508  			},
  2509  
  2510  			Err: false,
  2511  		},
  2512  
  2513  		{
  2514  			Name:   "tainted in state w/ no attr changes is still a replacement",
  2515  			Schema: map[string]*Schema{},
  2516  
  2517  			State: &terraform.InstanceState{
  2518  				Attributes: map[string]string{
  2519  					"id": "someid",
  2520  				},
  2521  				Tainted: true,
  2522  			},
  2523  
  2524  			Config: map[string]interface{}{},
  2525  
  2526  			Diff: &terraform.InstanceDiff{
  2527  				Attributes:     map[string]*terraform.ResourceAttrDiff{},
  2528  				DestroyTainted: true,
  2529  			},
  2530  
  2531  			Err: false,
  2532  		},
  2533  
  2534  		{
  2535  			Name: "Set ForceNew only marks the changing element as ForceNew",
  2536  			Schema: map[string]*Schema{
  2537  				"ports": {
  2538  					Type:     TypeSet,
  2539  					Required: true,
  2540  					ForceNew: true,
  2541  					Elem:     &Schema{Type: TypeInt},
  2542  					Set: func(a interface{}) int {
  2543  						return a.(int)
  2544  					},
  2545  				},
  2546  			},
  2547  
  2548  			State: &terraform.InstanceState{
  2549  				Attributes: map[string]string{
  2550  					"ports.#": "3",
  2551  					"ports.1": "1",
  2552  					"ports.2": "2",
  2553  					"ports.4": "4",
  2554  				},
  2555  			},
  2556  
  2557  			Config: map[string]interface{}{
  2558  				"ports": []interface{}{5, 2, 1},
  2559  			},
  2560  
  2561  			Diff: &terraform.InstanceDiff{
  2562  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2563  					"ports.#": {
  2564  						Old: "3",
  2565  						New: "3",
  2566  					},
  2567  					"ports.1": {
  2568  						Old: "1",
  2569  						New: "1",
  2570  					},
  2571  					"ports.2": {
  2572  						Old: "2",
  2573  						New: "2",
  2574  					},
  2575  					"ports.5": {
  2576  						Old:         "",
  2577  						New:         "5",
  2578  						RequiresNew: true,
  2579  					},
  2580  					"ports.4": {
  2581  						Old:         "4",
  2582  						New:         "0",
  2583  						NewRemoved:  true,
  2584  						RequiresNew: true,
  2585  					},
  2586  				},
  2587  			},
  2588  		},
  2589  
  2590  		{
  2591  			Name: "removed optional items should trigger ForceNew",
  2592  			Schema: map[string]*Schema{
  2593  				"description": {
  2594  					Type:     TypeString,
  2595  					ForceNew: true,
  2596  					Optional: true,
  2597  				},
  2598  			},
  2599  
  2600  			State: &terraform.InstanceState{
  2601  				Attributes: map[string]string{
  2602  					"description": "foo",
  2603  				},
  2604  			},
  2605  
  2606  			Config: map[string]interface{}{},
  2607  
  2608  			Diff: &terraform.InstanceDiff{
  2609  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2610  					"description": {
  2611  						Old:         "foo",
  2612  						New:         "",
  2613  						RequiresNew: true,
  2614  						NewRemoved:  true,
  2615  					},
  2616  				},
  2617  			},
  2618  
  2619  			Err: false,
  2620  		},
  2621  
  2622  		// GH-7715
  2623  		{
  2624  			Name: "computed value for boolean field",
  2625  			Schema: map[string]*Schema{
  2626  				"foo": {
  2627  					Type:     TypeBool,
  2628  					ForceNew: true,
  2629  					Computed: true,
  2630  					Optional: true,
  2631  				},
  2632  			},
  2633  
  2634  			State: &terraform.InstanceState{},
  2635  
  2636  			Config: map[string]interface{}{
  2637  				"foo": hcl2shim.UnknownVariableValue,
  2638  			},
  2639  
  2640  			Diff: &terraform.InstanceDiff{
  2641  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2642  					"foo": {
  2643  						Old:         "",
  2644  						New:         "false",
  2645  						NewComputed: true,
  2646  						RequiresNew: true,
  2647  					},
  2648  				},
  2649  			},
  2650  
  2651  			Err: false,
  2652  		},
  2653  
  2654  		{
  2655  			Name: "Set ForceNew marks count as ForceNew if computed",
  2656  			Schema: map[string]*Schema{
  2657  				"ports": {
  2658  					Type:     TypeSet,
  2659  					Required: true,
  2660  					ForceNew: true,
  2661  					Elem:     &Schema{Type: TypeInt},
  2662  					Set: func(a interface{}) int {
  2663  						return a.(int)
  2664  					},
  2665  				},
  2666  			},
  2667  
  2668  			State: &terraform.InstanceState{
  2669  				Attributes: map[string]string{
  2670  					"ports.#": "3",
  2671  					"ports.1": "1",
  2672  					"ports.2": "2",
  2673  					"ports.4": "4",
  2674  				},
  2675  			},
  2676  
  2677  			Config: map[string]interface{}{
  2678  				"ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1},
  2679  			},
  2680  
  2681  			Diff: &terraform.InstanceDiff{
  2682  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2683  					"ports.#": {
  2684  						Old:         "3",
  2685  						New:         "",
  2686  						NewComputed: true,
  2687  						RequiresNew: true,
  2688  					},
  2689  				},
  2690  			},
  2691  		},
  2692  
  2693  		{
  2694  			Name: "List with computed schema and ForceNew",
  2695  			Schema: map[string]*Schema{
  2696  				"config": {
  2697  					Type:     TypeList,
  2698  					Optional: true,
  2699  					ForceNew: true,
  2700  					Elem: &Schema{
  2701  						Type: TypeString,
  2702  					},
  2703  				},
  2704  			},
  2705  
  2706  			State: &terraform.InstanceState{
  2707  				Attributes: map[string]string{
  2708  					"config.#": "2",
  2709  					"config.0": "a",
  2710  					"config.1": "b",
  2711  				},
  2712  			},
  2713  
  2714  			Config: map[string]interface{}{
  2715  				"config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue},
  2716  			},
  2717  
  2718  			Diff: &terraform.InstanceDiff{
  2719  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2720  					"config.#": {
  2721  						Old:         "2",
  2722  						New:         "",
  2723  						RequiresNew: true,
  2724  						NewComputed: true,
  2725  					},
  2726  				},
  2727  			},
  2728  
  2729  			Err: false,
  2730  		},
  2731  
  2732  		{
  2733  			Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema",
  2734  			Schema: map[string]*Schema{
  2735  				"availability_zone": {
  2736  					Type:     TypeString,
  2737  					Optional: true,
  2738  					Computed: true,
  2739  				},
  2740  			},
  2741  
  2742  			State: nil,
  2743  
  2744  			Config: map[string]interface{}{
  2745  				"availability_zone": "foo",
  2746  			},
  2747  
  2748  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  2749  				if err := d.SetNew("availability_zone", "bar"); err != nil {
  2750  					return err
  2751  				}
  2752  				if err := d.ForceNew("availability_zone"); err != nil {
  2753  					return err
  2754  				}
  2755  				return nil
  2756  			},
  2757  
  2758  			Diff: &terraform.InstanceDiff{
  2759  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2760  					"availability_zone": {
  2761  						Old:         "",
  2762  						New:         "bar",
  2763  						RequiresNew: true,
  2764  					},
  2765  				},
  2766  			},
  2767  
  2768  			Err: false,
  2769  		},
  2770  
  2771  		{
  2772  			// NOTE: This case is technically impossible in the current
  2773  			// implementation, because optional+computed values never show up in the
  2774  			// diff. In the event behavior changes this test should ensure that the
  2775  			// intended diff still shows up.
  2776  			Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema",
  2777  			Schema: map[string]*Schema{
  2778  				"availability_zone": {
  2779  					Type:     TypeString,
  2780  					Optional: true,
  2781  					Computed: true,
  2782  				},
  2783  			},
  2784  
  2785  			State: nil,
  2786  
  2787  			Config: map[string]interface{}{},
  2788  
  2789  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  2790  				if err := d.SetNew("availability_zone", "bar"); err != nil {
  2791  					return err
  2792  				}
  2793  				if err := d.ForceNew("availability_zone"); err != nil {
  2794  					return err
  2795  				}
  2796  				return nil
  2797  			},
  2798  
  2799  			Diff: &terraform.InstanceDiff{
  2800  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2801  					"availability_zone": {
  2802  						Old:         "",
  2803  						New:         "bar",
  2804  						RequiresNew: true,
  2805  					},
  2806  				},
  2807  			},
  2808  
  2809  			Err: false,
  2810  		},
  2811  
  2812  		{
  2813  
  2814  			Name: "overridden diff with a CustomizeDiff function, ForceNew in schema",
  2815  			Schema: map[string]*Schema{
  2816  				"availability_zone": {
  2817  					Type:     TypeString,
  2818  					Optional: true,
  2819  					Computed: true,
  2820  					ForceNew: true,
  2821  				},
  2822  			},
  2823  
  2824  			State: nil,
  2825  
  2826  			Config: map[string]interface{}{
  2827  				"availability_zone": "foo",
  2828  			},
  2829  
  2830  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  2831  				if err := d.SetNew("availability_zone", "bar"); err != nil {
  2832  					return err
  2833  				}
  2834  				return nil
  2835  			},
  2836  
  2837  			Diff: &terraform.InstanceDiff{
  2838  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2839  					"availability_zone": {
  2840  						Old:         "",
  2841  						New:         "bar",
  2842  						RequiresNew: true,
  2843  					},
  2844  				},
  2845  			},
  2846  
  2847  			Err: false,
  2848  		},
  2849  
  2850  		{
  2851  			Name: "required field with computed diff added with CustomizeDiff function",
  2852  			Schema: map[string]*Schema{
  2853  				"ami_id": {
  2854  					Type:     TypeString,
  2855  					Required: true,
  2856  				},
  2857  				"instance_id": {
  2858  					Type:     TypeString,
  2859  					Computed: true,
  2860  				},
  2861  			},
  2862  
  2863  			State: nil,
  2864  
  2865  			Config: map[string]interface{}{
  2866  				"ami_id": "foo",
  2867  			},
  2868  
  2869  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  2870  				if err := d.SetNew("instance_id", "bar"); err != nil {
  2871  					return err
  2872  				}
  2873  				return nil
  2874  			},
  2875  
  2876  			Diff: &terraform.InstanceDiff{
  2877  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2878  					"ami_id": {
  2879  						Old: "",
  2880  						New: "foo",
  2881  					},
  2882  					"instance_id": {
  2883  						Old: "",
  2884  						New: "bar",
  2885  					},
  2886  				},
  2887  			},
  2888  
  2889  			Err: false,
  2890  		},
  2891  
  2892  		{
  2893  			Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition",
  2894  			Schema: map[string]*Schema{
  2895  				"ports": {
  2896  					Type:     TypeSet,
  2897  					Optional: true,
  2898  					Computed: true,
  2899  					Elem:     &Schema{Type: TypeInt},
  2900  					Set: func(a interface{}) int {
  2901  						return a.(int)
  2902  					},
  2903  				},
  2904  			},
  2905  
  2906  			State: &terraform.InstanceState{
  2907  				Attributes: map[string]string{
  2908  					"ports.#": "3",
  2909  					"ports.1": "1",
  2910  					"ports.2": "2",
  2911  					"ports.4": "4",
  2912  				},
  2913  			},
  2914  
  2915  			Config: map[string]interface{}{
  2916  				"ports": []interface{}{5, 2, 6},
  2917  			},
  2918  
  2919  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  2920  				if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil {
  2921  					return err
  2922  				}
  2923  				if err := d.ForceNew("ports"); err != nil {
  2924  					return err
  2925  				}
  2926  				return nil
  2927  			},
  2928  
  2929  			Diff: &terraform.InstanceDiff{
  2930  				Attributes: map[string]*terraform.ResourceAttrDiff{
  2931  					"ports.#": {
  2932  						Old: "3",
  2933  						New: "3",
  2934  					},
  2935  					"ports.1": {
  2936  						Old: "1",
  2937  						New: "1",
  2938  					},
  2939  					"ports.2": {
  2940  						Old: "2",
  2941  						New: "2",
  2942  					},
  2943  					"ports.5": {
  2944  						Old:         "",
  2945  						New:         "5",
  2946  						RequiresNew: true,
  2947  					},
  2948  					"ports.4": {
  2949  						Old:         "4",
  2950  						New:         "0",
  2951  						NewRemoved:  true,
  2952  						RequiresNew: true,
  2953  					},
  2954  				},
  2955  			},
  2956  		},
  2957  
  2958  		{
  2959  			Name:   "tainted resource does not run CustomizeDiffFunc",
  2960  			Schema: map[string]*Schema{},
  2961  
  2962  			State: &terraform.InstanceState{
  2963  				Attributes: map[string]string{
  2964  					"id": "someid",
  2965  				},
  2966  				Tainted: true,
  2967  			},
  2968  
  2969  			Config: map[string]interface{}{},
  2970  
  2971  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  2972  				return errors.New("diff customization should not have run")
  2973  			},
  2974  
  2975  			Diff: &terraform.InstanceDiff{
  2976  				Attributes:     map[string]*terraform.ResourceAttrDiff{},
  2977  				DestroyTainted: true,
  2978  			},
  2979  
  2980  			Err: false,
  2981  		},
  2982  
  2983  		{
  2984  			Name: "NewComputed based on a conditional with CustomizeDiffFunc",
  2985  			Schema: map[string]*Schema{
  2986  				"etag": {
  2987  					Type:     TypeString,
  2988  					Optional: true,
  2989  					Computed: true,
  2990  				},
  2991  				"version_id": {
  2992  					Type:     TypeString,
  2993  					Computed: true,
  2994  				},
  2995  			},
  2996  
  2997  			State: &terraform.InstanceState{
  2998  				Attributes: map[string]string{
  2999  					"etag":       "foo",
  3000  					"version_id": "1",
  3001  				},
  3002  			},
  3003  
  3004  			Config: map[string]interface{}{
  3005  				"etag": "bar",
  3006  			},
  3007  
  3008  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3009  				if d.HasChange("etag") {
  3010  					d.SetNewComputed("version_id")
  3011  				}
  3012  				return nil
  3013  			},
  3014  
  3015  			Diff: &terraform.InstanceDiff{
  3016  				Attributes: map[string]*terraform.ResourceAttrDiff{
  3017  					"etag": {
  3018  						Old: "foo",
  3019  						New: "bar",
  3020  					},
  3021  					"version_id": {
  3022  						Old:         "1",
  3023  						New:         "",
  3024  						NewComputed: true,
  3025  					},
  3026  				},
  3027  			},
  3028  
  3029  			Err: false,
  3030  		},
  3031  
  3032  		{
  3033  			Name: "NewComputed should always propagate with CustomizeDiff",
  3034  			Schema: map[string]*Schema{
  3035  				"foo": {
  3036  					Type:     TypeString,
  3037  					Computed: true,
  3038  				},
  3039  			},
  3040  
  3041  			State: &terraform.InstanceState{
  3042  				Attributes: map[string]string{
  3043  					"foo": "",
  3044  				},
  3045  				ID: "pre-existing",
  3046  			},
  3047  
  3048  			Config: map[string]interface{}{},
  3049  
  3050  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3051  				d.SetNewComputed("foo")
  3052  				return nil
  3053  			},
  3054  
  3055  			Diff: &terraform.InstanceDiff{
  3056  				Attributes: map[string]*terraform.ResourceAttrDiff{
  3057  					"foo": {
  3058  						NewComputed: true,
  3059  					},
  3060  				},
  3061  			},
  3062  
  3063  			Err: false,
  3064  		},
  3065  
  3066  		{
  3067  			Name: "vetoing a diff",
  3068  			Schema: map[string]*Schema{
  3069  				"foo": {
  3070  					Type:     TypeString,
  3071  					Optional: true,
  3072  					Computed: true,
  3073  				},
  3074  			},
  3075  
  3076  			State: &terraform.InstanceState{
  3077  				Attributes: map[string]string{
  3078  					"foo": "bar",
  3079  				},
  3080  			},
  3081  
  3082  			Config: map[string]interface{}{
  3083  				"foo": "baz",
  3084  			},
  3085  
  3086  			CustomizeDiff: func(d *ResourceDiff, meta interface{}) error {
  3087  				return fmt.Errorf("diff vetoed")
  3088  			},
  3089  
  3090  			Err: true,
  3091  		},
  3092  
  3093  		// A lot of resources currently depended on using the empty string as a
  3094  		// nil/unset value.
  3095  		// FIXME: We want this to eventually produce a diff, since there
  3096  		// technically is a new value in the config.
  3097  		{
  3098  			Name: "optional, computed, empty string",
  3099  			Schema: map[string]*Schema{
  3100  				"attr": {
  3101  					Type:     TypeString,
  3102  					Optional: true,
  3103  					Computed: true,
  3104  				},
  3105  			},
  3106  
  3107  			State: &terraform.InstanceState{
  3108  				Attributes: map[string]string{
  3109  					"attr": "bar",
  3110  				},
  3111  			},
  3112  
  3113  			Config: map[string]interface{}{
  3114  				"attr": "",
  3115  			},
  3116  		},
  3117  
  3118  		{
  3119  			Name: "optional, computed, empty string should not crash in CustomizeDiff",
  3120  			Schema: map[string]*Schema{
  3121  				"unrelated_set": {
  3122  					Type:     TypeSet,
  3123  					Optional: true,
  3124  					Elem:     &Schema{Type: TypeString},
  3125  				},
  3126  				"stream_enabled": {
  3127  					Type:     TypeBool,
  3128  					Optional: true,
  3129  				},
  3130  				"stream_view_type": {
  3131  					Type:     TypeString,
  3132  					Optional: true,
  3133  					Computed: true,
  3134  				},
  3135  			},
  3136  
  3137  			State: &terraform.InstanceState{
  3138  				Attributes: map[string]string{
  3139  					"unrelated_set.#":  "0",
  3140  					"stream_enabled":   "true",
  3141  					"stream_view_type": "KEYS_ONLY",
  3142  				},
  3143  			},
  3144  			Config: map[string]interface{}{
  3145  				"stream_enabled":   false,
  3146  				"stream_view_type": "",
  3147  			},
  3148  			CustomizeDiff: func(diff *ResourceDiff, v interface{}) error {
  3149  				v, ok := diff.GetOk("unrelated_set")
  3150  				if ok {
  3151  					return fmt.Errorf("Didn't expect unrelated_set: %#v", v)
  3152  				}
  3153  				return nil
  3154  			},
  3155  			Diff: &terraform.InstanceDiff{
  3156  				Attributes: map[string]*terraform.ResourceAttrDiff{
  3157  					"stream_enabled": {
  3158  						Old: "true",
  3159  						New: "false",
  3160  					},
  3161  				},
  3162  			},
  3163  		},
  3164  	}
  3165  
  3166  	for i, tc := range cases {
  3167  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
  3168  			c := terraform.NewResourceConfigRaw(tc.Config)
  3169  
  3170  			d, err := schemaMap(tc.Schema).Diff(tc.State, c, tc.CustomizeDiff, nil, true)
  3171  			if err != nil != tc.Err {
  3172  				t.Fatalf("err: %s", err)
  3173  			}
  3174  
  3175  			if !reflect.DeepEqual(tc.Diff, d) {
  3176  				t.Fatalf("expected:\n%#v\n\ngot:\n%#v", tc.Diff, d)
  3177  			}
  3178  		})
  3179  	}
  3180  }
  3181  
  3182  func TestSchemaMap_Input(t *testing.T) {
  3183  	cases := map[string]struct {
  3184  		Schema map[string]*Schema
  3185  		Config map[string]interface{}
  3186  		Input  map[string]string
  3187  		Result map[string]interface{}
  3188  		Err    bool
  3189  	}{
  3190  		/*
  3191  		 * String decode
  3192  		 */
  3193  
  3194  		"no input on optional field with no config": {
  3195  			Schema: map[string]*Schema{
  3196  				"availability_zone": {
  3197  					Type:     TypeString,
  3198  					Optional: true,
  3199  				},
  3200  			},
  3201  
  3202  			Input:  map[string]string{},
  3203  			Result: map[string]interface{}{},
  3204  			Err:    false,
  3205  		},
  3206  
  3207  		"input ignored when config has a value": {
  3208  			Schema: map[string]*Schema{
  3209  				"availability_zone": {
  3210  					Type:     TypeString,
  3211  					Optional: true,
  3212  				},
  3213  			},
  3214  
  3215  			Config: map[string]interface{}{
  3216  				"availability_zone": "bar",
  3217  			},
  3218  
  3219  			Input: map[string]string{
  3220  				"availability_zone": "foo",
  3221  			},
  3222  
  3223  			Result: map[string]interface{}{},
  3224  
  3225  			Err: false,
  3226  		},
  3227  
  3228  		"input ignored when schema has a default": {
  3229  			Schema: map[string]*Schema{
  3230  				"availability_zone": {
  3231  					Type:     TypeString,
  3232  					Default:  "foo",
  3233  					Optional: true,
  3234  				},
  3235  			},
  3236  
  3237  			Input: map[string]string{
  3238  				"availability_zone": "bar",
  3239  			},
  3240  
  3241  			Result: map[string]interface{}{},
  3242  
  3243  			Err: false,
  3244  		},
  3245  
  3246  		"input ignored when default function returns a value": {
  3247  			Schema: map[string]*Schema{
  3248  				"availability_zone": {
  3249  					Type: TypeString,
  3250  					DefaultFunc: func() (interface{}, error) {
  3251  						return "foo", nil
  3252  					},
  3253  					Optional: true,
  3254  				},
  3255  			},
  3256  
  3257  			Input: map[string]string{
  3258  				"availability_zone": "bar",
  3259  			},
  3260  
  3261  			Result: map[string]interface{}{},
  3262  
  3263  			Err: false,
  3264  		},
  3265  
  3266  		"input ignored when default function returns an empty string": {
  3267  			Schema: map[string]*Schema{
  3268  				"availability_zone": {
  3269  					Type:     TypeString,
  3270  					Default:  "",
  3271  					Optional: true,
  3272  				},
  3273  			},
  3274  
  3275  			Input: map[string]string{
  3276  				"availability_zone": "bar",
  3277  			},
  3278  
  3279  			Result: map[string]interface{}{},
  3280  
  3281  			Err: false,
  3282  		},
  3283  
  3284  		"input used when default function returns nil": {
  3285  			Schema: map[string]*Schema{
  3286  				"availability_zone": {
  3287  					Type: TypeString,
  3288  					DefaultFunc: func() (interface{}, error) {
  3289  						return nil, nil
  3290  					},
  3291  					Required: true,
  3292  				},
  3293  			},
  3294  
  3295  			Input: map[string]string{
  3296  				"availability_zone": "bar",
  3297  			},
  3298  
  3299  			Result: map[string]interface{}{
  3300  				"availability_zone": "bar",
  3301  			},
  3302  
  3303  			Err: false,
  3304  		},
  3305  
  3306  		"input not used when optional default function returns nil": {
  3307  			Schema: map[string]*Schema{
  3308  				"availability_zone": {
  3309  					Type: TypeString,
  3310  					DefaultFunc: func() (interface{}, error) {
  3311  						return nil, nil
  3312  					},
  3313  					Optional: true,
  3314  				},
  3315  			},
  3316  
  3317  			Input:  map[string]string{},
  3318  			Result: map[string]interface{}{},
  3319  			Err:    false,
  3320  		},
  3321  	}
  3322  
  3323  	for i, tc := range cases {
  3324  		if tc.Config == nil {
  3325  			tc.Config = make(map[string]interface{})
  3326  		}
  3327  
  3328  		input := new(terraform.MockUIInput)
  3329  		input.InputReturnMap = tc.Input
  3330  
  3331  		rc := terraform.NewResourceConfigRaw(tc.Config)
  3332  		rc.Config = make(map[string]interface{})
  3333  
  3334  		actual, err := schemaMap(tc.Schema).Input(input, rc)
  3335  		if err != nil != tc.Err {
  3336  			t.Fatalf("#%v err: %s", i, err)
  3337  		}
  3338  
  3339  		if !reflect.DeepEqual(tc.Result, actual.Config) {
  3340  			t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result)
  3341  		}
  3342  	}
  3343  }
  3344  
  3345  func TestSchemaMap_InputDefault(t *testing.T) {
  3346  	emptyConfig := make(map[string]interface{})
  3347  	rc := terraform.NewResourceConfigRaw(emptyConfig)
  3348  	rc.Config = make(map[string]interface{})
  3349  
  3350  	input := new(terraform.MockUIInput)
  3351  	input.InputFn = func(opts *terraform.InputOpts) (string, error) {
  3352  		t.Fatalf("InputFn should not be called on: %#v", opts)
  3353  		return "", nil
  3354  	}
  3355  
  3356  	schema := map[string]*Schema{
  3357  		"availability_zone": {
  3358  			Type:     TypeString,
  3359  			Default:  "foo",
  3360  			Optional: true,
  3361  		},
  3362  	}
  3363  	actual, err := schemaMap(schema).Input(input, rc)
  3364  	if err != nil {
  3365  		t.Fatalf("err: %s", err)
  3366  	}
  3367  
  3368  	expected := map[string]interface{}{}
  3369  
  3370  	if !reflect.DeepEqual(expected, actual.Config) {
  3371  		t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected)
  3372  	}
  3373  }
  3374  
  3375  func TestSchemaMap_InputDeprecated(t *testing.T) {
  3376  	emptyConfig := make(map[string]interface{})
  3377  	rc := terraform.NewResourceConfigRaw(emptyConfig)
  3378  	rc.Config = make(map[string]interface{})
  3379  
  3380  	input := new(terraform.MockUIInput)
  3381  	input.InputFn = func(opts *terraform.InputOpts) (string, error) {
  3382  		t.Fatalf("InputFn should not be called on: %#v", opts)
  3383  		return "", nil
  3384  	}
  3385  
  3386  	schema := map[string]*Schema{
  3387  		"availability_zone": {
  3388  			Type:       TypeString,
  3389  			Deprecated: "long gone",
  3390  			Optional:   true,
  3391  		},
  3392  	}
  3393  	actual, err := schemaMap(schema).Input(input, rc)
  3394  	if err != nil {
  3395  		t.Fatalf("err: %s", err)
  3396  	}
  3397  
  3398  	expected := map[string]interface{}{}
  3399  
  3400  	if !reflect.DeepEqual(expected, actual.Config) {
  3401  		t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected)
  3402  	}
  3403  }
  3404  
  3405  func TestSchemaMap_InternalValidate(t *testing.T) {
  3406  	cases := map[string]struct {
  3407  		In  map[string]*Schema
  3408  		Err bool
  3409  	}{
  3410  		"nothing": {
  3411  			nil,
  3412  			false,
  3413  		},
  3414  
  3415  		"Both optional and required": {
  3416  			map[string]*Schema{
  3417  				"foo": {
  3418  					Type:     TypeInt,
  3419  					Optional: true,
  3420  					Required: true,
  3421  				},
  3422  			},
  3423  			true,
  3424  		},
  3425  
  3426  		"No optional and no required": {
  3427  			map[string]*Schema{
  3428  				"foo": {
  3429  					Type: TypeInt,
  3430  				},
  3431  			},
  3432  			true,
  3433  		},
  3434  
  3435  		"Missing Type": {
  3436  			map[string]*Schema{
  3437  				"foo": {
  3438  					Required: true,
  3439  				},
  3440  			},
  3441  			true,
  3442  		},
  3443  
  3444  		"Required but computed": {
  3445  			map[string]*Schema{
  3446  				"foo": {
  3447  					Type:     TypeInt,
  3448  					Required: true,
  3449  					Computed: true,
  3450  				},
  3451  			},
  3452  			true,
  3453  		},
  3454  
  3455  		"Looks good": {
  3456  			map[string]*Schema{
  3457  				"foo": {
  3458  					Type:     TypeString,
  3459  					Required: true,
  3460  				},
  3461  			},
  3462  			false,
  3463  		},
  3464  
  3465  		"Computed but has default": {
  3466  			map[string]*Schema{
  3467  				"foo": {
  3468  					Type:     TypeInt,
  3469  					Optional: true,
  3470  					Computed: true,
  3471  					Default:  "foo",
  3472  				},
  3473  			},
  3474  			true,
  3475  		},
  3476  
  3477  		"Required but has default": {
  3478  			map[string]*Schema{
  3479  				"foo": {
  3480  					Type:     TypeInt,
  3481  					Optional: true,
  3482  					Required: true,
  3483  					Default:  "foo",
  3484  				},
  3485  			},
  3486  			true,
  3487  		},
  3488  
  3489  		"List element not set": {
  3490  			map[string]*Schema{
  3491  				"foo": {
  3492  					Type: TypeList,
  3493  				},
  3494  			},
  3495  			true,
  3496  		},
  3497  
  3498  		"List default": {
  3499  			map[string]*Schema{
  3500  				"foo": {
  3501  					Type:    TypeList,
  3502  					Elem:    &Schema{Type: TypeInt},
  3503  					Default: "foo",
  3504  				},
  3505  			},
  3506  			true,
  3507  		},
  3508  
  3509  		"List element computed": {
  3510  			map[string]*Schema{
  3511  				"foo": {
  3512  					Type:     TypeList,
  3513  					Optional: true,
  3514  					Elem: &Schema{
  3515  						Type:     TypeInt,
  3516  						Computed: true,
  3517  					},
  3518  				},
  3519  			},
  3520  			true,
  3521  		},
  3522  
  3523  		"List element with Set set": {
  3524  			map[string]*Schema{
  3525  				"foo": {
  3526  					Type:     TypeList,
  3527  					Elem:     &Schema{Type: TypeInt},
  3528  					Set:      func(interface{}) int { return 0 },
  3529  					Optional: true,
  3530  				},
  3531  			},
  3532  			true,
  3533  		},
  3534  
  3535  		"Set element with no Set set": {
  3536  			map[string]*Schema{
  3537  				"foo": {
  3538  					Type:     TypeSet,
  3539  					Elem:     &Schema{Type: TypeInt},
  3540  					Optional: true,
  3541  				},
  3542  			},
  3543  			false,
  3544  		},
  3545  
  3546  		"Required but computedWhen": {
  3547  			map[string]*Schema{
  3548  				"foo": {
  3549  					Type:         TypeInt,
  3550  					Required:     true,
  3551  					ComputedWhen: []string{"foo"},
  3552  				},
  3553  			},
  3554  			true,
  3555  		},
  3556  
  3557  		"Conflicting attributes cannot be required": {
  3558  			map[string]*Schema{
  3559  				"blacklist": {
  3560  					Type:     TypeBool,
  3561  					Required: true,
  3562  				},
  3563  				"whitelist": {
  3564  					Type:          TypeBool,
  3565  					Optional:      true,
  3566  					ConflictsWith: []string{"blacklist"},
  3567  				},
  3568  			},
  3569  			true,
  3570  		},
  3571  
  3572  		"Attribute with conflicts cannot be required": {
  3573  			map[string]*Schema{
  3574  				"whitelist": {
  3575  					Type:          TypeBool,
  3576  					Required:      true,
  3577  					ConflictsWith: []string{"blacklist"},
  3578  				},
  3579  			},
  3580  			true,
  3581  		},
  3582  
  3583  		"ConflictsWith cannot be used w/ ComputedWhen": {
  3584  			map[string]*Schema{
  3585  				"blacklist": {
  3586  					Type:         TypeBool,
  3587  					ComputedWhen: []string{"foor"},
  3588  				},
  3589  				"whitelist": {
  3590  					Type:          TypeBool,
  3591  					Required:      true,
  3592  					ConflictsWith: []string{"blacklist"},
  3593  				},
  3594  			},
  3595  			true,
  3596  		},
  3597  
  3598  		"AtLeastOneOf list index syntax with self reference": {
  3599  			map[string]*Schema{
  3600  				"config_block_attr": {
  3601  					Type:     TypeList,
  3602  					Optional: true,
  3603  					MaxItems: 1,
  3604  					Elem: &Resource{
  3605  						Schema: map[string]*Schema{
  3606  							"nested_attr": {
  3607  								Type:         TypeString,
  3608  								Optional:     true,
  3609  								AtLeastOneOf: []string{"config_block_attr.0.nested_attr"},
  3610  							},
  3611  						},
  3612  					},
  3613  				},
  3614  			},
  3615  			false,
  3616  		},
  3617  
  3618  		"AtLeastOneOf list index syntax with list configuration block existing attribute": {
  3619  			map[string]*Schema{
  3620  				"config_block_attr": {
  3621  					Type:     TypeList,
  3622  					Optional: true,
  3623  					MaxItems: 1,
  3624  					Elem: &Resource{
  3625  						Schema: map[string]*Schema{
  3626  							"nested_attr": {
  3627  								Type:     TypeString,
  3628  								Optional: true,
  3629  							},
  3630  						},
  3631  					},
  3632  				},
  3633  				"test": {
  3634  					Type:         TypeBool,
  3635  					Optional:     true,
  3636  					AtLeastOneOf: []string{"config_block_attr.0.nested_attr"},
  3637  				},
  3638  			},
  3639  			false,
  3640  		},
  3641  
  3642  		"AtLeastOneOf list index syntax with list configuration block missing attribute": {
  3643  			map[string]*Schema{
  3644  				"config_block_attr": {
  3645  					Type:     TypeList,
  3646  					Optional: true,
  3647  					Elem: &Resource{
  3648  						Schema: map[string]*Schema{
  3649  							"nested_attr": {
  3650  								Type:     TypeString,
  3651  								Optional: true,
  3652  							},
  3653  						},
  3654  					},
  3655  				},
  3656  				"test": {
  3657  					Type:         TypeBool,
  3658  					Optional:     true,
  3659  					AtLeastOneOf: []string{"config_block_attr.0.missing_attr"},
  3660  				},
  3661  			},
  3662  			true,
  3663  		},
  3664  
  3665  		"AtLeastOneOf list index syntax with list configuration block missing MaxItems": {
  3666  			map[string]*Schema{
  3667  				"config_block_attr": {
  3668  					Type:     TypeList,
  3669  					Optional: true,
  3670  					Elem: &Resource{
  3671  						Schema: map[string]*Schema{
  3672  							"nested_attr": {
  3673  								Type:     TypeString,
  3674  								Optional: true,
  3675  							},
  3676  						},
  3677  					},
  3678  				},
  3679  				"test": {
  3680  					Type:         TypeBool,
  3681  					Optional:     true,
  3682  					AtLeastOneOf: []string{"config_block_attr.0.missing_attr"},
  3683  				},
  3684  			},
  3685  			true,
  3686  		},
  3687  
  3688  		"AtLeastOneOf list index syntax with set configuration block existing attribute": {
  3689  			map[string]*Schema{
  3690  				"config_block_attr": {
  3691  					Type:     TypeSet,
  3692  					Optional: true,
  3693  					Elem: &Resource{
  3694  						Schema: map[string]*Schema{
  3695  							"nested_attr": {
  3696  								Type:     TypeString,
  3697  								Optional: true,
  3698  							},
  3699  						},
  3700  					},
  3701  				},
  3702  				"test": {
  3703  					Type:         TypeBool,
  3704  					Optional:     true,
  3705  					AtLeastOneOf: []string{"config_block_attr.0.nested_attr"},
  3706  				},
  3707  			},
  3708  			true,
  3709  		},
  3710  
  3711  		"AtLeastOneOf list index syntax with set configuration block missing attribute": {
  3712  			map[string]*Schema{
  3713  				"config_block_attr": {
  3714  					Type:     TypeSet,
  3715  					Optional: true,
  3716  					Elem: &Resource{
  3717  						Schema: map[string]*Schema{
  3718  							"nested_attr": {
  3719  								Type:     TypeString,
  3720  								Optional: true,
  3721  							},
  3722  						},
  3723  					},
  3724  				},
  3725  				"test": {
  3726  					Type:         TypeBool,
  3727  					Optional:     true,
  3728  					AtLeastOneOf: []string{"config_block_attr.0.missing_attr"},
  3729  				},
  3730  			},
  3731  			true,
  3732  		},
  3733  
  3734  		"AtLeastOneOf map key syntax with list configuration block existing attribute": {
  3735  			map[string]*Schema{
  3736  				"config_block_attr": {
  3737  					Type:     TypeList,
  3738  					Optional: true,
  3739  					Elem: &Resource{
  3740  						Schema: map[string]*Schema{
  3741  							"nested_attr": {
  3742  								Type:     TypeString,
  3743  								Optional: true,
  3744  							},
  3745  						},
  3746  					},
  3747  				},
  3748  				"test": {
  3749  					Type:         TypeBool,
  3750  					Optional:     true,
  3751  					AtLeastOneOf: []string{"config_block_attr.nested_attr"},
  3752  				},
  3753  			},
  3754  			true,
  3755  		},
  3756  
  3757  		"AtLeastOneOf map key syntax with list configuration block self reference": {
  3758  			map[string]*Schema{
  3759  				"config_block_attr": {
  3760  					Type:     TypeList,
  3761  					Optional: true,
  3762  					Elem: &Resource{
  3763  						Schema: map[string]*Schema{
  3764  							"nested_attr": {
  3765  								Type:         TypeString,
  3766  								Optional:     true,
  3767  								AtLeastOneOf: []string{"config_block_attr.nested_attr"},
  3768  							},
  3769  						},
  3770  					},
  3771  				},
  3772  			},
  3773  			true,
  3774  		},
  3775  
  3776  		"AtLeastOneOf map key syntax with set configuration block existing attribute": {
  3777  			map[string]*Schema{
  3778  				"config_block_attr": {
  3779  					Type:     TypeSet,
  3780  					Optional: true,
  3781  					Elem: &Resource{
  3782  						Schema: map[string]*Schema{
  3783  							"nested_attr": {
  3784  								Type:     TypeString,
  3785  								Optional: true,
  3786  							},
  3787  						},
  3788  					},
  3789  				},
  3790  				"test": {
  3791  					Type:         TypeBool,
  3792  					Optional:     true,
  3793  					AtLeastOneOf: []string{"config_block_attr.nested_attr"},
  3794  				},
  3795  			},
  3796  			true,
  3797  		},
  3798  
  3799  		"AtLeastOneOf map key syntax with set configuration block self reference": {
  3800  			map[string]*Schema{
  3801  				"config_block_attr": {
  3802  					Type:     TypeSet,
  3803  					Optional: true,
  3804  					Elem: &Resource{
  3805  						Schema: map[string]*Schema{
  3806  							"nested_attr": {
  3807  								Type:         TypeString,
  3808  								Optional:     true,
  3809  								AtLeastOneOf: []string{"config_block_attr.nested_attr"},
  3810  							},
  3811  						},
  3812  					},
  3813  				},
  3814  			},
  3815  			true,
  3816  		},
  3817  
  3818  		"AtLeastOneOf map key syntax with map attribute": {
  3819  			map[string]*Schema{
  3820  				"map_attr": {
  3821  					Type:     TypeMap,
  3822  					Optional: true,
  3823  					Elem:     &Schema{Type: TypeString},
  3824  				},
  3825  				"test": {
  3826  					Type:         TypeBool,
  3827  					Optional:     true,
  3828  					AtLeastOneOf: []string{"map_attr.some_key"},
  3829  				},
  3830  			},
  3831  			true,
  3832  		},
  3833  
  3834  		"AtLeastOneOf string syntax with list attribute": {
  3835  			map[string]*Schema{
  3836  				"list_attr": {
  3837  					Type:     TypeList,
  3838  					Optional: true,
  3839  					Elem:     &Schema{Type: TypeString},
  3840  				},
  3841  				"test": {
  3842  					Type:         TypeBool,
  3843  					Optional:     true,
  3844  					AtLeastOneOf: []string{"list_attr"},
  3845  				},
  3846  			},
  3847  			false,
  3848  		},
  3849  
  3850  		"AtLeastOneOf string syntax with list configuration block": {
  3851  			map[string]*Schema{
  3852  				"config_block_attr": {
  3853  					Type:     TypeList,
  3854  					Optional: true,
  3855  					Elem: &Resource{
  3856  						Schema: map[string]*Schema{
  3857  							"nested_attr": {
  3858  								Type:     TypeString,
  3859  								Optional: true,
  3860  							},
  3861  						},
  3862  					},
  3863  				},
  3864  				"test": {
  3865  					Type:         TypeBool,
  3866  					Optional:     true,
  3867  					AtLeastOneOf: []string{"config_block_attr"},
  3868  				},
  3869  			},
  3870  			false,
  3871  		},
  3872  
  3873  		"AtLeastOneOf string syntax with map attribute": {
  3874  			map[string]*Schema{
  3875  				"map_attr": {
  3876  					Type:     TypeMap,
  3877  					Optional: true,
  3878  					Elem:     &Schema{Type: TypeString},
  3879  				},
  3880  				"test": {
  3881  					Type:         TypeBool,
  3882  					Optional:     true,
  3883  					AtLeastOneOf: []string{"map_attr"},
  3884  				},
  3885  			},
  3886  			false,
  3887  		},
  3888  
  3889  		"AtLeastOneOf string syntax with set attribute": {
  3890  			map[string]*Schema{
  3891  				"set_attr": {
  3892  					Type:     TypeSet,
  3893  					Optional: true,
  3894  					Elem:     &Schema{Type: TypeString},
  3895  				},
  3896  				"test": {
  3897  					Type:         TypeBool,
  3898  					Optional:     true,
  3899  					AtLeastOneOf: []string{"set_attr"},
  3900  				},
  3901  			},
  3902  			false,
  3903  		},
  3904  
  3905  		"AtLeastOneOf string syntax with set configuration block": {
  3906  			map[string]*Schema{
  3907  				"config_block_attr": {
  3908  					Type:     TypeSet,
  3909  					Optional: true,
  3910  					Elem: &Resource{
  3911  						Schema: map[string]*Schema{
  3912  							"nested_attr": {
  3913  								Type:     TypeString,
  3914  								Optional: true,
  3915  							},
  3916  						},
  3917  					},
  3918  				},
  3919  				"test": {
  3920  					Type:         TypeBool,
  3921  					Optional:     true,
  3922  					AtLeastOneOf: []string{"config_block_attr"},
  3923  				},
  3924  			},
  3925  			false,
  3926  		},
  3927  
  3928  		"AtLeastOneOf string syntax with self reference": {
  3929  			map[string]*Schema{
  3930  				"test": {
  3931  					Type:         TypeBool,
  3932  					Optional:     true,
  3933  					AtLeastOneOf: []string{"test"},
  3934  				},
  3935  			},
  3936  			false,
  3937  		},
  3938  
  3939  		"ConflictsWith list index syntax with self reference": {
  3940  			map[string]*Schema{
  3941  				"config_block_attr": {
  3942  					Type:     TypeList,
  3943  					Optional: true,
  3944  					Elem: &Resource{
  3945  						Schema: map[string]*Schema{
  3946  							"nested_attr": {
  3947  								Type:          TypeString,
  3948  								Optional:      true,
  3949  								ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  3950  							},
  3951  						},
  3952  					},
  3953  				},
  3954  			},
  3955  			true,
  3956  		},
  3957  
  3958  		"ConflictsWith list index syntax with list configuration block existing attribute": {
  3959  			map[string]*Schema{
  3960  				"config_block_attr": {
  3961  					Type:     TypeList,
  3962  					Optional: true,
  3963  					MaxItems: 1,
  3964  					Elem: &Resource{
  3965  						Schema: map[string]*Schema{
  3966  							"nested_attr": {
  3967  								Type:     TypeString,
  3968  								Optional: true,
  3969  							},
  3970  						},
  3971  					},
  3972  				},
  3973  				"test": {
  3974  					Type:          TypeBool,
  3975  					Optional:      true,
  3976  					ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  3977  				},
  3978  			},
  3979  			false,
  3980  		},
  3981  
  3982  		"ConflictsWith list index syntax with list configuration block missing attribute": {
  3983  			map[string]*Schema{
  3984  				"config_block_attr": {
  3985  					Type:     TypeList,
  3986  					Optional: true,
  3987  					Elem: &Resource{
  3988  						Schema: map[string]*Schema{
  3989  							"nested_attr": {
  3990  								Type:     TypeString,
  3991  								Optional: true,
  3992  							},
  3993  						},
  3994  					},
  3995  				},
  3996  				"test": {
  3997  					Type:          TypeBool,
  3998  					Optional:      true,
  3999  					ConflictsWith: []string{"config_block_attr.0.missing_attr"},
  4000  				},
  4001  			},
  4002  			true,
  4003  		},
  4004  
  4005  		"ConflictsWith list index syntax with list configuration block missing MaxItems": {
  4006  			map[string]*Schema{
  4007  				"config_block_attr": {
  4008  					Type:     TypeList,
  4009  					Optional: true,
  4010  					Elem: &Resource{
  4011  						Schema: map[string]*Schema{
  4012  							"nested_attr": {
  4013  								Type:     TypeString,
  4014  								Optional: true,
  4015  							},
  4016  						},
  4017  					},
  4018  				},
  4019  				"test": {
  4020  					Type:          TypeBool,
  4021  					Optional:      true,
  4022  					ConflictsWith: []string{"config_block_attr.0.missing_attr"},
  4023  				},
  4024  			},
  4025  			true,
  4026  		},
  4027  
  4028  		"ConflictsWith list index syntax with set configuration block existing attribute": {
  4029  			map[string]*Schema{
  4030  				"config_block_attr": {
  4031  					Type:     TypeSet,
  4032  					Optional: true,
  4033  					Elem: &Resource{
  4034  						Schema: map[string]*Schema{
  4035  							"nested_attr": {
  4036  								Type:     TypeString,
  4037  								Optional: true,
  4038  							},
  4039  						},
  4040  					},
  4041  				},
  4042  				"test": {
  4043  					Type:          TypeBool,
  4044  					Optional:      true,
  4045  					ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  4046  				},
  4047  			},
  4048  			true,
  4049  		},
  4050  
  4051  		"ConflictsWith list index syntax with set configuration block missing attribute": {
  4052  			map[string]*Schema{
  4053  				"config_block_attr": {
  4054  					Type:     TypeSet,
  4055  					Optional: true,
  4056  					Elem: &Resource{
  4057  						Schema: map[string]*Schema{
  4058  							"nested_attr": {
  4059  								Type:     TypeString,
  4060  								Optional: true,
  4061  							},
  4062  						},
  4063  					},
  4064  				},
  4065  				"test": {
  4066  					Type:          TypeBool,
  4067  					Optional:      true,
  4068  					ConflictsWith: []string{"config_block_attr.0.missing_attr"},
  4069  				},
  4070  			},
  4071  			true,
  4072  		},
  4073  
  4074  		"ConflictsWith map key syntax with list configuration block existing attribute": {
  4075  			map[string]*Schema{
  4076  				"config_block_attr": {
  4077  					Type:     TypeList,
  4078  					Optional: true,
  4079  					Elem: &Resource{
  4080  						Schema: map[string]*Schema{
  4081  							"nested_attr": {
  4082  								Type:     TypeString,
  4083  								Optional: true,
  4084  							},
  4085  						},
  4086  					},
  4087  				},
  4088  				"test": {
  4089  					Type:          TypeBool,
  4090  					Optional:      true,
  4091  					ConflictsWith: []string{"config_block_attr.nested_attr"},
  4092  				},
  4093  			},
  4094  			true,
  4095  		},
  4096  
  4097  		"ConflictsWith map key syntax with list configuration block self reference": {
  4098  			map[string]*Schema{
  4099  				"config_block_attr": {
  4100  					Type:     TypeList,
  4101  					Optional: true,
  4102  					Elem: &Resource{
  4103  						Schema: map[string]*Schema{
  4104  							"nested_attr": {
  4105  								Type:          TypeString,
  4106  								Optional:      true,
  4107  								ConflictsWith: []string{"config_block_attr.nested_attr"},
  4108  							},
  4109  						},
  4110  					},
  4111  				},
  4112  			},
  4113  			true,
  4114  		},
  4115  
  4116  		"ConflictsWith map key syntax with set configuration block existing attribute": {
  4117  			map[string]*Schema{
  4118  				"config_block_attr": {
  4119  					Type:     TypeSet,
  4120  					Optional: true,
  4121  					Elem: &Resource{
  4122  						Schema: map[string]*Schema{
  4123  							"nested_attr": {
  4124  								Type:     TypeString,
  4125  								Optional: true,
  4126  							},
  4127  						},
  4128  					},
  4129  				},
  4130  				"test": {
  4131  					Type:          TypeBool,
  4132  					Optional:      true,
  4133  					ConflictsWith: []string{"config_block_attr.nested_attr"},
  4134  				},
  4135  			},
  4136  			true,
  4137  		},
  4138  
  4139  		"ConflictsWith map key syntax with set configuration block self reference": {
  4140  			map[string]*Schema{
  4141  				"config_block_attr": {
  4142  					Type:     TypeSet,
  4143  					Optional: true,
  4144  					Elem: &Resource{
  4145  						Schema: map[string]*Schema{
  4146  							"nested_attr": {
  4147  								Type:          TypeString,
  4148  								Optional:      true,
  4149  								ConflictsWith: []string{"config_block_attr.nested_attr"},
  4150  							},
  4151  						},
  4152  					},
  4153  				},
  4154  			},
  4155  			true,
  4156  		},
  4157  
  4158  		"ConflictsWith map key syntax with map attribute": {
  4159  			map[string]*Schema{
  4160  				"map_attr": {
  4161  					Type:     TypeMap,
  4162  					Optional: true,
  4163  					Elem:     &Schema{Type: TypeString},
  4164  				},
  4165  				"test": {
  4166  					Type:          TypeBool,
  4167  					Optional:      true,
  4168  					ConflictsWith: []string{"map_attr.some_key"},
  4169  				},
  4170  			},
  4171  			true,
  4172  		},
  4173  
  4174  		"ConflictsWith string syntax with list attribute": {
  4175  			map[string]*Schema{
  4176  				"list_attr": {
  4177  					Type:     TypeList,
  4178  					Optional: true,
  4179  					Elem:     &Schema{Type: TypeString},
  4180  				},
  4181  				"test": {
  4182  					Type:          TypeBool,
  4183  					Optional:      true,
  4184  					ConflictsWith: []string{"list_attr"},
  4185  				},
  4186  			},
  4187  			false,
  4188  		},
  4189  
  4190  		"ConflictsWith string syntax with list configuration block": {
  4191  			map[string]*Schema{
  4192  				"config_block_attr": {
  4193  					Type:     TypeList,
  4194  					Optional: true,
  4195  					Elem: &Resource{
  4196  						Schema: map[string]*Schema{
  4197  							"nested_attr": {
  4198  								Type:     TypeString,
  4199  								Optional: true,
  4200  							},
  4201  						},
  4202  					},
  4203  				},
  4204  				"test": {
  4205  					Type:          TypeBool,
  4206  					Optional:      true,
  4207  					ConflictsWith: []string{"config_block_attr"},
  4208  				},
  4209  			},
  4210  			false,
  4211  		},
  4212  
  4213  		"ConflictsWith string syntax with map attribute": {
  4214  			map[string]*Schema{
  4215  				"map_attr": {
  4216  					Type:     TypeMap,
  4217  					Optional: true,
  4218  					Elem:     &Schema{Type: TypeString},
  4219  				},
  4220  				"test": {
  4221  					Type:          TypeBool,
  4222  					Optional:      true,
  4223  					ConflictsWith: []string{"map_attr"},
  4224  				},
  4225  			},
  4226  			false,
  4227  		},
  4228  
  4229  		"ConflictsWith string syntax with set attribute": {
  4230  			map[string]*Schema{
  4231  				"set_attr": {
  4232  					Type:     TypeSet,
  4233  					Optional: true,
  4234  					Elem:     &Schema{Type: TypeString},
  4235  				},
  4236  				"test": {
  4237  					Type:          TypeBool,
  4238  					Optional:      true,
  4239  					ConflictsWith: []string{"set_attr"},
  4240  				},
  4241  			},
  4242  			false,
  4243  		},
  4244  
  4245  		"ConflictsWith string syntax with set configuration block": {
  4246  			map[string]*Schema{
  4247  				"config_block_attr": {
  4248  					Type:     TypeSet,
  4249  					Optional: true,
  4250  					Elem: &Resource{
  4251  						Schema: map[string]*Schema{
  4252  							"nested_attr": {
  4253  								Type:     TypeString,
  4254  								Optional: true,
  4255  							},
  4256  						},
  4257  					},
  4258  				},
  4259  				"test": {
  4260  					Type:          TypeBool,
  4261  					Optional:      true,
  4262  					ConflictsWith: []string{"config_block_attr"},
  4263  				},
  4264  			},
  4265  			false,
  4266  		},
  4267  
  4268  		"ConflictsWith string syntax with self reference": {
  4269  			map[string]*Schema{
  4270  				"test": {
  4271  					Type:          TypeBool,
  4272  					Optional:      true,
  4273  					ConflictsWith: []string{"test"},
  4274  				},
  4275  			},
  4276  			true,
  4277  		},
  4278  
  4279  		"ExactlyOneOf list index syntax with self reference": {
  4280  			map[string]*Schema{
  4281  				"config_block_attr": {
  4282  					Type:     TypeList,
  4283  					Optional: true,
  4284  					MaxItems: 1,
  4285  					Elem: &Resource{
  4286  						Schema: map[string]*Schema{
  4287  							"nested_attr": {
  4288  								Type:         TypeString,
  4289  								Optional:     true,
  4290  								ExactlyOneOf: []string{"config_block_attr.0.nested_attr"},
  4291  							},
  4292  						},
  4293  					},
  4294  				},
  4295  			},
  4296  			false,
  4297  		},
  4298  
  4299  		"ExactlyOneOf list index syntax with list configuration block existing attribute": {
  4300  			map[string]*Schema{
  4301  				"config_block_attr": {
  4302  					Type:     TypeList,
  4303  					Optional: true,
  4304  					MaxItems: 1,
  4305  					Elem: &Resource{
  4306  						Schema: map[string]*Schema{
  4307  							"nested_attr": {
  4308  								Type:     TypeString,
  4309  								Optional: true,
  4310  							},
  4311  						},
  4312  					},
  4313  				},
  4314  				"test": {
  4315  					Type:         TypeBool,
  4316  					Optional:     true,
  4317  					ExactlyOneOf: []string{"config_block_attr.0.nested_attr"},
  4318  				},
  4319  			},
  4320  			false,
  4321  		},
  4322  
  4323  		"ExactlyOneOf list index syntax with list configuration block missing attribute": {
  4324  			map[string]*Schema{
  4325  				"config_block_attr": {
  4326  					Type:     TypeList,
  4327  					Optional: true,
  4328  					Elem: &Resource{
  4329  						Schema: map[string]*Schema{
  4330  							"nested_attr": {
  4331  								Type:     TypeString,
  4332  								Optional: true,
  4333  							},
  4334  						},
  4335  					},
  4336  				},
  4337  				"test": {
  4338  					Type:         TypeBool,
  4339  					Optional:     true,
  4340  					ExactlyOneOf: []string{"config_block_attr.0.missing_attr"},
  4341  				},
  4342  			},
  4343  			true,
  4344  		},
  4345  
  4346  		"ExactlyOneOf list index syntax with list configuration block missing MaxItems": {
  4347  			map[string]*Schema{
  4348  				"config_block_attr": {
  4349  					Type:     TypeList,
  4350  					Optional: true,
  4351  					Elem: &Resource{
  4352  						Schema: map[string]*Schema{
  4353  							"nested_attr": {
  4354  								Type:     TypeString,
  4355  								Optional: true,
  4356  							},
  4357  						},
  4358  					},
  4359  				},
  4360  				"test": {
  4361  					Type:         TypeBool,
  4362  					Optional:     true,
  4363  					ExactlyOneOf: []string{"config_block_attr.0.missing_attr"},
  4364  				},
  4365  			},
  4366  			true,
  4367  		},
  4368  
  4369  		"ExactlyOneOf list index syntax with set configuration block existing attribute": {
  4370  			map[string]*Schema{
  4371  				"config_block_attr": {
  4372  					Type:     TypeSet,
  4373  					Optional: true,
  4374  					Elem: &Resource{
  4375  						Schema: map[string]*Schema{
  4376  							"nested_attr": {
  4377  								Type:     TypeString,
  4378  								Optional: true,
  4379  							},
  4380  						},
  4381  					},
  4382  				},
  4383  				"test": {
  4384  					Type:         TypeBool,
  4385  					Optional:     true,
  4386  					ExactlyOneOf: []string{"config_block_attr.0.nested_attr"},
  4387  				},
  4388  			},
  4389  			true,
  4390  		},
  4391  
  4392  		"ExactlyOneOf list index syntax with set configuration block missing attribute": {
  4393  			map[string]*Schema{
  4394  				"config_block_attr": {
  4395  					Type:     TypeSet,
  4396  					Optional: true,
  4397  					Elem: &Resource{
  4398  						Schema: map[string]*Schema{
  4399  							"nested_attr": {
  4400  								Type:     TypeString,
  4401  								Optional: true,
  4402  							},
  4403  						},
  4404  					},
  4405  				},
  4406  				"test": {
  4407  					Type:         TypeBool,
  4408  					Optional:     true,
  4409  					ExactlyOneOf: []string{"config_block_attr.0.missing_attr"},
  4410  				},
  4411  			},
  4412  			true,
  4413  		},
  4414  
  4415  		"ExactlyOneOf map key syntax with list configuration block existing attribute": {
  4416  			map[string]*Schema{
  4417  				"config_block_attr": {
  4418  					Type:     TypeList,
  4419  					Optional: true,
  4420  					Elem: &Resource{
  4421  						Schema: map[string]*Schema{
  4422  							"nested_attr": {
  4423  								Type:     TypeString,
  4424  								Optional: true,
  4425  							},
  4426  						},
  4427  					},
  4428  				},
  4429  				"test": {
  4430  					Type:         TypeBool,
  4431  					Optional:     true,
  4432  					ExactlyOneOf: []string{"config_block_attr.nested_attr"},
  4433  				},
  4434  			},
  4435  			true,
  4436  		},
  4437  
  4438  		"ExactlyOneOf map key syntax with list configuration block self reference": {
  4439  			map[string]*Schema{
  4440  				"config_block_attr": {
  4441  					Type:     TypeList,
  4442  					Optional: true,
  4443  					Elem: &Resource{
  4444  						Schema: map[string]*Schema{
  4445  							"nested_attr": {
  4446  								Type:         TypeString,
  4447  								Optional:     true,
  4448  								ExactlyOneOf: []string{"config_block_attr.nested_attr"},
  4449  							},
  4450  						},
  4451  					},
  4452  				},
  4453  			},
  4454  			true,
  4455  		},
  4456  
  4457  		"ExactlyOneOf map key syntax with set configuration block existing attribute": {
  4458  			map[string]*Schema{
  4459  				"config_block_attr": {
  4460  					Type:     TypeSet,
  4461  					Optional: true,
  4462  					Elem: &Resource{
  4463  						Schema: map[string]*Schema{
  4464  							"nested_attr": {
  4465  								Type:     TypeString,
  4466  								Optional: true,
  4467  							},
  4468  						},
  4469  					},
  4470  				},
  4471  				"test": {
  4472  					Type:         TypeBool,
  4473  					Optional:     true,
  4474  					ExactlyOneOf: []string{"config_block_attr.nested_attr"},
  4475  				},
  4476  			},
  4477  			true,
  4478  		},
  4479  
  4480  		"ExactlyOneOf map key syntax with set configuration block self reference": {
  4481  			map[string]*Schema{
  4482  				"config_block_attr": {
  4483  					Type:     TypeSet,
  4484  					Optional: true,
  4485  					Elem: &Resource{
  4486  						Schema: map[string]*Schema{
  4487  							"nested_attr": {
  4488  								Type:         TypeString,
  4489  								Optional:     true,
  4490  								ExactlyOneOf: []string{"config_block_attr.nested_attr"},
  4491  							},
  4492  						},
  4493  					},
  4494  				},
  4495  			},
  4496  			true,
  4497  		},
  4498  
  4499  		"ExactlyOneOf map key syntax with map attribute": {
  4500  			map[string]*Schema{
  4501  				"map_attr": {
  4502  					Type:     TypeMap,
  4503  					Optional: true,
  4504  					Elem:     &Schema{Type: TypeString},
  4505  				},
  4506  				"test": {
  4507  					Type:         TypeBool,
  4508  					Optional:     true,
  4509  					ExactlyOneOf: []string{"map_attr.some_key"},
  4510  				},
  4511  			},
  4512  			true,
  4513  		},
  4514  
  4515  		"ExactlyOneOf string syntax with list attribute": {
  4516  			map[string]*Schema{
  4517  				"list_attr": {
  4518  					Type:     TypeList,
  4519  					Optional: true,
  4520  					Elem:     &Schema{Type: TypeString},
  4521  				},
  4522  				"test": {
  4523  					Type:         TypeBool,
  4524  					Optional:     true,
  4525  					ExactlyOneOf: []string{"list_attr"},
  4526  				},
  4527  			},
  4528  			false,
  4529  		},
  4530  
  4531  		"ExactlyOneOf string syntax with list configuration block": {
  4532  			map[string]*Schema{
  4533  				"config_block_attr": {
  4534  					Type:     TypeList,
  4535  					Optional: true,
  4536  					Elem: &Resource{
  4537  						Schema: map[string]*Schema{
  4538  							"nested_attr": {
  4539  								Type:     TypeString,
  4540  								Optional: true,
  4541  							},
  4542  						},
  4543  					},
  4544  				},
  4545  				"test": {
  4546  					Type:         TypeBool,
  4547  					Optional:     true,
  4548  					ExactlyOneOf: []string{"config_block_attr"},
  4549  				},
  4550  			},
  4551  			false,
  4552  		},
  4553  
  4554  		"ExactlyOneOf string syntax with map attribute": {
  4555  			map[string]*Schema{
  4556  				"map_attr": {
  4557  					Type:     TypeMap,
  4558  					Optional: true,
  4559  					Elem:     &Schema{Type: TypeString},
  4560  				},
  4561  				"test": {
  4562  					Type:         TypeBool,
  4563  					Optional:     true,
  4564  					ExactlyOneOf: []string{"map_attr"},
  4565  				},
  4566  			},
  4567  			false,
  4568  		},
  4569  
  4570  		"ExactlyOneOf string syntax with set attribute": {
  4571  			map[string]*Schema{
  4572  				"set_attr": {
  4573  					Type:     TypeSet,
  4574  					Optional: true,
  4575  					Elem:     &Schema{Type: TypeString},
  4576  				},
  4577  				"test": {
  4578  					Type:         TypeBool,
  4579  					Optional:     true,
  4580  					ExactlyOneOf: []string{"set_attr"},
  4581  				},
  4582  			},
  4583  			false,
  4584  		},
  4585  
  4586  		"ExactlyOneOf string syntax with set configuration block": {
  4587  			map[string]*Schema{
  4588  				"config_block_attr": {
  4589  					Type:     TypeSet,
  4590  					Optional: true,
  4591  					Elem: &Resource{
  4592  						Schema: map[string]*Schema{
  4593  							"nested_attr": {
  4594  								Type:     TypeString,
  4595  								Optional: true,
  4596  							},
  4597  						},
  4598  					},
  4599  				},
  4600  				"test": {
  4601  					Type:         TypeBool,
  4602  					Optional:     true,
  4603  					ExactlyOneOf: []string{"config_block_attr"},
  4604  				},
  4605  			},
  4606  			false,
  4607  		},
  4608  
  4609  		"ExactlyOneOf string syntax with self reference": {
  4610  			map[string]*Schema{
  4611  				"test": {
  4612  					Type:         TypeBool,
  4613  					Optional:     true,
  4614  					AtLeastOneOf: []string{"test"},
  4615  				},
  4616  			},
  4617  			false,
  4618  		},
  4619  
  4620  		"RequiredWith list index syntax with self reference": {
  4621  			map[string]*Schema{
  4622  				"config_block_attr": {
  4623  					Type:     TypeList,
  4624  					Optional: true,
  4625  					MaxItems: 1,
  4626  					Elem: &Resource{
  4627  						Schema: map[string]*Schema{
  4628  							"nested_attr": {
  4629  								Type:         TypeString,
  4630  								Optional:     true,
  4631  								RequiredWith: []string{"config_block_attr.0.nested_attr"},
  4632  							},
  4633  						},
  4634  					},
  4635  				},
  4636  			},
  4637  			false,
  4638  		},
  4639  
  4640  		"RequiredWith list index syntax with list configuration block existing attribute": {
  4641  			map[string]*Schema{
  4642  				"config_block_attr": {
  4643  					Type:     TypeList,
  4644  					Optional: true,
  4645  					MaxItems: 1,
  4646  					Elem: &Resource{
  4647  						Schema: map[string]*Schema{
  4648  							"nested_attr": {
  4649  								Type:     TypeString,
  4650  								Optional: true,
  4651  							},
  4652  						},
  4653  					},
  4654  				},
  4655  				"test": {
  4656  					Type:         TypeBool,
  4657  					Optional:     true,
  4658  					RequiredWith: []string{"config_block_attr.0.nested_attr"},
  4659  				},
  4660  			},
  4661  			false,
  4662  		},
  4663  
  4664  		"RequiredWith list index syntax with list configuration block missing attribute": {
  4665  			map[string]*Schema{
  4666  				"config_block_attr": {
  4667  					Type:     TypeList,
  4668  					Optional: true,
  4669  					Elem: &Resource{
  4670  						Schema: map[string]*Schema{
  4671  							"nested_attr": {
  4672  								Type:     TypeString,
  4673  								Optional: true,
  4674  							},
  4675  						},
  4676  					},
  4677  				},
  4678  				"test": {
  4679  					Type:         TypeBool,
  4680  					Optional:     true,
  4681  					RequiredWith: []string{"config_block_attr.0.missing_attr"},
  4682  				},
  4683  			},
  4684  			true,
  4685  		},
  4686  
  4687  		"RequiredWith list index syntax with list configuration block missing MaxItems": {
  4688  			map[string]*Schema{
  4689  				"config_block_attr": {
  4690  					Type:     TypeList,
  4691  					Optional: true,
  4692  					Elem: &Resource{
  4693  						Schema: map[string]*Schema{
  4694  							"nested_attr": {
  4695  								Type:     TypeString,
  4696  								Optional: true,
  4697  							},
  4698  						},
  4699  					},
  4700  				},
  4701  				"test": {
  4702  					Type:         TypeBool,
  4703  					Optional:     true,
  4704  					RequiredWith: []string{"config_block_attr.0.missing_attr"},
  4705  				},
  4706  			},
  4707  			true,
  4708  		},
  4709  
  4710  		"RequiredWith list index syntax with set configuration block existing attribute": {
  4711  			map[string]*Schema{
  4712  				"config_block_attr": {
  4713  					Type:     TypeSet,
  4714  					Optional: true,
  4715  					Elem: &Resource{
  4716  						Schema: map[string]*Schema{
  4717  							"nested_attr": {
  4718  								Type:     TypeString,
  4719  								Optional: true,
  4720  							},
  4721  						},
  4722  					},
  4723  				},
  4724  				"test": {
  4725  					Type:         TypeBool,
  4726  					Optional:     true,
  4727  					RequiredWith: []string{"config_block_attr.0.nested_attr"},
  4728  				},
  4729  			},
  4730  			true,
  4731  		},
  4732  
  4733  		"RequiredWith list index syntax with set configuration block missing attribute": {
  4734  			map[string]*Schema{
  4735  				"config_block_attr": {
  4736  					Type:     TypeSet,
  4737  					Optional: true,
  4738  					Elem: &Resource{
  4739  						Schema: map[string]*Schema{
  4740  							"nested_attr": {
  4741  								Type:     TypeString,
  4742  								Optional: true,
  4743  							},
  4744  						},
  4745  					},
  4746  				},
  4747  				"test": {
  4748  					Type:         TypeBool,
  4749  					Optional:     true,
  4750  					RequiredWith: []string{"config_block_attr.0.missing_attr"},
  4751  				},
  4752  			},
  4753  			true,
  4754  		},
  4755  
  4756  		"RequiredWith map key syntax with list configuration block existing attribute": {
  4757  			map[string]*Schema{
  4758  				"config_block_attr": {
  4759  					Type:     TypeList,
  4760  					Optional: true,
  4761  					Elem: &Resource{
  4762  						Schema: map[string]*Schema{
  4763  							"nested_attr": {
  4764  								Type:     TypeString,
  4765  								Optional: true,
  4766  							},
  4767  						},
  4768  					},
  4769  				},
  4770  				"test": {
  4771  					Type:         TypeBool,
  4772  					Optional:     true,
  4773  					RequiredWith: []string{"config_block_attr.nested_attr"},
  4774  				},
  4775  			},
  4776  			true,
  4777  		},
  4778  
  4779  		"RequiredWith map key syntax with list configuration block self reference": {
  4780  			map[string]*Schema{
  4781  				"config_block_attr": {
  4782  					Type:     TypeList,
  4783  					Optional: true,
  4784  					Elem: &Resource{
  4785  						Schema: map[string]*Schema{
  4786  							"nested_attr": {
  4787  								Type:         TypeString,
  4788  								Optional:     true,
  4789  								RequiredWith: []string{"config_block_attr.nested_attr"},
  4790  							},
  4791  						},
  4792  					},
  4793  				},
  4794  			},
  4795  			true,
  4796  		},
  4797  
  4798  		"RequiredWith map key syntax with set configuration block existing attribute": {
  4799  			map[string]*Schema{
  4800  				"config_block_attr": {
  4801  					Type:     TypeSet,
  4802  					Optional: true,
  4803  					Elem: &Resource{
  4804  						Schema: map[string]*Schema{
  4805  							"nested_attr": {
  4806  								Type:     TypeString,
  4807  								Optional: true,
  4808  							},
  4809  						},
  4810  					},
  4811  				},
  4812  				"test": {
  4813  					Type:         TypeBool,
  4814  					Optional:     true,
  4815  					RequiredWith: []string{"config_block_attr.nested_attr"},
  4816  				},
  4817  			},
  4818  			true,
  4819  		},
  4820  
  4821  		"RequiredWith map key syntax with set configuration block self reference": {
  4822  			map[string]*Schema{
  4823  				"config_block_attr": {
  4824  					Type:     TypeSet,
  4825  					Optional: true,
  4826  					Elem: &Resource{
  4827  						Schema: map[string]*Schema{
  4828  							"nested_attr": {
  4829  								Type:         TypeString,
  4830  								Optional:     true,
  4831  								RequiredWith: []string{"config_block_attr.nested_attr"},
  4832  							},
  4833  						},
  4834  					},
  4835  				},
  4836  			},
  4837  			true,
  4838  		},
  4839  
  4840  		"RequiredWith map key syntax with map attribute": {
  4841  			map[string]*Schema{
  4842  				"map_attr": {
  4843  					Type:     TypeMap,
  4844  					Optional: true,
  4845  					Elem:     &Schema{Type: TypeString},
  4846  				},
  4847  				"test": {
  4848  					Type:         TypeBool,
  4849  					Optional:     true,
  4850  					RequiredWith: []string{"map_attr.some_key"},
  4851  				},
  4852  			},
  4853  			true,
  4854  		},
  4855  
  4856  		"RequiredWith string syntax with list attribute": {
  4857  			map[string]*Schema{
  4858  				"list_attr": {
  4859  					Type:     TypeList,
  4860  					Optional: true,
  4861  					Elem:     &Schema{Type: TypeString},
  4862  				},
  4863  				"test": {
  4864  					Type:         TypeBool,
  4865  					Optional:     true,
  4866  					RequiredWith: []string{"list_attr"},
  4867  				},
  4868  			},
  4869  			false,
  4870  		},
  4871  
  4872  		"RequiredWith string syntax with list configuration block": {
  4873  			map[string]*Schema{
  4874  				"config_block_attr": {
  4875  					Type:     TypeList,
  4876  					Optional: true,
  4877  					Elem: &Resource{
  4878  						Schema: map[string]*Schema{
  4879  							"nested_attr": {
  4880  								Type:     TypeString,
  4881  								Optional: true,
  4882  							},
  4883  						},
  4884  					},
  4885  				},
  4886  				"test": {
  4887  					Type:         TypeBool,
  4888  					Optional:     true,
  4889  					RequiredWith: []string{"config_block_attr"},
  4890  				},
  4891  			},
  4892  			false,
  4893  		},
  4894  
  4895  		"RequiredWith string syntax with map attribute": {
  4896  			map[string]*Schema{
  4897  				"map_attr": {
  4898  					Type:     TypeMap,
  4899  					Optional: true,
  4900  					Elem:     &Schema{Type: TypeString},
  4901  				},
  4902  				"test": {
  4903  					Type:         TypeBool,
  4904  					Optional:     true,
  4905  					RequiredWith: []string{"map_attr"},
  4906  				},
  4907  			},
  4908  			false,
  4909  		},
  4910  
  4911  		"RequiredWith string syntax with set attribute": {
  4912  			map[string]*Schema{
  4913  				"set_attr": {
  4914  					Type:     TypeSet,
  4915  					Optional: true,
  4916  					Elem:     &Schema{Type: TypeString},
  4917  				},
  4918  				"test": {
  4919  					Type:         TypeBool,
  4920  					Optional:     true,
  4921  					RequiredWith: []string{"set_attr"},
  4922  				},
  4923  			},
  4924  			false,
  4925  		},
  4926  
  4927  		"RequiredWith string syntax with set configuration block": {
  4928  			map[string]*Schema{
  4929  				"config_block_attr": {
  4930  					Type:     TypeSet,
  4931  					Optional: true,
  4932  					Elem: &Resource{
  4933  						Schema: map[string]*Schema{
  4934  							"nested_attr": {
  4935  								Type:     TypeString,
  4936  								Optional: true,
  4937  							},
  4938  						},
  4939  					},
  4940  				},
  4941  				"test": {
  4942  					Type:         TypeBool,
  4943  					Optional:     true,
  4944  					RequiredWith: []string{"config_block_attr"},
  4945  				},
  4946  			},
  4947  			false,
  4948  		},
  4949  
  4950  		"RequiredWith string syntax with self reference": {
  4951  			map[string]*Schema{
  4952  				"test": {
  4953  					Type:         TypeBool,
  4954  					Optional:     true,
  4955  					RequiredWith: []string{"test"},
  4956  				},
  4957  			},
  4958  			false,
  4959  		},
  4960  
  4961  		"Sub-resource invalid": {
  4962  			map[string]*Schema{
  4963  				"foo": {
  4964  					Type:     TypeList,
  4965  					Optional: true,
  4966  					Elem: &Resource{
  4967  						Schema: map[string]*Schema{
  4968  							"foo": new(Schema),
  4969  						},
  4970  					},
  4971  				},
  4972  			},
  4973  			true,
  4974  		},
  4975  
  4976  		"Sub-resource valid": {
  4977  			map[string]*Schema{
  4978  				"foo": {
  4979  					Type:     TypeList,
  4980  					Optional: true,
  4981  					Elem: &Resource{
  4982  						Schema: map[string]*Schema{
  4983  							"foo": {
  4984  								Type:     TypeInt,
  4985  								Optional: true,
  4986  							},
  4987  						},
  4988  					},
  4989  				},
  4990  			},
  4991  			false,
  4992  		},
  4993  
  4994  		"ValidateFunc on non-primitive": {
  4995  			map[string]*Schema{
  4996  				"foo": {
  4997  					Type:     TypeSet,
  4998  					Required: true,
  4999  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  5000  						return
  5001  					},
  5002  				},
  5003  			},
  5004  			true,
  5005  		},
  5006  
  5007  		"computed-only field with validateFunc": {
  5008  			map[string]*Schema{
  5009  				"string": {
  5010  					Type:     TypeString,
  5011  					Computed: true,
  5012  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  5013  						es = append(es, fmt.Errorf("this is not fine"))
  5014  						return
  5015  					},
  5016  				},
  5017  			},
  5018  			true,
  5019  		},
  5020  
  5021  		"computed-only field with diffSuppressFunc": {
  5022  			map[string]*Schema{
  5023  				"string": {
  5024  					Type:     TypeString,
  5025  					Computed: true,
  5026  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  5027  						// Always suppress any diff
  5028  						return false
  5029  					},
  5030  				},
  5031  			},
  5032  			true,
  5033  		},
  5034  
  5035  		"invalid field name format #1": {
  5036  			map[string]*Schema{
  5037  				"with space": {
  5038  					Type:     TypeString,
  5039  					Optional: true,
  5040  				},
  5041  			},
  5042  			true,
  5043  		},
  5044  
  5045  		"invalid field name format #2": {
  5046  			map[string]*Schema{
  5047  				"WithCapitals": {
  5048  					Type:     TypeString,
  5049  					Optional: true,
  5050  				},
  5051  			},
  5052  			true,
  5053  		},
  5054  
  5055  		"invalid field name format of a Deprecated field": {
  5056  			map[string]*Schema{
  5057  				"WithCapitals": {
  5058  					Type:       TypeString,
  5059  					Optional:   true,
  5060  					Deprecated: "Use with_underscores instead",
  5061  				},
  5062  			},
  5063  			false,
  5064  		},
  5065  
  5066  		"invalid field name format of a Removed field": {
  5067  			map[string]*Schema{
  5068  				"WithCapitals": {
  5069  					Type:     TypeString,
  5070  					Optional: true,
  5071  					Removed:  "Use with_underscores instead",
  5072  				},
  5073  			},
  5074  			false,
  5075  		},
  5076  
  5077  		"ConfigModeBlock with Elem *Resource": {
  5078  			map[string]*Schema{
  5079  				"block": {
  5080  					Type:       TypeList,
  5081  					ConfigMode: SchemaConfigModeBlock,
  5082  					Optional:   true,
  5083  					Elem:       &Resource{},
  5084  				},
  5085  			},
  5086  			false,
  5087  		},
  5088  
  5089  		"ConfigModeBlock Computed with Elem *Resource": {
  5090  			map[string]*Schema{
  5091  				"block": {
  5092  					Type:       TypeList,
  5093  					ConfigMode: SchemaConfigModeBlock,
  5094  					Computed:   true,
  5095  					Elem:       &Resource{},
  5096  				},
  5097  			},
  5098  			true, // ConfigMode of block cannot be used for computed schema
  5099  		},
  5100  
  5101  		"ConfigModeBlock with Elem *Schema": {
  5102  			map[string]*Schema{
  5103  				"block": {
  5104  					Type:       TypeList,
  5105  					ConfigMode: SchemaConfigModeBlock,
  5106  					Optional:   true,
  5107  					Elem: &Schema{
  5108  						Type: TypeString,
  5109  					},
  5110  				},
  5111  			},
  5112  			true,
  5113  		},
  5114  
  5115  		"ConfigModeBlock with no Elem": {
  5116  			map[string]*Schema{
  5117  				"block": {
  5118  					Type:       TypeString,
  5119  					ConfigMode: SchemaConfigModeBlock,
  5120  					Optional:   true,
  5121  				},
  5122  			},
  5123  			true,
  5124  		},
  5125  
  5126  		"ConfigModeBlock inside ConfigModeAttr": {
  5127  			map[string]*Schema{
  5128  				"block": {
  5129  					Type:       TypeList,
  5130  					ConfigMode: SchemaConfigModeAttr,
  5131  					Optional:   true,
  5132  					Elem: &Resource{
  5133  						Schema: map[string]*Schema{
  5134  							"sub": {
  5135  								Type:       TypeList,
  5136  								ConfigMode: SchemaConfigModeBlock,
  5137  								Elem:       &Resource{},
  5138  							},
  5139  						},
  5140  					},
  5141  				},
  5142  			},
  5143  			true, // ConfigMode of block cannot be used in child of schema with ConfigMode of attribute
  5144  		},
  5145  
  5146  		"ConfigModeAuto with *Resource inside ConfigModeAttr": {
  5147  			map[string]*Schema{
  5148  				"block": {
  5149  					Type:       TypeList,
  5150  					ConfigMode: SchemaConfigModeAttr,
  5151  					Optional:   true,
  5152  					Elem: &Resource{
  5153  						Schema: map[string]*Schema{
  5154  							"sub": {
  5155  								Type: TypeList,
  5156  								Elem: &Resource{},
  5157  							},
  5158  						},
  5159  					},
  5160  				},
  5161  			},
  5162  			true, // in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute
  5163  		},
  5164  	}
  5165  
  5166  	for tn, tc := range cases {
  5167  		t.Run(tn, func(t *testing.T) {
  5168  			err := schemaMap(tc.In).InternalValidate(nil)
  5169  			if err != nil != tc.Err {
  5170  				if tc.Err {
  5171  					t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In)
  5172  				}
  5173  				t.Fatalf("%q: Unexpected error occurred: %s\n\n%#v", tn, err, tc.In)
  5174  			}
  5175  		})
  5176  	}
  5177  
  5178  }
  5179  
  5180  func TestSchemaMap_DiffSuppress(t *testing.T) {
  5181  	cases := map[string]struct {
  5182  		Schema       map[string]*Schema
  5183  		State        *terraform.InstanceState
  5184  		Config       map[string]interface{}
  5185  		ExpectedDiff *terraform.InstanceDiff
  5186  		Err          bool
  5187  	}{
  5188  		"#0 - Suppress otherwise valid diff by returning true": {
  5189  			Schema: map[string]*Schema{
  5190  				"availability_zone": {
  5191  					Type:     TypeString,
  5192  					Optional: true,
  5193  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  5194  						// Always suppress any diff
  5195  						return true
  5196  					},
  5197  				},
  5198  			},
  5199  
  5200  			State: nil,
  5201  
  5202  			Config: map[string]interface{}{
  5203  				"availability_zone": "foo",
  5204  			},
  5205  
  5206  			ExpectedDiff: nil,
  5207  
  5208  			Err: false,
  5209  		},
  5210  
  5211  		"#1 - Don't suppress diff by returning false": {
  5212  			Schema: map[string]*Schema{
  5213  				"availability_zone": {
  5214  					Type:     TypeString,
  5215  					Optional: true,
  5216  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  5217  						// Always suppress any diff
  5218  						return false
  5219  					},
  5220  				},
  5221  			},
  5222  
  5223  			State: nil,
  5224  
  5225  			Config: map[string]interface{}{
  5226  				"availability_zone": "foo",
  5227  			},
  5228  
  5229  			ExpectedDiff: &terraform.InstanceDiff{
  5230  				Attributes: map[string]*terraform.ResourceAttrDiff{
  5231  					"availability_zone": {
  5232  						Old: "",
  5233  						New: "foo",
  5234  					},
  5235  				},
  5236  			},
  5237  
  5238  			Err: false,
  5239  		},
  5240  
  5241  		"Default with suppress makes no diff": {
  5242  			Schema: map[string]*Schema{
  5243  				"availability_zone": {
  5244  					Type:     TypeString,
  5245  					Optional: true,
  5246  					Default:  "foo",
  5247  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  5248  						return true
  5249  					},
  5250  				},
  5251  			},
  5252  
  5253  			State: nil,
  5254  
  5255  			Config: map[string]interface{}{},
  5256  
  5257  			ExpectedDiff: nil,
  5258  
  5259  			Err: false,
  5260  		},
  5261  
  5262  		"Default with false suppress makes diff": {
  5263  			Schema: map[string]*Schema{
  5264  				"availability_zone": {
  5265  					Type:     TypeString,
  5266  					Optional: true,
  5267  					Default:  "foo",
  5268  					DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool {
  5269  						return false
  5270  					},
  5271  				},
  5272  			},
  5273  
  5274  			State: nil,
  5275  
  5276  			Config: map[string]interface{}{},
  5277  
  5278  			ExpectedDiff: &terraform.InstanceDiff{
  5279  				Attributes: map[string]*terraform.ResourceAttrDiff{
  5280  					"availability_zone": {
  5281  						Old: "",
  5282  						New: "foo",
  5283  					},
  5284  				},
  5285  			},
  5286  
  5287  			Err: false,
  5288  		},
  5289  
  5290  		"Complex structure with set of computed string should mark root set as computed": {
  5291  			Schema: map[string]*Schema{
  5292  				"outer": {
  5293  					Type:     TypeSet,
  5294  					Optional: true,
  5295  					Elem: &Resource{
  5296  						Schema: map[string]*Schema{
  5297  							"outer_str": {
  5298  								Type:     TypeString,
  5299  								Optional: true,
  5300  							},
  5301  							"inner": {
  5302  								Type:     TypeSet,
  5303  								Optional: true,
  5304  								Elem: &Resource{
  5305  									Schema: map[string]*Schema{
  5306  										"inner_str": {
  5307  											Type:     TypeString,
  5308  											Optional: true,
  5309  										},
  5310  									},
  5311  								},
  5312  								Set: func(v interface{}) int {
  5313  									return 2
  5314  								},
  5315  							},
  5316  						},
  5317  					},
  5318  					Set: func(v interface{}) int {
  5319  						return 1
  5320  					},
  5321  				},
  5322  			},
  5323  
  5324  			State: nil,
  5325  
  5326  			Config: map[string]interface{}{
  5327  				"outer": []interface{}{
  5328  					map[string]interface{}{
  5329  						"outer_str": "foo",
  5330  						"inner": []interface{}{
  5331  							map[string]interface{}{
  5332  								"inner_str": hcl2shim.UnknownVariableValue,
  5333  							},
  5334  						},
  5335  					},
  5336  				},
  5337  			},
  5338  
  5339  			ExpectedDiff: &terraform.InstanceDiff{
  5340  				Attributes: map[string]*terraform.ResourceAttrDiff{
  5341  					"outer.#": {
  5342  						Old: "0",
  5343  						New: "1",
  5344  					},
  5345  					"outer.~1.outer_str": {
  5346  						Old: "",
  5347  						New: "foo",
  5348  					},
  5349  					"outer.~1.inner.#": {
  5350  						Old: "0",
  5351  						New: "1",
  5352  					},
  5353  					"outer.~1.inner.~2.inner_str": {
  5354  						Old:         "",
  5355  						New:         hcl2shim.UnknownVariableValue,
  5356  						NewComputed: true,
  5357  					},
  5358  				},
  5359  			},
  5360  
  5361  			Err: false,
  5362  		},
  5363  
  5364  		"Complex structure with complex list of computed string should mark root set as computed": {
  5365  			Schema: map[string]*Schema{
  5366  				"outer": {
  5367  					Type:     TypeSet,
  5368  					Optional: true,
  5369  					Elem: &Resource{
  5370  						Schema: map[string]*Schema{
  5371  							"outer_str": {
  5372  								Type:     TypeString,
  5373  								Optional: true,
  5374  							},
  5375  							"inner": {
  5376  								Type:     TypeList,
  5377  								Optional: true,
  5378  								Elem: &Resource{
  5379  									Schema: map[string]*Schema{
  5380  										"inner_str": {
  5381  											Type:     TypeString,
  5382  											Optional: true,
  5383  										},
  5384  									},
  5385  								},
  5386  							},
  5387  						},
  5388  					},
  5389  					Set: func(v interface{}) int {
  5390  						return 1
  5391  					},
  5392  				},
  5393  			},
  5394  
  5395  			State: nil,
  5396  
  5397  			Config: map[string]interface{}{
  5398  				"outer": []interface{}{
  5399  					map[string]interface{}{
  5400  						"outer_str": "foo",
  5401  						"inner": []interface{}{
  5402  							map[string]interface{}{
  5403  								"inner_str": hcl2shim.UnknownVariableValue,
  5404  							},
  5405  						},
  5406  					},
  5407  				},
  5408  			},
  5409  
  5410  			ExpectedDiff: &terraform.InstanceDiff{
  5411  				Attributes: map[string]*terraform.ResourceAttrDiff{
  5412  					"outer.#": {
  5413  						Old: "0",
  5414  						New: "1",
  5415  					},
  5416  					"outer.~1.outer_str": {
  5417  						Old: "",
  5418  						New: "foo",
  5419  					},
  5420  					"outer.~1.inner.#": {
  5421  						Old: "0",
  5422  						New: "1",
  5423  					},
  5424  					"outer.~1.inner.0.inner_str": {
  5425  						Old:         "",
  5426  						New:         hcl2shim.UnknownVariableValue,
  5427  						NewComputed: true,
  5428  					},
  5429  				},
  5430  			},
  5431  
  5432  			Err: false,
  5433  		},
  5434  	}
  5435  
  5436  	for tn, tc := range cases {
  5437  		t.Run(tn, func(t *testing.T) {
  5438  			c := terraform.NewResourceConfigRaw(tc.Config)
  5439  
  5440  			d, err := schemaMap(tc.Schema).Diff(tc.State, c, nil, nil, true)
  5441  			if err != nil != tc.Err {
  5442  				t.Fatalf("#%q err: %s", tn, err)
  5443  			}
  5444  
  5445  			if !reflect.DeepEqual(tc.ExpectedDiff, d) {
  5446  				t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.ExpectedDiff, d)
  5447  			}
  5448  		})
  5449  	}
  5450  }
  5451  
  5452  func TestSchemaMap_Validate(t *testing.T) {
  5453  	cases := map[string]struct {
  5454  		Schema   map[string]*Schema
  5455  		Config   map[string]interface{}
  5456  		Err      bool
  5457  		Errors   []error
  5458  		Warnings []string
  5459  	}{
  5460  		"Good": {
  5461  			Schema: map[string]*Schema{
  5462  				"availability_zone": {
  5463  					Type:     TypeString,
  5464  					Optional: true,
  5465  					Computed: true,
  5466  					ForceNew: true,
  5467  				},
  5468  			},
  5469  
  5470  			Config: map[string]interface{}{
  5471  				"availability_zone": "foo",
  5472  			},
  5473  		},
  5474  
  5475  		"Good, because the var is not set and that error will come elsewhere": {
  5476  			Schema: map[string]*Schema{
  5477  				"size": {
  5478  					Type:     TypeInt,
  5479  					Required: true,
  5480  				},
  5481  			},
  5482  
  5483  			Config: map[string]interface{}{
  5484  				"size": hcl2shim.UnknownVariableValue,
  5485  			},
  5486  		},
  5487  
  5488  		"Required field not set": {
  5489  			Schema: map[string]*Schema{
  5490  				"availability_zone": {
  5491  					Type:     TypeString,
  5492  					Required: true,
  5493  				},
  5494  			},
  5495  
  5496  			Config: map[string]interface{}{},
  5497  
  5498  			Err: true,
  5499  		},
  5500  
  5501  		"Invalid basic type": {
  5502  			Schema: map[string]*Schema{
  5503  				"port": {
  5504  					Type:     TypeInt,
  5505  					Required: true,
  5506  				},
  5507  			},
  5508  
  5509  			Config: map[string]interface{}{
  5510  				"port": "I am invalid",
  5511  			},
  5512  
  5513  			Err: true,
  5514  		},
  5515  
  5516  		"Invalid complex type": {
  5517  			Schema: map[string]*Schema{
  5518  				"user_data": {
  5519  					Type:     TypeString,
  5520  					Optional: true,
  5521  				},
  5522  			},
  5523  
  5524  			Config: map[string]interface{}{
  5525  				"user_data": []interface{}{
  5526  					map[string]interface{}{
  5527  						"foo": "bar",
  5528  					},
  5529  				},
  5530  			},
  5531  
  5532  			Err: true,
  5533  		},
  5534  
  5535  		"Bad type": {
  5536  			Schema: map[string]*Schema{
  5537  				"size": {
  5538  					Type:     TypeInt,
  5539  					Required: true,
  5540  				},
  5541  			},
  5542  
  5543  			Config: map[string]interface{}{
  5544  				"size": "nope",
  5545  			},
  5546  
  5547  			Err: true,
  5548  		},
  5549  
  5550  		"Required but has DefaultFunc": {
  5551  			Schema: map[string]*Schema{
  5552  				"availability_zone": {
  5553  					Type:     TypeString,
  5554  					Required: true,
  5555  					DefaultFunc: func() (interface{}, error) {
  5556  						return "foo", nil
  5557  					},
  5558  				},
  5559  			},
  5560  
  5561  			Config: nil,
  5562  		},
  5563  
  5564  		"Required but has DefaultFunc return nil": {
  5565  			Schema: map[string]*Schema{
  5566  				"availability_zone": {
  5567  					Type:     TypeString,
  5568  					Required: true,
  5569  					DefaultFunc: func() (interface{}, error) {
  5570  						return nil, nil
  5571  					},
  5572  				},
  5573  			},
  5574  
  5575  			Config: nil,
  5576  
  5577  			Err: true,
  5578  		},
  5579  
  5580  		"List with promotion": {
  5581  			Schema: map[string]*Schema{
  5582  				"ingress": {
  5583  					Type:          TypeList,
  5584  					Elem:          &Schema{Type: TypeInt},
  5585  					PromoteSingle: true,
  5586  					Optional:      true,
  5587  				},
  5588  			},
  5589  
  5590  			Config: map[string]interface{}{
  5591  				"ingress": "5",
  5592  			},
  5593  
  5594  			Err: false,
  5595  		},
  5596  
  5597  		"List with promotion set as list": {
  5598  			Schema: map[string]*Schema{
  5599  				"ingress": {
  5600  					Type:          TypeList,
  5601  					Elem:          &Schema{Type: TypeInt},
  5602  					PromoteSingle: true,
  5603  					Optional:      true,
  5604  				},
  5605  			},
  5606  
  5607  			Config: map[string]interface{}{
  5608  				"ingress": []interface{}{"5"},
  5609  			},
  5610  
  5611  			Err: false,
  5612  		},
  5613  
  5614  		"Optional sub-resource": {
  5615  			Schema: map[string]*Schema{
  5616  				"ingress": {
  5617  					Type: TypeList,
  5618  					Elem: &Resource{
  5619  						Schema: map[string]*Schema{
  5620  							"from": {
  5621  								Type:     TypeInt,
  5622  								Required: true,
  5623  							},
  5624  						},
  5625  					},
  5626  				},
  5627  			},
  5628  
  5629  			Config: map[string]interface{}{},
  5630  
  5631  			Err: false,
  5632  		},
  5633  
  5634  		"Sub-resource is the wrong type": {
  5635  			Schema: map[string]*Schema{
  5636  				"ingress": {
  5637  					Type:     TypeList,
  5638  					Required: true,
  5639  					Elem: &Resource{
  5640  						Schema: map[string]*Schema{
  5641  							"from": {
  5642  								Type:     TypeInt,
  5643  								Required: true,
  5644  							},
  5645  						},
  5646  					},
  5647  				},
  5648  			},
  5649  
  5650  			Config: map[string]interface{}{
  5651  				"ingress": []interface{}{"foo"},
  5652  			},
  5653  
  5654  			Err: true,
  5655  		},
  5656  
  5657  		"Not a list nested block": {
  5658  			Schema: map[string]*Schema{
  5659  				"ingress": {
  5660  					Type:     TypeList,
  5661  					Optional: true,
  5662  					Elem: &Resource{
  5663  						Schema: map[string]*Schema{
  5664  							"from": {
  5665  								Type:     TypeInt,
  5666  								Required: true,
  5667  							},
  5668  						},
  5669  					},
  5670  				},
  5671  			},
  5672  
  5673  			Config: map[string]interface{}{
  5674  				"ingress": "foo",
  5675  			},
  5676  
  5677  			Err: true,
  5678  			Errors: []error{
  5679  				fmt.Errorf(`ingress: should be a list`),
  5680  			},
  5681  		},
  5682  
  5683  		"Not a list primitive": {
  5684  			Schema: map[string]*Schema{
  5685  				"strings": {
  5686  					Type:     TypeList,
  5687  					Optional: true,
  5688  					Elem: &Schema{
  5689  						Type: TypeString,
  5690  					},
  5691  				},
  5692  			},
  5693  
  5694  			Config: map[string]interface{}{
  5695  				"strings": "foo",
  5696  			},
  5697  
  5698  			Err: true,
  5699  			Errors: []error{
  5700  				fmt.Errorf(`strings: should be a list`),
  5701  			},
  5702  		},
  5703  
  5704  		"Unknown list": {
  5705  			Schema: map[string]*Schema{
  5706  				"strings": {
  5707  					Type:     TypeList,
  5708  					Optional: true,
  5709  					Elem: &Schema{
  5710  						Type: TypeString,
  5711  					},
  5712  				},
  5713  			},
  5714  
  5715  			Config: map[string]interface{}{
  5716  				"strings": hcl2shim.UnknownVariableValue,
  5717  			},
  5718  
  5719  			Err: false,
  5720  		},
  5721  
  5722  		"Unknown + Deprecation": {
  5723  			Schema: map[string]*Schema{
  5724  				"old_news": {
  5725  					Type:       TypeString,
  5726  					Optional:   true,
  5727  					Deprecated: "please use 'new_news' instead",
  5728  				},
  5729  			},
  5730  
  5731  			Config: map[string]interface{}{
  5732  				"old_news": hcl2shim.UnknownVariableValue,
  5733  			},
  5734  
  5735  			Warnings: []string{
  5736  				"\"old_news\": [DEPRECATED] please use 'new_news' instead",
  5737  			},
  5738  		},
  5739  
  5740  		"Required sub-resource field": {
  5741  			Schema: map[string]*Schema{
  5742  				"ingress": {
  5743  					Type: TypeList,
  5744  					Elem: &Resource{
  5745  						Schema: map[string]*Schema{
  5746  							"from": {
  5747  								Type:     TypeInt,
  5748  								Required: true,
  5749  							},
  5750  						},
  5751  					},
  5752  				},
  5753  			},
  5754  
  5755  			Config: map[string]interface{}{
  5756  				"ingress": []interface{}{
  5757  					map[string]interface{}{},
  5758  				},
  5759  			},
  5760  
  5761  			Err: true,
  5762  		},
  5763  
  5764  		"Good sub-resource": {
  5765  			Schema: map[string]*Schema{
  5766  				"ingress": {
  5767  					Type:     TypeList,
  5768  					Optional: true,
  5769  					Elem: &Resource{
  5770  						Schema: map[string]*Schema{
  5771  							"from": {
  5772  								Type:     TypeInt,
  5773  								Required: true,
  5774  							},
  5775  						},
  5776  					},
  5777  				},
  5778  			},
  5779  
  5780  			Config: map[string]interface{}{
  5781  				"ingress": []interface{}{
  5782  					map[string]interface{}{
  5783  						"from": 80,
  5784  					},
  5785  				},
  5786  			},
  5787  
  5788  			Err: false,
  5789  		},
  5790  
  5791  		"Good sub-resource, computed value": {
  5792  			Schema: map[string]*Schema{
  5793  				"ingress": {
  5794  					Type:     TypeList,
  5795  					Optional: true,
  5796  					Elem: &Resource{
  5797  						Schema: map[string]*Schema{
  5798  							"from": {
  5799  								Type:     TypeInt,
  5800  								Optional: true,
  5801  							},
  5802  						},
  5803  					},
  5804  				},
  5805  			},
  5806  
  5807  			Config: map[string]interface{}{
  5808  				"ingress": []interface{}{
  5809  					map[string]interface{}{
  5810  						"from": hcl2shim.UnknownVariableValue,
  5811  					},
  5812  				},
  5813  			},
  5814  
  5815  			Err: false,
  5816  		},
  5817  
  5818  		"Invalid/unknown field": {
  5819  			Schema: map[string]*Schema{
  5820  				"availability_zone": {
  5821  					Type:     TypeString,
  5822  					Optional: true,
  5823  					Computed: true,
  5824  					ForceNew: true,
  5825  				},
  5826  			},
  5827  
  5828  			Config: map[string]interface{}{
  5829  				"foo": "bar",
  5830  			},
  5831  
  5832  			Err: true,
  5833  		},
  5834  
  5835  		"Invalid/unknown field with computed value": {
  5836  			Schema: map[string]*Schema{
  5837  				"availability_zone": {
  5838  					Type:     TypeString,
  5839  					Optional: true,
  5840  					Computed: true,
  5841  					ForceNew: true,
  5842  				},
  5843  			},
  5844  
  5845  			Config: map[string]interface{}{
  5846  				"foo": hcl2shim.UnknownVariableValue,
  5847  			},
  5848  
  5849  			Err: true,
  5850  		},
  5851  
  5852  		"Computed field set": {
  5853  			Schema: map[string]*Schema{
  5854  				"availability_zone": {
  5855  					Type:     TypeString,
  5856  					Computed: true,
  5857  				},
  5858  			},
  5859  
  5860  			Config: map[string]interface{}{
  5861  				"availability_zone": "bar",
  5862  			},
  5863  
  5864  			Err: true,
  5865  		},
  5866  
  5867  		"Not a set": {
  5868  			Schema: map[string]*Schema{
  5869  				"ports": {
  5870  					Type:     TypeSet,
  5871  					Required: true,
  5872  					Elem:     &Schema{Type: TypeInt},
  5873  					Set: func(a interface{}) int {
  5874  						return a.(int)
  5875  					},
  5876  				},
  5877  			},
  5878  
  5879  			Config: map[string]interface{}{
  5880  				"ports": "foo",
  5881  			},
  5882  
  5883  			Err: true,
  5884  		},
  5885  
  5886  		"Maps": {
  5887  			Schema: map[string]*Schema{
  5888  				"user_data": {
  5889  					Type:     TypeMap,
  5890  					Optional: true,
  5891  				},
  5892  			},
  5893  
  5894  			Config: map[string]interface{}{
  5895  				"user_data": "foo",
  5896  			},
  5897  
  5898  			Err: true,
  5899  		},
  5900  
  5901  		"Good map: data surrounded by extra slice": {
  5902  			Schema: map[string]*Schema{
  5903  				"user_data": {
  5904  					Type:     TypeMap,
  5905  					Optional: true,
  5906  				},
  5907  			},
  5908  
  5909  			Config: map[string]interface{}{
  5910  				"user_data": []interface{}{
  5911  					map[string]interface{}{
  5912  						"foo": "bar",
  5913  					},
  5914  				},
  5915  			},
  5916  		},
  5917  
  5918  		"Good map": {
  5919  			Schema: map[string]*Schema{
  5920  				"user_data": {
  5921  					Type:     TypeMap,
  5922  					Optional: true,
  5923  				},
  5924  			},
  5925  
  5926  			Config: map[string]interface{}{
  5927  				"user_data": map[string]interface{}{
  5928  					"foo": "bar",
  5929  				},
  5930  			},
  5931  		},
  5932  
  5933  		"Map with type specified as value type": {
  5934  			Schema: map[string]*Schema{
  5935  				"user_data": {
  5936  					Type:     TypeMap,
  5937  					Optional: true,
  5938  					Elem:     TypeBool,
  5939  				},
  5940  			},
  5941  
  5942  			Config: map[string]interface{}{
  5943  				"user_data": map[string]interface{}{
  5944  					"foo": "not_a_bool",
  5945  				},
  5946  			},
  5947  
  5948  			Err: true,
  5949  		},
  5950  
  5951  		"Map with type specified as nested Schema": {
  5952  			Schema: map[string]*Schema{
  5953  				"user_data": {
  5954  					Type:     TypeMap,
  5955  					Optional: true,
  5956  					Elem:     &Schema{Type: TypeBool},
  5957  				},
  5958  			},
  5959  
  5960  			Config: map[string]interface{}{
  5961  				"user_data": map[string]interface{}{
  5962  					"foo": "not_a_bool",
  5963  				},
  5964  			},
  5965  
  5966  			Err: true,
  5967  		},
  5968  
  5969  		"Bad map: just a slice": {
  5970  			Schema: map[string]*Schema{
  5971  				"user_data": {
  5972  					Type:     TypeMap,
  5973  					Optional: true,
  5974  				},
  5975  			},
  5976  
  5977  			Config: map[string]interface{}{
  5978  				"user_data": []interface{}{
  5979  					"foo",
  5980  				},
  5981  			},
  5982  
  5983  			Err: true,
  5984  		},
  5985  
  5986  		"Good set: config has slice with single interpolated value": {
  5987  			Schema: map[string]*Schema{
  5988  				"security_groups": {
  5989  					Type:     TypeSet,
  5990  					Optional: true,
  5991  					Computed: true,
  5992  					ForceNew: true,
  5993  					Elem:     &Schema{Type: TypeString},
  5994  					Set: func(v interface{}) int {
  5995  						return len(v.(string))
  5996  					},
  5997  				},
  5998  			},
  5999  
  6000  			Config: map[string]interface{}{
  6001  				"security_groups": []interface{}{"${var.foo}"},
  6002  			},
  6003  
  6004  			Err: false,
  6005  		},
  6006  
  6007  		"Bad set: config has single interpolated value": {
  6008  			Schema: map[string]*Schema{
  6009  				"security_groups": {
  6010  					Type:     TypeSet,
  6011  					Optional: true,
  6012  					Computed: true,
  6013  					ForceNew: true,
  6014  					Elem:     &Schema{Type: TypeString},
  6015  				},
  6016  			},
  6017  
  6018  			Config: map[string]interface{}{
  6019  				"security_groups": "${var.foo}",
  6020  			},
  6021  
  6022  			Err: true,
  6023  		},
  6024  
  6025  		"Bad, subresource should not allow unknown elements": {
  6026  			Schema: map[string]*Schema{
  6027  				"ingress": {
  6028  					Type:     TypeList,
  6029  					Optional: true,
  6030  					Elem: &Resource{
  6031  						Schema: map[string]*Schema{
  6032  							"port": {
  6033  								Type:     TypeInt,
  6034  								Required: true,
  6035  							},
  6036  						},
  6037  					},
  6038  				},
  6039  			},
  6040  
  6041  			Config: map[string]interface{}{
  6042  				"ingress": []interface{}{
  6043  					map[string]interface{}{
  6044  						"port":  80,
  6045  						"other": "yes",
  6046  					},
  6047  				},
  6048  			},
  6049  
  6050  			Err: true,
  6051  		},
  6052  
  6053  		"Bad, subresource should not allow invalid types": {
  6054  			Schema: map[string]*Schema{
  6055  				"ingress": {
  6056  					Type:     TypeList,
  6057  					Optional: true,
  6058  					Elem: &Resource{
  6059  						Schema: map[string]*Schema{
  6060  							"port": {
  6061  								Type:     TypeInt,
  6062  								Required: true,
  6063  							},
  6064  						},
  6065  					},
  6066  				},
  6067  			},
  6068  
  6069  			Config: map[string]interface{}{
  6070  				"ingress": []interface{}{
  6071  					map[string]interface{}{
  6072  						"port": "bad",
  6073  					},
  6074  				},
  6075  			},
  6076  
  6077  			Err: true,
  6078  		},
  6079  
  6080  		"Bad, should not allow lists to be assigned to string attributes": {
  6081  			Schema: map[string]*Schema{
  6082  				"availability_zone": {
  6083  					Type:     TypeString,
  6084  					Required: true,
  6085  				},
  6086  			},
  6087  
  6088  			Config: map[string]interface{}{
  6089  				"availability_zone": []interface{}{"foo", "bar", "baz"},
  6090  			},
  6091  
  6092  			Err: true,
  6093  		},
  6094  
  6095  		"Bad, should not allow maps to be assigned to string attributes": {
  6096  			Schema: map[string]*Schema{
  6097  				"availability_zone": {
  6098  					Type:     TypeString,
  6099  					Required: true,
  6100  				},
  6101  			},
  6102  
  6103  			Config: map[string]interface{}{
  6104  				"availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"},
  6105  			},
  6106  
  6107  			Err: true,
  6108  		},
  6109  
  6110  		"Deprecated attribute usage generates warning, but not error": {
  6111  			Schema: map[string]*Schema{
  6112  				"old_news": {
  6113  					Type:       TypeString,
  6114  					Optional:   true,
  6115  					Deprecated: "please use 'new_news' instead",
  6116  				},
  6117  			},
  6118  
  6119  			Config: map[string]interface{}{
  6120  				"old_news": "extra extra!",
  6121  			},
  6122  
  6123  			Err: false,
  6124  
  6125  			Warnings: []string{
  6126  				"\"old_news\": [DEPRECATED] please use 'new_news' instead",
  6127  			},
  6128  		},
  6129  
  6130  		"Deprecated generates no warnings if attr not used": {
  6131  			Schema: map[string]*Schema{
  6132  				"old_news": {
  6133  					Type:       TypeString,
  6134  					Optional:   true,
  6135  					Deprecated: "please use 'new_news' instead",
  6136  				},
  6137  			},
  6138  
  6139  			Err: false,
  6140  
  6141  			Warnings: nil,
  6142  		},
  6143  
  6144  		"Removed attribute usage generates error": {
  6145  			Schema: map[string]*Schema{
  6146  				"long_gone": {
  6147  					Type:     TypeString,
  6148  					Optional: true,
  6149  					Removed:  "no longer supported by Cloud API",
  6150  				},
  6151  			},
  6152  
  6153  			Config: map[string]interface{}{
  6154  				"long_gone": "still here!",
  6155  			},
  6156  
  6157  			Err: true,
  6158  			Errors: []error{
  6159  				fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"),
  6160  			},
  6161  		},
  6162  
  6163  		"Removed generates no errors if attr not used": {
  6164  			Schema: map[string]*Schema{
  6165  				"long_gone": {
  6166  					Type:     TypeString,
  6167  					Optional: true,
  6168  					Removed:  "no longer supported by Cloud API",
  6169  				},
  6170  			},
  6171  
  6172  			Err: false,
  6173  		},
  6174  
  6175  		"Conflicting attributes generate error": {
  6176  			Schema: map[string]*Schema{
  6177  				"whitelist": {
  6178  					Type:     TypeString,
  6179  					Optional: true,
  6180  				},
  6181  				"blacklist": {
  6182  					Type:          TypeString,
  6183  					Optional:      true,
  6184  					ConflictsWith: []string{"whitelist"},
  6185  				},
  6186  			},
  6187  
  6188  			Config: map[string]interface{}{
  6189  				"whitelist": "white-val",
  6190  				"blacklist": "black-val",
  6191  			},
  6192  
  6193  			Err: true,
  6194  			Errors: []error{
  6195  				fmt.Errorf("\"blacklist\": conflicts with whitelist"),
  6196  			},
  6197  		},
  6198  
  6199  		"Conflicting attributes okay when unknown 1": {
  6200  			Schema: map[string]*Schema{
  6201  				"whitelist": {
  6202  					Type:     TypeString,
  6203  					Optional: true,
  6204  				},
  6205  				"blacklist": {
  6206  					Type:          TypeString,
  6207  					Optional:      true,
  6208  					ConflictsWith: []string{"whitelist"},
  6209  				},
  6210  			},
  6211  
  6212  			Config: map[string]interface{}{
  6213  				"whitelist": "white-val",
  6214  				"blacklist": hcl2shim.UnknownVariableValue,
  6215  			},
  6216  
  6217  			Err: false,
  6218  		},
  6219  
  6220  		"Conflicting list attributes okay when unknown 1": {
  6221  			Schema: map[string]*Schema{
  6222  				"whitelist": {
  6223  					Type:     TypeList,
  6224  					Optional: true,
  6225  					Elem:     &Schema{Type: TypeString},
  6226  				},
  6227  				"blacklist": {
  6228  					Type:          TypeList,
  6229  					Optional:      true,
  6230  					Elem:          &Schema{Type: TypeString},
  6231  					ConflictsWith: []string{"whitelist"},
  6232  				},
  6233  			},
  6234  
  6235  			Config: map[string]interface{}{
  6236  				"whitelist": []interface{}{"white-val"},
  6237  				"blacklist": []interface{}{hcl2shim.UnknownVariableValue},
  6238  			},
  6239  
  6240  			Err: false,
  6241  		},
  6242  
  6243  		"Conflicting attributes okay when unknown 2": {
  6244  			Schema: map[string]*Schema{
  6245  				"whitelist": {
  6246  					Type:     TypeString,
  6247  					Optional: true,
  6248  				},
  6249  				"blacklist": {
  6250  					Type:          TypeString,
  6251  					Optional:      true,
  6252  					ConflictsWith: []string{"whitelist"},
  6253  				},
  6254  			},
  6255  
  6256  			Config: map[string]interface{}{
  6257  				"whitelist": hcl2shim.UnknownVariableValue,
  6258  				"blacklist": "black-val",
  6259  			},
  6260  
  6261  			Err: false,
  6262  		},
  6263  
  6264  		"Conflicting attributes generate error even if one is unknown": {
  6265  			Schema: map[string]*Schema{
  6266  				"whitelist": {
  6267  					Type:          TypeString,
  6268  					Optional:      true,
  6269  					ConflictsWith: []string{"blacklist", "greenlist"},
  6270  				},
  6271  				"blacklist": {
  6272  					Type:          TypeString,
  6273  					Optional:      true,
  6274  					ConflictsWith: []string{"whitelist", "greenlist"},
  6275  				},
  6276  				"greenlist": {
  6277  					Type:          TypeString,
  6278  					Optional:      true,
  6279  					ConflictsWith: []string{"whitelist", "blacklist"},
  6280  				},
  6281  			},
  6282  
  6283  			Config: map[string]interface{}{
  6284  				"whitelist": hcl2shim.UnknownVariableValue,
  6285  				"blacklist": "black-val",
  6286  				"greenlist": "green-val",
  6287  			},
  6288  
  6289  			Err: true,
  6290  			Errors: []error{
  6291  				fmt.Errorf("\"blacklist\": conflicts with greenlist"),
  6292  				fmt.Errorf("\"greenlist\": conflicts with blacklist"),
  6293  			},
  6294  		},
  6295  
  6296  		"Required attribute & undefined conflicting optional are good": {
  6297  			Schema: map[string]*Schema{
  6298  				"required_att": {
  6299  					Type:     TypeString,
  6300  					Required: true,
  6301  				},
  6302  				"optional_att": {
  6303  					Type:          TypeString,
  6304  					Optional:      true,
  6305  					ConflictsWith: []string{"required_att"},
  6306  				},
  6307  			},
  6308  
  6309  			Config: map[string]interface{}{
  6310  				"required_att": "required-val",
  6311  			},
  6312  
  6313  			Err: false,
  6314  		},
  6315  
  6316  		"Required conflicting attribute & defined optional generate error": {
  6317  			Schema: map[string]*Schema{
  6318  				"required_att": {
  6319  					Type:     TypeString,
  6320  					Required: true,
  6321  				},
  6322  				"optional_att": {
  6323  					Type:          TypeString,
  6324  					Optional:      true,
  6325  					ConflictsWith: []string{"required_att"},
  6326  				},
  6327  			},
  6328  
  6329  			Config: map[string]interface{}{
  6330  				"required_att": "required-val",
  6331  				"optional_att": "optional-val",
  6332  			},
  6333  
  6334  			Err: true,
  6335  			Errors: []error{
  6336  				fmt.Errorf(`"optional_att": conflicts with required_att`),
  6337  			},
  6338  		},
  6339  
  6340  		"Computed + Optional fields conflicting with each other": {
  6341  			Schema: map[string]*Schema{
  6342  				"foo_att": {
  6343  					Type:          TypeString,
  6344  					Optional:      true,
  6345  					Computed:      true,
  6346  					ConflictsWith: []string{"bar_att"},
  6347  				},
  6348  				"bar_att": {
  6349  					Type:          TypeString,
  6350  					Optional:      true,
  6351  					Computed:      true,
  6352  					ConflictsWith: []string{"foo_att"},
  6353  				},
  6354  			},
  6355  
  6356  			Config: map[string]interface{}{
  6357  				"foo_att": "foo-val",
  6358  				"bar_att": "bar-val",
  6359  			},
  6360  
  6361  			Err: true,
  6362  			Errors: []error{
  6363  				fmt.Errorf(`"foo_att": conflicts with bar_att`),
  6364  				fmt.Errorf(`"bar_att": conflicts with foo_att`),
  6365  			},
  6366  		},
  6367  
  6368  		"Computed + Optional fields NOT conflicting with each other": {
  6369  			Schema: map[string]*Schema{
  6370  				"foo_att": {
  6371  					Type:          TypeString,
  6372  					Optional:      true,
  6373  					Computed:      true,
  6374  					ConflictsWith: []string{"bar_att"},
  6375  				},
  6376  				"bar_att": {
  6377  					Type:          TypeString,
  6378  					Optional:      true,
  6379  					Computed:      true,
  6380  					ConflictsWith: []string{"foo_att"},
  6381  				},
  6382  			},
  6383  
  6384  			Config: map[string]interface{}{
  6385  				"foo_att": "foo-val",
  6386  			},
  6387  
  6388  			Err: false,
  6389  		},
  6390  
  6391  		"Computed + Optional fields that conflict with none set": {
  6392  			Schema: map[string]*Schema{
  6393  				"foo_att": {
  6394  					Type:          TypeString,
  6395  					Optional:      true,
  6396  					Computed:      true,
  6397  					ConflictsWith: []string{"bar_att"},
  6398  				},
  6399  				"bar_att": {
  6400  					Type:          TypeString,
  6401  					Optional:      true,
  6402  					Computed:      true,
  6403  					ConflictsWith: []string{"foo_att"},
  6404  				},
  6405  			},
  6406  
  6407  			Config: map[string]interface{}{},
  6408  
  6409  			Err: false,
  6410  		},
  6411  
  6412  		"Good with ValidateFunc": {
  6413  			Schema: map[string]*Schema{
  6414  				"validate_me": {
  6415  					Type:     TypeString,
  6416  					Required: true,
  6417  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6418  						return
  6419  					},
  6420  				},
  6421  			},
  6422  			Config: map[string]interface{}{
  6423  				"validate_me": "valid",
  6424  			},
  6425  			Err: false,
  6426  		},
  6427  
  6428  		"Bad with ValidateFunc": {
  6429  			Schema: map[string]*Schema{
  6430  				"validate_me": {
  6431  					Type:     TypeString,
  6432  					Required: true,
  6433  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6434  						es = append(es, fmt.Errorf("something is not right here"))
  6435  						return
  6436  					},
  6437  				},
  6438  			},
  6439  			Config: map[string]interface{}{
  6440  				"validate_me": "invalid",
  6441  			},
  6442  			Err: true,
  6443  			Errors: []error{
  6444  				fmt.Errorf(`something is not right here`),
  6445  			},
  6446  		},
  6447  
  6448  		"ValidateFunc not called when type does not match": {
  6449  			Schema: map[string]*Schema{
  6450  				"number": {
  6451  					Type:     TypeInt,
  6452  					Required: true,
  6453  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6454  						t.Fatalf("Should not have gotten validate call")
  6455  						return
  6456  					},
  6457  				},
  6458  			},
  6459  			Config: map[string]interface{}{
  6460  				"number": "NaN",
  6461  			},
  6462  			Err: true,
  6463  		},
  6464  
  6465  		"ValidateFunc gets decoded type": {
  6466  			Schema: map[string]*Schema{
  6467  				"maybe": {
  6468  					Type:     TypeBool,
  6469  					Required: true,
  6470  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6471  						if _, ok := v.(bool); !ok {
  6472  							t.Fatalf("Expected bool, got: %#v", v)
  6473  						}
  6474  						return
  6475  					},
  6476  				},
  6477  			},
  6478  			Config: map[string]interface{}{
  6479  				"maybe": "true",
  6480  			},
  6481  		},
  6482  
  6483  		"ValidateFunc is not called with a computed value": {
  6484  			Schema: map[string]*Schema{
  6485  				"validate_me": {
  6486  					Type:     TypeString,
  6487  					Required: true,
  6488  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6489  						es = append(es, fmt.Errorf("something is not right here"))
  6490  						return
  6491  					},
  6492  				},
  6493  			},
  6494  			Config: map[string]interface{}{
  6495  				"validate_me": hcl2shim.UnknownVariableValue,
  6496  			},
  6497  
  6498  			Err: false,
  6499  		},
  6500  
  6501  		"special timeouts field": {
  6502  			Schema: map[string]*Schema{
  6503  				"availability_zone": {
  6504  					Type:     TypeString,
  6505  					Optional: true,
  6506  					Computed: true,
  6507  					ForceNew: true,
  6508  				},
  6509  			},
  6510  
  6511  			Config: map[string]interface{}{
  6512  				TimeoutsConfigKey: "bar",
  6513  			},
  6514  
  6515  			Err: false,
  6516  		},
  6517  
  6518  		"invalid bool field": {
  6519  			Schema: map[string]*Schema{
  6520  				"bool_field": {
  6521  					Type:     TypeBool,
  6522  					Optional: true,
  6523  				},
  6524  			},
  6525  			Config: map[string]interface{}{
  6526  				"bool_field": "abcdef",
  6527  			},
  6528  			Err: true,
  6529  		},
  6530  		"invalid integer field": {
  6531  			Schema: map[string]*Schema{
  6532  				"integer_field": {
  6533  					Type:     TypeInt,
  6534  					Optional: true,
  6535  				},
  6536  			},
  6537  			Config: map[string]interface{}{
  6538  				"integer_field": "abcdef",
  6539  			},
  6540  			Err: true,
  6541  		},
  6542  		"invalid float field": {
  6543  			Schema: map[string]*Schema{
  6544  				"float_field": {
  6545  					Type:     TypeFloat,
  6546  					Optional: true,
  6547  				},
  6548  			},
  6549  			Config: map[string]interface{}{
  6550  				"float_field": "abcdef",
  6551  			},
  6552  			Err: true,
  6553  		},
  6554  
  6555  		// Invalid map values
  6556  		"invalid bool map value": {
  6557  			Schema: map[string]*Schema{
  6558  				"boolMap": {
  6559  					Type:     TypeMap,
  6560  					Elem:     TypeBool,
  6561  					Optional: true,
  6562  				},
  6563  			},
  6564  			Config: map[string]interface{}{
  6565  				"boolMap": map[string]interface{}{
  6566  					"boolField": "notbool",
  6567  				},
  6568  			},
  6569  			Err: true,
  6570  		},
  6571  		"invalid int map value": {
  6572  			Schema: map[string]*Schema{
  6573  				"intMap": {
  6574  					Type:     TypeMap,
  6575  					Elem:     TypeInt,
  6576  					Optional: true,
  6577  				},
  6578  			},
  6579  			Config: map[string]interface{}{
  6580  				"intMap": map[string]interface{}{
  6581  					"intField": "notInt",
  6582  				},
  6583  			},
  6584  			Err: true,
  6585  		},
  6586  		"invalid float map value": {
  6587  			Schema: map[string]*Schema{
  6588  				"floatMap": {
  6589  					Type:     TypeMap,
  6590  					Elem:     TypeFloat,
  6591  					Optional: true,
  6592  				},
  6593  			},
  6594  			Config: map[string]interface{}{
  6595  				"floatMap": map[string]interface{}{
  6596  					"floatField": "notFloat",
  6597  				},
  6598  			},
  6599  			Err: true,
  6600  		},
  6601  
  6602  		"map with positive validate function": {
  6603  			Schema: map[string]*Schema{
  6604  				"floatInt": {
  6605  					Type:     TypeMap,
  6606  					Elem:     TypeInt,
  6607  					Optional: true,
  6608  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6609  						return
  6610  					},
  6611  				},
  6612  			},
  6613  			Config: map[string]interface{}{
  6614  				"floatInt": map[string]interface{}{
  6615  					"rightAnswer": "42",
  6616  					"tooMuch":     "43",
  6617  				},
  6618  			},
  6619  			Err: false,
  6620  		},
  6621  		"map with negative validate function": {
  6622  			Schema: map[string]*Schema{
  6623  				"floatInt": {
  6624  					Type:     TypeMap,
  6625  					Elem:     TypeInt,
  6626  					Optional: true,
  6627  					ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6628  						es = append(es, fmt.Errorf("this is not fine"))
  6629  						return
  6630  					},
  6631  				},
  6632  			},
  6633  			Config: map[string]interface{}{
  6634  				"floatInt": map[string]interface{}{
  6635  					"rightAnswer": "42",
  6636  					"tooMuch":     "43",
  6637  				},
  6638  			},
  6639  			Err: true,
  6640  		},
  6641  
  6642  		// The Validation function should not see interpolation strings from
  6643  		// non-computed values.
  6644  		"set with partially computed list and map": {
  6645  			Schema: map[string]*Schema{
  6646  				"outer": {
  6647  					Type:     TypeSet,
  6648  					Optional: true,
  6649  					Computed: true,
  6650  					Elem: &Resource{
  6651  						Schema: map[string]*Schema{
  6652  							"list": {
  6653  								Type:     TypeList,
  6654  								Optional: true,
  6655  								Elem: &Schema{
  6656  									Type: TypeString,
  6657  									ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
  6658  										if strings.HasPrefix(v.(string), "${") {
  6659  											es = append(es, fmt.Errorf("should not have interpolations"))
  6660  										}
  6661  										return
  6662  									},
  6663  								},
  6664  							},
  6665  						},
  6666  					},
  6667  				},
  6668  			},
  6669  			Config: map[string]interface{}{
  6670  				"outer": []interface{}{
  6671  					map[string]interface{}{
  6672  						"list": []interface{}{"A", hcl2shim.UnknownVariableValue, "c"},
  6673  					},
  6674  				},
  6675  			},
  6676  			Err: false,
  6677  		},
  6678  		"unexpected nils values": {
  6679  			Schema: map[string]*Schema{
  6680  				"strings": {
  6681  					Type:     TypeList,
  6682  					Optional: true,
  6683  					Elem: &Schema{
  6684  						Type: TypeString,
  6685  					},
  6686  				},
  6687  				"block": {
  6688  					Type:     TypeList,
  6689  					Optional: true,
  6690  					Elem: &Resource{
  6691  						Schema: map[string]*Schema{
  6692  							"int": {
  6693  								Type:     TypeInt,
  6694  								Required: true,
  6695  							},
  6696  						},
  6697  					},
  6698  				},
  6699  			},
  6700  
  6701  			Config: map[string]interface{}{
  6702  				"strings": []interface{}{"1", nil},
  6703  				"block": []interface{}{map[string]interface{}{
  6704  					"int": nil,
  6705  				},
  6706  					nil,
  6707  				},
  6708  			},
  6709  			Err: true,
  6710  		},
  6711  	}
  6712  
  6713  	for tn, tc := range cases {
  6714  		t.Run(tn, func(t *testing.T) {
  6715  			c := terraform.NewResourceConfigRaw(tc.Config)
  6716  
  6717  			ws, es := schemaMap(tc.Schema).Validate(c)
  6718  			if len(es) > 0 != tc.Err {
  6719  				if len(es) == 0 {
  6720  					t.Errorf("%q: no errors", tn)
  6721  				}
  6722  
  6723  				for _, e := range es {
  6724  					t.Errorf("%q: err: %s", tn, e)
  6725  				}
  6726  
  6727  				t.FailNow()
  6728  			}
  6729  
  6730  			if !reflect.DeepEqual(ws, tc.Warnings) {
  6731  				t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws)
  6732  			}
  6733  
  6734  			if tc.Errors != nil {
  6735  				sort.Sort(errorSort(es))
  6736  				sort.Sort(errorSort(tc.Errors))
  6737  
  6738  				if !reflect.DeepEqual(es, tc.Errors) {
  6739  					t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es)
  6740  				}
  6741  			}
  6742  		})
  6743  
  6744  	}
  6745  }
  6746  
  6747  func TestSchemaSet_ValidateMaxItems(t *testing.T) {
  6748  	cases := map[string]struct {
  6749  		Schema          map[string]*Schema
  6750  		State           *terraform.InstanceState
  6751  		Config          map[string]interface{}
  6752  		ConfigVariables map[string]string
  6753  		Diff            *terraform.InstanceDiff
  6754  		Err             bool
  6755  		Errors          []error
  6756  	}{
  6757  		"#0": {
  6758  			Schema: map[string]*Schema{
  6759  				"aliases": {
  6760  					Type:     TypeSet,
  6761  					Optional: true,
  6762  					MaxItems: 1,
  6763  					Elem:     &Schema{Type: TypeString},
  6764  				},
  6765  			},
  6766  			State: nil,
  6767  			Config: map[string]interface{}{
  6768  				"aliases": []interface{}{"foo", "bar"},
  6769  			},
  6770  			Diff: nil,
  6771  			Err:  true,
  6772  			Errors: []error{
  6773  				fmt.Errorf("aliases: attribute supports 1 item maximum, config has 2 declared"),
  6774  			},
  6775  		},
  6776  		"#1": {
  6777  			Schema: map[string]*Schema{
  6778  				"aliases": {
  6779  					Type:     TypeSet,
  6780  					Optional: true,
  6781  					Elem:     &Schema{Type: TypeString},
  6782  				},
  6783  			},
  6784  			State: nil,
  6785  			Config: map[string]interface{}{
  6786  				"aliases": []interface{}{"foo", "bar"},
  6787  			},
  6788  			Diff:   nil,
  6789  			Err:    false,
  6790  			Errors: nil,
  6791  		},
  6792  		"#2": {
  6793  			Schema: map[string]*Schema{
  6794  				"aliases": {
  6795  					Type:     TypeSet,
  6796  					Optional: true,
  6797  					MaxItems: 1,
  6798  					Elem:     &Schema{Type: TypeString},
  6799  				},
  6800  			},
  6801  			State: nil,
  6802  			Config: map[string]interface{}{
  6803  				"aliases": []interface{}{"foo"},
  6804  			},
  6805  			Diff:   nil,
  6806  			Err:    false,
  6807  			Errors: nil,
  6808  		},
  6809  	}
  6810  
  6811  	for tn, tc := range cases {
  6812  		c := terraform.NewResourceConfigRaw(tc.Config)
  6813  		_, es := schemaMap(tc.Schema).Validate(c)
  6814  
  6815  		if len(es) > 0 != tc.Err {
  6816  			if len(es) == 0 {
  6817  				t.Errorf("%q: no errors", tn)
  6818  			}
  6819  
  6820  			for _, e := range es {
  6821  				t.Errorf("%q: err: %s", tn, e)
  6822  			}
  6823  
  6824  			t.FailNow()
  6825  		}
  6826  
  6827  		if tc.Errors != nil {
  6828  			if !reflect.DeepEqual(es, tc.Errors) {
  6829  				t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es)
  6830  			}
  6831  		}
  6832  	}
  6833  }
  6834  
  6835  func TestSchemaSet_ValidateMinItems(t *testing.T) {
  6836  	cases := map[string]struct {
  6837  		Schema          map[string]*Schema
  6838  		State           *terraform.InstanceState
  6839  		Config          map[string]interface{}
  6840  		ConfigVariables map[string]string
  6841  		Diff            *terraform.InstanceDiff
  6842  		Err             bool
  6843  		Errors          []error
  6844  	}{
  6845  		"#0": {
  6846  			Schema: map[string]*Schema{
  6847  				"aliases": {
  6848  					Type:     TypeSet,
  6849  					Optional: true,
  6850  					MinItems: 2,
  6851  					Elem:     &Schema{Type: TypeString},
  6852  				},
  6853  			},
  6854  			State: nil,
  6855  			Config: map[string]interface{}{
  6856  				"aliases": []interface{}{"foo", "bar"},
  6857  			},
  6858  			Diff:   nil,
  6859  			Err:    false,
  6860  			Errors: nil,
  6861  		},
  6862  		"#1": {
  6863  			Schema: map[string]*Schema{
  6864  				"aliases": {
  6865  					Type:     TypeSet,
  6866  					Optional: true,
  6867  					Elem:     &Schema{Type: TypeString},
  6868  				},
  6869  			},
  6870  			State: nil,
  6871  			Config: map[string]interface{}{
  6872  				"aliases": []interface{}{"foo", "bar"},
  6873  			},
  6874  			Diff:   nil,
  6875  			Err:    false,
  6876  			Errors: nil,
  6877  		},
  6878  		"#2": {
  6879  			Schema: map[string]*Schema{
  6880  				"aliases": {
  6881  					Type:     TypeSet,
  6882  					Optional: true,
  6883  					MinItems: 2,
  6884  					Elem:     &Schema{Type: TypeString},
  6885  				},
  6886  			},
  6887  			State: nil,
  6888  			Config: map[string]interface{}{
  6889  				"aliases": []interface{}{"foo"},
  6890  			},
  6891  			Diff: nil,
  6892  			Err:  true,
  6893  			Errors: []error{
  6894  				fmt.Errorf("aliases: attribute supports 2 item as a minimum, config has 1 declared"),
  6895  			},
  6896  		},
  6897  	}
  6898  
  6899  	for tn, tc := range cases {
  6900  		c := terraform.NewResourceConfigRaw(tc.Config)
  6901  		_, es := schemaMap(tc.Schema).Validate(c)
  6902  
  6903  		if len(es) > 0 != tc.Err {
  6904  			if len(es) == 0 {
  6905  				t.Errorf("%q: no errors", tn)
  6906  			}
  6907  
  6908  			for _, e := range es {
  6909  				t.Errorf("%q: err: %s", tn, e)
  6910  			}
  6911  
  6912  			t.FailNow()
  6913  		}
  6914  
  6915  		if tc.Errors != nil {
  6916  			if !reflect.DeepEqual(es, tc.Errors) {
  6917  				t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es)
  6918  			}
  6919  		}
  6920  	}
  6921  }
  6922  
  6923  // errorSort implements sort.Interface to sort errors by their error message
  6924  type errorSort []error
  6925  
  6926  func (e errorSort) Len() int      { return len(e) }
  6927  func (e errorSort) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
  6928  func (e errorSort) Less(i, j int) bool {
  6929  	return e[i].Error() < e[j].Error()
  6930  }
  6931  
  6932  func TestSchemaMapDeepCopy(t *testing.T) {
  6933  	schema := map[string]*Schema{
  6934  		"foo": {
  6935  			Type: TypeString,
  6936  		},
  6937  	}
  6938  	source := schemaMap(schema)
  6939  	dest := source.DeepCopy()
  6940  	dest["foo"].ForceNew = true
  6941  	if reflect.DeepEqual(source, dest) {
  6942  		t.Fatalf("source and dest should not match")
  6943  	}
  6944  }
  6945  
  6946  func TestValidateConflictingAttributes(t *testing.T) {
  6947  	cases := map[string]struct {
  6948  		Key    string
  6949  		Schema *Schema
  6950  		Config map[string]interface{}
  6951  		Err    bool
  6952  	}{
  6953  		"root attribute self conflicting": {
  6954  			Key: "self",
  6955  			Schema: &Schema{
  6956  				Type:          TypeBool,
  6957  				Optional:      true,
  6958  				ConflictsWith: []string{"self"},
  6959  			},
  6960  			Config: map[string]interface{}{
  6961  				"self": true,
  6962  			},
  6963  			Err: true,
  6964  		},
  6965  
  6966  		"root attribute conflicting unconfigured self unconfigured": {
  6967  			Key: "self",
  6968  			Schema: &Schema{
  6969  				Type:          TypeBool,
  6970  				Optional:      true,
  6971  				ConflictsWith: []string{"root_attr"},
  6972  			},
  6973  			Config: map[string]interface{}{},
  6974  			Err:    false,
  6975  		},
  6976  
  6977  		"root attribute conflicting unconfigured self unknown": {
  6978  			Key: "self",
  6979  			Schema: &Schema{
  6980  				Type:          TypeBool,
  6981  				Optional:      true,
  6982  				ConflictsWith: []string{"root_attr"},
  6983  			},
  6984  			Config: map[string]interface{}{
  6985  				"self": hcl2shim.UnknownVariableValue,
  6986  			},
  6987  			Err: false,
  6988  		},
  6989  
  6990  		"root attribute conflicting unconfigured self known": {
  6991  			Key: "self",
  6992  			Schema: &Schema{
  6993  				Type:          TypeBool,
  6994  				Optional:      true,
  6995  				ConflictsWith: []string{"root_attr"},
  6996  			},
  6997  			Config: map[string]interface{}{
  6998  				"self": true,
  6999  			},
  7000  			Err: false,
  7001  		},
  7002  
  7003  		"root attribute conflicting unknown self unconfigured": {
  7004  			Key: "self",
  7005  			Schema: &Schema{
  7006  				Type:          TypeBool,
  7007  				Optional:      true,
  7008  				ConflictsWith: []string{"root_attr"},
  7009  			},
  7010  			Config: map[string]interface{}{
  7011  				"root_attr": hcl2shim.UnknownVariableValue,
  7012  			},
  7013  			Err: false,
  7014  		},
  7015  
  7016  		"root attribute conflicting unknown self unknown": {
  7017  			Key: "self",
  7018  			Schema: &Schema{
  7019  				Type:          TypeBool,
  7020  				Optional:      true,
  7021  				ConflictsWith: []string{"root_attr"},
  7022  			},
  7023  			Config: map[string]interface{}{
  7024  				"root_attr": hcl2shim.UnknownVariableValue,
  7025  				"self":      hcl2shim.UnknownVariableValue,
  7026  			},
  7027  			Err: false,
  7028  		},
  7029  
  7030  		"root attribute conflicting unknown self known": {
  7031  			Key: "self",
  7032  			Schema: &Schema{
  7033  				Type:          TypeBool,
  7034  				Optional:      true,
  7035  				ConflictsWith: []string{"root_attr"},
  7036  			},
  7037  			Config: map[string]interface{}{
  7038  				"root_attr": hcl2shim.UnknownVariableValue,
  7039  				"self":      true,
  7040  			},
  7041  			Err: false,
  7042  		},
  7043  
  7044  		"root attribute conflicting known self unconfigured": {
  7045  			Key: "self",
  7046  			Schema: &Schema{
  7047  				Type:          TypeBool,
  7048  				Optional:      true,
  7049  				ConflictsWith: []string{"root_attr"},
  7050  			},
  7051  			Config: map[string]interface{}{
  7052  				"root_attr": true,
  7053  			},
  7054  			Err: true,
  7055  		},
  7056  
  7057  		"root attribute conflicting known self unknown": {
  7058  			Key: "self",
  7059  			Schema: &Schema{
  7060  				Type:          TypeBool,
  7061  				Optional:      true,
  7062  				ConflictsWith: []string{"root_attr"},
  7063  			},
  7064  			Config: map[string]interface{}{
  7065  				"root_attr": true,
  7066  				"self":      hcl2shim.UnknownVariableValue,
  7067  			},
  7068  			Err: true,
  7069  		},
  7070  
  7071  		"root attribute conflicting known self known": {
  7072  			Key: "self",
  7073  			Schema: &Schema{
  7074  				Type:          TypeBool,
  7075  				Optional:      true,
  7076  				ConflictsWith: []string{"root_attr"},
  7077  			},
  7078  			Config: map[string]interface{}{
  7079  				"root_attr": true,
  7080  				"self":      true,
  7081  			},
  7082  			Err: true,
  7083  		},
  7084  
  7085  		"configuration block attribute list index syntax self conflicting": {
  7086  			Key: "self",
  7087  			Schema: &Schema{
  7088  				Type:          TypeBool,
  7089  				Optional:      true,
  7090  				ConflictsWith: []string{"config_block_attr.0.self"},
  7091  			},
  7092  			Config: map[string]interface{}{
  7093  				"config_block_attr": []interface{}{
  7094  					map[string]interface{}{
  7095  						"self": true,
  7096  					},
  7097  				},
  7098  			},
  7099  			Err: true,
  7100  		},
  7101  
  7102  		"configuration block attribute list index syntax conflicting unconfigured self unconfigured": {
  7103  			Key: "self",
  7104  			Schema: &Schema{
  7105  				Type:          TypeBool,
  7106  				Optional:      true,
  7107  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7108  			},
  7109  			Config: map[string]interface{}{},
  7110  			Err:    false,
  7111  		},
  7112  
  7113  		"configuration block attribute list index syntax conflicting unconfigured self unknown": {
  7114  			Key: "self",
  7115  			Schema: &Schema{
  7116  				Type:          TypeBool,
  7117  				Optional:      true,
  7118  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7119  			},
  7120  			Config: map[string]interface{}{
  7121  				"config_block_attr": []interface{}{
  7122  					map[string]interface{}{
  7123  						"self": hcl2shim.UnknownVariableValue,
  7124  					},
  7125  				},
  7126  			},
  7127  			Err: false,
  7128  		},
  7129  
  7130  		"configuration block attribute list index syntax conflicting unconfigured self known": {
  7131  			Key: "self",
  7132  			Schema: &Schema{
  7133  				Type:          TypeBool,
  7134  				Optional:      true,
  7135  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7136  			},
  7137  			Config: map[string]interface{}{
  7138  				"config_block_attr": []interface{}{
  7139  					map[string]interface{}{
  7140  						"self": true,
  7141  					},
  7142  				},
  7143  			},
  7144  			Err: false,
  7145  		},
  7146  
  7147  		"configuration block attribute list index syntax conflicting unknown self unconfigured": {
  7148  			Key: "self",
  7149  			Schema: &Schema{
  7150  				Type:          TypeBool,
  7151  				Optional:      true,
  7152  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7153  			},
  7154  			Config: map[string]interface{}{
  7155  				"config_block_attr": []interface{}{
  7156  					map[string]interface{}{
  7157  						"nested_attr": hcl2shim.UnknownVariableValue,
  7158  					},
  7159  				},
  7160  			},
  7161  			Err: false,
  7162  		},
  7163  
  7164  		"configuration block attribute list index syntax conflicting unknown self unknown": {
  7165  			Key: "self",
  7166  			Schema: &Schema{
  7167  				Type:          TypeBool,
  7168  				Optional:      true,
  7169  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7170  			},
  7171  			Config: map[string]interface{}{
  7172  				"config_block_attr": []interface{}{
  7173  					map[string]interface{}{
  7174  						"nested_attr": hcl2shim.UnknownVariableValue,
  7175  						"self":        hcl2shim.UnknownVariableValue,
  7176  					},
  7177  				},
  7178  			},
  7179  			Err: false,
  7180  		},
  7181  
  7182  		"configuration block attribute list index syntax conflicting unknown self known": {
  7183  			Key: "self",
  7184  			Schema: &Schema{
  7185  				Type:          TypeBool,
  7186  				Optional:      true,
  7187  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7188  			},
  7189  			Config: map[string]interface{}{
  7190  				"config_block_attr": []interface{}{
  7191  					map[string]interface{}{
  7192  						"nested_attr": hcl2shim.UnknownVariableValue,
  7193  						"self":        true,
  7194  					},
  7195  				},
  7196  			},
  7197  			Err: false,
  7198  		},
  7199  
  7200  		"configuration block attribute list index syntax conflicting known self unconfigured": {
  7201  			Key: "self",
  7202  			Schema: &Schema{
  7203  				Type:          TypeBool,
  7204  				Optional:      true,
  7205  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7206  			},
  7207  			Config: map[string]interface{}{
  7208  				"config_block_attr": []interface{}{
  7209  					map[string]interface{}{
  7210  						"nested_attr": true,
  7211  					},
  7212  				},
  7213  			},
  7214  			Err: true,
  7215  		},
  7216  
  7217  		"configuration block attribute list index syntax conflicting known self unknown": {
  7218  			Key: "self",
  7219  			Schema: &Schema{
  7220  				Type:          TypeBool,
  7221  				Optional:      true,
  7222  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7223  			},
  7224  			Config: map[string]interface{}{
  7225  				"config_block_attr": []interface{}{
  7226  					map[string]interface{}{
  7227  						"nested_attr": true,
  7228  						"self":        hcl2shim.UnknownVariableValue,
  7229  					},
  7230  				},
  7231  			},
  7232  			Err: true,
  7233  		},
  7234  
  7235  		"configuration block attribute list index syntax conflicting known self known": {
  7236  			Key: "self",
  7237  			Schema: &Schema{
  7238  				Type:          TypeBool,
  7239  				Optional:      true,
  7240  				ConflictsWith: []string{"config_block_attr.0.nested_attr"},
  7241  			},
  7242  			Config: map[string]interface{}{
  7243  				"config_block_attr": []interface{}{
  7244  					map[string]interface{}{
  7245  						"nested_attr": true,
  7246  						"self":        true,
  7247  					},
  7248  				},
  7249  			},
  7250  			Err: true,
  7251  		},
  7252  
  7253  		"configuration block attribute map key syntax self conflicting": {
  7254  			Key: "self",
  7255  			Schema: &Schema{
  7256  				Type:          TypeBool,
  7257  				Optional:      true,
  7258  				ConflictsWith: []string{"config_block_attr.self"},
  7259  			},
  7260  			Config: map[string]interface{}{
  7261  				"config_block_attr": []interface{}{
  7262  					map[string]interface{}{
  7263  						"self": true,
  7264  					},
  7265  				},
  7266  			},
  7267  			Err: false,
  7268  		},
  7269  
  7270  		"configuration block attribute map key syntax conflicting known self unconfigured": {
  7271  			Key: "self",
  7272  			Schema: &Schema{
  7273  				Type:          TypeBool,
  7274  				Optional:      true,
  7275  				ConflictsWith: []string{"config_block_attr.nested_attr"},
  7276  			},
  7277  			Config: map[string]interface{}{
  7278  				"config_block_attr": []interface{}{
  7279  					map[string]interface{}{
  7280  						"nested_attr": true,
  7281  					},
  7282  				},
  7283  			},
  7284  			Err: false,
  7285  		},
  7286  
  7287  		"configuration block attribute map key syntax conflicting known self unknown": {
  7288  			Key: "self",
  7289  			Schema: &Schema{
  7290  				Type:          TypeBool,
  7291  				Optional:      true,
  7292  				ConflictsWith: []string{"config_block_attr.nested_attr"},
  7293  			},
  7294  			Config: map[string]interface{}{
  7295  				"config_block_attr": []interface{}{
  7296  					map[string]interface{}{
  7297  						"nested_attr": true,
  7298  						"self":        hcl2shim.UnknownVariableValue,
  7299  					},
  7300  				},
  7301  			},
  7302  			Err: false,
  7303  		},
  7304  
  7305  		"configuration block attribute map key syntax conflicting known self known": {
  7306  			Key: "self",
  7307  			Schema: &Schema{
  7308  				Type:          TypeBool,
  7309  				Optional:      true,
  7310  				ConflictsWith: []string{"config_block_attr.nested_attr"},
  7311  			},
  7312  			Config: map[string]interface{}{
  7313  				"config_block_attr": []interface{}{
  7314  					map[string]interface{}{
  7315  						"nested_attr": true,
  7316  						"self":        true,
  7317  					},
  7318  				},
  7319  			},
  7320  			Err: false,
  7321  		},
  7322  	}
  7323  
  7324  	for tn, tc := range cases {
  7325  		t.Run(tn, func(t *testing.T) {
  7326  			c := terraform.NewResourceConfigRaw(tc.Config)
  7327  
  7328  			err := validateConflictingAttributes(tc.Key, tc.Schema, c)
  7329  			if err == nil && tc.Err {
  7330  				t.Fatalf("expected error")
  7331  			}
  7332  
  7333  			if err != nil && !tc.Err {
  7334  				t.Fatalf("didn't expect error, got error: %+v", err)
  7335  			}
  7336  		})
  7337  	}
  7338  
  7339  }
  7340  
  7341  func TestValidateExactlyOneOfAttributes(t *testing.T) {
  7342  	cases := map[string]struct {
  7343  		Key    string
  7344  		Schema map[string]*Schema
  7345  		Config map[string]interface{}
  7346  		Err    bool
  7347  	}{
  7348  
  7349  		"two attributes specified": {
  7350  			Key: "whitelist",
  7351  			Schema: map[string]*Schema{
  7352  				"whitelist": {
  7353  					Type:         TypeBool,
  7354  					Optional:     true,
  7355  					ExactlyOneOf: []string{"blacklist"},
  7356  				},
  7357  				"blacklist": {
  7358  					Type:         TypeBool,
  7359  					Optional:     true,
  7360  					ExactlyOneOf: []string{"whitelist"},
  7361  				},
  7362  			},
  7363  
  7364  			Config: map[string]interface{}{
  7365  				"whitelist": true,
  7366  				"blacklist": true,
  7367  			},
  7368  			Err: true,
  7369  		},
  7370  
  7371  		"one attributes specified": {
  7372  			Key: "whitelist",
  7373  			Schema: map[string]*Schema{
  7374  				"whitelist": {
  7375  					Type:         TypeBool,
  7376  					Optional:     true,
  7377  					ExactlyOneOf: []string{"blacklist"},
  7378  				},
  7379  				"blacklist": {
  7380  					Type:         TypeBool,
  7381  					Optional:     true,
  7382  					ExactlyOneOf: []string{"whitelist"},
  7383  				},
  7384  			},
  7385  
  7386  			Config: map[string]interface{}{
  7387  				"whitelist": true,
  7388  			},
  7389  			Err: false,
  7390  		},
  7391  
  7392  		"two attributes of three specified": {
  7393  			Key: "whitelist",
  7394  			Schema: map[string]*Schema{
  7395  				"whitelist": {
  7396  					Type:         TypeBool,
  7397  					Optional:     true,
  7398  					ExactlyOneOf: []string{"blacklist", "purplelist"},
  7399  				},
  7400  				"blacklist": {
  7401  					Type:         TypeBool,
  7402  					Optional:     true,
  7403  					ExactlyOneOf: []string{"whitelist", "purplelist"},
  7404  				},
  7405  				"purplelist": {
  7406  					Type:         TypeBool,
  7407  					Optional:     true,
  7408  					ExactlyOneOf: []string{"whitelist", "blacklist"},
  7409  				},
  7410  			},
  7411  
  7412  			Config: map[string]interface{}{
  7413  				"whitelist":  true,
  7414  				"purplelist": true,
  7415  			},
  7416  			Err: true,
  7417  		},
  7418  
  7419  		"one attributes of three specified": {
  7420  			Key: "whitelist",
  7421  			Schema: map[string]*Schema{
  7422  				"whitelist": {
  7423  					Type:         TypeBool,
  7424  					Optional:     true,
  7425  					ExactlyOneOf: []string{"blacklist", "purplelist"},
  7426  				},
  7427  				"blacklist": {
  7428  					Type:         TypeBool,
  7429  					Optional:     true,
  7430  					ExactlyOneOf: []string{"whitelist", "purplelist"},
  7431  				},
  7432  				"purplelist": {
  7433  					Type:         TypeBool,
  7434  					Optional:     true,
  7435  					ExactlyOneOf: []string{"whitelist", "blacklist"},
  7436  				},
  7437  			},
  7438  
  7439  			Config: map[string]interface{}{
  7440  				"purplelist": true,
  7441  			},
  7442  			Err: false,
  7443  		},
  7444  
  7445  		"no attributes of three specified": {
  7446  			Key: "whitelist",
  7447  			Schema: map[string]*Schema{
  7448  				"whitelist": {
  7449  					Type:         TypeBool,
  7450  					Optional:     true,
  7451  					ExactlyOneOf: []string{"blacklist", "purplelist"},
  7452  				},
  7453  				"blacklist": {
  7454  					Type:         TypeBool,
  7455  					Optional:     true,
  7456  					ExactlyOneOf: []string{"whitelist", "purplelist"},
  7457  				},
  7458  				"purplelist": {
  7459  					Type:         TypeBool,
  7460  					Optional:     true,
  7461  					ExactlyOneOf: []string{"whitelist", "blacklist"},
  7462  				},
  7463  			},
  7464  
  7465  			Config: map[string]interface{}{},
  7466  			Err:    true,
  7467  		},
  7468  
  7469  		"Only Unknown Variable Value": {
  7470  			Key: "whitelist",
  7471  			Schema: map[string]*Schema{
  7472  				"whitelist": {
  7473  					Type:         TypeBool,
  7474  					Optional:     true,
  7475  					ExactlyOneOf: []string{"blacklist", "purplelist"},
  7476  				},
  7477  				"blacklist": {
  7478  					Type:         TypeBool,
  7479  					Optional:     true,
  7480  					ExactlyOneOf: []string{"whitelist", "purplelist"},
  7481  				},
  7482  				"purplelist": {
  7483  					Type:         TypeBool,
  7484  					Optional:     true,
  7485  					ExactlyOneOf: []string{"whitelist", "blacklist"},
  7486  				},
  7487  			},
  7488  
  7489  			Config: map[string]interface{}{
  7490  				"purplelist": hcl2shim.UnknownVariableValue,
  7491  			},
  7492  			Err: false,
  7493  		},
  7494  
  7495  		"Unknown Variable Value and Known Value": {
  7496  			Key: "whitelist",
  7497  			Schema: map[string]*Schema{
  7498  				"whitelist": {
  7499  					Type:         TypeBool,
  7500  					Optional:     true,
  7501  					ExactlyOneOf: []string{"blacklist", "purplelist"},
  7502  				},
  7503  				"blacklist": {
  7504  					Type:         TypeBool,
  7505  					Optional:     true,
  7506  					ExactlyOneOf: []string{"whitelist", "purplelist"},
  7507  				},
  7508  				"purplelist": {
  7509  					Type:         TypeBool,
  7510  					Optional:     true,
  7511  					ExactlyOneOf: []string{"whitelist", "blacklist"},
  7512  				},
  7513  			},
  7514  
  7515  			Config: map[string]interface{}{
  7516  				"purplelist": hcl2shim.UnknownVariableValue,
  7517  				"whitelist":  true,
  7518  			},
  7519  			Err: false,
  7520  		},
  7521  
  7522  		"Unknown Variable Value and 2 Known Value": {
  7523  			Key: "whitelist",
  7524  			Schema: map[string]*Schema{
  7525  				"whitelist": {
  7526  					Type:         TypeBool,
  7527  					Optional:     true,
  7528  					ExactlyOneOf: []string{"blacklist", "purplelist"},
  7529  				},
  7530  				"blacklist": {
  7531  					Type:         TypeBool,
  7532  					Optional:     true,
  7533  					ExactlyOneOf: []string{"whitelist", "purplelist"},
  7534  				},
  7535  				"purplelist": {
  7536  					Type:         TypeBool,
  7537  					Optional:     true,
  7538  					ExactlyOneOf: []string{"whitelist", "blacklist"},
  7539  				},
  7540  			},
  7541  
  7542  			Config: map[string]interface{}{
  7543  				"purplelist": hcl2shim.UnknownVariableValue,
  7544  				"whitelist":  true,
  7545  				"blacklist":  true,
  7546  			},
  7547  			Err: true,
  7548  		},
  7549  
  7550  		"unknown list values": {
  7551  			Key: "allow",
  7552  			Schema: map[string]*Schema{
  7553  				"allow": {
  7554  					Type:     TypeList,
  7555  					Optional: true,
  7556  					Elem: &Resource{
  7557  						Schema: map[string]*Schema{
  7558  							"protocol": {
  7559  								Type:     TypeString,
  7560  								Required: true,
  7561  							},
  7562  							"ports": {
  7563  								Type:     TypeList,
  7564  								Optional: true,
  7565  								Elem: &Schema{
  7566  									Type: TypeString,
  7567  								},
  7568  							},
  7569  						},
  7570  					},
  7571  					ExactlyOneOf: []string{"allow", "deny"},
  7572  				},
  7573  				"deny": {
  7574  					Type:     TypeList,
  7575  					Optional: true,
  7576  					Elem: &Resource{
  7577  						Schema: map[string]*Schema{
  7578  							"protocol": {
  7579  								Type:     TypeString,
  7580  								Required: true,
  7581  							},
  7582  							"ports": {
  7583  								Type:     TypeList,
  7584  								Optional: true,
  7585  								Elem: &Schema{
  7586  									Type: TypeString,
  7587  								},
  7588  							},
  7589  						},
  7590  					},
  7591  					ExactlyOneOf: []string{"allow", "deny"},
  7592  				},
  7593  				"purplelist": {
  7594  					Type:     TypeString,
  7595  					Optional: true,
  7596  				},
  7597  			},
  7598  			Config: map[string]interface{}{
  7599  				"allow": []interface{}{map[string]interface{}{
  7600  					"ports":    hcl2shim.UnknownVariableValue,
  7601  					"protocol": hcl2shim.UnknownVariableValue,
  7602  				}},
  7603  				"deny": []interface{}{map[string]interface{}{
  7604  					"ports":    hcl2shim.UnknownVariableValue,
  7605  					"protocol": hcl2shim.UnknownVariableValue,
  7606  				}},
  7607  				"purplelist": "blah",
  7608  			},
  7609  			Err: false,
  7610  		},
  7611  
  7612  		// This should probably fail, but we let it pass and rely on 2nd
  7613  		// validation phase when unknowns become known, which will then fail.
  7614  		"partially known list values": {
  7615  			Key: "allow",
  7616  			Schema: map[string]*Schema{
  7617  				"allow": {
  7618  					Type:     TypeList,
  7619  					Optional: true,
  7620  					Elem: &Resource{
  7621  						Schema: map[string]*Schema{
  7622  							"protocol": {
  7623  								Type:     TypeString,
  7624  								Required: true,
  7625  							},
  7626  							"ports": {
  7627  								Type:     TypeList,
  7628  								Optional: true,
  7629  								Elem: &Schema{
  7630  									Type: TypeString,
  7631  								},
  7632  							},
  7633  						},
  7634  					},
  7635  					ExactlyOneOf: []string{"allow", "deny"},
  7636  				},
  7637  				"deny": {
  7638  					Type:     TypeList,
  7639  					Optional: true,
  7640  					Elem: &Resource{
  7641  						Schema: map[string]*Schema{
  7642  							"protocol": {
  7643  								Type:     TypeString,
  7644  								Required: true,
  7645  							},
  7646  							"ports": {
  7647  								Type:     TypeList,
  7648  								Optional: true,
  7649  								Elem: &Schema{
  7650  									Type: TypeString,
  7651  								},
  7652  							},
  7653  						},
  7654  					},
  7655  					ExactlyOneOf: []string{"allow", "deny"},
  7656  				},
  7657  				"purplelist": {
  7658  					Type:     TypeString,
  7659  					Optional: true,
  7660  				},
  7661  			},
  7662  			Config: map[string]interface{}{
  7663  				"allow": []interface{}{map[string]interface{}{
  7664  					"ports":    hcl2shim.UnknownVariableValue,
  7665  					"protocol": "TCP",
  7666  				}},
  7667  				"deny": []interface{}{map[string]interface{}{
  7668  					"ports":    hcl2shim.UnknownVariableValue,
  7669  					"protocol": "TCP",
  7670  				}},
  7671  				"purplelist": "blah",
  7672  			},
  7673  			Err: false,
  7674  		},
  7675  
  7676  		"known list values": {
  7677  			Key: "allow",
  7678  			Schema: map[string]*Schema{
  7679  				"allow": {
  7680  					Type:     TypeList,
  7681  					Optional: true,
  7682  					Elem: &Resource{
  7683  						Schema: map[string]*Schema{
  7684  							"protocol": {
  7685  								Type:     TypeString,
  7686  								Required: true,
  7687  							},
  7688  							"ports": {
  7689  								Type:     TypeList,
  7690  								Optional: true,
  7691  								Elem: &Schema{
  7692  									Type: TypeString,
  7693  								},
  7694  							},
  7695  						},
  7696  					},
  7697  					ExactlyOneOf: []string{"allow", "deny"},
  7698  				},
  7699  				"deny": {
  7700  					Type:     TypeList,
  7701  					Optional: true,
  7702  					Elem: &Resource{
  7703  						Schema: map[string]*Schema{
  7704  							"protocol": {
  7705  								Type:     TypeString,
  7706  								Required: true,
  7707  							},
  7708  							"ports": {
  7709  								Type:     TypeList,
  7710  								Optional: true,
  7711  								Elem: &Schema{
  7712  									Type: TypeString,
  7713  								},
  7714  							},
  7715  						},
  7716  					},
  7717  					ExactlyOneOf: []string{"allow", "deny"},
  7718  				},
  7719  			},
  7720  			Config: map[string]interface{}{
  7721  				"allow": []interface{}{map[string]interface{}{
  7722  					"ports":    []interface{}{"80"},
  7723  					"protocol": "TCP",
  7724  				}},
  7725  				"deny": []interface{}{map[string]interface{}{
  7726  					"ports":    []interface{}{"80"},
  7727  					"protocol": "TCP",
  7728  				}},
  7729  			},
  7730  			Err: true,
  7731  		},
  7732  
  7733  		"wholly unknown set values": {
  7734  			Key: "allow",
  7735  			Schema: map[string]*Schema{
  7736  				"allow": {
  7737  					Type:     TypeSet,
  7738  					Optional: true,
  7739  					Elem: &Resource{
  7740  						Schema: map[string]*Schema{
  7741  							"protocol": {
  7742  								Type:     TypeString,
  7743  								Required: true,
  7744  							},
  7745  							"ports": {
  7746  								Type:     TypeList,
  7747  								Optional: true,
  7748  								Elem: &Schema{
  7749  									Type: TypeString,
  7750  								},
  7751  							},
  7752  						},
  7753  					},
  7754  					ExactlyOneOf: []string{"allow", "deny"},
  7755  				},
  7756  				"deny": {
  7757  					Type:     TypeSet,
  7758  					Optional: true,
  7759  					Elem: &Resource{
  7760  						Schema: map[string]*Schema{
  7761  							"protocol": {
  7762  								Type:     TypeString,
  7763  								Required: true,
  7764  							},
  7765  							"ports": {
  7766  								Type:     TypeList,
  7767  								Optional: true,
  7768  								Elem: &Schema{
  7769  									Type: TypeString,
  7770  								},
  7771  							},
  7772  						},
  7773  					},
  7774  					ExactlyOneOf: []string{"allow", "deny"},
  7775  				},
  7776  				"purplelist": {
  7777  					Type:     TypeString,
  7778  					Optional: true,
  7779  				},
  7780  			},
  7781  			Config: map[string]interface{}{
  7782  				"allow": []interface{}{map[string]interface{}{
  7783  					"ports":    hcl2shim.UnknownVariableValue,
  7784  					"protocol": hcl2shim.UnknownVariableValue,
  7785  				}},
  7786  				"deny": []interface{}{map[string]interface{}{
  7787  					"ports":    hcl2shim.UnknownVariableValue,
  7788  					"protocol": hcl2shim.UnknownVariableValue,
  7789  				}},
  7790  				"purplelist": "blah",
  7791  			},
  7792  			Err: false,
  7793  		},
  7794  
  7795  		// This should probably fail, but we let it pass and rely on 2nd
  7796  		// validation phase when unknowns become known, which will then fail.
  7797  		"partially known set values": {
  7798  			Key: "allow",
  7799  			Schema: map[string]*Schema{
  7800  				"allow": {
  7801  					Type:     TypeSet,
  7802  					Optional: true,
  7803  					Elem: &Resource{
  7804  						Schema: map[string]*Schema{
  7805  							"protocol": {
  7806  								Type:     TypeString,
  7807  								Required: true,
  7808  							},
  7809  							"ports": {
  7810  								Type:     TypeList,
  7811  								Optional: true,
  7812  								Elem: &Schema{
  7813  									Type: TypeString,
  7814  								},
  7815  							},
  7816  						},
  7817  					},
  7818  					ExactlyOneOf: []string{"allow", "deny"},
  7819  				},
  7820  				"deny": {
  7821  					Type:     TypeSet,
  7822  					Optional: true,
  7823  					Elem: &Resource{
  7824  						Schema: map[string]*Schema{
  7825  							"protocol": {
  7826  								Type:     TypeString,
  7827  								Required: true,
  7828  							},
  7829  							"ports": {
  7830  								Type:     TypeList,
  7831  								Optional: true,
  7832  								Elem: &Schema{
  7833  									Type: TypeString,
  7834  								},
  7835  							},
  7836  						},
  7837  					},
  7838  					ExactlyOneOf: []string{"allow", "deny"},
  7839  				},
  7840  			},
  7841  			Config: map[string]interface{}{
  7842  				"allow": []interface{}{map[string]interface{}{
  7843  					"ports":    hcl2shim.UnknownVariableValue,
  7844  					"protocol": "TCP",
  7845  				}},
  7846  				"deny": []interface{}{map[string]interface{}{
  7847  					"ports":    hcl2shim.UnknownVariableValue,
  7848  					"protocol": "UDP",
  7849  				}},
  7850  			},
  7851  			Err: false,
  7852  		},
  7853  
  7854  		"known set values": {
  7855  			Key: "allow",
  7856  			Schema: map[string]*Schema{
  7857  				"allow": {
  7858  					Type:     TypeSet,
  7859  					Optional: true,
  7860  					Elem: &Resource{
  7861  						Schema: map[string]*Schema{
  7862  							"protocol": {
  7863  								Type:     TypeString,
  7864  								Required: true,
  7865  							},
  7866  							"ports": {
  7867  								Type:     TypeList,
  7868  								Optional: true,
  7869  								Elem: &Schema{
  7870  									Type: TypeString,
  7871  								},
  7872  							},
  7873  						},
  7874  					},
  7875  					ExactlyOneOf: []string{"allow", "deny"},
  7876  				},
  7877  				"deny": {
  7878  					Type:     TypeSet,
  7879  					Optional: true,
  7880  					Elem: &Resource{
  7881  						Schema: map[string]*Schema{
  7882  							"protocol": {
  7883  								Type:     TypeString,
  7884  								Required: true,
  7885  							},
  7886  							"ports": {
  7887  								Type:     TypeList,
  7888  								Optional: true,
  7889  								Elem: &Schema{
  7890  									Type: TypeString,
  7891  								},
  7892  							},
  7893  						},
  7894  					},
  7895  					ExactlyOneOf: []string{"allow", "deny"},
  7896  				},
  7897  			},
  7898  			Config: map[string]interface{}{
  7899  				"allow": []interface{}{map[string]interface{}{
  7900  					"ports":    []interface{}{"80"},
  7901  					"protocol": "TCP",
  7902  				}},
  7903  				"deny": []interface{}{map[string]interface{}{
  7904  					"ports":    []interface{}{"80"},
  7905  					"protocol": "TCP",
  7906  				}},
  7907  			},
  7908  			Err: true,
  7909  		},
  7910  
  7911  		"wholly unknown simple lists": {
  7912  			Key: "allow",
  7913  			Schema: map[string]*Schema{
  7914  				"allow": {
  7915  					Type:         TypeList,
  7916  					Optional:     true,
  7917  					Elem:         &Schema{Type: TypeString},
  7918  					ExactlyOneOf: []string{"allow", "deny"},
  7919  				},
  7920  				"deny": {
  7921  					Type:         TypeList,
  7922  					Optional:     true,
  7923  					Elem:         &Schema{Type: TypeString},
  7924  					ExactlyOneOf: []string{"allow", "deny"},
  7925  				},
  7926  				"purplelist": {
  7927  					Type:     TypeString,
  7928  					Optional: true,
  7929  				},
  7930  			},
  7931  			Config: map[string]interface{}{
  7932  				"allow": []interface{}{
  7933  					hcl2shim.UnknownVariableValue,
  7934  					hcl2shim.UnknownVariableValue,
  7935  				},
  7936  				"deny": []interface{}{
  7937  					hcl2shim.UnknownVariableValue,
  7938  					hcl2shim.UnknownVariableValue,
  7939  				},
  7940  				"purplelist": "blah",
  7941  			},
  7942  			Err: false,
  7943  		},
  7944  
  7945  		// This should probably fail, but we let it pass and rely on 2nd
  7946  		// validation phase when unknowns become known, which will then fail.
  7947  		"partially known simple lists": {
  7948  			Key: "allow",
  7949  			Schema: map[string]*Schema{
  7950  				"allow": {
  7951  					Type:         TypeList,
  7952  					Optional:     true,
  7953  					Elem:         &Schema{Type: TypeString},
  7954  					ExactlyOneOf: []string{"allow", "deny"},
  7955  				},
  7956  				"deny": {
  7957  					Type:         TypeList,
  7958  					Optional:     true,
  7959  					Elem:         &Schema{Type: TypeString},
  7960  					ExactlyOneOf: []string{"allow", "deny"},
  7961  				},
  7962  			},
  7963  			Config: map[string]interface{}{
  7964  				"allow": []interface{}{
  7965  					hcl2shim.UnknownVariableValue,
  7966  					"known",
  7967  				},
  7968  				"deny": []interface{}{
  7969  					hcl2shim.UnknownVariableValue,
  7970  					"known",
  7971  				},
  7972  			},
  7973  			Err: false,
  7974  		},
  7975  
  7976  		"known simple lists": {
  7977  			Key: "allow",
  7978  			Schema: map[string]*Schema{
  7979  				"allow": {
  7980  					Type:         TypeList,
  7981  					Optional:     true,
  7982  					Elem:         &Schema{Type: TypeString},
  7983  					ExactlyOneOf: []string{"allow", "deny"},
  7984  				},
  7985  				"deny": {
  7986  					Type:         TypeList,
  7987  					Optional:     true,
  7988  					Elem:         &Schema{Type: TypeString},
  7989  					ExactlyOneOf: []string{"allow", "deny"},
  7990  				},
  7991  			},
  7992  			Config: map[string]interface{}{
  7993  				"allow": []interface{}{
  7994  					"blah",
  7995  					"known",
  7996  				},
  7997  				"deny": []interface{}{
  7998  					"known",
  7999  				},
  8000  			},
  8001  			Err: true,
  8002  		},
  8003  
  8004  		"wholly unknown map keys and values": {
  8005  			Key: "allow",
  8006  			Schema: map[string]*Schema{
  8007  				"allow": {
  8008  					Type:         TypeMap,
  8009  					Optional:     true,
  8010  					ExactlyOneOf: []string{"allow", "deny"},
  8011  				},
  8012  				"deny": {
  8013  					Type:         TypeList,
  8014  					Optional:     true,
  8015  					ExactlyOneOf: []string{"allow", "deny"},
  8016  				},
  8017  				"purplelist": {
  8018  					Type:     TypeString,
  8019  					Optional: true,
  8020  				},
  8021  			},
  8022  			Config: map[string]interface{}{
  8023  				"allow": map[string]interface{}{
  8024  					hcl2shim.UnknownVariableValue: hcl2shim.UnknownVariableValue,
  8025  				},
  8026  				"deny": map[string]interface{}{
  8027  					hcl2shim.UnknownVariableValue: hcl2shim.UnknownVariableValue,
  8028  				},
  8029  				"purplelist": "blah",
  8030  			},
  8031  			Err: false,
  8032  		},
  8033  
  8034  		// This should probably fail, but we let it pass and rely on 2nd
  8035  		// validation phase when unknowns become known, which will then fail.
  8036  		"wholly unknown map values": {
  8037  			Key: "allow",
  8038  			Schema: map[string]*Schema{
  8039  				"allow": {
  8040  					Type:         TypeMap,
  8041  					Optional:     true,
  8042  					ExactlyOneOf: []string{"allow", "deny"},
  8043  				},
  8044  				"deny": {
  8045  					Type:         TypeList,
  8046  					Optional:     true,
  8047  					ExactlyOneOf: []string{"allow", "deny"},
  8048  				},
  8049  				"purplelist": {
  8050  					Type:     TypeString,
  8051  					Optional: true,
  8052  				},
  8053  			},
  8054  			Config: map[string]interface{}{
  8055  				"allow": map[string]interface{}{
  8056  					"key": hcl2shim.UnknownVariableValue,
  8057  				},
  8058  				"deny": map[string]interface{}{
  8059  					"key": hcl2shim.UnknownVariableValue,
  8060  				},
  8061  				"purplelist": "blah",
  8062  			},
  8063  			Err: false,
  8064  		},
  8065  
  8066  		// This should probably fail, but we let it pass and rely on 2nd
  8067  		// validation phase when unknowns become known, which will then fail.
  8068  		"partially known maps": {
  8069  			Key: "allow",
  8070  			Schema: map[string]*Schema{
  8071  				"allow": {
  8072  					Type:         TypeMap,
  8073  					Optional:     true,
  8074  					ExactlyOneOf: []string{"allow", "deny"},
  8075  				},
  8076  				"deny": {
  8077  					Type:         TypeList,
  8078  					Optional:     true,
  8079  					ExactlyOneOf: []string{"allow", "deny"},
  8080  				},
  8081  				"purplelist": {
  8082  					Type:     TypeString,
  8083  					Optional: true,
  8084  				},
  8085  			},
  8086  			Config: map[string]interface{}{
  8087  				"allow": map[string]interface{}{
  8088  					"first":  "value",
  8089  					"second": hcl2shim.UnknownVariableValue,
  8090  				},
  8091  				"deny": map[string]interface{}{
  8092  					"first":  "value",
  8093  					"second": hcl2shim.UnknownVariableValue,
  8094  				},
  8095  				"purplelist": "blah",
  8096  			},
  8097  			Err: false,
  8098  		},
  8099  
  8100  		"known maps": {
  8101  			Key: "allow",
  8102  			Schema: map[string]*Schema{
  8103  				"allow": {
  8104  					Type:         TypeMap,
  8105  					Optional:     true,
  8106  					ExactlyOneOf: []string{"allow", "deny"},
  8107  				},
  8108  				"deny": {
  8109  					Type:         TypeList,
  8110  					Optional:     true,
  8111  					ExactlyOneOf: []string{"allow", "deny"},
  8112  				},
  8113  			},
  8114  			Config: map[string]interface{}{
  8115  				"allow": map[string]interface{}{
  8116  					"first":  "value",
  8117  					"second": "blah",
  8118  				},
  8119  				"deny": map[string]interface{}{
  8120  					"first":  "value",
  8121  					"second": "boo",
  8122  				},
  8123  			},
  8124  			Err: true,
  8125  		},
  8126  	}
  8127  
  8128  	for tn, tc := range cases {
  8129  		t.Run(tn, func(t *testing.T) {
  8130  			c := terraform.NewResourceConfigRaw(tc.Config)
  8131  
  8132  			err := validateExactlyOneAttribute(tc.Key, tc.Schema[tc.Key], c)
  8133  			if err == nil && tc.Err {
  8134  				t.Fatalf("expected error")
  8135  			}
  8136  
  8137  			if err != nil && !tc.Err {
  8138  				t.Fatalf("didn't expect error, got error: %+v", err)
  8139  			}
  8140  		})
  8141  	}
  8142  
  8143  }
  8144  
  8145  func TestValidateAtLeastOneOfAttributes(t *testing.T) {
  8146  	cases := map[string]struct {
  8147  		Key    string
  8148  		Schema map[string]*Schema
  8149  		Config map[string]interface{}
  8150  		Err    bool
  8151  	}{
  8152  
  8153  		"two attributes specified": {
  8154  			Key: "whitelist",
  8155  			Schema: map[string]*Schema{
  8156  				"whitelist": {
  8157  					Type:         TypeBool,
  8158  					Optional:     true,
  8159  					AtLeastOneOf: []string{"blacklist"},
  8160  				},
  8161  				"blacklist": {
  8162  					Type:         TypeBool,
  8163  					Optional:     true,
  8164  					AtLeastOneOf: []string{"whitelist"},
  8165  				},
  8166  			},
  8167  
  8168  			Config: map[string]interface{}{
  8169  				"whitelist": true,
  8170  				"blacklist": true,
  8171  			},
  8172  			Err: false,
  8173  		},
  8174  
  8175  		"one attributes specified": {
  8176  			Key: "whitelist",
  8177  			Schema: map[string]*Schema{
  8178  				"whitelist": {
  8179  					Type:         TypeBool,
  8180  					Optional:     true,
  8181  					AtLeastOneOf: []string{"blacklist"},
  8182  				},
  8183  				"blacklist": {
  8184  					Type:         TypeBool,
  8185  					Optional:     true,
  8186  					AtLeastOneOf: []string{"whitelist"},
  8187  				},
  8188  			},
  8189  
  8190  			Config: map[string]interface{}{
  8191  				"whitelist": true,
  8192  			},
  8193  			Err: false,
  8194  		},
  8195  
  8196  		"two attributes of three specified": {
  8197  			Key: "whitelist",
  8198  			Schema: map[string]*Schema{
  8199  				"whitelist": {
  8200  					Type:         TypeBool,
  8201  					Optional:     true,
  8202  					AtLeastOneOf: []string{"blacklist", "purplelist"},
  8203  				},
  8204  				"blacklist": {
  8205  					Type:         TypeBool,
  8206  					Optional:     true,
  8207  					AtLeastOneOf: []string{"whitelist", "purplelist"},
  8208  				},
  8209  				"purplelist": {
  8210  					Type:         TypeBool,
  8211  					Optional:     true,
  8212  					AtLeastOneOf: []string{"whitelist", "blacklist"},
  8213  				},
  8214  			},
  8215  
  8216  			Config: map[string]interface{}{
  8217  				"whitelist":  true,
  8218  				"purplelist": true,
  8219  			},
  8220  			Err: false,
  8221  		},
  8222  
  8223  		"three attributes of three specified": {
  8224  			Key: "whitelist",
  8225  			Schema: map[string]*Schema{
  8226  				"whitelist": {
  8227  					Type:         TypeBool,
  8228  					Optional:     true,
  8229  					AtLeastOneOf: []string{"blacklist", "purplelist"},
  8230  				},
  8231  				"blacklist": {
  8232  					Type:         TypeBool,
  8233  					Optional:     true,
  8234  					AtLeastOneOf: []string{"whitelist", "purplelist"},
  8235  				},
  8236  				"purplelist": {
  8237  					Type:         TypeBool,
  8238  					Optional:     true,
  8239  					AtLeastOneOf: []string{"whitelist", "blacklist"},
  8240  				},
  8241  			},
  8242  
  8243  			Config: map[string]interface{}{
  8244  				"whitelist":  true,
  8245  				"purplelist": true,
  8246  				"blacklist":  true,
  8247  			},
  8248  			Err: false,
  8249  		},
  8250  
  8251  		"one attributes of three specified": {
  8252  			Key: "whitelist",
  8253  			Schema: map[string]*Schema{
  8254  				"whitelist": {
  8255  					Type:         TypeBool,
  8256  					Optional:     true,
  8257  					AtLeastOneOf: []string{"blacklist", "purplelist"},
  8258  				},
  8259  				"blacklist": {
  8260  					Type:         TypeBool,
  8261  					Optional:     true,
  8262  					AtLeastOneOf: []string{"whitelist", "purplelist"},
  8263  				},
  8264  				"purplelist": {
  8265  					Type:         TypeBool,
  8266  					Optional:     true,
  8267  					AtLeastOneOf: []string{"whitelist", "blacklist"},
  8268  				},
  8269  			},
  8270  
  8271  			Config: map[string]interface{}{
  8272  				"purplelist": true,
  8273  			},
  8274  			Err: false,
  8275  		},
  8276  
  8277  		"no attributes of three specified": {
  8278  			Key: "whitelist",
  8279  			Schema: map[string]*Schema{
  8280  				"whitelist": {
  8281  					Type:         TypeBool,
  8282  					Optional:     true,
  8283  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8284  				},
  8285  				"blacklist": {
  8286  					Type:         TypeBool,
  8287  					Optional:     true,
  8288  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8289  				},
  8290  				"purplelist": {
  8291  					Type:         TypeBool,
  8292  					Optional:     true,
  8293  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8294  				},
  8295  			},
  8296  
  8297  			Config: map[string]interface{}{},
  8298  			Err:    true,
  8299  		},
  8300  
  8301  		"Only Unknown Variable Value": {
  8302  			Schema: map[string]*Schema{
  8303  				"whitelist": {
  8304  					Type:         TypeBool,
  8305  					Optional:     true,
  8306  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8307  				},
  8308  				"blacklist": {
  8309  					Type:         TypeBool,
  8310  					Optional:     true,
  8311  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8312  				},
  8313  				"purplelist": {
  8314  					Type:         TypeBool,
  8315  					Optional:     true,
  8316  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8317  				},
  8318  			},
  8319  
  8320  			Config: map[string]interface{}{
  8321  				"whitelist": hcl2shim.UnknownVariableValue,
  8322  			},
  8323  
  8324  			Err: false,
  8325  		},
  8326  
  8327  		"only unknown list value": {
  8328  			Schema: map[string]*Schema{
  8329  				"whitelist": {
  8330  					Type:         TypeList,
  8331  					Optional:     true,
  8332  					Elem:         &Schema{Type: TypeString},
  8333  					AtLeastOneOf: []string{"whitelist", "blacklist"},
  8334  				},
  8335  				"blacklist": {
  8336  					Type:         TypeList,
  8337  					Optional:     true,
  8338  					Elem:         &Schema{Type: TypeString},
  8339  					AtLeastOneOf: []string{"whitelist", "blacklist"},
  8340  				},
  8341  			},
  8342  
  8343  			Config: map[string]interface{}{
  8344  				"whitelist": []interface{}{hcl2shim.UnknownVariableValue},
  8345  			},
  8346  
  8347  			Err: false,
  8348  		},
  8349  
  8350  		"Unknown Variable Value and Known Value": {
  8351  			Schema: map[string]*Schema{
  8352  				"whitelist": {
  8353  					Type:         TypeBool,
  8354  					Optional:     true,
  8355  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8356  				},
  8357  				"blacklist": {
  8358  					Type:         TypeBool,
  8359  					Optional:     true,
  8360  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8361  				},
  8362  				"purplelist": {
  8363  					Type:         TypeBool,
  8364  					Optional:     true,
  8365  					AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"},
  8366  				},
  8367  			},
  8368  
  8369  			Config: map[string]interface{}{
  8370  				"whitelist": hcl2shim.UnknownVariableValue,
  8371  				"blacklist": true,
  8372  			},
  8373  
  8374  			Err: false,
  8375  		},
  8376  	}
  8377  
  8378  	for tn, tc := range cases {
  8379  		t.Run(tn, func(t *testing.T) {
  8380  			c := terraform.NewResourceConfigRaw(tc.Config)
  8381  			_, es := schemaMap(tc.Schema).Validate(c)
  8382  			if len(es) > 0 != tc.Err {
  8383  				if len(es) == 0 {
  8384  					t.Fatalf("expected error")
  8385  				}
  8386  
  8387  				for _, e := range es {
  8388  					t.Fatalf("didn't expect error, got error: %+v", e)
  8389  				}
  8390  
  8391  				t.FailNow()
  8392  			}
  8393  		})
  8394  	}
  8395  }
  8396  
  8397  func TestValidateRequiredWithAttributes(t *testing.T) {
  8398  	cases := map[string]struct {
  8399  		Key    string
  8400  		Schema map[string]*Schema
  8401  		Config map[string]interface{}
  8402  		Err    bool
  8403  	}{
  8404  
  8405  		"two attributes specified": {
  8406  			Key: "whitelist",
  8407  			Schema: map[string]*Schema{
  8408  				"whitelist": {
  8409  					Type:         TypeBool,
  8410  					Optional:     true,
  8411  					RequiredWith: []string{"blacklist"},
  8412  				},
  8413  				"blacklist": {
  8414  					Type:         TypeBool,
  8415  					Optional:     true,
  8416  					RequiredWith: []string{"whitelist"},
  8417  				},
  8418  			},
  8419  
  8420  			Config: map[string]interface{}{
  8421  				"whitelist": true,
  8422  				"blacklist": true,
  8423  			},
  8424  			Err: false,
  8425  		},
  8426  
  8427  		"one attributes specified": {
  8428  			Key: "whitelist",
  8429  			Schema: map[string]*Schema{
  8430  				"whitelist": {
  8431  					Type:         TypeBool,
  8432  					Optional:     true,
  8433  					RequiredWith: []string{"blacklist"},
  8434  				},
  8435  				"blacklist": {
  8436  					Type:         TypeBool,
  8437  					Optional:     true,
  8438  					RequiredWith: []string{"whitelist"},
  8439  				},
  8440  			},
  8441  
  8442  			Config: map[string]interface{}{
  8443  				"whitelist": true,
  8444  			},
  8445  			Err: true,
  8446  		},
  8447  
  8448  		"no attributes specified": {
  8449  			Key: "whitelist",
  8450  			Schema: map[string]*Schema{
  8451  				"whitelist": {
  8452  					Type:         TypeBool,
  8453  					Optional:     true,
  8454  					RequiredWith: []string{"blacklist"},
  8455  				},
  8456  				"blacklist": {
  8457  					Type:         TypeBool,
  8458  					Optional:     true,
  8459  					RequiredWith: []string{"whitelist"},
  8460  				},
  8461  			},
  8462  
  8463  			Config: map[string]interface{}{},
  8464  			Err:    false,
  8465  		},
  8466  
  8467  		"two attributes of three specified": {
  8468  			Key: "whitelist",
  8469  			Schema: map[string]*Schema{
  8470  				"whitelist": {
  8471  					Type:         TypeBool,
  8472  					Optional:     true,
  8473  					RequiredWith: []string{"purplelist"},
  8474  				},
  8475  				"blacklist": {
  8476  					Type:         TypeBool,
  8477  					Optional:     true,
  8478  					RequiredWith: []string{"whitelist", "purplelist"},
  8479  				},
  8480  				"purplelist": {
  8481  					Type:         TypeBool,
  8482  					Optional:     true,
  8483  					RequiredWith: []string{"whitelist"},
  8484  				},
  8485  			},
  8486  
  8487  			Config: map[string]interface{}{
  8488  				"whitelist":  true,
  8489  				"purplelist": true,
  8490  			},
  8491  			Err: false,
  8492  		},
  8493  
  8494  		"three attributes of three specified": {
  8495  			Key: "whitelist",
  8496  			Schema: map[string]*Schema{
  8497  				"whitelist": {
  8498  					Type:         TypeBool,
  8499  					Optional:     true,
  8500  					RequiredWith: []string{"blacklist", "purplelist"},
  8501  				},
  8502  				"blacklist": {
  8503  					Type:         TypeBool,
  8504  					Optional:     true,
  8505  					RequiredWith: []string{"whitelist", "purplelist"},
  8506  				},
  8507  				"purplelist": {
  8508  					Type:         TypeBool,
  8509  					Optional:     true,
  8510  					RequiredWith: []string{"whitelist", "blacklist"},
  8511  				},
  8512  			},
  8513  
  8514  			Config: map[string]interface{}{
  8515  				"whitelist":  true,
  8516  				"purplelist": true,
  8517  				"blacklist":  true,
  8518  			},
  8519  			Err: false,
  8520  		},
  8521  
  8522  		"one attributes of three specified": {
  8523  			Key: "whitelist",
  8524  			Schema: map[string]*Schema{
  8525  				"whitelist": {
  8526  					Type:         TypeBool,
  8527  					Optional:     true,
  8528  					RequiredWith: []string{"blacklist", "purplelist"},
  8529  				},
  8530  				"blacklist": {
  8531  					Type:         TypeBool,
  8532  					Optional:     true,
  8533  					RequiredWith: []string{"whitelist", "purplelist"},
  8534  				},
  8535  				"purplelist": {
  8536  					Type:         TypeBool,
  8537  					Optional:     true,
  8538  					RequiredWith: []string{"whitelist", "blacklist"},
  8539  				},
  8540  			},
  8541  
  8542  			Config: map[string]interface{}{
  8543  				"purplelist": true,
  8544  			},
  8545  			Err: true,
  8546  		},
  8547  
  8548  		"no attributes of three specified": {
  8549  			Key: "whitelist",
  8550  			Schema: map[string]*Schema{
  8551  				"whitelist": {
  8552  					Type:         TypeBool,
  8553  					Optional:     true,
  8554  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8555  				},
  8556  				"blacklist": {
  8557  					Type:         TypeBool,
  8558  					Optional:     true,
  8559  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8560  				},
  8561  				"purplelist": {
  8562  					Type:         TypeBool,
  8563  					Optional:     true,
  8564  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8565  				},
  8566  			},
  8567  
  8568  			Config: map[string]interface{}{},
  8569  			Err:    false,
  8570  		},
  8571  
  8572  		"Only Unknown Variable Value": {
  8573  			Schema: map[string]*Schema{
  8574  				"whitelist": {
  8575  					Type:         TypeBool,
  8576  					Optional:     true,
  8577  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8578  				},
  8579  				"blacklist": {
  8580  					Type:         TypeBool,
  8581  					Optional:     true,
  8582  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8583  				},
  8584  				"purplelist": {
  8585  					Type:         TypeBool,
  8586  					Optional:     true,
  8587  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8588  				},
  8589  			},
  8590  
  8591  			Config: map[string]interface{}{
  8592  				"whitelist": hcl2shim.UnknownVariableValue,
  8593  			},
  8594  
  8595  			Err: true,
  8596  		},
  8597  
  8598  		"only unknown list value": {
  8599  			Schema: map[string]*Schema{
  8600  				"whitelist": {
  8601  					Type:         TypeList,
  8602  					Optional:     true,
  8603  					Elem:         &Schema{Type: TypeString},
  8604  					RequiredWith: []string{"whitelist", "blacklist"},
  8605  				},
  8606  				"blacklist": {
  8607  					Type:         TypeList,
  8608  					Optional:     true,
  8609  					Elem:         &Schema{Type: TypeString},
  8610  					RequiredWith: []string{"whitelist", "blacklist"},
  8611  				},
  8612  			},
  8613  
  8614  			Config: map[string]interface{}{
  8615  				"whitelist": []interface{}{hcl2shim.UnknownVariableValue},
  8616  			},
  8617  
  8618  			Err: true,
  8619  		},
  8620  
  8621  		"Unknown Variable Value and Known Value": {
  8622  			Schema: map[string]*Schema{
  8623  				"whitelist": {
  8624  					Type:         TypeBool,
  8625  					Optional:     true,
  8626  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8627  				},
  8628  				"blacklist": {
  8629  					Type:         TypeBool,
  8630  					Optional:     true,
  8631  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8632  				},
  8633  				"purplelist": {
  8634  					Type:         TypeBool,
  8635  					Optional:     true,
  8636  					RequiredWith: []string{"whitelist", "blacklist", "purplelist"},
  8637  				},
  8638  			},
  8639  
  8640  			Config: map[string]interface{}{
  8641  				"whitelist": hcl2shim.UnknownVariableValue,
  8642  				"blacklist": true,
  8643  			},
  8644  
  8645  			Err: true,
  8646  		},
  8647  	}
  8648  
  8649  	for tn, tc := range cases {
  8650  		t.Run(tn, func(t *testing.T) {
  8651  			c := terraform.NewResourceConfigRaw(tc.Config)
  8652  			_, es := schemaMap(tc.Schema).Validate(c)
  8653  			if len(es) > 0 != tc.Err {
  8654  				if len(es) == 0 {
  8655  					t.Fatalf("expected error")
  8656  				}
  8657  
  8658  				for _, e := range es {
  8659  					t.Fatalf("didn't expect error, got error: %+v", e)
  8660  				}
  8661  
  8662  				t.FailNow()
  8663  			}
  8664  		})
  8665  	}
  8666  }