github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/terraform/interpolate_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"reflect"
     7  	"sync"
     8  	"testing"
     9  
    10  	"github.com/davecgh/go-spew/spew"
    11  	"github.com/hashicorp/hil"
    12  	"github.com/hashicorp/hil/ast"
    13  	"github.com/hashicorp/terraform/config"
    14  )
    15  
    16  func TestInterpolater_simpleVar(t *testing.T) {
    17  	i := &Interpolater{}
    18  	scope := &InterpolationScope{}
    19  	testInterpolateErr(t, i, scope, "simple")
    20  }
    21  
    22  func TestInterpolater_countIndex(t *testing.T) {
    23  	i := &Interpolater{}
    24  
    25  	scope := &InterpolationScope{
    26  		Path:     rootModulePath,
    27  		Resource: &Resource{CountIndex: 42},
    28  	}
    29  
    30  	testInterpolate(t, i, scope, "count.index", ast.Variable{
    31  		Value: 42,
    32  		Type:  ast.TypeInt,
    33  	})
    34  }
    35  
    36  func TestInterpolater_countIndexInWrongContext(t *testing.T) {
    37  	i := &Interpolater{}
    38  
    39  	scope := &InterpolationScope{
    40  		Path: rootModulePath,
    41  	}
    42  
    43  	n := "count.index"
    44  
    45  	v, err := config.NewInterpolatedVariable(n)
    46  	if err != nil {
    47  		t.Fatalf("err: %s", err)
    48  	}
    49  
    50  	expectedErr := fmt.Errorf("foo: count.index is only valid within resources")
    51  
    52  	_, err = i.Values(scope, map[string]config.InterpolatedVariable{
    53  		"foo": v,
    54  	})
    55  
    56  	if !reflect.DeepEqual(expectedErr, err) {
    57  		t.Fatalf("expected: %#v, got %#v", expectedErr, err)
    58  	}
    59  }
    60  
    61  func TestInterpolater_moduleVariable(t *testing.T) {
    62  	lock := new(sync.RWMutex)
    63  	state := &State{
    64  		Modules: []*ModuleState{
    65  			&ModuleState{
    66  				Path: rootModulePath,
    67  				Resources: map[string]*ResourceState{
    68  					"aws_instance.web": &ResourceState{
    69  						Type: "aws_instance",
    70  						Primary: &InstanceState{
    71  							ID: "bar",
    72  						},
    73  					},
    74  				},
    75  			},
    76  			&ModuleState{
    77  				Path: []string{RootModuleName, "child"},
    78  				Outputs: map[string]*OutputState{
    79  					"foo": &OutputState{
    80  						Type:  "string",
    81  						Value: "bar",
    82  					},
    83  				},
    84  			},
    85  		},
    86  	}
    87  
    88  	i := &Interpolater{
    89  		State:     state,
    90  		StateLock: lock,
    91  	}
    92  
    93  	scope := &InterpolationScope{
    94  		Path: rootModulePath,
    95  	}
    96  
    97  	testInterpolate(t, i, scope, "module.child.foo", ast.Variable{
    98  		Value: "bar",
    99  		Type:  ast.TypeString,
   100  	})
   101  }
   102  
   103  func TestInterpolater_pathCwd(t *testing.T) {
   104  	i := &Interpolater{}
   105  	scope := &InterpolationScope{}
   106  
   107  	expected, err := os.Getwd()
   108  	if err != nil {
   109  		t.Fatalf("err: %s", err)
   110  	}
   111  
   112  	testInterpolate(t, i, scope, "path.cwd", ast.Variable{
   113  		Value: expected,
   114  		Type:  ast.TypeString,
   115  	})
   116  }
   117  
   118  func TestInterpolater_pathModule(t *testing.T) {
   119  	mod := testModule(t, "interpolate-path-module")
   120  	i := &Interpolater{
   121  		Module: mod,
   122  	}
   123  	scope := &InterpolationScope{
   124  		Path: []string{RootModuleName, "child"},
   125  	}
   126  
   127  	path := mod.Child([]string{"child"}).Config().Dir
   128  	testInterpolate(t, i, scope, "path.module", ast.Variable{
   129  		Value: path,
   130  		Type:  ast.TypeString,
   131  	})
   132  }
   133  
   134  func TestInterpolater_pathRoot(t *testing.T) {
   135  	mod := testModule(t, "interpolate-path-module")
   136  	i := &Interpolater{
   137  		Module: mod,
   138  	}
   139  	scope := &InterpolationScope{
   140  		Path: []string{RootModuleName, "child"},
   141  	}
   142  
   143  	path := mod.Config().Dir
   144  	testInterpolate(t, i, scope, "path.root", ast.Variable{
   145  		Value: path,
   146  		Type:  ast.TypeString,
   147  	})
   148  }
   149  
   150  func TestInterpolater_resourceVariableMap(t *testing.T) {
   151  	lock := new(sync.RWMutex)
   152  	state := &State{
   153  		Modules: []*ModuleState{
   154  			&ModuleState{
   155  				Path: rootModulePath,
   156  				Resources: map[string]*ResourceState{
   157  					"aws_instance.web": &ResourceState{
   158  						Type: "aws_instance",
   159  						Primary: &InstanceState{
   160  							ID: "bar",
   161  							Attributes: map[string]string{
   162  								"amap.%":    "3",
   163  								"amap.key1": "value1",
   164  								"amap.key2": "value2",
   165  								"amap.key3": "value3",
   166  							},
   167  						},
   168  					},
   169  				},
   170  			},
   171  		},
   172  	}
   173  
   174  	i := &Interpolater{
   175  		Module:    testModule(t, "interpolate-resource-variable"),
   176  		State:     state,
   177  		StateLock: lock,
   178  	}
   179  
   180  	scope := &InterpolationScope{
   181  		Path: rootModulePath,
   182  	}
   183  
   184  	expected := map[string]interface{}{
   185  		"key1": "value1",
   186  		"key2": "value2",
   187  		"key3": "value3",
   188  	}
   189  
   190  	testInterpolate(t, i, scope, "aws_instance.web.amap",
   191  		interfaceToVariableSwallowError(expected))
   192  }
   193  
   194  func TestInterpolater_resourceVariableComplexMap(t *testing.T) {
   195  	lock := new(sync.RWMutex)
   196  	state := &State{
   197  		Modules: []*ModuleState{
   198  			&ModuleState{
   199  				Path: rootModulePath,
   200  				Resources: map[string]*ResourceState{
   201  					"aws_instance.web": &ResourceState{
   202  						Type: "aws_instance",
   203  						Primary: &InstanceState{
   204  							ID: "bar",
   205  							Attributes: map[string]string{
   206  								"amap.%":      "2",
   207  								"amap.key1.#": "2",
   208  								"amap.key1.0": "hello",
   209  								"amap.key1.1": "world",
   210  								"amap.key2.#": "1",
   211  								"amap.key2.0": "foo",
   212  							},
   213  						},
   214  					},
   215  				},
   216  			},
   217  		},
   218  	}
   219  
   220  	i := &Interpolater{
   221  		Module:    testModule(t, "interpolate-resource-variable"),
   222  		State:     state,
   223  		StateLock: lock,
   224  	}
   225  
   226  	scope := &InterpolationScope{
   227  		Path: rootModulePath,
   228  	}
   229  
   230  	expected := map[string]interface{}{
   231  		"key1": []interface{}{"hello", "world"},
   232  		"key2": []interface{}{"foo"},
   233  	}
   234  
   235  	testInterpolate(t, i, scope, "aws_instance.web.amap",
   236  		interfaceToVariableSwallowError(expected))
   237  }
   238  
   239  func TestInterpolater_resourceVariable(t *testing.T) {
   240  	lock := new(sync.RWMutex)
   241  	state := &State{
   242  		Modules: []*ModuleState{
   243  			&ModuleState{
   244  				Path: rootModulePath,
   245  				Resources: map[string]*ResourceState{
   246  					"aws_instance.web": &ResourceState{
   247  						Type: "aws_instance",
   248  						Primary: &InstanceState{
   249  							ID: "bar",
   250  							Attributes: map[string]string{
   251  								"foo": "bar",
   252  							},
   253  						},
   254  					},
   255  				},
   256  			},
   257  		},
   258  	}
   259  
   260  	i := &Interpolater{
   261  		Module:    testModule(t, "interpolate-resource-variable"),
   262  		State:     state,
   263  		StateLock: lock,
   264  	}
   265  
   266  	scope := &InterpolationScope{
   267  		Path: rootModulePath,
   268  	}
   269  
   270  	testInterpolate(t, i, scope, "aws_instance.web.foo", ast.Variable{
   271  		Value: "bar",
   272  		Type:  ast.TypeString,
   273  	})
   274  }
   275  
   276  func TestInterpolater_resourceVariableMissingDuringInput(t *testing.T) {
   277  	// During the input walk, computed resource attributes may be entirely
   278  	// absent since we've not yet produced diffs that tell us what computed
   279  	// attributes to expect. In that case, interpolator tolerates it and
   280  	// indicates the value is computed.
   281  
   282  	lock := new(sync.RWMutex)
   283  	state := &State{
   284  		Modules: []*ModuleState{
   285  			&ModuleState{
   286  				Path:      rootModulePath,
   287  				Resources: map[string]*ResourceState{
   288  				// No resources at all yet, because we're still dealing
   289  				// with input and so the resources haven't been created.
   290  				},
   291  			},
   292  		},
   293  	}
   294  
   295  	{
   296  		i := &Interpolater{
   297  			Operation: walkInput,
   298  			Module:    testModule(t, "interpolate-resource-variable"),
   299  			State:     state,
   300  			StateLock: lock,
   301  		}
   302  
   303  		scope := &InterpolationScope{
   304  			Path: rootModulePath,
   305  		}
   306  
   307  		testInterpolate(t, i, scope, "aws_instance.web.foo", ast.Variable{
   308  			Value: config.UnknownVariableValue,
   309  			Type:  ast.TypeUnknown,
   310  		})
   311  	}
   312  
   313  	// This doesn't apply during other walks, like plan
   314  	{
   315  		i := &Interpolater{
   316  			Operation: walkPlan,
   317  			Module:    testModule(t, "interpolate-resource-variable"),
   318  			State:     state,
   319  			StateLock: lock,
   320  		}
   321  
   322  		scope := &InterpolationScope{
   323  			Path: rootModulePath,
   324  		}
   325  
   326  		testInterpolateErr(t, i, scope, "aws_instance.web.foo")
   327  	}
   328  }
   329  
   330  func TestInterpolater_resourceVariableMulti(t *testing.T) {
   331  	lock := new(sync.RWMutex)
   332  	state := &State{
   333  		Modules: []*ModuleState{
   334  			&ModuleState{
   335  				Path: rootModulePath,
   336  				Resources: map[string]*ResourceState{
   337  					"aws_instance.web": &ResourceState{
   338  						Type: "aws_instance",
   339  						Primary: &InstanceState{
   340  							ID: "bar",
   341  							Attributes: map[string]string{
   342  								"foo": config.UnknownVariableValue,
   343  							},
   344  						},
   345  					},
   346  				},
   347  			},
   348  		},
   349  	}
   350  
   351  	i := &Interpolater{
   352  		Module:    testModule(t, "interpolate-resource-variable"),
   353  		State:     state,
   354  		StateLock: lock,
   355  	}
   356  
   357  	scope := &InterpolationScope{
   358  		Path: rootModulePath,
   359  	}
   360  
   361  	testInterpolate(t, i, scope, "aws_instance.web.*.foo", ast.Variable{
   362  		Value: config.UnknownVariableValue,
   363  		Type:  ast.TypeUnknown,
   364  	})
   365  }
   366  
   367  // When a splat reference is made to an attribute that is a computed list,
   368  // the result should be unknown.
   369  func TestInterpolater_resourceVariableMultiList(t *testing.T) {
   370  	lock := new(sync.RWMutex)
   371  	state := &State{
   372  		Modules: []*ModuleState{
   373  			&ModuleState{
   374  				Path: rootModulePath,
   375  				Resources: map[string]*ResourceState{
   376  					"aws_instance.web.0": &ResourceState{
   377  						Type: "aws_instance",
   378  						Primary: &InstanceState{
   379  							ID: "bar",
   380  							Attributes: map[string]string{
   381  								"ip.#": config.UnknownVariableValue,
   382  							},
   383  						},
   384  					},
   385  
   386  					"aws_instance.web.1": &ResourceState{
   387  						Type: "aws_instance",
   388  						Primary: &InstanceState{
   389  							ID: "bar",
   390  							Attributes: map[string]string{
   391  								"ip.#": "0",
   392  							},
   393  						},
   394  					},
   395  				},
   396  			},
   397  		},
   398  	}
   399  
   400  	i := &Interpolater{
   401  		Module:    testModule(t, "interpolate-resource-variable"),
   402  		State:     state,
   403  		StateLock: lock,
   404  	}
   405  
   406  	scope := &InterpolationScope{
   407  		Path: rootModulePath,
   408  	}
   409  
   410  	testInterpolate(t, i, scope, "aws_instance.web.*.ip", ast.Variable{
   411  		Value: config.UnknownVariableValue,
   412  		Type:  ast.TypeUnknown,
   413  	})
   414  }
   415  
   416  func TestInterpolater_resourceVariableMulti_interpolated(t *testing.T) {
   417  	lock := new(sync.RWMutex)
   418  	state := &State{
   419  		Modules: []*ModuleState{
   420  			&ModuleState{
   421  				Path: rootModulePath,
   422  				Resources: map[string]*ResourceState{
   423  					"aws_instance.web.0": &ResourceState{
   424  						Type: "aws_instance",
   425  						Primary: &InstanceState{
   426  							ID:         "a",
   427  							Attributes: map[string]string{"foo": "a"},
   428  						},
   429  					},
   430  
   431  					"aws_instance.web.1": &ResourceState{
   432  						Type: "aws_instance",
   433  						Primary: &InstanceState{
   434  							ID:         "b",
   435  							Attributes: map[string]string{"foo": "b"},
   436  						},
   437  					},
   438  				},
   439  			},
   440  		},
   441  	}
   442  
   443  	i := &Interpolater{
   444  		Operation: walkApply,
   445  		Module:    testModule(t, "interpolate-multi-interp"),
   446  		State:     state,
   447  		StateLock: lock,
   448  	}
   449  
   450  	scope := &InterpolationScope{
   451  		Path: rootModulePath,
   452  	}
   453  
   454  	expected := []interface{}{"a", "b"}
   455  	testInterpolate(t, i, scope, "aws_instance.web.*.foo",
   456  		interfaceToVariableSwallowError(expected))
   457  }
   458  
   459  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
   460  	variable, _ := hil.InterfaceToVariable(input)
   461  	return variable
   462  }
   463  
   464  func TestInterpolator_resourceMultiAttributes(t *testing.T) {
   465  	lock := new(sync.RWMutex)
   466  	state := &State{
   467  		Modules: []*ModuleState{
   468  			{
   469  				Path: rootModulePath,
   470  				Resources: map[string]*ResourceState{
   471  					"aws_route53_zone.yada": {
   472  						Type:         "aws_route53_zone",
   473  						Dependencies: []string{},
   474  						Primary: &InstanceState{
   475  							ID: "AAABBBCCCDDDEEE",
   476  							Attributes: map[string]string{
   477  								"name_servers.#": "4",
   478  								"name_servers.0": "ns-1334.awsdns-38.org",
   479  								"name_servers.1": "ns-1680.awsdns-18.co.uk",
   480  								"name_servers.2": "ns-498.awsdns-62.com",
   481  								"name_servers.3": "ns-601.awsdns-11.net",
   482  								"listeners.#":    "1",
   483  								"listeners.0":    "red",
   484  								"tags.%":         "1",
   485  								"tags.Name":      "reindeer",
   486  								"nothing.#":      "0",
   487  							},
   488  						},
   489  					},
   490  				},
   491  			},
   492  		},
   493  	}
   494  
   495  	i := &Interpolater{
   496  		Module:    testModule(t, "interpolate-multi-vars"),
   497  		StateLock: lock,
   498  		State:     state,
   499  	}
   500  
   501  	scope := &InterpolationScope{
   502  		Path: rootModulePath,
   503  	}
   504  
   505  	name_servers := []interface{}{
   506  		"ns-1334.awsdns-38.org",
   507  		"ns-1680.awsdns-18.co.uk",
   508  		"ns-498.awsdns-62.com",
   509  		"ns-601.awsdns-11.net",
   510  	}
   511  
   512  	// More than 1 element
   513  	testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers",
   514  		interfaceToVariableSwallowError(name_servers))
   515  
   516  	// Exactly 1 element
   517  	testInterpolate(t, i, scope, "aws_route53_zone.yada.listeners",
   518  		interfaceToVariableSwallowError([]interface{}{"red"}))
   519  
   520  	// Zero elements
   521  	testInterpolate(t, i, scope, "aws_route53_zone.yada.nothing",
   522  		interfaceToVariableSwallowError([]interface{}{}))
   523  
   524  	// Maps still need to work
   525  	testInterpolate(t, i, scope, "aws_route53_zone.yada.tags.Name", ast.Variable{
   526  		Value: "reindeer",
   527  		Type:  ast.TypeString,
   528  	})
   529  }
   530  
   531  func TestInterpolator_resourceMultiAttributesWithResourceCount(t *testing.T) {
   532  	i := getInterpolaterFixture(t)
   533  	scope := &InterpolationScope{
   534  		Path: rootModulePath,
   535  	}
   536  
   537  	name_servers := []interface{}{
   538  		"ns-1334.awsdns-38.org",
   539  		"ns-1680.awsdns-18.co.uk",
   540  		"ns-498.awsdns-62.com",
   541  		"ns-601.awsdns-11.net",
   542  		"ns-000.awsdns-38.org",
   543  		"ns-444.awsdns-18.co.uk",
   544  		"ns-999.awsdns-62.com",
   545  		"ns-666.awsdns-11.net",
   546  	}
   547  
   548  	// More than 1 element
   549  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.name_servers",
   550  		interfaceToVariableSwallowError(name_servers[:4]))
   551  
   552  	// More than 1 element in both
   553  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.name_servers",
   554  		interfaceToVariableSwallowError([]interface{}{name_servers[:4], name_servers[4:]}))
   555  
   556  	// Exactly 1 element
   557  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.listeners",
   558  		interfaceToVariableSwallowError([]interface{}{"red"}))
   559  
   560  	// Exactly 1 element in both
   561  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.listeners",
   562  		interfaceToVariableSwallowError([]interface{}{[]interface{}{"red"}, []interface{}{"blue"}}))
   563  
   564  	// Zero elements
   565  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.nothing",
   566  		interfaceToVariableSwallowError([]interface{}{}))
   567  
   568  	// Zero + 1 element
   569  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special",
   570  		interfaceToVariableSwallowError([]interface{}{[]interface{}{"extra"}}))
   571  
   572  	// Maps still need to work
   573  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.tags.Name", ast.Variable{
   574  		Value: "reindeer",
   575  		Type:  ast.TypeString,
   576  	})
   577  
   578  	// Maps still need to work in both
   579  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.tags.Name",
   580  		interfaceToVariableSwallowError([]interface{}{"reindeer", "white-hart"}))
   581  }
   582  
   583  func TestInterpolator_resourceMultiAttributesComputed(t *testing.T) {
   584  	lock := new(sync.RWMutex)
   585  	// The state would never be written with an UnknownVariableValue in it, but
   586  	// it can/does exist that way in memory during the plan phase.
   587  	state := &State{
   588  		Modules: []*ModuleState{
   589  			&ModuleState{
   590  				Path: rootModulePath,
   591  				Resources: map[string]*ResourceState{
   592  					"aws_route53_zone.yada": &ResourceState{
   593  						Type: "aws_route53_zone",
   594  						Primary: &InstanceState{
   595  							ID: "z-abc123",
   596  							Attributes: map[string]string{
   597  								"name_servers.#": config.UnknownVariableValue,
   598  							},
   599  						},
   600  					},
   601  				},
   602  			},
   603  		},
   604  	}
   605  	i := &Interpolater{
   606  		Module:    testModule(t, "interpolate-multi-vars"),
   607  		StateLock: lock,
   608  		State:     state,
   609  	}
   610  
   611  	scope := &InterpolationScope{
   612  		Path: rootModulePath,
   613  	}
   614  
   615  	testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers", ast.Variable{
   616  		Value: config.UnknownVariableValue,
   617  		Type:  ast.TypeUnknown,
   618  	})
   619  }
   620  
   621  func TestInterpolator_resourceAttributeComputed(t *testing.T) {
   622  	lock := new(sync.RWMutex)
   623  	// The state would never be written with an UnknownVariableValue in it, but
   624  	// it can/does exist that way in memory during the plan phase.
   625  	state := &State{
   626  		Modules: []*ModuleState{
   627  			&ModuleState{
   628  				Path: rootModulePath,
   629  				Resources: map[string]*ResourceState{
   630  					"aws_route53_zone.yada": &ResourceState{
   631  						Type: "aws_route53_zone",
   632  						Primary: &InstanceState{
   633  							ID: "z-abc123",
   634  							Attributes: map[string]string{
   635  								"id": config.UnknownVariableValue,
   636  							},
   637  						},
   638  					},
   639  				},
   640  			},
   641  		},
   642  	}
   643  	i := &Interpolater{
   644  		Module:    testModule(t, "interpolate-multi-vars"),
   645  		StateLock: lock,
   646  		State:     state,
   647  	}
   648  
   649  	scope := &InterpolationScope{
   650  		Path: rootModulePath,
   651  	}
   652  
   653  	testInterpolate(t, i, scope, "aws_route53_zone.yada.id", ast.Variable{
   654  		Value: config.UnknownVariableValue,
   655  		Type:  ast.TypeUnknown,
   656  	})
   657  }
   658  
   659  func TestInterpolater_selfVarWithoutResource(t *testing.T) {
   660  	i := &Interpolater{}
   661  
   662  	scope := &InterpolationScope{
   663  		Path: rootModulePath,
   664  	}
   665  
   666  	v, err := config.NewInterpolatedVariable("self.name")
   667  	if err != nil {
   668  		t.Fatalf("err: %s", err)
   669  	}
   670  
   671  	_, err = i.Values(scope, map[string]config.InterpolatedVariable{"foo": v})
   672  	if err == nil {
   673  		t.Fatalf("expected err, got none")
   674  	}
   675  }
   676  
   677  func TestInterpolator_interpolatedListOrder(t *testing.T) {
   678  	state := &State{
   679  		Modules: []*ModuleState{
   680  			&ModuleState{
   681  				Path: rootModulePath,
   682  				Resources: map[string]*ResourceState{
   683  					"aws_route53_zone.yada": &ResourceState{
   684  						Type:         "aws_route53_zone",
   685  						Dependencies: []string{},
   686  						Primary: &InstanceState{
   687  							ID: "null",
   688  							Attributes: map[string]string{
   689  								"foo.#":  "12",
   690  								"foo.0":  "a",
   691  								"foo.1":  "b",
   692  								"foo.2":  "c",
   693  								"foo.3":  "d",
   694  								"foo.4":  "e",
   695  								"foo.5":  "f",
   696  								"foo.6":  "g",
   697  								"foo.7":  "h",
   698  								"foo.8":  "i",
   699  								"foo.9":  "j",
   700  								"foo.10": "k",
   701  								"foo.11": "l",
   702  							},
   703  						},
   704  					},
   705  				},
   706  			},
   707  		},
   708  	}
   709  
   710  	i := &Interpolater{
   711  		Module:    testModule(t, "interpolate-multi-vars"),
   712  		StateLock: new(sync.RWMutex),
   713  		State:     state,
   714  	}
   715  
   716  	scope := &InterpolationScope{
   717  		Path: rootModulePath,
   718  	}
   719  
   720  	list := []interface{}{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}
   721  
   722  	testInterpolate(t, i, scope, "aws_route53_zone.yada.foo",
   723  		interfaceToVariableSwallowError(list))
   724  }
   725  
   726  func getInterpolaterFixture(t *testing.T) *Interpolater {
   727  	lock := new(sync.RWMutex)
   728  	state := &State{
   729  		Modules: []*ModuleState{
   730  			&ModuleState{
   731  				Path: rootModulePath,
   732  				Resources: map[string]*ResourceState{
   733  					"aws_route53_zone.terra.0": &ResourceState{
   734  						Type:         "aws_route53_zone",
   735  						Dependencies: []string{},
   736  						Primary: &InstanceState{
   737  							ID: "AAABBBCCCDDDEEE",
   738  							Attributes: map[string]string{
   739  								"name_servers.#": "4",
   740  								"name_servers.0": "ns-1334.awsdns-38.org",
   741  								"name_servers.1": "ns-1680.awsdns-18.co.uk",
   742  								"name_servers.2": "ns-498.awsdns-62.com",
   743  								"name_servers.3": "ns-601.awsdns-11.net",
   744  								"listeners.#":    "1",
   745  								"listeners.0":    "red",
   746  								"tags.%":         "1",
   747  								"tags.Name":      "reindeer",
   748  								"nothing.#":      "0",
   749  							},
   750  						},
   751  					},
   752  					"aws_route53_zone.terra.1": &ResourceState{
   753  						Type:         "aws_route53_zone",
   754  						Dependencies: []string{},
   755  						Primary: &InstanceState{
   756  							ID: "EEEFFFGGGHHHIII",
   757  							Attributes: map[string]string{
   758  								"name_servers.#": "4",
   759  								"name_servers.0": "ns-000.awsdns-38.org",
   760  								"name_servers.1": "ns-444.awsdns-18.co.uk",
   761  								"name_servers.2": "ns-999.awsdns-62.com",
   762  								"name_servers.3": "ns-666.awsdns-11.net",
   763  								"listeners.#":    "1",
   764  								"listeners.0":    "blue",
   765  								"special.#":      "1",
   766  								"special.0":      "extra",
   767  								"tags.%":         "1",
   768  								"tags.Name":      "white-hart",
   769  								"nothing.#":      "0",
   770  							},
   771  						},
   772  					},
   773  				},
   774  			},
   775  		},
   776  	}
   777  
   778  	return &Interpolater{
   779  		Module:    testModule(t, "interpolate-multi-vars"),
   780  		StateLock: lock,
   781  		State:     state,
   782  	}
   783  }
   784  
   785  func TestInterpolator_nestedMapsAndLists(t *testing.T) {
   786  	state := &State{
   787  		Modules: []*ModuleState{
   788  			&ModuleState{
   789  				Path: rootModulePath,
   790  				Resources: map[string]*ResourceState{
   791  					"aws_route53_zone.yada": &ResourceState{
   792  						Type:         "aws_route53_zone",
   793  						Dependencies: []string{},
   794  						Primary: &InstanceState{
   795  							ID: "null",
   796  							Attributes: map[string]string{
   797  								"list_of_map.#":       "2",
   798  								"list_of_map.0.%":     "1",
   799  								"list_of_map.0.a":     "1",
   800  								"list_of_map.1.%":     "1",
   801  								"list_of_map.1.b":     "2",
   802  								"map_of_list.%":       "2",
   803  								"map_of_list.list2.#": "1",
   804  								"map_of_list.list2.0": "b",
   805  								"map_of_list.list1.#": "1",
   806  								"map_of_list.list1.0": "a",
   807  							},
   808  						},
   809  					},
   810  				},
   811  			},
   812  		},
   813  	}
   814  
   815  	i := &Interpolater{
   816  		Module:    testModule(t, "interpolate-multi-vars"),
   817  		StateLock: new(sync.RWMutex),
   818  		State:     state,
   819  	}
   820  
   821  	scope := &InterpolationScope{
   822  		Path: rootModulePath,
   823  	}
   824  
   825  	listOfMap := []interface{}{
   826  		map[string]interface{}{"a": "1"},
   827  		map[string]interface{}{"b": "2"},
   828  	}
   829  
   830  	mapOfList := map[string]interface{}{
   831  		"list1": []interface{}{"a"},
   832  		"list2": []interface{}{"b"},
   833  	}
   834  
   835  	testInterpolate(t, i, scope, "aws_route53_zone.yada.list_of_map",
   836  		interfaceToVariableSwallowError(listOfMap))
   837  	testInterpolate(t, i, scope, `aws_route53_zone.yada.map_of_list`,
   838  		interfaceToVariableSwallowError(mapOfList))
   839  }
   840  
   841  func TestInterpolator_sets(t *testing.T) {
   842  	state := &State{
   843  		Modules: []*ModuleState{
   844  			&ModuleState{
   845  				Path: rootModulePath,
   846  				Resources: map[string]*ResourceState{
   847  					"aws_route53_zone.yada": &ResourceState{
   848  						Type:         "aws_network_interface",
   849  						Dependencies: []string{},
   850  						Primary: &InstanceState{
   851  							ID: "null",
   852  							Attributes: map[string]string{
   853  								"private_ips.#":          "1",
   854  								"private_ips.3977356764": "10.42.16.179",
   855  							},
   856  						},
   857  					},
   858  				},
   859  			},
   860  		},
   861  	}
   862  
   863  	i := &Interpolater{
   864  		Module:    testModule(t, "interpolate-multi-vars"),
   865  		StateLock: new(sync.RWMutex),
   866  		State:     state,
   867  	}
   868  
   869  	scope := &InterpolationScope{
   870  		Path: rootModulePath,
   871  	}
   872  
   873  	set := []interface{}{"10.42.16.179"}
   874  
   875  	testInterpolate(t, i, scope, "aws_route53_zone.yada.private_ips",
   876  		interfaceToVariableSwallowError(set))
   877  }
   878  
   879  // When a splat reference is made to a resource that is unknown, we should
   880  // return an empty list rather than panicking.
   881  func TestInterpolater_resourceUnknownVariableList(t *testing.T) {
   882  	i := &Interpolater{
   883  		Module:    testModule(t, "plan-computed-data-resource"),
   884  		State:     NewState(), // state,
   885  		StateLock: new(sync.RWMutex),
   886  	}
   887  
   888  	scope := &InterpolationScope{
   889  		Path: rootModulePath,
   890  	}
   891  
   892  	testInterpolate(t, i, scope, "aws_vpc.bar.*.foo",
   893  		interfaceToVariableSwallowError([]interface{}{}))
   894  }
   895  
   896  func testInterpolate(
   897  	t *testing.T, i *Interpolater,
   898  	scope *InterpolationScope,
   899  	n string, expectedVar ast.Variable) {
   900  	v, err := config.NewInterpolatedVariable(n)
   901  	if err != nil {
   902  		t.Fatalf("err: %s", err)
   903  	}
   904  
   905  	actual, err := i.Values(scope, map[string]config.InterpolatedVariable{
   906  		"foo": v,
   907  	})
   908  	if err != nil {
   909  		t.Fatalf("err: %s", err)
   910  	}
   911  
   912  	expected := map[string]ast.Variable{
   913  		"foo": expectedVar,
   914  	}
   915  	if !reflect.DeepEqual(actual, expected) {
   916  		spew.Config.DisableMethods = true
   917  		t.Fatalf("%q:\n\n  actual: %#v\nexpected: %#v\n\n%s\n\n%s\n\n", n, actual, expected,
   918  			spew.Sdump(actual), spew.Sdump(expected))
   919  	}
   920  }
   921  
   922  func testInterpolateErr(
   923  	t *testing.T, i *Interpolater,
   924  	scope *InterpolationScope,
   925  	n string) {
   926  	v, err := config.NewInterpolatedVariable(n)
   927  	if err != nil {
   928  		t.Fatalf("err: %s", err)
   929  	}
   930  
   931  	_, err = i.Values(scope, map[string]config.InterpolatedVariable{
   932  		"foo": v,
   933  	})
   934  	if err == nil {
   935  		t.Fatalf("%q: succeeded, but wanted error", n)
   936  	}
   937  }