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