github.com/federicobaldo/terraform@v0.6.15-0.20160323222747-b20f680cbf05/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/hil/ast" 11 "github.com/hashicorp/terraform/config" 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 TestInterpolator_resourceMultiAttributesComputed(t *testing.T) { 352 lock := new(sync.RWMutex) 353 // The state would never be written with an UnknownVariableValue in it, but 354 // it can/does exist that way in memory during the plan phase. 355 state := &State{ 356 Modules: []*ModuleState{ 357 &ModuleState{ 358 Path: rootModulePath, 359 Resources: map[string]*ResourceState{ 360 "aws_route53_zone.yada": &ResourceState{ 361 Type: "aws_route53_zone", 362 Primary: &InstanceState{ 363 ID: "z-abc123", 364 Attributes: map[string]string{ 365 "name_servers.#": config.UnknownVariableValue, 366 }, 367 }, 368 }, 369 }, 370 }, 371 }, 372 } 373 i := &Interpolater{ 374 Module: testModule(t, "interpolate-multi-vars"), 375 StateLock: lock, 376 State: state, 377 } 378 379 scope := &InterpolationScope{ 380 Path: rootModulePath, 381 } 382 383 testInterpolate(t, i, scope, "aws_route53_zone.yada.name_servers", ast.Variable{ 384 Value: config.UnknownVariableValue, 385 Type: ast.TypeString, 386 }) 387 } 388 389 func TestInterpolater_selfVarWithoutResource(t *testing.T) { 390 i := &Interpolater{} 391 392 scope := &InterpolationScope{ 393 Path: rootModulePath, 394 } 395 396 v, err := config.NewInterpolatedVariable("self.name") 397 if err != nil { 398 t.Fatalf("err: %s", err) 399 } 400 401 _, err = i.Values(scope, map[string]config.InterpolatedVariable{"foo": v}) 402 if err == nil { 403 t.Fatalf("expected err, got none") 404 } 405 } 406 407 func getInterpolaterFixture(t *testing.T) *Interpolater { 408 lock := new(sync.RWMutex) 409 state := &State{ 410 Modules: []*ModuleState{ 411 &ModuleState{ 412 Path: rootModulePath, 413 Resources: map[string]*ResourceState{ 414 "aws_route53_zone.terra.0": &ResourceState{ 415 Type: "aws_route53_zone", 416 Dependencies: []string{}, 417 Primary: &InstanceState{ 418 ID: "AAABBBCCCDDDEEE", 419 Attributes: map[string]string{ 420 "name_servers.#": "4", 421 "name_servers.0": "ns-1334.awsdns-38.org", 422 "name_servers.1": "ns-1680.awsdns-18.co.uk", 423 "name_servers.2": "ns-498.awsdns-62.com", 424 "name_servers.3": "ns-601.awsdns-11.net", 425 "listeners.#": "1", 426 "listeners.0": "red", 427 "tags.#": "1", 428 "tags.Name": "reindeer", 429 "nothing.#": "0", 430 }, 431 }, 432 }, 433 "aws_route53_zone.terra.1": &ResourceState{ 434 Type: "aws_route53_zone", 435 Dependencies: []string{}, 436 Primary: &InstanceState{ 437 ID: "EEEFFFGGGHHHIII", 438 Attributes: map[string]string{ 439 "name_servers.#": "4", 440 "name_servers.0": "ns-000.awsdns-38.org", 441 "name_servers.1": "ns-444.awsdns-18.co.uk", 442 "name_servers.2": "ns-999.awsdns-62.com", 443 "name_servers.3": "ns-666.awsdns-11.net", 444 "listeners.#": "1", 445 "listeners.0": "blue", 446 "special.#": "1", 447 "special.0": "extra", 448 "tags.#": "1", 449 "tags.Name": "white-hart", 450 "nothing.#": "0", 451 }, 452 }, 453 }, 454 }, 455 }, 456 }, 457 } 458 459 return &Interpolater{ 460 Module: testModule(t, "interpolate-multi-vars"), 461 StateLock: lock, 462 State: state, 463 } 464 } 465 466 func testInterpolate( 467 t *testing.T, i *Interpolater, 468 scope *InterpolationScope, 469 n string, expectedVar ast.Variable) { 470 v, err := config.NewInterpolatedVariable(n) 471 if err != nil { 472 t.Fatalf("err: %s", err) 473 } 474 475 actual, err := i.Values(scope, map[string]config.InterpolatedVariable{ 476 "foo": v, 477 }) 478 if err != nil { 479 t.Fatalf("err: %s", err) 480 } 481 482 expected := map[string]ast.Variable{ 483 "foo": expectedVar, 484 } 485 if !reflect.DeepEqual(actual, expected) { 486 t.Fatalf("%q: actual: %#v\nexpected: %#v", n, actual, expected) 487 } 488 }