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