github.com/andrewrynhard/terraform@v0.9.5-0.20170502003928-8d286b83eae4/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 TestInterpolater_terraformEnv(t *testing.T) { 897 i := &Interpolater{ 898 Meta: &ContextMeta{Env: "foo"}, 899 } 900 901 scope := &InterpolationScope{ 902 Path: rootModulePath, 903 } 904 905 testInterpolate(t, i, scope, "terraform.env", ast.Variable{ 906 Value: "foo", 907 Type: ast.TypeString, 908 }) 909 } 910 911 func TestInterpolater_terraformInvalid(t *testing.T) { 912 i := &Interpolater{ 913 Meta: &ContextMeta{Env: "foo"}, 914 } 915 916 scope := &InterpolationScope{ 917 Path: rootModulePath, 918 } 919 920 testInterpolateErr(t, i, scope, "terraform.nope") 921 } 922 923 func testInterpolate( 924 t *testing.T, i *Interpolater, 925 scope *InterpolationScope, 926 n string, expectedVar ast.Variable) { 927 v, err := config.NewInterpolatedVariable(n) 928 if err != nil { 929 t.Fatalf("err: %s", err) 930 } 931 932 actual, err := i.Values(scope, map[string]config.InterpolatedVariable{ 933 "foo": v, 934 }) 935 if err != nil { 936 t.Fatalf("err: %s", err) 937 } 938 939 expected := map[string]ast.Variable{ 940 "foo": expectedVar, 941 } 942 if !reflect.DeepEqual(actual, expected) { 943 spew.Config.DisableMethods = true 944 t.Fatalf("%q:\n\n actual: %#v\nexpected: %#v\n\n%s\n\n%s\n\n", n, actual, expected, 945 spew.Sdump(actual), spew.Sdump(expected)) 946 } 947 } 948 949 func testInterpolateErr( 950 t *testing.T, i *Interpolater, 951 scope *InterpolationScope, 952 n string) { 953 v, err := config.NewInterpolatedVariable(n) 954 if err != nil { 955 t.Fatalf("err: %s", err) 956 } 957 958 _, err = i.Values(scope, map[string]config.InterpolatedVariable{ 959 "foo": v, 960 }) 961 if err == nil { 962 t.Fatalf("%q: succeeded, but wanted error", n) 963 } 964 }