github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/terraform/resource_test.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 "testing" 8 9 "github.com/hashicorp/hil" 10 "github.com/hashicorp/hil/ast" 11 "github.com/hashicorp/terraform/config" 12 "github.com/mitchellh/reflectwalk" 13 ) 14 15 func TestInstanceInfo(t *testing.T) { 16 cases := []struct { 17 Info *InstanceInfo 18 Result string 19 }{ 20 { 21 &InstanceInfo{ 22 Id: "foo", 23 }, 24 "foo", 25 }, 26 { 27 &InstanceInfo{ 28 Id: "foo", 29 ModulePath: rootModulePath, 30 }, 31 "foo", 32 }, 33 { 34 &InstanceInfo{ 35 Id: "foo", 36 ModulePath: []string{"root", "consul"}, 37 }, 38 "module.consul.foo", 39 }, 40 } 41 42 for i, tc := range cases { 43 actual := tc.Info.HumanId() 44 if actual != tc.Result { 45 t.Fatalf("%d: %s", i, actual) 46 } 47 } 48 } 49 50 func TestInstanceInfoResourceAddress(t *testing.T) { 51 tests := []struct { 52 Input *InstanceInfo 53 Want string 54 }{ 55 { 56 &InstanceInfo{ 57 Id: "test_resource.baz", 58 }, 59 "test_resource.baz", 60 }, 61 { 62 &InstanceInfo{ 63 Id: "test_resource.baz", 64 ModulePath: rootModulePath, 65 }, 66 "test_resource.baz", 67 }, 68 { 69 &InstanceInfo{ 70 Id: "test_resource.baz", 71 ModulePath: []string{"root", "foo"}, 72 }, 73 "module.foo.test_resource.baz", 74 }, 75 { 76 &InstanceInfo{ 77 Id: "test_resource.baz", 78 ModulePath: []string{"root", "foo", "bar"}, 79 }, 80 "module.foo.module.bar.test_resource.baz", 81 }, 82 { 83 &InstanceInfo{ 84 Id: "test_resource.baz (tainted)", 85 }, 86 "test_resource.baz.tainted", 87 }, 88 { 89 &InstanceInfo{ 90 Id: "test_resource.baz (deposed #0)", 91 }, 92 "test_resource.baz.deposed", 93 }, 94 } 95 96 for i, test := range tests { 97 t.Run(strconv.Itoa(i), func(t *testing.T) { 98 gotAddr := test.Input.ResourceAddress() 99 got := gotAddr.String() 100 if got != test.Want { 101 t.Fatalf("wrong result\ngot: %s\nwant: %s", got, test.Want) 102 } 103 }) 104 } 105 } 106 107 func TestResourceConfigGet(t *testing.T) { 108 cases := []struct { 109 Config map[string]interface{} 110 Vars map[string]interface{} 111 Key string 112 Value interface{} 113 }{ 114 { 115 Config: nil, 116 Key: "foo", 117 Value: nil, 118 }, 119 120 { 121 Config: map[string]interface{}{ 122 "foo": "bar", 123 }, 124 Key: "foo", 125 Value: "bar", 126 }, 127 128 { 129 Config: map[string]interface{}{ 130 "foo": "${var.foo}", 131 }, 132 Key: "foo", 133 Value: "${var.foo}", 134 }, 135 136 { 137 Config: map[string]interface{}{ 138 "foo": "${var.foo}", 139 }, 140 Vars: map[string]interface{}{"foo": unknownValue()}, 141 Key: "foo", 142 Value: "${var.foo}", 143 }, 144 145 { 146 Config: map[string]interface{}{ 147 "foo": []interface{}{1, 2, 5}, 148 }, 149 Key: "foo.0", 150 Value: 1, 151 }, 152 153 { 154 Config: map[string]interface{}{ 155 "foo": []interface{}{1, 2, 5}, 156 }, 157 Key: "foo.5", 158 Value: nil, 159 }, 160 161 // get from map 162 { 163 Config: map[string]interface{}{ 164 "mapname": []map[string]interface{}{ 165 map[string]interface{}{"key": 1}, 166 }, 167 }, 168 Key: "mapname.0.key", 169 Value: 1, 170 }, 171 172 // get from map with dot in key 173 { 174 Config: map[string]interface{}{ 175 "mapname": []map[string]interface{}{ 176 map[string]interface{}{"key.name": 1}, 177 }, 178 }, 179 Key: "mapname.0.key.name", 180 Value: 1, 181 }, 182 183 // get from map with overlapping key names 184 { 185 Config: map[string]interface{}{ 186 "mapname": []map[string]interface{}{ 187 map[string]interface{}{ 188 "key.name": 1, 189 "key.name.2": 2, 190 }, 191 }, 192 }, 193 Key: "mapname.0.key.name.2", 194 Value: 2, 195 }, 196 { 197 Config: map[string]interface{}{ 198 "mapname": []map[string]interface{}{ 199 map[string]interface{}{ 200 "key.name": 1, 201 "key.name.foo": 2, 202 }, 203 }, 204 }, 205 Key: "mapname.0.key.name", 206 Value: 1, 207 }, 208 { 209 Config: map[string]interface{}{ 210 "mapname": []map[string]interface{}{ 211 map[string]interface{}{ 212 "listkey": []map[string]interface{}{ 213 {"key": 3}, 214 }, 215 }, 216 }, 217 }, 218 Key: "mapname.0.listkey.0.key", 219 Value: 3, 220 }, 221 222 // A map assigned to a list via interpolation should Get a non-existent 223 // value. The test code now also checks that Get doesn't return (nil, 224 // true), which it previously did for this configuration. 225 { 226 Config: map[string]interface{}{ 227 "maplist": "${var.maplist}", 228 }, 229 Key: "maplist.0", 230 Value: nil, 231 }, 232 233 // Reference list of maps variable. 234 // This does not work from GetRaw. 235 { 236 Vars: map[string]interface{}{ 237 "maplist": []interface{}{ 238 map[string]interface{}{ 239 "key": "a", 240 }, 241 map[string]interface{}{ 242 "key": "b", 243 }, 244 }, 245 }, 246 Config: map[string]interface{}{ 247 "maplist": "${var.maplist}", 248 }, 249 Key: "maplist.0", 250 Value: map[string]interface{}{"key": "a"}, 251 }, 252 253 // Reference a map-of-lists variable. 254 // This does not work from GetRaw. 255 { 256 Vars: map[string]interface{}{ 257 "listmap": map[string]interface{}{ 258 "key1": []interface{}{"a", "b"}, 259 "key2": []interface{}{"c", "d"}, 260 }, 261 }, 262 Config: map[string]interface{}{ 263 "listmap": "${var.listmap}", 264 }, 265 Key: "listmap.key1", 266 Value: []interface{}{"a", "b"}, 267 }, 268 269 // FIXME: this is ambiguous, and matches the nested map 270 // leaving here to catch this behaviour if it changes. 271 { 272 Config: map[string]interface{}{ 273 "mapname": []map[string]interface{}{ 274 map[string]interface{}{ 275 "key.name": 1, 276 "key.name.0": 2, 277 "key": map[string]interface{}{"name": 3}, 278 }, 279 }, 280 }, 281 Key: "mapname.0.key.name", 282 Value: 3, 283 }, 284 /* 285 // TODO: can't access this nested list at all. 286 // FIXME: key with name matching substring of nested list can panic 287 { 288 Config: map[string]interface{}{ 289 "mapname": []map[string]interface{}{ 290 map[string]interface{}{ 291 "key.name": []map[string]interface{}{ 292 {"subkey": 1}, 293 }, 294 "key": 3, 295 }, 296 }, 297 }, 298 Key: "mapname.0.key.name.0.subkey", 299 Value: 3, 300 }, 301 */ 302 } 303 304 for i, tc := range cases { 305 var rawC *config.RawConfig 306 if tc.Config != nil { 307 var err error 308 rawC, err = config.NewRawConfig(tc.Config) 309 if err != nil { 310 t.Fatalf("err: %s", err) 311 } 312 } 313 314 if tc.Vars != nil { 315 vs := make(map[string]ast.Variable) 316 for k, v := range tc.Vars { 317 hilVar, err := hil.InterfaceToVariable(v) 318 if err != nil { 319 t.Fatalf("%#v to var: %s", v, err) 320 } 321 322 vs["var."+k] = hilVar 323 } 324 325 if err := rawC.Interpolate(vs); err != nil { 326 t.Fatalf("err: %s", err) 327 } 328 } 329 330 rc := NewResourceConfig(rawC) 331 rc.interpolateForce() 332 333 // Test getting a key 334 t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) { 335 v, ok := rc.Get(tc.Key) 336 if ok && v == nil { 337 t.Fatal("(nil, true) returned from Get") 338 } 339 340 if !reflect.DeepEqual(v, tc.Value) { 341 t.Fatalf("%d bad: %#v", i, v) 342 } 343 }) 344 345 // If we have vars, we don't test copying 346 if len(tc.Vars) > 0 { 347 continue 348 } 349 350 // Test copying and equality 351 t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) { 352 copy := rc.DeepCopy() 353 if !reflect.DeepEqual(copy, rc) { 354 t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc) 355 } 356 357 if !copy.Equal(rc) { 358 t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc) 359 } 360 if !rc.Equal(copy) { 361 t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc) 362 } 363 }) 364 } 365 } 366 367 func TestResourceConfigGetRaw(t *testing.T) { 368 cases := []struct { 369 Config map[string]interface{} 370 Vars map[string]interface{} 371 Key string 372 Value interface{} 373 }{ 374 // Referencing a list-of-maps variable doesn't work from GetRaw. 375 // The ConfigFieldReader currently catches this case and looks up the 376 // variable in the config. 377 { 378 Vars: map[string]interface{}{ 379 "maplist": []interface{}{ 380 map[string]interface{}{ 381 "key": "a", 382 }, 383 map[string]interface{}{ 384 "key": "b", 385 }, 386 }, 387 }, 388 Config: map[string]interface{}{ 389 "maplist": "${var.maplist}", 390 }, 391 Key: "maplist.0", 392 Value: nil, 393 }, 394 // Reference a map-of-lists variable. 395 // The ConfigFieldReader currently catches this case and looks up the 396 // variable in the config. 397 { 398 Vars: map[string]interface{}{ 399 "listmap": map[string]interface{}{ 400 "key1": []interface{}{"a", "b"}, 401 "key2": []interface{}{"c", "d"}, 402 }, 403 }, 404 Config: map[string]interface{}{ 405 "listmap": "${var.listmap}", 406 }, 407 Key: "listmap.key1", 408 Value: nil, 409 }, 410 } 411 412 for i, tc := range cases { 413 var rawC *config.RawConfig 414 if tc.Config != nil { 415 var err error 416 rawC, err = config.NewRawConfig(tc.Config) 417 if err != nil { 418 t.Fatalf("err: %s", err) 419 } 420 } 421 422 if tc.Vars != nil { 423 vs := make(map[string]ast.Variable) 424 for k, v := range tc.Vars { 425 hilVar, err := hil.InterfaceToVariable(v) 426 if err != nil { 427 t.Fatalf("%#v to var: %s", v, err) 428 } 429 vs["var."+k] = hilVar 430 } 431 if err := rawC.Interpolate(vs); err != nil { 432 t.Fatalf("err: %s", err) 433 } 434 } 435 436 rc := NewResourceConfig(rawC) 437 rc.interpolateForce() 438 439 // Test getting a key 440 t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) { 441 v, ok := rc.GetRaw(tc.Key) 442 if ok && v == nil { 443 t.Fatal("(nil, true) returned from GetRaw") 444 } 445 446 if !reflect.DeepEqual(v, tc.Value) { 447 t.Fatalf("%d bad: %#v", i, v) 448 } 449 }) 450 } 451 } 452 453 func TestResourceConfigIsComputed(t *testing.T) { 454 cases := []struct { 455 Name string 456 Config map[string]interface{} 457 Vars map[string]interface{} 458 Key string 459 Result bool 460 }{ 461 { 462 Name: "basic value", 463 Config: map[string]interface{}{ 464 "foo": "${var.foo}", 465 }, 466 Vars: map[string]interface{}{ 467 "foo": unknownValue(), 468 }, 469 Key: "foo", 470 Result: true, 471 }, 472 473 { 474 Name: "set with a computed element", 475 Config: map[string]interface{}{ 476 "foo": "${var.foo}", 477 }, 478 Vars: map[string]interface{}{ 479 "foo": []string{ 480 "a", 481 unknownValue(), 482 }, 483 }, 484 Key: "foo", 485 Result: true, 486 }, 487 488 { 489 Name: "set with no computed elements", 490 Config: map[string]interface{}{ 491 "foo": "${var.foo}", 492 }, 493 Vars: map[string]interface{}{ 494 "foo": []string{ 495 "a", 496 "b", 497 }, 498 }, 499 Key: "foo", 500 Result: false, 501 }, 502 503 /* 504 { 505 Name: "set count with computed elements", 506 Config: map[string]interface{}{ 507 "foo": "${var.foo}", 508 }, 509 Vars: map[string]interface{}{ 510 "foo": []string{ 511 "a", 512 unknownValue(), 513 }, 514 }, 515 Key: "foo.#", 516 Result: true, 517 }, 518 */ 519 520 { 521 Name: "set count with computed elements", 522 Config: map[string]interface{}{ 523 "foo": []interface{}{"${var.foo}"}, 524 }, 525 Vars: map[string]interface{}{ 526 "foo": []string{ 527 "a", 528 unknownValue(), 529 }, 530 }, 531 Key: "foo.#", 532 Result: true, 533 }, 534 535 { 536 Name: "nested set with computed elements", 537 Config: map[string]interface{}{ 538 "route": []map[string]interface{}{ 539 map[string]interface{}{ 540 "index": "1", 541 "gateway": []interface{}{"${var.foo}"}, 542 }, 543 }, 544 }, 545 Vars: map[string]interface{}{ 546 "foo": unknownValue(), 547 }, 548 Key: "route.0.gateway", 549 Result: true, 550 }, 551 } 552 553 for i, tc := range cases { 554 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 555 var rawC *config.RawConfig 556 if tc.Config != nil { 557 var err error 558 rawC, err = config.NewRawConfig(tc.Config) 559 if err != nil { 560 t.Fatalf("err: %s", err) 561 } 562 } 563 564 if tc.Vars != nil { 565 vs := make(map[string]ast.Variable) 566 for k, v := range tc.Vars { 567 hilVar, err := hil.InterfaceToVariable(v) 568 if err != nil { 569 t.Fatalf("%#v to var: %s", v, err) 570 } 571 572 vs["var."+k] = hilVar 573 } 574 575 if err := rawC.Interpolate(vs); err != nil { 576 t.Fatalf("err: %s", err) 577 } 578 } 579 580 rc := NewResourceConfig(rawC) 581 rc.interpolateForce() 582 583 t.Logf("Config: %#v", rc) 584 585 actual := rc.IsComputed(tc.Key) 586 if actual != tc.Result { 587 t.Fatalf("bad: %#v", actual) 588 } 589 }) 590 } 591 } 592 593 func TestResourceConfigCheckSet(t *testing.T) { 594 cases := []struct { 595 Name string 596 Config map[string]interface{} 597 Vars map[string]interface{} 598 Input []string 599 Errs bool 600 }{ 601 { 602 Name: "computed basic", 603 Config: map[string]interface{}{ 604 "foo": "${var.foo}", 605 }, 606 Vars: map[string]interface{}{ 607 "foo": unknownValue(), 608 }, 609 Input: []string{"foo"}, 610 Errs: false, 611 }, 612 613 { 614 Name: "basic", 615 Config: map[string]interface{}{ 616 "foo": "bar", 617 }, 618 Vars: nil, 619 Input: []string{"foo"}, 620 Errs: false, 621 }, 622 623 { 624 Name: "basic with not set", 625 Config: map[string]interface{}{ 626 "foo": "bar", 627 }, 628 Vars: nil, 629 Input: []string{"foo", "bar"}, 630 Errs: true, 631 }, 632 633 { 634 Name: "basic with one computed", 635 Config: map[string]interface{}{ 636 "foo": "bar", 637 "bar": "${var.foo}", 638 }, 639 Vars: map[string]interface{}{ 640 "foo": unknownValue(), 641 }, 642 Input: []string{"foo", "bar"}, 643 Errs: false, 644 }, 645 } 646 647 for i, tc := range cases { 648 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 649 var rawC *config.RawConfig 650 if tc.Config != nil { 651 var err error 652 rawC, err = config.NewRawConfig(tc.Config) 653 if err != nil { 654 t.Fatalf("err: %s", err) 655 } 656 } 657 658 if tc.Vars != nil { 659 vs := make(map[string]ast.Variable) 660 for k, v := range tc.Vars { 661 hilVar, err := hil.InterfaceToVariable(v) 662 if err != nil { 663 t.Fatalf("%#v to var: %s", v, err) 664 } 665 666 vs["var."+k] = hilVar 667 } 668 669 if err := rawC.Interpolate(vs); err != nil { 670 t.Fatalf("err: %s", err) 671 } 672 } 673 674 rc := NewResourceConfig(rawC) 675 rc.interpolateForce() 676 677 t.Logf("Config: %#v", rc) 678 679 errs := rc.CheckSet(tc.Input) 680 if tc.Errs != (len(errs) > 0) { 681 t.Fatalf("bad: %#v", errs) 682 } 683 }) 684 } 685 } 686 687 func TestResourceConfigDeepCopy_nil(t *testing.T) { 688 var nilRc *ResourceConfig 689 actual := nilRc.DeepCopy() 690 if actual != nil { 691 t.Fatalf("bad: %#v", actual) 692 } 693 } 694 695 func TestResourceConfigDeepCopy_nilComputed(t *testing.T) { 696 rc := &ResourceConfig{} 697 actual := rc.DeepCopy() 698 if actual.ComputedKeys != nil { 699 t.Fatalf("bad: %#v", actual) 700 } 701 } 702 703 func TestResourceConfigEqual_nil(t *testing.T) { 704 var nilRc *ResourceConfig 705 notNil := NewResourceConfig(nil) 706 707 if nilRc.Equal(notNil) { 708 t.Fatal("should not be equal") 709 } 710 711 if notNil.Equal(nilRc) { 712 t.Fatal("should not be equal") 713 } 714 } 715 716 func TestResourceConfigEqual_computedKeyOrder(t *testing.T) { 717 c := map[string]interface{}{"foo": "${a.b.c}"} 718 rc := NewResourceConfig(config.TestRawConfig(t, c)) 719 rc2 := NewResourceConfig(config.TestRawConfig(t, c)) 720 721 // Set the computed keys manual 722 rc.ComputedKeys = []string{"foo", "bar"} 723 rc2.ComputedKeys = []string{"bar", "foo"} 724 725 if !rc.Equal(rc2) { 726 t.Fatal("should be equal") 727 } 728 } 729 730 func TestUnknownCheckWalker(t *testing.T) { 731 cases := []struct { 732 Name string 733 Input interface{} 734 Result bool 735 }{ 736 { 737 "primitive", 738 42, 739 false, 740 }, 741 742 { 743 "primitive computed", 744 unknownValue(), 745 true, 746 }, 747 748 { 749 "list", 750 []interface{}{"foo", unknownValue()}, 751 true, 752 }, 753 754 { 755 "nested list", 756 []interface{}{ 757 "foo", 758 []interface{}{unknownValue()}, 759 }, 760 true, 761 }, 762 } 763 764 for i, tc := range cases { 765 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 766 var w unknownCheckWalker 767 if err := reflectwalk.Walk(tc.Input, &w); err != nil { 768 t.Fatalf("err: %s", err) 769 } 770 771 if w.Unknown != tc.Result { 772 t.Fatalf("bad: %v", w.Unknown) 773 } 774 }) 775 } 776 } 777 778 func testResourceConfig( 779 t *testing.T, c map[string]interface{}) *ResourceConfig { 780 raw, err := config.NewRawConfig(c) 781 if err != nil { 782 t.Fatalf("err: %s", err) 783 } 784 785 return NewResourceConfig(raw) 786 }