github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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/hashicorp/terraform/config"
    11  	"github.com/hashicorp/terraform/config/lang/ast"
    12  )
    13  
    14  func TestInterpolater_countIndex(t *testing.T) {
    15  	i := &Interpolater{}
    16  
    17  	scope := &InterpolationScope{
    18  		Path:     rootModulePath,
    19  		Resource: &Resource{CountIndex: 42},
    20  	}
    21  
    22  	testInterpolate(t, i, scope, "count.index", ast.Variable{
    23  		Value: 42,
    24  		Type:  ast.TypeInt,
    25  	})
    26  }
    27  
    28  func TestInterpolater_countIndexInWrongContext(t *testing.T) {
    29  	i := &Interpolater{}
    30  
    31  	scope := &InterpolationScope{
    32  		Path: rootModulePath,
    33  	}
    34  
    35  	n := "count.index"
    36  
    37  	v, err := config.NewInterpolatedVariable(n)
    38  	if err != nil {
    39  		t.Fatalf("err: %s", err)
    40  	}
    41  
    42  	expectedErr := fmt.Errorf("foo: count.index is only valid within resources")
    43  
    44  	_, err = i.Values(scope, map[string]config.InterpolatedVariable{
    45  		"foo": v,
    46  	})
    47  
    48  	if !reflect.DeepEqual(expectedErr, err) {
    49  		t.Fatalf("expected: %#v, got %#v", expectedErr, err)
    50  	}
    51  }
    52  
    53  func TestInterpolater_moduleVariable(t *testing.T) {
    54  	lock := new(sync.RWMutex)
    55  	state := &State{
    56  		Modules: []*ModuleState{
    57  			&ModuleState{
    58  				Path: rootModulePath,
    59  				Resources: map[string]*ResourceState{
    60  					"aws_instance.web": &ResourceState{
    61  						Type: "aws_instance",
    62  						Primary: &InstanceState{
    63  							ID: "bar",
    64  						},
    65  					},
    66  				},
    67  			},
    68  			&ModuleState{
    69  				Path: []string{RootModuleName, "child"},
    70  				Outputs: map[string]string{
    71  					"foo": "bar",
    72  				},
    73  			},
    74  		},
    75  	}
    76  
    77  	i := &Interpolater{
    78  		State:     state,
    79  		StateLock: lock,
    80  	}
    81  
    82  	scope := &InterpolationScope{
    83  		Path: rootModulePath,
    84  	}
    85  
    86  	testInterpolate(t, i, scope, "module.child.foo", ast.Variable{
    87  		Value: "bar",
    88  		Type:  ast.TypeString,
    89  	})
    90  }
    91  
    92  func TestInterpolater_pathCwd(t *testing.T) {
    93  	i := &Interpolater{}
    94  	scope := &InterpolationScope{}
    95  
    96  	expected, err := os.Getwd()
    97  	if err != nil {
    98  		t.Fatalf("err: %s", err)
    99  	}
   100  
   101  	testInterpolate(t, i, scope, "path.cwd", ast.Variable{
   102  		Value: expected,
   103  		Type:  ast.TypeString,
   104  	})
   105  }
   106  
   107  func TestInterpolater_pathModule(t *testing.T) {
   108  	mod := testModule(t, "interpolate-path-module")
   109  	i := &Interpolater{
   110  		Module: mod,
   111  	}
   112  	scope := &InterpolationScope{
   113  		Path: []string{RootModuleName, "child"},
   114  	}
   115  
   116  	path := mod.Child([]string{"child"}).Config().Dir
   117  	testInterpolate(t, i, scope, "path.module", ast.Variable{
   118  		Value: path,
   119  		Type:  ast.TypeString,
   120  	})
   121  }
   122  
   123  func TestInterpolater_pathRoot(t *testing.T) {
   124  	mod := testModule(t, "interpolate-path-module")
   125  	i := &Interpolater{
   126  		Module: mod,
   127  	}
   128  	scope := &InterpolationScope{
   129  		Path: []string{RootModuleName, "child"},
   130  	}
   131  
   132  	path := mod.Config().Dir
   133  	testInterpolate(t, i, scope, "path.root", ast.Variable{
   134  		Value: path,
   135  		Type:  ast.TypeString,
   136  	})
   137  }
   138  
   139  func TestInterpolater_resourceVariable(t *testing.T) {
   140  	lock := new(sync.RWMutex)
   141  	state := &State{
   142  		Modules: []*ModuleState{
   143  			&ModuleState{
   144  				Path: rootModulePath,
   145  				Resources: map[string]*ResourceState{
   146  					"aws_instance.web": &ResourceState{
   147  						Type: "aws_instance",
   148  						Primary: &InstanceState{
   149  							ID: "bar",
   150  							Attributes: map[string]string{
   151  								"foo": "bar",
   152  							},
   153  						},
   154  					},
   155  				},
   156  			},
   157  		},
   158  	}
   159  
   160  	i := &Interpolater{
   161  		Module:    testModule(t, "interpolate-resource-variable"),
   162  		State:     state,
   163  		StateLock: lock,
   164  	}
   165  
   166  	scope := &InterpolationScope{
   167  		Path: rootModulePath,
   168  	}
   169  
   170  	testInterpolate(t, i, scope, "aws_instance.web.foo", ast.Variable{
   171  		Value: "bar",
   172  		Type:  ast.TypeString,
   173  	})
   174  }
   175  
   176  func TestInterpolater_resourceVariableMulti(t *testing.T) {
   177  	lock := new(sync.RWMutex)
   178  	state := &State{
   179  		Modules: []*ModuleState{
   180  			&ModuleState{
   181  				Path: rootModulePath,
   182  				Resources: map[string]*ResourceState{
   183  					"aws_instance.web": &ResourceState{
   184  						Type: "aws_instance",
   185  						Primary: &InstanceState{
   186  							ID: "bar",
   187  							Attributes: map[string]string{
   188  								"foo": config.UnknownVariableValue,
   189  							},
   190  						},
   191  					},
   192  				},
   193  			},
   194  		},
   195  	}
   196  
   197  	i := &Interpolater{
   198  		Module:    testModule(t, "interpolate-resource-variable"),
   199  		State:     state,
   200  		StateLock: lock,
   201  	}
   202  
   203  	scope := &InterpolationScope{
   204  		Path: rootModulePath,
   205  	}
   206  
   207  	testInterpolate(t, i, scope, "aws_instance.web.*.foo", ast.Variable{
   208  		Value: config.UnknownVariableValue,
   209  		Type:  ast.TypeString,
   210  	})
   211  }
   212  
   213  func TestInterpolator_resourceMultiAttributes(t *testing.T) {
   214  	lock := new(sync.RWMutex)
   215  	state := &State{
   216  		Modules: []*ModuleState{
   217  			&ModuleState{
   218  				Path: rootModulePath,
   219  				Resources: map[string]*ResourceState{
   220  					"aws_route53_zone.yada": &ResourceState{
   221  						Type:         "aws_route53_zone",
   222  						Dependencies: []string{},
   223  						Primary: &InstanceState{
   224  							ID: "AAABBBCCCDDDEEE",
   225  							Attributes: map[string]string{
   226  								"name_servers.#": "4",
   227  								"name_servers.0": "ns-1334.awsdns-38.org",
   228  								"name_servers.1": "ns-1680.awsdns-18.co.uk",
   229  								"name_servers.2": "ns-498.awsdns-62.com",
   230  								"name_servers.3": "ns-601.awsdns-11.net",
   231  								"listeners.#":    "1",
   232  								"listeners.0":    "red",
   233  								"tags.#":         "1",
   234  								"tags.Name":      "reindeer",
   235  								"nothing.#":      "0",
   236  							},
   237  						},
   238  					},
   239  				},
   240  			},
   241  		},
   242  	}
   243  
   244  	i := &Interpolater{
   245  		Module:    testModule(t, "interpolate-multi-vars"),
   246  		StateLock: lock,
   247  		State:     state,
   248  	}
   249  
   250  	scope := &InterpolationScope{
   251  		Path: rootModulePath,
   252  	}
   253  
   254  	name_servers := []string{
   255  		"ns-1334.awsdns-38.org",
   256  		"ns-1680.awsdns-18.co.uk",
   257  		"ns-498.awsdns-62.com",
   258  		"ns-601.awsdns-11.net",
   259  	}
   260  	expectedNameServers := config.NewStringList(name_servers).String()
   261  
   262  	// More than 1 element
   263  	testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers", ast.Variable{
   264  		Value: expectedNameServers,
   265  		Type:  ast.TypeString,
   266  	})
   267  
   268  	// Exactly 1 element
   269  	testInterpolate(t, i, scope, "aws_route53_zone.yada.listeners", ast.Variable{
   270  		Value: config.NewStringList([]string{"red"}).String(),
   271  		Type:  ast.TypeString,
   272  	})
   273  
   274  	// Zero elements
   275  	testInterpolate(t, i, scope, "aws_route53_zone.yada.nothing", ast.Variable{
   276  		Value: config.NewStringList([]string{}).String(),
   277  		Type:  ast.TypeString,
   278  	})
   279  
   280  	// Maps still need to work
   281  	testInterpolate(t, i, scope, "aws_route53_zone.yada.tags.Name", ast.Variable{
   282  		Value: "reindeer",
   283  		Type:  ast.TypeString,
   284  	})
   285  }
   286  
   287  func TestInterpolator_resourceMultiAttributesWithResourceCount(t *testing.T) {
   288  	i := getInterpolaterFixture(t)
   289  	scope := &InterpolationScope{
   290  		Path: rootModulePath,
   291  	}
   292  
   293  	name_servers := []string{
   294  		"ns-1334.awsdns-38.org",
   295  		"ns-1680.awsdns-18.co.uk",
   296  		"ns-498.awsdns-62.com",
   297  		"ns-601.awsdns-11.net",
   298  		"ns-000.awsdns-38.org",
   299  		"ns-444.awsdns-18.co.uk",
   300  		"ns-666.awsdns-11.net",
   301  		"ns-999.awsdns-62.com",
   302  	}
   303  
   304  	// More than 1 element
   305  	expectedNameServers := config.NewStringList(name_servers[0:4]).String()
   306  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.name_servers", ast.Variable{
   307  		Value: expectedNameServers,
   308  		Type:  ast.TypeString,
   309  	})
   310  	// More than 1 element in both
   311  	expectedNameServers = config.NewStringList(name_servers).String()
   312  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.name_servers", ast.Variable{
   313  		Value: expectedNameServers,
   314  		Type:  ast.TypeString,
   315  	})
   316  
   317  	// Exactly 1 element
   318  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.listeners", ast.Variable{
   319  		Value: config.NewStringList([]string{"red"}).String(),
   320  		Type:  ast.TypeString,
   321  	})
   322  	// Exactly 1 element in both
   323  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.listeners", ast.Variable{
   324  		Value: config.NewStringList([]string{"red", "blue"}).String(),
   325  		Type:  ast.TypeString,
   326  	})
   327  
   328  	// Zero elements
   329  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.nothing", ast.Variable{
   330  		Value: config.NewStringList([]string{}).String(),
   331  		Type:  ast.TypeString,
   332  	})
   333  	// Zero + 1 element
   334  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special", ast.Variable{
   335  		Value: config.NewStringList([]string{"extra"}).String(),
   336  		Type:  ast.TypeString,
   337  	})
   338  
   339  	// Maps still need to work
   340  	testInterpolate(t, i, scope, "aws_route53_zone.terra.0.tags.Name", ast.Variable{
   341  		Value: "reindeer",
   342  		Type:  ast.TypeString,
   343  	})
   344  	// Maps still need to work in both
   345  	testInterpolate(t, i, scope, "aws_route53_zone.terra.*.tags.Name", ast.Variable{
   346  		Value: config.NewStringList([]string{"reindeer", "white-hart"}).String(),
   347  		Type:  ast.TypeString,
   348  	})
   349  }
   350  
   351  func getInterpolaterFixture(t *testing.T) *Interpolater {
   352  	lock := new(sync.RWMutex)
   353  	state := &State{
   354  		Modules: []*ModuleState{
   355  			&ModuleState{
   356  				Path: rootModulePath,
   357  				Resources: map[string]*ResourceState{
   358  					"aws_route53_zone.terra.0": &ResourceState{
   359  						Type:         "aws_route53_zone",
   360  						Dependencies: []string{},
   361  						Primary: &InstanceState{
   362  							ID: "AAABBBCCCDDDEEE",
   363  							Attributes: map[string]string{
   364  								"name_servers.#": "4",
   365  								"name_servers.0": "ns-1334.awsdns-38.org",
   366  								"name_servers.1": "ns-1680.awsdns-18.co.uk",
   367  								"name_servers.2": "ns-498.awsdns-62.com",
   368  								"name_servers.3": "ns-601.awsdns-11.net",
   369  								"listeners.#":    "1",
   370  								"listeners.0":    "red",
   371  								"tags.#":         "1",
   372  								"tags.Name":      "reindeer",
   373  								"nothing.#":      "0",
   374  							},
   375  						},
   376  					},
   377  					"aws_route53_zone.terra.1": &ResourceState{
   378  						Type:         "aws_route53_zone",
   379  						Dependencies: []string{},
   380  						Primary: &InstanceState{
   381  							ID: "EEEFFFGGGHHHIII",
   382  							Attributes: map[string]string{
   383  								"name_servers.#": "4",
   384  								"name_servers.0": "ns-000.awsdns-38.org",
   385  								"name_servers.1": "ns-444.awsdns-18.co.uk",
   386  								"name_servers.2": "ns-999.awsdns-62.com",
   387  								"name_servers.3": "ns-666.awsdns-11.net",
   388  								"listeners.#":    "1",
   389  								"listeners.0":    "blue",
   390  								"special.#":      "1",
   391  								"special.0":      "extra",
   392  								"tags.#":         "1",
   393  								"tags.Name":      "white-hart",
   394  								"nothing.#":      "0",
   395  							},
   396  						},
   397  					},
   398  				},
   399  			},
   400  		},
   401  	}
   402  
   403  	return &Interpolater{
   404  		Module:    testModule(t, "interpolate-multi-vars"),
   405  		StateLock: lock,
   406  		State:     state,
   407  	}
   408  }
   409  
   410  func testInterpolate(
   411  	t *testing.T, i *Interpolater,
   412  	scope *InterpolationScope,
   413  	n string, expectedVar ast.Variable) {
   414  	v, err := config.NewInterpolatedVariable(n)
   415  	if err != nil {
   416  		t.Fatalf("err: %s", err)
   417  	}
   418  
   419  	actual, err := i.Values(scope, map[string]config.InterpolatedVariable{
   420  		"foo": v,
   421  	})
   422  	if err != nil {
   423  		t.Fatalf("err: %s", err)
   424  	}
   425  
   426  	expected := map[string]ast.Variable{
   427  		"foo": expectedVar,
   428  	}
   429  	if !reflect.DeepEqual(actual, expected) {
   430  		t.Fatalf("%q: actual: %#v\nexpected: %#v", n, actual, expected)
   431  	}
   432  }