github.com/Hashicorp/terraform@v0.11.12-beta1/helper/schema/field_reader_config_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/hil/ast"
    10  	"github.com/hashicorp/terraform/config"
    11  	"github.com/hashicorp/terraform/helper/hashcode"
    12  	"github.com/hashicorp/terraform/terraform"
    13  )
    14  
    15  func TestConfigFieldReader_impl(t *testing.T) {
    16  	var _ FieldReader = new(ConfigFieldReader)
    17  }
    18  
    19  func TestConfigFieldReader(t *testing.T) {
    20  	testFieldReader(t, func(s map[string]*Schema) FieldReader {
    21  		return &ConfigFieldReader{
    22  			Schema: s,
    23  
    24  			Config: testConfig(t, map[string]interface{}{
    25  				"bool":   true,
    26  				"float":  3.1415,
    27  				"int":    42,
    28  				"string": "string",
    29  
    30  				"list": []interface{}{"foo", "bar"},
    31  
    32  				"listInt": []interface{}{21, 42},
    33  
    34  				"map": map[string]interface{}{
    35  					"foo": "bar",
    36  					"bar": "baz",
    37  				},
    38  				"mapInt": map[string]interface{}{
    39  					"one": "1",
    40  					"two": "2",
    41  				},
    42  				"mapIntNestedSchema": map[string]interface{}{
    43  					"one": "1",
    44  					"two": "2",
    45  				},
    46  				"mapFloat": map[string]interface{}{
    47  					"oneDotTwo": "1.2",
    48  				},
    49  				"mapBool": map[string]interface{}{
    50  					"True":  "true",
    51  					"False": "false",
    52  				},
    53  
    54  				"set": []interface{}{10, 50},
    55  				"setDeep": []interface{}{
    56  					map[string]interface{}{
    57  						"index": 10,
    58  						"value": "foo",
    59  					},
    60  					map[string]interface{}{
    61  						"index": 50,
    62  						"value": "bar",
    63  					},
    64  				},
    65  			}),
    66  		}
    67  	})
    68  }
    69  
    70  // This contains custom table tests for our ConfigFieldReader
    71  func TestConfigFieldReader_custom(t *testing.T) {
    72  	schema := map[string]*Schema{
    73  		"bool": &Schema{
    74  			Type: TypeBool,
    75  		},
    76  	}
    77  
    78  	cases := map[string]struct {
    79  		Addr   []string
    80  		Result FieldReadResult
    81  		Config *terraform.ResourceConfig
    82  		Err    bool
    83  	}{
    84  		"basic": {
    85  			[]string{"bool"},
    86  			FieldReadResult{
    87  				Value:  true,
    88  				Exists: true,
    89  			},
    90  			testConfig(t, map[string]interface{}{
    91  				"bool": true,
    92  			}),
    93  			false,
    94  		},
    95  
    96  		"computed": {
    97  			[]string{"bool"},
    98  			FieldReadResult{
    99  				Exists:   true,
   100  				Computed: true,
   101  			},
   102  			testConfigInterpolate(t, map[string]interface{}{
   103  				"bool": "${var.foo}",
   104  			}, map[string]ast.Variable{
   105  				"var.foo": ast.Variable{
   106  					Value: config.UnknownVariableValue,
   107  					Type:  ast.TypeString,
   108  				},
   109  			}),
   110  			false,
   111  		},
   112  	}
   113  
   114  	for name, tc := range cases {
   115  		t.Run(name, func(t *testing.T) {
   116  			r := &ConfigFieldReader{
   117  				Schema: schema,
   118  				Config: tc.Config,
   119  			}
   120  			out, err := r.ReadField(tc.Addr)
   121  			if err != nil != tc.Err {
   122  				t.Fatalf("%s: err: %s", name, err)
   123  			}
   124  			if s, ok := out.Value.(*Set); ok {
   125  				// If it is a set, convert to a list so its more easily checked.
   126  				out.Value = s.List()
   127  			}
   128  			if !reflect.DeepEqual(tc.Result, out) {
   129  				t.Fatalf("%s: bad: %#v", name, out)
   130  			}
   131  		})
   132  	}
   133  }
   134  
   135  func TestConfigFieldReader_DefaultHandling(t *testing.T) {
   136  	schema := map[string]*Schema{
   137  		"strWithDefault": &Schema{
   138  			Type:    TypeString,
   139  			Default: "ImADefault",
   140  		},
   141  		"strWithDefaultFunc": &Schema{
   142  			Type: TypeString,
   143  			DefaultFunc: func() (interface{}, error) {
   144  				return "FuncDefault", nil
   145  			},
   146  		},
   147  	}
   148  
   149  	cases := map[string]struct {
   150  		Addr   []string
   151  		Result FieldReadResult
   152  		Config *terraform.ResourceConfig
   153  		Err    bool
   154  	}{
   155  		"gets default value when no config set": {
   156  			[]string{"strWithDefault"},
   157  			FieldReadResult{
   158  				Value:    "ImADefault",
   159  				Exists:   true,
   160  				Computed: false,
   161  			},
   162  			testConfig(t, map[string]interface{}{}),
   163  			false,
   164  		},
   165  		"config overrides default value": {
   166  			[]string{"strWithDefault"},
   167  			FieldReadResult{
   168  				Value:    "fromConfig",
   169  				Exists:   true,
   170  				Computed: false,
   171  			},
   172  			testConfig(t, map[string]interface{}{
   173  				"strWithDefault": "fromConfig",
   174  			}),
   175  			false,
   176  		},
   177  		"gets default from function when no config set": {
   178  			[]string{"strWithDefaultFunc"},
   179  			FieldReadResult{
   180  				Value:    "FuncDefault",
   181  				Exists:   true,
   182  				Computed: false,
   183  			},
   184  			testConfig(t, map[string]interface{}{}),
   185  			false,
   186  		},
   187  		"config overrides default function": {
   188  			[]string{"strWithDefaultFunc"},
   189  			FieldReadResult{
   190  				Value:    "fromConfig",
   191  				Exists:   true,
   192  				Computed: false,
   193  			},
   194  			testConfig(t, map[string]interface{}{
   195  				"strWithDefaultFunc": "fromConfig",
   196  			}),
   197  			false,
   198  		},
   199  	}
   200  
   201  	for name, tc := range cases {
   202  		r := &ConfigFieldReader{
   203  			Schema: schema,
   204  			Config: tc.Config,
   205  		}
   206  		out, err := r.ReadField(tc.Addr)
   207  		if err != nil != tc.Err {
   208  			t.Fatalf("%s: err: %s", name, err)
   209  		}
   210  		if s, ok := out.Value.(*Set); ok {
   211  			// If it is a set, convert to a list so its more easily checked.
   212  			out.Value = s.List()
   213  		}
   214  		if !reflect.DeepEqual(tc.Result, out) {
   215  			t.Fatalf("%s: bad: %#v", name, out)
   216  		}
   217  	}
   218  }
   219  
   220  func TestConfigFieldReader_ComputedMap(t *testing.T) {
   221  	schema := map[string]*Schema{
   222  		"map": &Schema{
   223  			Type:     TypeMap,
   224  			Computed: true,
   225  		},
   226  		"listmap": &Schema{
   227  			Type:     TypeMap,
   228  			Computed: true,
   229  			Elem:     TypeList,
   230  		},
   231  		"maplist": &Schema{
   232  			Type:     TypeList,
   233  			Computed: true,
   234  			Elem:     TypeMap,
   235  		},
   236  	}
   237  
   238  	cases := []struct {
   239  		Name   string
   240  		Addr   []string
   241  		Result FieldReadResult
   242  		Config *terraform.ResourceConfig
   243  		Err    bool
   244  	}{
   245  		{
   246  			"set, normal",
   247  			[]string{"map"},
   248  			FieldReadResult{
   249  				Value: map[string]interface{}{
   250  					"foo": "bar",
   251  				},
   252  				Exists:   true,
   253  				Computed: false,
   254  			},
   255  			testConfig(t, map[string]interface{}{
   256  				"map": map[string]interface{}{
   257  					"foo": "bar",
   258  				},
   259  			}),
   260  			false,
   261  		},
   262  
   263  		{
   264  			"computed element",
   265  			[]string{"map"},
   266  			FieldReadResult{
   267  				Exists:   true,
   268  				Computed: true,
   269  			},
   270  			testConfigInterpolate(t, map[string]interface{}{
   271  				"map": map[string]interface{}{
   272  					"foo": "${var.foo}",
   273  				},
   274  			}, map[string]ast.Variable{
   275  				"var.foo": ast.Variable{
   276  					Value: config.UnknownVariableValue,
   277  					Type:  ast.TypeString,
   278  				},
   279  			}),
   280  			false,
   281  		},
   282  
   283  		{
   284  			"native map",
   285  			[]string{"map"},
   286  			FieldReadResult{
   287  				Value: map[string]interface{}{
   288  					"bar": "baz",
   289  					"baz": "bar",
   290  				},
   291  				Exists:   true,
   292  				Computed: false,
   293  			},
   294  			testConfigInterpolate(t, map[string]interface{}{
   295  				"map": "${var.foo}",
   296  			}, map[string]ast.Variable{
   297  				"var.foo": ast.Variable{
   298  					Type: ast.TypeMap,
   299  					Value: map[string]ast.Variable{
   300  						"bar": ast.Variable{
   301  							Type:  ast.TypeString,
   302  							Value: "baz",
   303  						},
   304  						"baz": ast.Variable{
   305  							Type:  ast.TypeString,
   306  							Value: "bar",
   307  						},
   308  					},
   309  				},
   310  			}),
   311  			false,
   312  		},
   313  
   314  		{
   315  			"map-from-list-of-maps",
   316  			[]string{"maplist", "0"},
   317  			FieldReadResult{
   318  				Value: map[string]interface{}{
   319  					"key": "bar",
   320  				},
   321  				Exists:   true,
   322  				Computed: false,
   323  			},
   324  			testConfigInterpolate(t, map[string]interface{}{
   325  				"maplist": "${var.foo}",
   326  			}, map[string]ast.Variable{
   327  				"var.foo": ast.Variable{
   328  					Type: ast.TypeList,
   329  					Value: []ast.Variable{
   330  						{
   331  							Type: ast.TypeMap,
   332  							Value: map[string]ast.Variable{
   333  								"key": ast.Variable{
   334  									Type:  ast.TypeString,
   335  									Value: "bar",
   336  								},
   337  							},
   338  						},
   339  					},
   340  				},
   341  			}),
   342  			false,
   343  		},
   344  
   345  		{
   346  			"value-from-list-of-maps",
   347  			[]string{"maplist", "0", "key"},
   348  			FieldReadResult{
   349  				Value:    "bar",
   350  				Exists:   true,
   351  				Computed: false,
   352  			},
   353  			testConfigInterpolate(t, map[string]interface{}{
   354  				"maplist": "${var.foo}",
   355  			}, map[string]ast.Variable{
   356  				"var.foo": ast.Variable{
   357  					Type: ast.TypeList,
   358  					Value: []ast.Variable{
   359  						{
   360  							Type: ast.TypeMap,
   361  							Value: map[string]ast.Variable{
   362  								"key": ast.Variable{
   363  									Type:  ast.TypeString,
   364  									Value: "bar",
   365  								},
   366  							},
   367  						},
   368  					},
   369  				},
   370  			}),
   371  			false,
   372  		},
   373  
   374  		{
   375  			"list-from-map-of-lists",
   376  			[]string{"listmap", "key"},
   377  			FieldReadResult{
   378  				Value:    []interface{}{"bar"},
   379  				Exists:   true,
   380  				Computed: false,
   381  			},
   382  			testConfigInterpolate(t, map[string]interface{}{
   383  				"listmap": "${var.foo}",
   384  			}, map[string]ast.Variable{
   385  				"var.foo": ast.Variable{
   386  					Type: ast.TypeMap,
   387  					Value: map[string]ast.Variable{
   388  						"key": ast.Variable{
   389  							Type: ast.TypeList,
   390  							Value: []ast.Variable{
   391  								ast.Variable{
   392  									Type:  ast.TypeString,
   393  									Value: "bar",
   394  								},
   395  							},
   396  						},
   397  					},
   398  				},
   399  			}),
   400  			false,
   401  		},
   402  
   403  		{
   404  			"value-from-map-of-lists",
   405  			[]string{"listmap", "key", "0"},
   406  			FieldReadResult{
   407  				Value:    "bar",
   408  				Exists:   true,
   409  				Computed: false,
   410  			},
   411  			testConfigInterpolate(t, map[string]interface{}{
   412  				"listmap": "${var.foo}",
   413  			}, map[string]ast.Variable{
   414  				"var.foo": ast.Variable{
   415  					Type: ast.TypeMap,
   416  					Value: map[string]ast.Variable{
   417  						"key": ast.Variable{
   418  							Type: ast.TypeList,
   419  							Value: []ast.Variable{
   420  								ast.Variable{
   421  									Type:  ast.TypeString,
   422  									Value: "bar",
   423  								},
   424  							},
   425  						},
   426  					},
   427  				},
   428  			}),
   429  			false,
   430  		},
   431  	}
   432  
   433  	for i, tc := range cases {
   434  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   435  			r := &ConfigFieldReader{
   436  				Schema: schema,
   437  				Config: tc.Config,
   438  			}
   439  			out, err := r.ReadField(tc.Addr)
   440  			if err != nil != tc.Err {
   441  				t.Fatal(err)
   442  			}
   443  			if s, ok := out.Value.(*Set); ok {
   444  				// If it is a set, convert to the raw map
   445  				out.Value = s.m
   446  				if len(s.m) == 0 {
   447  					out.Value = nil
   448  				}
   449  			}
   450  			if !reflect.DeepEqual(tc.Result, out) {
   451  				t.Fatalf("\nexpected: %#v\ngot:      %#v", tc.Result, out)
   452  			}
   453  		})
   454  	}
   455  }
   456  
   457  func TestConfigFieldReader_ComputedSet(t *testing.T) {
   458  	schema := map[string]*Schema{
   459  		"strSet": &Schema{
   460  			Type: TypeSet,
   461  			Elem: &Schema{Type: TypeString},
   462  			Set:  HashString,
   463  		},
   464  	}
   465  
   466  	cases := map[string]struct {
   467  		Addr   []string
   468  		Result FieldReadResult
   469  		Config *terraform.ResourceConfig
   470  		Err    bool
   471  	}{
   472  		"set, normal": {
   473  			[]string{"strSet"},
   474  			FieldReadResult{
   475  				Value: map[string]interface{}{
   476  					"2356372769": "foo",
   477  				},
   478  				Exists:   true,
   479  				Computed: false,
   480  			},
   481  			testConfig(t, map[string]interface{}{
   482  				"strSet": []interface{}{"foo"},
   483  			}),
   484  			false,
   485  		},
   486  
   487  		"set, computed element": {
   488  			[]string{"strSet"},
   489  			FieldReadResult{
   490  				Value:    nil,
   491  				Exists:   true,
   492  				Computed: true,
   493  			},
   494  			testConfigInterpolate(t, map[string]interface{}{
   495  				"strSet": []interface{}{"${var.foo}"},
   496  			}, map[string]ast.Variable{
   497  				"var.foo": ast.Variable{
   498  					Value: config.UnknownVariableValue,
   499  					Type:  ast.TypeUnknown,
   500  				},
   501  			}),
   502  			false,
   503  		},
   504  
   505  		"set, computed element substring": {
   506  			[]string{"strSet"},
   507  			FieldReadResult{
   508  				Value:    nil,
   509  				Exists:   true,
   510  				Computed: true,
   511  			},
   512  			testConfigInterpolate(t, map[string]interface{}{
   513  				"strSet": []interface{}{"${var.foo}/32"},
   514  			}, map[string]ast.Variable{
   515  				"var.foo": ast.Variable{
   516  					Value: config.UnknownVariableValue,
   517  					Type:  ast.TypeUnknown,
   518  				},
   519  			}),
   520  			false,
   521  		},
   522  	}
   523  
   524  	for name, tc := range cases {
   525  		r := &ConfigFieldReader{
   526  			Schema: schema,
   527  			Config: tc.Config,
   528  		}
   529  		out, err := r.ReadField(tc.Addr)
   530  		if err != nil != tc.Err {
   531  			t.Fatalf("%s: err: %s", name, err)
   532  		}
   533  		if s, ok := out.Value.(*Set); ok {
   534  			// If it is a set, convert to the raw map
   535  			out.Value = s.m
   536  			if len(s.m) == 0 {
   537  				out.Value = nil
   538  			}
   539  		}
   540  		if !reflect.DeepEqual(tc.Result, out) {
   541  			t.Fatalf("%s: bad: %#v", name, out)
   542  		}
   543  	}
   544  }
   545  
   546  func TestConfigFieldReader_computedComplexSet(t *testing.T) {
   547  	hashfunc := func(v interface{}) int {
   548  		var buf bytes.Buffer
   549  		m := v.(map[string]interface{})
   550  		buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
   551  		buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string)))
   552  		return hashcode.String(buf.String())
   553  	}
   554  
   555  	schema := map[string]*Schema{
   556  		"set": &Schema{
   557  			Type: TypeSet,
   558  			Elem: &Resource{
   559  				Schema: map[string]*Schema{
   560  					"name": {
   561  						Type:     TypeString,
   562  						Required: true,
   563  					},
   564  
   565  					"vhd_uri": {
   566  						Type:     TypeString,
   567  						Required: true,
   568  					},
   569  				},
   570  			},
   571  			Set: hashfunc,
   572  		},
   573  	}
   574  
   575  	cases := map[string]struct {
   576  		Addr   []string
   577  		Result FieldReadResult
   578  		Config *terraform.ResourceConfig
   579  		Err    bool
   580  	}{
   581  		"set, normal": {
   582  			[]string{"set"},
   583  			FieldReadResult{
   584  				Value: map[string]interface{}{
   585  					"532860136": map[string]interface{}{
   586  						"name":    "myosdisk1",
   587  						"vhd_uri": "bar",
   588  					},
   589  				},
   590  				Exists:   true,
   591  				Computed: false,
   592  			},
   593  			testConfig(t, map[string]interface{}{
   594  				"set": []interface{}{
   595  					map[string]interface{}{
   596  						"name":    "myosdisk1",
   597  						"vhd_uri": "bar",
   598  					},
   599  				},
   600  			}),
   601  			false,
   602  		},
   603  
   604  		"set, computed element": {
   605  			[]string{"set"},
   606  			FieldReadResult{
   607  				Value: map[string]interface{}{
   608  					"~3596295623": map[string]interface{}{
   609  						"name":    "myosdisk1",
   610  						"vhd_uri": "${var.foo}/bar",
   611  					},
   612  				},
   613  				Exists:   true,
   614  				Computed: false,
   615  			},
   616  			testConfigInterpolate(t, map[string]interface{}{
   617  				"set": []interface{}{
   618  					map[string]interface{}{
   619  						"name":    "myosdisk1",
   620  						"vhd_uri": "${var.foo}/bar",
   621  					},
   622  				},
   623  			}, map[string]ast.Variable{
   624  				"var.foo": ast.Variable{
   625  					Value: config.UnknownVariableValue,
   626  					Type:  ast.TypeUnknown,
   627  				},
   628  			}),
   629  			false,
   630  		},
   631  
   632  		"set, computed element single": {
   633  			[]string{"set", "~3596295623", "vhd_uri"},
   634  			FieldReadResult{
   635  				Value:    "${var.foo}/bar",
   636  				Exists:   true,
   637  				Computed: true,
   638  			},
   639  			testConfigInterpolate(t, map[string]interface{}{
   640  				"set": []interface{}{
   641  					map[string]interface{}{
   642  						"name":    "myosdisk1",
   643  						"vhd_uri": "${var.foo}/bar",
   644  					},
   645  				},
   646  			}, map[string]ast.Variable{
   647  				"var.foo": ast.Variable{
   648  					Value: config.UnknownVariableValue,
   649  					Type:  ast.TypeUnknown,
   650  				},
   651  			}),
   652  			false,
   653  		},
   654  	}
   655  
   656  	for name, tc := range cases {
   657  		r := &ConfigFieldReader{
   658  			Schema: schema,
   659  			Config: tc.Config,
   660  		}
   661  		out, err := r.ReadField(tc.Addr)
   662  		if err != nil != tc.Err {
   663  			t.Fatalf("%s: err: %s", name, err)
   664  		}
   665  		if s, ok := out.Value.(*Set); ok {
   666  			// If it is a set, convert to the raw map
   667  			out.Value = s.m
   668  			if len(s.m) == 0 {
   669  				out.Value = nil
   670  			}
   671  		}
   672  		if !reflect.DeepEqual(tc.Result, out) {
   673  			t.Fatalf("%s: bad: %#v", name, out)
   674  		}
   675  	}
   676  }
   677  
   678  func testConfig(
   679  	t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig {
   680  	return testConfigInterpolate(t, raw, nil)
   681  }
   682  
   683  func testConfigInterpolate(
   684  	t *testing.T,
   685  	raw map[string]interface{},
   686  	vs map[string]ast.Variable) *terraform.ResourceConfig {
   687  
   688  	rc, err := config.NewRawConfig(raw)
   689  	if err != nil {
   690  		t.Fatalf("err: %s", err)
   691  	}
   692  	if len(vs) > 0 {
   693  		if err := rc.Interpolate(vs); err != nil {
   694  			t.Fatalf("err: %s", err)
   695  		}
   696  	}
   697  
   698  	return terraform.NewResourceConfig(rc)
   699  }