github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/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  		Type: ast.TypeList,
   363  		Value: []ast.Variable{
   364  			{
   365  				Type:  ast.TypeUnknown,
   366  				Value: config.UnknownVariableValue,
   367  			},
   368  		},
   369  	})
   370  }
   371  
   372  func TestInterpolater_resourceVariableMultiPartialUnknown(t *testing.T) {
   373  	lock := new(sync.RWMutex)
   374  	state := &State{
   375  		Modules: []*ModuleState{
   376  			&ModuleState{
   377  				Path: rootModulePath,
   378  				Resources: map[string]*ResourceState{
   379  					"aws_instance.web.0": &ResourceState{
   380  						Type: "aws_instance",
   381  						Primary: &InstanceState{
   382  							ID: "bar",
   383  							Attributes: map[string]string{
   384  								"foo": "1",
   385  							},
   386  						},
   387  					},
   388  					"aws_instance.web.1": &ResourceState{
   389  						Type: "aws_instance",
   390  						Primary: &InstanceState{
   391  							ID: "bar",
   392  							Attributes: map[string]string{
   393  								"foo": config.UnknownVariableValue,
   394  							},
   395  						},
   396  					},
   397  					"aws_instance.web.2": &ResourceState{
   398  						Type: "aws_instance",
   399  						Primary: &InstanceState{
   400  							ID: "bar",
   401  							Attributes: map[string]string{
   402  								"foo": "2",
   403  							},
   404  						},
   405  					},
   406  				},
   407  			},
   408  		},
   409  	}
   410  
   411  	i := &Interpolater{
   412  		Module:    testModule(t, "interpolate-resource-variable-multi"),
   413  		State:     state,
   414  		StateLock: lock,
   415  	}
   416  
   417  	scope := &InterpolationScope{
   418  		Path: rootModulePath,
   419  	}
   420  
   421  	testInterpolate(t, i, scope, "aws_instance.web.*.foo", ast.Variable{
   422  		Type: ast.TypeList,
   423  		Value: []ast.Variable{
   424  			{
   425  				Type:  ast.TypeString,
   426  				Value: "1",
   427  			},
   428  			{
   429  				Type:  ast.TypeUnknown,
   430  				Value: config.UnknownVariableValue,
   431  			},
   432  			{
   433  				Type:  ast.TypeString,
   434  				Value: "2",
   435  			},
   436  		},
   437  	})
   438  }
   439  
   440  func TestInterpolater_resourceVariableMultiNoState(t *testing.T) {
   441  	// When evaluating a "splat" variable in a module that doesn't have
   442  	// any state yet, we should still be able to resolve to an empty
   443  	// list.
   444  	// See https://github.com/hashicorp/terraform/issues/14438 for an
   445  	// example of what we're testing for here.
   446  	lock := new(sync.RWMutex)
   447  	state := &State{
   448  		Modules: []*ModuleState{},
   449  	}
   450  
   451  	i := &Interpolater{
   452  		Module:    testModule(t, "interpolate-resource-variable-multi"),
   453  		State:     state,
   454  		StateLock: lock,
   455  		Operation: walkApply,
   456  	}
   457  
   458  	scope := &InterpolationScope{
   459  		Path: rootModulePath,
   460  	}
   461  
   462  	testInterpolate(t, i, scope, "aws_instance.web.*.foo", ast.Variable{
   463  		Type:  ast.TypeList,
   464  		Value: []ast.Variable{},
   465  	})
   466  }
   467  
   468  // When a splat reference is made to an attribute that is a computed list,
   469  // the result should be unknown.
   470  func TestInterpolater_resourceVariableMultiList(t *testing.T) {
   471  	lock := new(sync.RWMutex)
   472  	state := &State{
   473  		Modules: []*ModuleState{
   474  			&ModuleState{
   475  				Path: rootModulePath,
   476  				Resources: map[string]*ResourceState{
   477  					"aws_instance.web.0": &ResourceState{
   478  						Type: "aws_instance",
   479  						Primary: &InstanceState{
   480  							ID: "bar",
   481  							Attributes: map[string]string{
   482  								"ip.#": config.UnknownVariableValue,
   483  							},
   484  						},
   485  					},
   486  
   487  					"aws_instance.web.1": &ResourceState{
   488  						Type: "aws_instance",
   489  						Primary: &InstanceState{
   490  							ID: "bar",
   491  							Attributes: map[string]string{
   492  								"ip.#": "0",
   493  							},
   494  						},
   495  					},
   496  				},
   497  			},
   498  		},
   499  	}
   500  
   501  	i := &Interpolater{
   502  		Module:    testModule(t, "interpolate-resource-variable"),
   503  		State:     state,
   504  		StateLock: lock,
   505  	}
   506  
   507  	scope := &InterpolationScope{
   508  		Path: rootModulePath,
   509  	}
   510  
   511  	testInterpolate(t, i, scope, "aws_instance.web.*.ip", ast.Variable{
   512  		Type: ast.TypeList,
   513  		Value: []ast.Variable{
   514  			{
   515  				Type:  ast.TypeUnknown,
   516  				Value: config.UnknownVariableValue,
   517  			},
   518  		},
   519  	})
   520  }
   521  
   522  func TestInterpolater_resourceVariableMulti_interpolated(t *testing.T) {
   523  	lock := new(sync.RWMutex)
   524  	state := &State{
   525  		Modules: []*ModuleState{
   526  			&ModuleState{
   527  				Path: rootModulePath,
   528  				Resources: map[string]*ResourceState{
   529  					"aws_instance.web.0": &ResourceState{
   530  						Type: "aws_instance",
   531  						Primary: &InstanceState{
   532  							ID:         "a",
   533  							Attributes: map[string]string{"foo": "a"},
   534  						},
   535  					},
   536  
   537  					"aws_instance.web.1": &ResourceState{
   538  						Type: "aws_instance",
   539  						Primary: &InstanceState{
   540  							ID:         "b",
   541  							Attributes: map[string]string{"foo": "b"},
   542  						},
   543  					},
   544  				},
   545  			},
   546  		},
   547  	}
   548  
   549  	i := &Interpolater{
   550  		Operation: walkApply,
   551  		Module:    testModule(t, "interpolate-multi-interp"),
   552  		State:     state,
   553  		StateLock: lock,
   554  	}
   555  
   556  	scope := &InterpolationScope{
   557  		Path: rootModulePath,
   558  	}
   559  
   560  	expected := []interface{}{"a", "b"}
   561  	testInterpolate(t, i, scope, "aws_instance.web.*.foo",
   562  		interfaceToVariableSwallowError(expected))
   563  }
   564  
   565  func interfaceToVariableSwallowError(input interface{}) ast.Variable {
   566  	variable, _ := hil.InterfaceToVariable(input)
   567  	return variable
   568  }
   569  
   570  func TestInterpolator_resourceMultiAttributes(t *testing.T) {
   571  	lock := new(sync.RWMutex)
   572  	state := &State{
   573  		Modules: []*ModuleState{
   574  			{
   575  				Path: rootModulePath,
   576  				Resources: map[string]*ResourceState{
   577  					"aws_route53_zone.yada": {
   578  						Type:         "aws_route53_zone",
   579  						Dependencies: []string{},
   580  						Primary: &InstanceState{
   581  							ID: "AAABBBCCCDDDEEE",
   582  							Attributes: map[string]string{
   583  								"name_servers.#": "4",
   584  								"name_servers.0": "ns-1334.awsdns-38.org",
   585  								"name_servers.1": "ns-1680.awsdns-18.co.uk",
   586  								"name_servers.2": "ns-498.awsdns-62.com",
   587  								"name_servers.3": "ns-601.awsdns-11.net",
   588  								"listeners.#":    "1",
   589  								"listeners.0":    "red",
   590  								"tags.%":         "1",
   591  								"tags.Name":      "reindeer",
   592  								"nothing.#":      "0",
   593  							},
   594  						},
   595  					},
   596  				},
   597  			},
   598  		},
   599  	}
   600  
   601  	i := &Interpolater{
   602  		Module:    testModule(t, "interpolate-multi-vars"),
   603  		StateLock: lock,
   604  		State:     state,
   605  	}
   606  
   607  	scope := &InterpolationScope{
   608  		Path: rootModulePath,
   609  	}
   610  
   611  	name_servers := []interface{}{
   612  		"ns-1334.awsdns-38.org",
   613  		"ns-1680.awsdns-18.co.uk",
   614  		"ns-498.awsdns-62.com",
   615  		"ns-601.awsdns-11.net",
   616  	}
   617  
   618  	// More than 1 element
   619  	testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers",
   620  		interfaceToVariableSwallowError(name_servers))
   621  
   622  	// Exactly 1 element
   623  	testInterpolate(t, i, scope, "aws_route53_zone.yada.listeners",
   624  		interfaceToVariableSwallowError([]interface{}{"red"}))
   625  
   626  	// Zero elements
   627  	testInterpolate(t, i, scope, "aws_route53_zone.yada.nothing",
   628  		interfaceToVariableSwallowError([]interface{}{}))
   629  
   630  	// Maps still need to work
   631  	testInterpolate(t, i, scope, "aws_route53_zone.yada.tags.Name", ast.Variable{
   632  		Value: "reindeer",
   633  		Type:  ast.TypeString,
   634  	})
   635  }
   636  
   637  func TestInterpolator_resourceMultiAttributesWithResourceCount(t *testing.T) {
   638  	i := getInterpolaterFixture(t)
   639  	scope := &InterpolationScope{
   640  		Path: rootModulePath,
   641  	}
   642  
   643  	name_servers := []interface{}{
   644  		"ns-1334.awsdns-38.org",
   645  		"ns-1680.awsdns-18.co.uk",
   646  		"ns-498.awsdns-62.com",
   647  		"ns-601.awsdns-11.net",
   648  		"ns-000.awsdns-38.org",
   649  		"ns-444.awsdns-18.co.uk",
   650  		"ns-999.awsdns-62.com",
   651  		"ns-666.awsdns-11.net",
   652  	}
   653  
   654  	// More than 1 element
   655  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.name_servers",
   656  		interfaceToVariableSwallowError(name_servers[:4]))
   657  
   658  	// More than 1 element in both
   659  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.name_servers",
   660  		interfaceToVariableSwallowError([]interface{}{name_servers[:4], name_servers[4:]}))
   661  
   662  	// Exactly 1 element
   663  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.listeners",
   664  		interfaceToVariableSwallowError([]interface{}{"red"}))
   665  
   666  	// Exactly 1 element in both
   667  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.listeners",
   668  		interfaceToVariableSwallowError([]interface{}{[]interface{}{"red"}, []interface{}{"blue"}}))
   669  
   670  	// Zero elements
   671  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.nothing",
   672  		interfaceToVariableSwallowError([]interface{}{}))
   673  
   674  	// Zero + 1 element
   675  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special",
   676  		interfaceToVariableSwallowError([]interface{}{[]interface{}{"extra"}}))
   677  
   678  	// Maps still need to work
   679  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.tags.Name", ast.Variable{
   680  		Value: "reindeer",
   681  		Type:  ast.TypeString,
   682  	})
   683  
   684  	// Maps still need to work in both
   685  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.tags.Name",
   686  		interfaceToVariableSwallowError([]interface{}{"reindeer", "white-hart"}))
   687  }
   688  
   689  func TestInterpolator_resourceMultiAttributesComputed(t *testing.T) {
   690  	lock := new(sync.RWMutex)
   691  	// The state would never be written with an UnknownVariableValue in it, but
   692  	// it can/does exist that way in memory during the plan phase.
   693  	state := &State{
   694  		Modules: []*ModuleState{
   695  			&ModuleState{
   696  				Path: rootModulePath,
   697  				Resources: map[string]*ResourceState{
   698  					"aws_route53_zone.yada": &ResourceState{
   699  						Type: "aws_route53_zone",
   700  						Primary: &InstanceState{
   701  							ID: "z-abc123",
   702  							Attributes: map[string]string{
   703  								"name_servers.#": config.UnknownVariableValue,
   704  							},
   705  						},
   706  					},
   707  				},
   708  			},
   709  		},
   710  	}
   711  	i := &Interpolater{
   712  		Module:    testModule(t, "interpolate-multi-vars"),
   713  		StateLock: lock,
   714  		State:     state,
   715  	}
   716  
   717  	scope := &InterpolationScope{
   718  		Path: rootModulePath,
   719  	}
   720  
   721  	testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers", ast.Variable{
   722  		Value: config.UnknownVariableValue,
   723  		Type:  ast.TypeUnknown,
   724  	})
   725  }
   726  
   727  func TestInterpolator_resourceAttributeComputed(t *testing.T) {
   728  	lock := new(sync.RWMutex)
   729  	// The state would never be written with an UnknownVariableValue in it, but
   730  	// it can/does exist that way in memory during the plan phase.
   731  	state := &State{
   732  		Modules: []*ModuleState{
   733  			&ModuleState{
   734  				Path: rootModulePath,
   735  				Resources: map[string]*ResourceState{
   736  					"aws_route53_zone.yada": &ResourceState{
   737  						Type: "aws_route53_zone",
   738  						Primary: &InstanceState{
   739  							ID: "z-abc123",
   740  							Attributes: map[string]string{
   741  								"id": config.UnknownVariableValue,
   742  							},
   743  						},
   744  					},
   745  				},
   746  			},
   747  		},
   748  	}
   749  	i := &Interpolater{
   750  		Module:    testModule(t, "interpolate-multi-vars"),
   751  		StateLock: lock,
   752  		State:     state,
   753  	}
   754  
   755  	scope := &InterpolationScope{
   756  		Path: rootModulePath,
   757  	}
   758  
   759  	testInterpolate(t, i, scope, "aws_route53_zone.yada.id", ast.Variable{
   760  		Value: config.UnknownVariableValue,
   761  		Type:  ast.TypeUnknown,
   762  	})
   763  }
   764  
   765  func TestInterpolater_selfVarWithoutResource(t *testing.T) {
   766  	i := &Interpolater{}
   767  
   768  	scope := &InterpolationScope{
   769  		Path: rootModulePath,
   770  	}
   771  
   772  	v, err := config.NewInterpolatedVariable("self.name")
   773  	if err != nil {
   774  		t.Fatalf("err: %s", err)
   775  	}
   776  
   777  	_, err = i.Values(scope, map[string]config.InterpolatedVariable{"foo": v})
   778  	if err == nil {
   779  		t.Fatalf("expected err, got none")
   780  	}
   781  }
   782  
   783  func TestInterpolator_interpolatedListOrder(t *testing.T) {
   784  	state := &State{
   785  		Modules: []*ModuleState{
   786  			&ModuleState{
   787  				Path: rootModulePath,
   788  				Resources: map[string]*ResourceState{
   789  					"aws_route53_zone.yada": &ResourceState{
   790  						Type:         "aws_route53_zone",
   791  						Dependencies: []string{},
   792  						Primary: &InstanceState{
   793  							ID: "null",
   794  							Attributes: map[string]string{
   795  								"foo.#":  "12",
   796  								"foo.0":  "a",
   797  								"foo.1":  "b",
   798  								"foo.2":  "c",
   799  								"foo.3":  "d",
   800  								"foo.4":  "e",
   801  								"foo.5":  "f",
   802  								"foo.6":  "g",
   803  								"foo.7":  "h",
   804  								"foo.8":  "i",
   805  								"foo.9":  "j",
   806  								"foo.10": "k",
   807  								"foo.11": "l",
   808  							},
   809  						},
   810  					},
   811  				},
   812  			},
   813  		},
   814  	}
   815  
   816  	i := &Interpolater{
   817  		Module:    testModule(t, "interpolate-multi-vars"),
   818  		StateLock: new(sync.RWMutex),
   819  		State:     state,
   820  	}
   821  
   822  	scope := &InterpolationScope{
   823  		Path: rootModulePath,
   824  	}
   825  
   826  	list := []interface{}{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}
   827  
   828  	testInterpolate(t, i, scope, "aws_route53_zone.yada.foo",
   829  		interfaceToVariableSwallowError(list))
   830  }
   831  
   832  func getInterpolaterFixture(t *testing.T) *Interpolater {
   833  	lock := new(sync.RWMutex)
   834  	state := &State{
   835  		Modules: []*ModuleState{
   836  			&ModuleState{
   837  				Path: rootModulePath,
   838  				Resources: map[string]*ResourceState{
   839  					"aws_route53_zone.terra.0": &ResourceState{
   840  						Type:         "aws_route53_zone",
   841  						Dependencies: []string{},
   842  						Primary: &InstanceState{
   843  							ID: "AAABBBCCCDDDEEE",
   844  							Attributes: map[string]string{
   845  								"name_servers.#": "4",
   846  								"name_servers.0": "ns-1334.awsdns-38.org",
   847  								"name_servers.1": "ns-1680.awsdns-18.co.uk",
   848  								"name_servers.2": "ns-498.awsdns-62.com",
   849  								"name_servers.3": "ns-601.awsdns-11.net",
   850  								"listeners.#":    "1",
   851  								"listeners.0":    "red",
   852  								"tags.%":         "1",
   853  								"tags.Name":      "reindeer",
   854  								"nothing.#":      "0",
   855  							},
   856  						},
   857  					},
   858  					"aws_route53_zone.terra.1": &ResourceState{
   859  						Type:         "aws_route53_zone",
   860  						Dependencies: []string{},
   861  						Primary: &InstanceState{
   862  							ID: "EEEFFFGGGHHHIII",
   863  							Attributes: map[string]string{
   864  								"name_servers.#": "4",
   865  								"name_servers.0": "ns-000.awsdns-38.org",
   866  								"name_servers.1": "ns-444.awsdns-18.co.uk",
   867  								"name_servers.2": "ns-999.awsdns-62.com",
   868  								"name_servers.3": "ns-666.awsdns-11.net",
   869  								"listeners.#":    "1",
   870  								"listeners.0":    "blue",
   871  								"special.#":      "1",
   872  								"special.0":      "extra",
   873  								"tags.%":         "1",
   874  								"tags.Name":      "white-hart",
   875  								"nothing.#":      "0",
   876  							},
   877  						},
   878  					},
   879  				},
   880  			},
   881  		},
   882  	}
   883  
   884  	return &Interpolater{
   885  		Module:    testModule(t, "interpolate-multi-vars"),
   886  		StateLock: lock,
   887  		State:     state,
   888  	}
   889  }
   890  
   891  func TestInterpolator_nestedMapsAndLists(t *testing.T) {
   892  	state := &State{
   893  		Modules: []*ModuleState{
   894  			&ModuleState{
   895  				Path: rootModulePath,
   896  				Resources: map[string]*ResourceState{
   897  					"aws_route53_zone.yada": &ResourceState{
   898  						Type:         "aws_route53_zone",
   899  						Dependencies: []string{},
   900  						Primary: &InstanceState{
   901  							ID: "null",
   902  							Attributes: map[string]string{
   903  								"list_of_map.#":       "2",
   904  								"list_of_map.0.%":     "1",
   905  								"list_of_map.0.a":     "1",
   906  								"list_of_map.1.%":     "1",
   907  								"list_of_map.1.b":     "2",
   908  								"map_of_list.%":       "2",
   909  								"map_of_list.list2.#": "1",
   910  								"map_of_list.list2.0": "b",
   911  								"map_of_list.list1.#": "1",
   912  								"map_of_list.list1.0": "a",
   913  							},
   914  						},
   915  					},
   916  				},
   917  			},
   918  		},
   919  	}
   920  
   921  	i := &Interpolater{
   922  		Module:    testModule(t, "interpolate-multi-vars"),
   923  		StateLock: new(sync.RWMutex),
   924  		State:     state,
   925  	}
   926  
   927  	scope := &InterpolationScope{
   928  		Path: rootModulePath,
   929  	}
   930  
   931  	listOfMap := []interface{}{
   932  		map[string]interface{}{"a": "1"},
   933  		map[string]interface{}{"b": "2"},
   934  	}
   935  
   936  	mapOfList := map[string]interface{}{
   937  		"list1": []interface{}{"a"},
   938  		"list2": []interface{}{"b"},
   939  	}
   940  
   941  	testInterpolate(t, i, scope, "aws_route53_zone.yada.list_of_map",
   942  		interfaceToVariableSwallowError(listOfMap))
   943  	testInterpolate(t, i, scope, `aws_route53_zone.yada.map_of_list`,
   944  		interfaceToVariableSwallowError(mapOfList))
   945  }
   946  
   947  func TestInterpolator_sets(t *testing.T) {
   948  	state := &State{
   949  		Modules: []*ModuleState{
   950  			&ModuleState{
   951  				Path: rootModulePath,
   952  				Resources: map[string]*ResourceState{
   953  					"aws_route53_zone.yada": &ResourceState{
   954  						Type:         "aws_network_interface",
   955  						Dependencies: []string{},
   956  						Primary: &InstanceState{
   957  							ID: "null",
   958  							Attributes: map[string]string{
   959  								"private_ips.#":          "1",
   960  								"private_ips.3977356764": "10.42.16.179",
   961  							},
   962  						},
   963  					},
   964  				},
   965  			},
   966  		},
   967  	}
   968  
   969  	i := &Interpolater{
   970  		Module:    testModule(t, "interpolate-multi-vars"),
   971  		StateLock: new(sync.RWMutex),
   972  		State:     state,
   973  	}
   974  
   975  	scope := &InterpolationScope{
   976  		Path: rootModulePath,
   977  	}
   978  
   979  	set := []interface{}{"10.42.16.179"}
   980  
   981  	testInterpolate(t, i, scope, "aws_route53_zone.yada.private_ips",
   982  		interfaceToVariableSwallowError(set))
   983  }
   984  
   985  // When a splat reference is made to a resource that is unknown, we should
   986  // return an empty list rather than panicking.
   987  func TestInterpolater_resourceUnknownVariableList(t *testing.T) {
   988  	i := &Interpolater{
   989  		Module:    testModule(t, "plan-computed-data-resource"),
   990  		State:     NewState(), // state,
   991  		StateLock: new(sync.RWMutex),
   992  	}
   993  
   994  	scope := &InterpolationScope{
   995  		Path: rootModulePath,
   996  	}
   997  
   998  	testInterpolate(t, i, scope, "aws_vpc.bar.*.foo",
   999  		interfaceToVariableSwallowError([]interface{}{}))
  1000  }
  1001  
  1002  func TestInterpolater_terraformEnv(t *testing.T) {
  1003  	i := &Interpolater{
  1004  		Meta: &ContextMeta{Env: "foo"},
  1005  	}
  1006  
  1007  	scope := &InterpolationScope{
  1008  		Path: rootModulePath,
  1009  	}
  1010  
  1011  	testInterpolate(t, i, scope, "terraform.env", ast.Variable{
  1012  		Value: "foo",
  1013  		Type:  ast.TypeString,
  1014  	})
  1015  }
  1016  
  1017  func TestInterpolater_terraformInvalid(t *testing.T) {
  1018  	i := &Interpolater{
  1019  		Meta: &ContextMeta{Env: "foo"},
  1020  	}
  1021  
  1022  	scope := &InterpolationScope{
  1023  		Path: rootModulePath,
  1024  	}
  1025  
  1026  	testInterpolateErr(t, i, scope, "terraform.nope")
  1027  }
  1028  
  1029  func testInterpolate(
  1030  	t *testing.T, i *Interpolater,
  1031  	scope *InterpolationScope,
  1032  	n string, expectedVar ast.Variable) {
  1033  	v, err := config.NewInterpolatedVariable(n)
  1034  	if err != nil {
  1035  		t.Fatalf("err: %s", err)
  1036  	}
  1037  
  1038  	actual, err := i.Values(scope, map[string]config.InterpolatedVariable{
  1039  		"foo": v,
  1040  	})
  1041  	if err != nil {
  1042  		t.Fatalf("err: %s", err)
  1043  	}
  1044  
  1045  	expected := map[string]ast.Variable{
  1046  		"foo": expectedVar,
  1047  	}
  1048  	if !reflect.DeepEqual(actual, expected) {
  1049  		spew.Config.DisableMethods = true
  1050  		t.Fatalf("%q:\n\n  actual: %#v\nexpected: %#v\n\n%s\n\n%s\n\n", n, actual, expected,
  1051  			spew.Sdump(actual), spew.Sdump(expected))
  1052  	}
  1053  }
  1054  
  1055  func testInterpolateErr(
  1056  	t *testing.T, i *Interpolater,
  1057  	scope *InterpolationScope,
  1058  	n string) {
  1059  	v, err := config.NewInterpolatedVariable(n)
  1060  	if err != nil {
  1061  		t.Fatalf("err: %s", err)
  1062  	}
  1063  
  1064  	_, err = i.Values(scope, map[string]config.InterpolatedVariable{
  1065  		"foo": v,
  1066  	})
  1067  	if err == nil {
  1068  		t.Fatalf("%q: succeeded, but wanted error", n)
  1069  	}
  1070  }