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