github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/terraform/graph_test.go (about) 1 package terraform 2 3 import ( 4 "reflect" 5 "sort" 6 "strings" 7 "testing" 8 ) 9 10 func TestGraph_basic(t *testing.T) { 11 m := testModule(t, "graph-basic") 12 13 g, err := Graph(&GraphOpts{Module: m}) 14 if err != nil { 15 t.Fatalf("err: %s", err) 16 } 17 18 actual := strings.TrimSpace(g.String()) 19 expected := strings.TrimSpace(testTerraformGraphStr) 20 if actual != expected { 21 t.Fatalf("bad:\n\n%s", actual) 22 } 23 } 24 25 func TestGraph_configRequired(t *testing.T) { 26 if _, err := Graph(new(GraphOpts)); err == nil { 27 t.Fatal("should error") 28 } 29 } 30 31 func TestGraph_count(t *testing.T) { 32 m := testModule(t, "graph-count") 33 34 g, err := Graph(&GraphOpts{Module: m}) 35 if err != nil { 36 t.Fatalf("err: %s", err) 37 } 38 39 actual := strings.TrimSpace(g.String()) 40 expected := strings.TrimSpace(testTerraformGraphCountStr) 41 if actual != expected { 42 t.Fatalf("bad:\n\n%s", actual) 43 } 44 } 45 46 func TestGraph_countTainted(t *testing.T) { 47 m := testModule(t, "graph-count") 48 state := &State{ 49 Modules: []*ModuleState{ 50 &ModuleState{ 51 Path: []string{"root"}, 52 Resources: map[string]*ResourceState{ 53 "aws_instance.web.0": &ResourceState{ 54 Type: "aws_instance", 55 Tainted: []*InstanceState{ 56 &InstanceState{ 57 ID: "foo", 58 }, 59 }, 60 }, 61 }, 62 }, 63 }, 64 } 65 66 g, err := Graph(&GraphOpts{Module: m, State: state}) 67 if err != nil { 68 t.Fatalf("err: %s", err) 69 } 70 71 actual := strings.TrimSpace(g.String()) 72 expected := strings.TrimSpace(testTerraformGraphCountTaintedStr) 73 if actual != expected { 74 t.Fatalf("bad:\n\n%s", actual) 75 } 76 } 77 78 func TestGraph_varResource(t *testing.T) { 79 m := testModule(t, "graph-count-var-resource") 80 81 g, err := Graph(&GraphOpts{Module: m}) 82 if err != nil { 83 t.Fatalf("err: %s", err) 84 } 85 86 actual := strings.TrimSpace(g.String()) 87 expected := strings.TrimSpace(testTerraformGraphCountVarResourceStr) 88 if actual != expected { 89 t.Fatalf("bad:\n\n%s", actual) 90 } 91 } 92 93 func TestGraph_cycle(t *testing.T) { 94 m := testModule(t, "graph-cycle") 95 96 _, err := Graph(&GraphOpts{Module: m}) 97 if err == nil { 98 t.Fatal("should error") 99 } 100 } 101 102 func TestGraph_dependsOn(t *testing.T) { 103 m := testModule(t, "graph-depends-on") 104 105 g, err := Graph(&GraphOpts{Module: m}) 106 if err != nil { 107 t.Fatalf("err: %s", err) 108 } 109 110 actual := strings.TrimSpace(g.String()) 111 expected := strings.TrimSpace(testTerraformGraphDependsStr) 112 if actual != expected { 113 t.Fatalf("bad:\n\n%s", actual) 114 } 115 } 116 117 func TestGraph_dependsOnCount(t *testing.T) { 118 m := testModule(t, "graph-depends-on-count") 119 120 g, err := Graph(&GraphOpts{Module: m}) 121 if err != nil { 122 t.Fatalf("err: %s", err) 123 } 124 125 actual := strings.TrimSpace(g.String()) 126 expected := strings.TrimSpace(testTerraformGraphDependsCountStr) 127 if actual != expected { 128 t.Fatalf("bad:\n\n%s", actual) 129 } 130 } 131 132 func TestGraph_dependsOnWithOrphan(t *testing.T) { 133 m := testModule(t, "graph-depends-on") 134 135 state := &State{ 136 Modules: []*ModuleState{ 137 &ModuleState{ 138 Path: []string{"root"}, 139 Resources: map[string]*ResourceState{ 140 "aws_instance.old": &ResourceState{ 141 Type: "aws_instance", 142 Primary: &InstanceState{ 143 ID: "foo", 144 }, 145 }, 146 }, 147 }, 148 }, 149 } 150 151 g, err := Graph(&GraphOpts{Module: m, State: state}) 152 if err != nil { 153 t.Fatalf("err: %s", err) 154 } 155 156 actual := strings.TrimSpace(g.String()) 157 expected := strings.TrimSpace(testTerraformGraphDependsOrphanStr) 158 if actual != expected { 159 t.Fatalf("bad:\n\n%s", actual) 160 } 161 } 162 163 func TestGraph_modules(t *testing.T) { 164 m := testModule(t, "graph-modules") 165 166 g, err := Graph(&GraphOpts{Module: m}) 167 if err != nil { 168 t.Fatalf("err: %s", err) 169 } 170 171 actual := strings.TrimSpace(g.String()) 172 expected := strings.TrimSpace(testTerraformGraphModulesStr) 173 if actual != expected { 174 t.Fatalf("bad:\n\n%s", actual) 175 } 176 177 n := g.Noun("module.consul") 178 if n == nil { 179 t.Fatal("can't find noun") 180 } 181 mn := n.Meta.(*GraphNodeModule) 182 183 if !reflect.DeepEqual(mn.Path, []string{"root", "consul"}) { 184 t.Fatalf("bad: %#v", mn.Path) 185 } 186 187 actual = strings.TrimSpace(mn.Graph.String()) 188 expected = strings.TrimSpace(testTerraformGraphModulesConsulStr) 189 if actual != expected { 190 t.Fatalf("bad:\n\n%s", actual) 191 } 192 } 193 194 func TestGraph_moduleOrphan(t *testing.T) { 195 m := testModule(t, "graph-module-orphan") 196 state := &State{ 197 Modules: []*ModuleState{ 198 &ModuleState{ 199 Path: []string{"root", "consul"}, 200 201 Resources: map[string]*ResourceState{ 202 "aws_instance.old": &ResourceState{ 203 Type: "aws_instance", 204 Primary: &InstanceState{ 205 ID: "foo", 206 }, 207 }, 208 }, 209 }, 210 }, 211 } 212 213 g, err := Graph(&GraphOpts{Module: m, State: state}) 214 if err != nil { 215 t.Fatalf("err: %s", err) 216 } 217 218 actual := strings.TrimSpace(g.String()) 219 expected := strings.TrimSpace(testTerraformGraphModuleOrphanStr) 220 if actual != expected { 221 t.Fatalf("bad:\n\n%s", actual) 222 } 223 224 n := g.Noun("module.consul") 225 if n == nil { 226 t.Fatal("can't find noun") 227 } 228 mn := n.Meta.(*GraphNodeModule) 229 230 if !reflect.DeepEqual(mn.Path, []string{"root", "consul"}) { 231 t.Fatalf("bad: %#v", mn.Path) 232 } 233 234 actual = strings.TrimSpace(mn.Graph.String()) 235 expected = strings.TrimSpace(testTerraformGraphModuleOrphanConsulStr) 236 if actual != expected { 237 t.Fatalf("bad:\n\n%s", actual) 238 } 239 } 240 241 func TestGraph_providerPrune(t *testing.T) { 242 m := testModule(t, "graph-provider-prune") 243 244 g, err := Graph(&GraphOpts{Module: m}) 245 if err != nil { 246 t.Fatalf("err: %s", err) 247 } 248 249 actual := strings.TrimSpace(g.String()) 250 expected := strings.TrimSpace(testTerraformGraphProviderPruneStr) 251 if actual != expected { 252 t.Fatalf("bad:\n\n%s", actual) 253 } 254 } 255 256 func TestGraph_state(t *testing.T) { 257 m := testModule(t, "graph-basic") 258 state := &State{ 259 Modules: []*ModuleState{ 260 &ModuleState{ 261 Path: rootModulePath, 262 263 Resources: map[string]*ResourceState{ 264 "aws_instance.old": &ResourceState{ 265 Type: "aws_instance", 266 Primary: &InstanceState{ 267 ID: "foo", 268 }, 269 }, 270 }, 271 }, 272 }, 273 } 274 275 g, err := Graph(&GraphOpts{Module: m, State: state}) 276 if err != nil { 277 t.Fatalf("err: %s", err) 278 } 279 280 actual := strings.TrimSpace(g.String()) 281 expected := strings.TrimSpace(testTerraformGraphStateStr) 282 if actual != expected { 283 t.Fatalf("bad:\n\n%s", actual) 284 } 285 } 286 287 func TestGraph_tainted(t *testing.T) { 288 m := testModule(t, "graph-tainted") 289 state := &State{ 290 Modules: []*ModuleState{ 291 &ModuleState{ 292 Path: rootModulePath, 293 294 Resources: map[string]*ResourceState{ 295 "aws_instance.web": &ResourceState{ 296 Type: "aws_instance", 297 Primary: &InstanceState{ 298 ID: "foo", 299 }, 300 Tainted: []*InstanceState{ 301 &InstanceState{ 302 ID: "bar", 303 }, 304 }, 305 }, 306 }, 307 }, 308 }, 309 } 310 311 g, err := Graph(&GraphOpts{Module: m, State: state}) 312 if err != nil { 313 t.Fatalf("err: %s", err) 314 } 315 316 actual := strings.TrimSpace(g.String()) 317 expected := strings.TrimSpace(testTerraformGraphTaintedStr) 318 if actual != expected { 319 t.Fatalf("bad:\n\n%s", actual) 320 } 321 } 322 323 func TestGraph_taintedMulti(t *testing.T) { 324 m := testModule(t, "graph-tainted") 325 state := &State{ 326 Modules: []*ModuleState{ 327 &ModuleState{ 328 Path: rootModulePath, 329 330 Resources: map[string]*ResourceState{ 331 "aws_instance.web": &ResourceState{ 332 Type: "aws_instance", 333 Primary: &InstanceState{ 334 ID: "foo", 335 }, 336 Tainted: []*InstanceState{ 337 &InstanceState{ 338 ID: "bar", 339 }, 340 &InstanceState{ 341 ID: "baz", 342 }, 343 }, 344 }, 345 }, 346 }, 347 }, 348 } 349 350 g, err := Graph(&GraphOpts{Module: m, State: state}) 351 if err != nil { 352 t.Fatalf("err: %s", err) 353 } 354 355 actual := strings.TrimSpace(g.String()) 356 expected := strings.TrimSpace(testTerraformGraphTaintedMultiStr) 357 if actual != expected { 358 t.Fatalf("bad:\n\n%s", actual) 359 } 360 } 361 362 func TestGraphFull(t *testing.T) { 363 rpAws := new(MockResourceProvider) 364 rpOS := new(MockResourceProvider) 365 366 rpAws.ResourcesReturn = []ResourceType{ 367 ResourceType{Name: "aws_instance"}, 368 ResourceType{Name: "aws_load_balancer"}, 369 ResourceType{Name: "aws_security_group"}, 370 } 371 rpOS.ResourcesReturn = []ResourceType{ 372 ResourceType{Name: "openstack_floating_ip"}, 373 } 374 375 ps := map[string]ResourceProviderFactory{ 376 "aws": testProviderFuncFixed(rpAws), 377 "open": testProviderFuncFixed(rpOS), 378 } 379 380 m := testModule(t, "graph-basic") 381 g, err := Graph(&GraphOpts{Module: m, Providers: ps}) 382 if err != nil { 383 t.Fatalf("err: %s", err) 384 } 385 386 // A helper to help get us the provider for a resource. 387 graphProvider := func(n string) ResourceProvider { 388 return g.Noun(n).Meta.(*GraphNodeResource).Resource.Provider 389 } 390 391 // Test a couple 392 if graphProvider("aws_instance.web") != rpAws { 393 t.Fatalf("bad: %#v", graphProvider("aws_instance.web")) 394 } 395 if graphProvider("openstack_floating_ip.random") != rpOS { 396 t.Fatalf("bad: %#v", graphProvider("openstack_floating_ip.random")) 397 } 398 399 // Test that all providers have been set 400 for _, n := range g.Nouns { 401 switch m := n.Meta.(type) { 402 case *GraphNodeResource: 403 if m.Resource.Provider == nil { 404 t.Fatalf("bad: %#v", m) 405 } 406 case *GraphNodeResourceProvider: 407 if len(m.Provider.Providers) == 0 { 408 t.Fatalf("bad: %#v", m) 409 } 410 default: 411 continue 412 } 413 } 414 } 415 416 func TestGraphProvisioners(t *testing.T) { 417 rpAws := new(MockResourceProvider) 418 provShell := new(MockResourceProvisioner) 419 provWinRM := new(MockResourceProvisioner) 420 421 rpAws.ResourcesReturn = []ResourceType{ 422 ResourceType{Name: "aws_instance"}, 423 ResourceType{Name: "aws_load_balancer"}, 424 ResourceType{Name: "aws_security_group"}, 425 } 426 427 ps := map[string]ResourceProvisionerFactory{ 428 "shell": testProvisionerFuncFixed(provShell), 429 "winrm": testProvisionerFuncFixed(provWinRM), 430 } 431 432 pf := map[string]ResourceProviderFactory{ 433 "aws": testProviderFuncFixed(rpAws), 434 } 435 436 m := testModule(t, "graph-provisioners") 437 g, err := Graph(&GraphOpts{Module: m, Providers: pf, Provisioners: ps}) 438 if err != nil { 439 t.Fatalf("err: %s", err) 440 } 441 442 // A helper to help get us the provider for a resource. 443 graphProvisioner := func(n string, idx int) *ResourceProvisionerConfig { 444 return g.Noun(n).Meta.(*GraphNodeResource).Resource.Provisioners[idx] 445 } 446 447 // A helper to verify depedencies 448 depends := func(a, b string) bool { 449 aNoun := g.Noun(a) 450 bNoun := g.Noun(b) 451 for _, dep := range aNoun.Deps { 452 if dep.Source == aNoun && dep.Target == bNoun { 453 return true 454 } 455 } 456 return false 457 } 458 459 // Test a couple 460 prov := graphProvisioner("aws_instance.web", 0) 461 if prov.Provisioner != provWinRM { 462 t.Fatalf("bad: %#v", prov) 463 } 464 if prov.RawConfig.Config()["cmd"] != "echo foo" { 465 t.Fatalf("bad: %#v", prov) 466 } 467 468 prov = graphProvisioner("aws_instance.web", 1) 469 if prov.Provisioner != provWinRM { 470 t.Fatalf("bad: %#v", prov) 471 } 472 if prov.RawConfig.Config()["cmd"] != "echo bar" { 473 t.Fatalf("bad: %#v", prov) 474 } 475 476 prov = graphProvisioner("aws_load_balancer.weblb", 0) 477 if prov.Provisioner != provShell { 478 t.Fatalf("bad: %#v", prov) 479 } 480 if prov.RawConfig.Config()["cmd"] != "add ${aws_instance.web.id}" { 481 t.Fatalf("bad: %#v", prov) 482 } 483 if prov.ConnInfo == nil || len(prov.ConnInfo.Raw) != 2 { 484 t.Fatalf("bad: %#v", prov) 485 } 486 487 // Check that the variable dependency is handled 488 if !depends("aws_load_balancer.weblb", "aws_instance.web") { 489 t.Fatalf("missing dependency from provisioner variable") 490 } 491 492 // Check that the connection variable dependency is handled 493 if !depends("aws_load_balancer.weblb", "aws_security_group.firewall") { 494 t.Fatalf("missing dependency from provisioner connection") 495 } 496 } 497 498 func TestGraphAddDiff(t *testing.T) { 499 m := testModule(t, "graph-diff") 500 diff := &Diff{ 501 Modules: []*ModuleDiff{ 502 &ModuleDiff{ 503 Path: rootModulePath, 504 Resources: map[string]*InstanceDiff{ 505 "aws_instance.foo": &InstanceDiff{ 506 Attributes: map[string]*ResourceAttrDiff{ 507 "foo": &ResourceAttrDiff{ 508 New: "bar", 509 }, 510 }, 511 }, 512 }, 513 }, 514 }, 515 } 516 517 g, err := Graph(&GraphOpts{Module: m, Diff: diff}) 518 if err != nil { 519 t.Fatalf("err: %s", err) 520 } 521 522 actual := strings.TrimSpace(g.String()) 523 expected := strings.TrimSpace(testTerraformGraphDiffStr) 524 if actual != expected { 525 t.Fatalf("bad:\n\n%s", actual) 526 } 527 528 /* 529 TODO: test this somewhere 530 // Verify that the state has been added 531 n := g.Noun("aws_instance.foo") 532 rn := n.Meta.(*GraphNodeResource) 533 534 expected2 := diff.RootModule().Resources["aws_instance.foo"] 535 actual2 := rn.Resource.Diff 536 if !reflect.DeepEqual(actual2, expected2) { 537 t.Fatalf("bad: %#v", actual2) 538 } 539 */ 540 } 541 542 func TestGraphAddDiff_destroy(t *testing.T) { 543 m := testModule(t, "graph-diff-destroy") 544 diff := &Diff{ 545 Modules: []*ModuleDiff{ 546 &ModuleDiff{ 547 Path: rootModulePath, 548 Resources: map[string]*InstanceDiff{ 549 "aws_instance.foo": &InstanceDiff{ 550 Destroy: true, 551 }, 552 "aws_instance.bar": &InstanceDiff{ 553 Destroy: true, 554 }, 555 }, 556 }, 557 }, 558 } 559 state := &State{ 560 Modules: []*ModuleState{ 561 &ModuleState{ 562 Path: rootModulePath, 563 Resources: map[string]*ResourceState{ 564 "aws_instance.foo": &ResourceState{ 565 Type: "aws_instance", 566 Primary: &InstanceState{ 567 ID: "foo", 568 }, 569 }, 570 571 "aws_instance.bar": &ResourceState{ 572 Type: "aws_instance", 573 Dependencies: []string{"foo"}, 574 Primary: &InstanceState{ 575 ID: "bar", 576 }, 577 }, 578 }, 579 }, 580 }, 581 } 582 583 diffHash := checksumStruct(t, diff) 584 585 g, err := Graph(&GraphOpts{ 586 Module: m, 587 Diff: diff, 588 State: state, 589 }) 590 if err != nil { 591 t.Fatalf("err: %s", err) 592 } 593 594 actual := strings.TrimSpace(g.String()) 595 expected := strings.TrimSpace(testTerraformGraphDiffDestroyStr) 596 if actual != expected { 597 t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected) 598 } 599 600 // Verify that the state has been added 601 n := g.Noun("aws_instance.foo (destroy)") 602 rn := n.Meta.(*GraphNodeResource) 603 604 expected2 := &InstanceDiff{Destroy: true} 605 actual2 := rn.Resource.Diff 606 if !reflect.DeepEqual(actual2, expected2) { 607 t.Fatalf("bad: %#v", actual2) 608 } 609 610 // Verify that our original structure has not been modified 611 diffHash2 := checksumStruct(t, diff) 612 if diffHash != diffHash2 { 613 t.Fatal("diff has been modified") 614 } 615 } 616 617 func TestGraphAddDiff_destroy_counts(t *testing.T) { 618 m := testModule(t, "graph-count") 619 diff := &Diff{ 620 Modules: []*ModuleDiff{ 621 &ModuleDiff{ 622 Path: rootModulePath, 623 Resources: map[string]*InstanceDiff{ 624 "aws_instance.web.0": &InstanceDiff{ 625 Destroy: true, 626 }, 627 "aws_instance.web.1": &InstanceDiff{ 628 Destroy: true, 629 }, 630 "aws_instance.web.2": &InstanceDiff{ 631 Destroy: true, 632 }, 633 "aws_load_balancer.weblb": &InstanceDiff{ 634 Destroy: true, 635 }, 636 }, 637 }, 638 }, 639 } 640 state := &State{ 641 Modules: []*ModuleState{ 642 &ModuleState{ 643 Path: rootModulePath, 644 Resources: map[string]*ResourceState{ 645 "aws_instance.web.0": &ResourceState{ 646 Type: "aws_instance", 647 Primary: &InstanceState{ 648 ID: "foo", 649 }, 650 }, 651 "aws_instance.web.1": &ResourceState{ 652 Type: "aws_instance", 653 Primary: &InstanceState{ 654 ID: "foo", 655 }, 656 }, 657 "aws_instance.web.2": &ResourceState{ 658 Type: "aws_instance", 659 Primary: &InstanceState{ 660 ID: "foo", 661 }, 662 }, 663 "aws_load_balancer.weblb": &ResourceState{ 664 Type: "aws_load_balancer", 665 Dependencies: []string{"aws_instance.web.0", "aws_instance.web.1", "aws_instance.web.2"}, 666 Primary: &InstanceState{ 667 ID: "bar", 668 }, 669 }, 670 }, 671 }, 672 }, 673 } 674 675 diffHash := checksumStruct(t, diff) 676 677 g, err := Graph(&GraphOpts{ 678 Module: m, 679 Diff: diff, 680 State: state, 681 }) 682 if err != nil { 683 t.Fatalf("err: %s", err) 684 } 685 686 actual := strings.TrimSpace(g.String()) 687 expected := strings.TrimSpace(testTerraformGraphDiffDestroyCountsStr) 688 if actual != expected { 689 t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected) 690 } 691 692 // Verify that the state has been added 693 n := g.Noun("aws_instance.web (destroy)") 694 rn := n.Meta.(*GraphNodeResource) 695 696 if rn.ExpandMode != ResourceExpandDestroy { 697 t.Fatalf("bad: %#v", rn) 698 } 699 700 // Verify that our original structure has not been modified 701 diffHash2 := checksumStruct(t, diff) 702 if diffHash != diffHash2 { 703 t.Fatal("diff has been modified") 704 } 705 } 706 707 func TestGraphAddDiff_module(t *testing.T) { 708 m := testModule(t, "graph-diff-module") 709 diff := &Diff{ 710 Modules: []*ModuleDiff{ 711 &ModuleDiff{ 712 Path: rootModulePath, 713 Resources: map[string]*InstanceDiff{ 714 "aws_instance.foo": &InstanceDiff{ 715 Destroy: true, 716 }, 717 }, 718 }, 719 }, 720 } 721 722 g, err := Graph(&GraphOpts{Module: m, Diff: diff}) 723 if err != nil { 724 t.Fatalf("err: %s", err) 725 } 726 727 actual := strings.TrimSpace(g.String()) 728 expected := strings.TrimSpace(testTerraformGraphDiffModuleStr) 729 if actual != expected { 730 t.Fatalf("bad:\n\n%s", actual) 731 } 732 } 733 734 func TestGraphAddDiff_createBeforeDestroy(t *testing.T) { 735 m := testModule(t, "graph-diff-create-before") 736 diff := &Diff{ 737 Modules: []*ModuleDiff{ 738 &ModuleDiff{ 739 Path: rootModulePath, 740 Resources: map[string]*InstanceDiff{ 741 "aws_instance.bar": &InstanceDiff{ 742 Destroy: true, 743 Attributes: map[string]*ResourceAttrDiff{ 744 "ami": &ResourceAttrDiff{ 745 Old: "abc", 746 New: "xyz", 747 RequiresNew: true, 748 }, 749 }, 750 }, 751 }, 752 }, 753 }, 754 } 755 state := &State{ 756 Modules: []*ModuleState{ 757 &ModuleState{ 758 Path: rootModulePath, 759 Resources: map[string]*ResourceState{ 760 "aws_instance.bar": &ResourceState{ 761 Type: "aws_instance", 762 Primary: &InstanceState{ 763 ID: "bar", 764 Attributes: map[string]string{ 765 "ami": "abc", 766 }, 767 }, 768 }, 769 }, 770 }, 771 }, 772 } 773 774 diffHash := checksumStruct(t, diff) 775 776 g, err := Graph(&GraphOpts{ 777 Module: m, 778 Diff: diff, 779 State: state, 780 }) 781 if err != nil { 782 t.Fatalf("err: %s", err) 783 } 784 785 actual := strings.TrimSpace(g.String()) 786 expected := strings.TrimSpace(testTerraformGraphDiffCreateBeforeDestroyStr) 787 if actual != expected { 788 t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected) 789 } 790 791 // Verify the flags are set 792 r := g.Noun("aws_instance.bar") 793 if r.Meta.(*GraphNodeResource).Resource.Flags&FlagReplacePrimary == 0 { 794 t.Fatalf("missing FlagReplacePrimary") 795 } 796 797 r = g.Noun("aws_instance.bar (destroy)") 798 if r.Meta.(*GraphNodeResource).Resource.Flags&FlagDeposed == 0 { 799 t.Fatalf("missing FlagDeposed") 800 } 801 802 // Verify that our original structure has not been modified 803 diffHash2 := checksumStruct(t, diff) 804 if diffHash != diffHash2 { 805 t.Fatal("diff has been modified") 806 } 807 } 808 809 func TestGraphAddDiff_moduleDestroy(t *testing.T) { 810 m := testModule(t, "graph-diff-module") 811 diff := &Diff{ 812 Modules: []*ModuleDiff{ 813 &ModuleDiff{ 814 Path: rootModulePath, 815 Resources: map[string]*InstanceDiff{ 816 "aws_instance.foo": &InstanceDiff{ 817 Destroy: true, 818 }, 819 }, 820 }, 821 &ModuleDiff{ 822 Path: []string{"root", "child"}, 823 Resources: map[string]*InstanceDiff{ 824 "aws_instance.foo": &InstanceDiff{ 825 Destroy: true, 826 }, 827 }, 828 }, 829 }, 830 } 831 832 g, err := Graph(&GraphOpts{Module: m, Diff: diff}) 833 if err != nil { 834 t.Fatalf("err: %s", err) 835 } 836 837 actual := strings.TrimSpace(g.String()) 838 expected := strings.TrimSpace(testTerraformGraphDiffModuleStr) 839 if actual != expected { 840 t.Fatalf("bad:\n\n%s", actual) 841 } 842 } 843 844 func TestGraphEncodeDependencies(t *testing.T) { 845 m := testModule(t, "graph-basic") 846 847 g, err := Graph(&GraphOpts{Module: m}) 848 if err != nil { 849 t.Fatalf("err: %s", err) 850 } 851 852 // This should encode the dependency information into the state 853 graphEncodeDependencies(g) 854 855 web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource 856 if len(web.Dependencies) != 1 || web.Dependencies[0] != "aws_security_group.firewall" { 857 t.Fatalf("bad: %#v", web) 858 } 859 860 weblb := g.Noun("aws_load_balancer.weblb").Meta.(*GraphNodeResource).Resource 861 if len(weblb.Dependencies) != 1 || weblb.Dependencies[0] != "aws_instance.web" { 862 t.Fatalf("bad: %#v", weblb) 863 } 864 } 865 866 func TestGraphEncodeDependencies_count(t *testing.T) { 867 m := testModule(t, "graph-count") 868 state := &State{ 869 Modules: []*ModuleState{ 870 &ModuleState{ 871 Path: rootModulePath, 872 Resources: map[string]*ResourceState{ 873 "aws_instance.web.0": &ResourceState{ 874 Type: "aws_instance", 875 Primary: &InstanceState{ 876 ID: "foo", 877 }, 878 }, 879 "aws_load_balancer.weblb": &ResourceState{ 880 Type: "aws_load_balancer", 881 Primary: &InstanceState{ 882 ID: "foo", 883 }, 884 }, 885 }, 886 }, 887 }, 888 } 889 890 g, err := Graph(&GraphOpts{Module: m, State: state}) 891 if err != nil { 892 t.Fatalf("err: %s", err) 893 } 894 895 // This should encode the dependency information into the state 896 graphEncodeDependencies(g) 897 898 web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource 899 if len(web.Dependencies) != 0 { 900 t.Fatalf("bad: %#v", web) 901 } 902 903 weblb := g.Noun("aws_load_balancer.weblb").Meta.(*GraphNodeResource).Resource 904 if len(weblb.Dependencies) != 1 { 905 t.Fatalf("bad: %#v", weblb) 906 } 907 } 908 909 func TestGraphEncodeDependencies_module(t *testing.T) { 910 m := testModule(t, "graph-modules") 911 912 g, err := Graph(&GraphOpts{Module: m}) 913 if err != nil { 914 t.Fatalf("err: %s", err) 915 } 916 917 // This should encode the dependency information into the state 918 graphEncodeDependencies(g) 919 920 web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource 921 sort.Strings(web.Dependencies) 922 if len(web.Dependencies) != 2 { 923 t.Fatalf("bad: %#v", web) 924 } 925 if web.Dependencies[0] != "aws_security_group.firewall" { 926 t.Fatalf("bad: %#v", web) 927 } 928 if web.Dependencies[1] != "module.consul" { 929 t.Fatalf("bad: %#v", web) 930 } 931 } 932 933 func TestGraph_orphan_dependencies(t *testing.T) { 934 m := testModule(t, "graph-count") 935 state := &State{ 936 Modules: []*ModuleState{ 937 &ModuleState{ 938 Path: rootModulePath, 939 940 Resources: map[string]*ResourceState{ 941 "aws_instance.web.0": &ResourceState{ 942 Type: "aws_instance", 943 Primary: &InstanceState{ 944 ID: "foo", 945 }, 946 }, 947 "aws_instance.web.1": &ResourceState{ 948 Type: "aws_instance", 949 Primary: &InstanceState{ 950 ID: "foo", 951 }, 952 }, 953 "aws_load_balancer.old": &ResourceState{ 954 Type: "aws_load_balancer", 955 Primary: &InstanceState{ 956 ID: "foo", 957 }, 958 Dependencies: []string{ 959 "aws_instance.web.0", 960 "aws_instance.web.1", 961 "aws_instance.web.2", 962 }, 963 }, 964 }, 965 }, 966 }, 967 } 968 969 g, err := Graph(&GraphOpts{Module: m, State: state}) 970 if err != nil { 971 t.Fatalf("err: %s", err) 972 } 973 974 actual := strings.TrimSpace(g.String()) 975 expected := strings.TrimSpace(testTerraformGraphCountOrphanStr) 976 if actual != expected { 977 t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) 978 } 979 } 980 981 func TestGraph_orphanDependenciesModules(t *testing.T) { 982 m := testModule(t, "graph-modules") 983 state := &State{ 984 Modules: []*ModuleState{ 985 &ModuleState{ 986 Path: rootModulePath, 987 988 Resources: map[string]*ResourceState{ 989 "aws_instance.foo": &ResourceState{ 990 Type: "aws_instance", 991 Primary: &InstanceState{ 992 ID: "foo", 993 }, 994 Dependencies: []string{ 995 "module.consul", 996 }, 997 }, 998 }, 999 }, 1000 }, 1001 } 1002 1003 g, err := Graph(&GraphOpts{Module: m, State: state}) 1004 if err != nil { 1005 t.Fatalf("err: %s", err) 1006 } 1007 1008 actual := strings.TrimSpace(g.String()) 1009 expected := strings.TrimSpace(testTerraformGraphOrphanModuleDepsStr) 1010 if actual != expected { 1011 t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) 1012 } 1013 } 1014 1015 func TestGraphNodeResourceExpand(t *testing.T) { 1016 m := testModule(t, "graph-resource-expand") 1017 1018 g, err := Graph(&GraphOpts{Module: m}) 1019 if err != nil { 1020 t.Fatalf("err: %s", err) 1021 } 1022 1023 // Get the resource we care about expanding 1024 n := g.Noun("aws_instance.web") 1025 if n == nil { 1026 t.Fatal("could not find") 1027 } 1028 rn := n.Meta.(*GraphNodeResource) 1029 1030 g, err = rn.Expand() 1031 if err != nil { 1032 t.Fatalf("err: %s", err) 1033 } 1034 1035 actual := strings.TrimSpace(g.String()) 1036 expected := strings.TrimSpace(testTerraformGraphResourceExpandStr) 1037 if actual != expected { 1038 t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) 1039 } 1040 } 1041 1042 func TestGraphNodeResourceExpand_provDeps(t *testing.T) { 1043 m := testModule(t, "graph-resource-expand-prov-deps") 1044 provs := map[string]ResourceProvisionerFactory{ 1045 "remote-exec": func() (ResourceProvisioner, error) { 1046 return new(MockResourceProvisioner), nil 1047 }, 1048 } 1049 1050 g, err := Graph(&GraphOpts{Module: m, Provisioners: provs}) 1051 if err != nil { 1052 t.Fatalf("err: %s", err) 1053 } 1054 1055 // Get the resource we care about expanding 1056 n := g.Noun("aws_instance.web") 1057 if n == nil { 1058 t.Fatal("could not find") 1059 } 1060 rn := n.Meta.(*GraphNodeResource) 1061 1062 g, err = rn.Expand() 1063 if err != nil { 1064 t.Fatalf("err: %s", err) 1065 } 1066 1067 actual := strings.TrimSpace(g.String()) 1068 expected := strings.TrimSpace(testTerraformGraphResourceExpandProvDepsStr) 1069 if actual != expected { 1070 t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected) 1071 } 1072 } 1073 1074 const testTerraformGraphStr = ` 1075 root: root 1076 aws_instance.web 1077 aws_instance.web -> aws_security_group.firewall 1078 aws_instance.web -> provider.aws 1079 aws_load_balancer.weblb 1080 aws_load_balancer.weblb -> aws_instance.web 1081 aws_load_balancer.weblb -> provider.aws 1082 aws_security_group.firewall 1083 aws_security_group.firewall -> provider.aws 1084 openstack_floating_ip.random 1085 provider.aws 1086 provider.aws -> openstack_floating_ip.random 1087 root 1088 root -> aws_instance.web 1089 root -> aws_load_balancer.weblb 1090 root -> aws_security_group.firewall 1091 root -> openstack_floating_ip.random 1092 ` 1093 1094 const testTerraformGraphCountStr = ` 1095 root: root 1096 aws_instance.web 1097 aws_load_balancer.weblb 1098 aws_load_balancer.weblb -> aws_instance.web 1099 root 1100 root -> aws_instance.web 1101 root -> aws_load_balancer.weblb 1102 ` 1103 1104 const testTerraformGraphCountTaintedStr = ` 1105 root: root 1106 aws_instance.web 1107 aws_instance.web -> aws_instance.web.0 (tainted #1) 1108 aws_instance.web.0 (tainted #1) 1109 aws_load_balancer.weblb 1110 aws_load_balancer.weblb -> aws_instance.web 1111 root 1112 root -> aws_instance.web 1113 root -> aws_instance.web.0 (tainted #1) 1114 root -> aws_load_balancer.weblb 1115 ` 1116 1117 const testTerraformGraphCountVarResourceStr = ` 1118 root: root 1119 aws_instance.foo 1120 aws_instance.web 1121 aws_instance.web -> aws_instance.foo 1122 aws_load_balancer.weblb 1123 aws_load_balancer.weblb -> aws_instance.web 1124 root 1125 root -> aws_instance.foo 1126 root -> aws_instance.web 1127 root -> aws_load_balancer.weblb 1128 ` 1129 1130 const testTerraformGraphDependsStr = ` 1131 root: root 1132 aws_instance.db 1133 aws_instance.db -> aws_instance.web 1134 aws_instance.web 1135 root 1136 root -> aws_instance.db 1137 root -> aws_instance.web 1138 ` 1139 1140 const testTerraformGraphDependsCountStr = ` 1141 root: root 1142 aws_instance.db 1143 aws_instance.db -> aws_instance.web 1144 aws_instance.web 1145 root 1146 root -> aws_instance.db 1147 root -> aws_instance.web 1148 ` 1149 1150 const testTerraformGraphDependsOrphanStr = ` 1151 root: root 1152 aws_instance.db 1153 aws_instance.db -> aws_instance.web 1154 aws_instance.old 1155 aws_instance.web 1156 root 1157 root -> aws_instance.db 1158 root -> aws_instance.old 1159 root -> aws_instance.web 1160 ` 1161 1162 const testTerraformGraphDiffStr = ` 1163 root: root 1164 aws_instance.foo 1165 root 1166 root -> aws_instance.foo 1167 ` 1168 1169 const testTerraformGraphDiffDestroyStr = ` 1170 root: root 1171 aws_instance.bar 1172 aws_instance.bar -> aws_instance.bar (destroy) 1173 aws_instance.bar -> aws_instance.foo 1174 aws_instance.bar -> provider.aws 1175 aws_instance.bar (destroy) 1176 aws_instance.bar (destroy) -> provider.aws 1177 aws_instance.foo 1178 aws_instance.foo -> aws_instance.foo (destroy) 1179 aws_instance.foo -> provider.aws 1180 aws_instance.foo (destroy) 1181 aws_instance.foo (destroy) -> aws_instance.bar (destroy) 1182 aws_instance.foo (destroy) -> provider.aws 1183 provider.aws 1184 root 1185 root -> aws_instance.bar 1186 root -> aws_instance.foo 1187 ` 1188 1189 const testTerraformGraphDiffDestroyCountsStr = ` 1190 root: root 1191 aws_instance.web 1192 aws_instance.web -> aws_instance.web (destroy) 1193 aws_instance.web (destroy) 1194 aws_instance.web (destroy) -> aws_load_balancer.weblb (destroy) 1195 aws_load_balancer.weblb 1196 aws_load_balancer.weblb -> aws_instance.web 1197 aws_load_balancer.weblb -> aws_load_balancer.weblb (destroy) 1198 aws_load_balancer.weblb (destroy) 1199 root 1200 root -> aws_instance.web 1201 root -> aws_load_balancer.weblb 1202 ` 1203 1204 const testTerraformGraphDiffModuleStr = ` 1205 root: root 1206 aws_instance.foo 1207 aws_instance.foo -> aws_instance.foo (destroy) 1208 aws_instance.foo -> module.child 1209 aws_instance.foo (destroy) 1210 module.child 1211 module.child -> aws_instance.foo (destroy) 1212 root 1213 root -> aws_instance.foo 1214 root -> module.child 1215 ` 1216 1217 const testTerraformGraphModulesStr = ` 1218 root: root 1219 aws_instance.web 1220 aws_instance.web -> aws_security_group.firewall 1221 aws_instance.web -> module.consul 1222 aws_instance.web -> provider.aws 1223 aws_security_group.firewall 1224 aws_security_group.firewall -> provider.aws 1225 module.consul 1226 module.consul -> aws_security_group.firewall 1227 module.consul -> provider.aws 1228 provider.aws 1229 root 1230 root -> aws_instance.web 1231 root -> aws_security_group.firewall 1232 root -> module.consul 1233 ` 1234 1235 const testTerraformGraphModulesConsulStr = ` 1236 root: root 1237 aws_instance.server 1238 aws_instance.server -> provider.aws 1239 provider.aws 1240 root 1241 root -> aws_instance.server 1242 ` 1243 1244 const testTerraformGraphModuleOrphanStr = ` 1245 root: root 1246 aws_instance.web 1247 aws_instance.web -> aws_security_group.firewall 1248 aws_instance.web -> provider.aws 1249 aws_security_group.firewall 1250 aws_security_group.firewall -> provider.aws 1251 module.consul 1252 module.consul -> provider.aws 1253 provider.aws 1254 root 1255 root -> aws_instance.web 1256 root -> aws_security_group.firewall 1257 root -> module.consul 1258 ` 1259 1260 const testTerraformGraphModuleOrphanConsulStr = ` 1261 root: root 1262 aws_instance.old 1263 aws_instance.old -> provider.aws 1264 provider.aws 1265 root 1266 root -> aws_instance.old 1267 ` 1268 1269 const testTerraformGraphProviderPruneStr = ` 1270 root: root 1271 aws_load_balancer.weblb 1272 aws_load_balancer.weblb -> provider.aws 1273 provider.aws 1274 root 1275 root -> aws_load_balancer.weblb` 1276 1277 const testTerraformGraphDiffCreateBeforeDestroyStr = ` 1278 root: root 1279 aws_instance.bar 1280 aws_instance.bar -> provider.aws 1281 aws_instance.bar (destroy) 1282 aws_instance.bar (destroy) -> aws_instance.bar 1283 aws_instance.bar (destroy) -> provider.aws 1284 provider.aws 1285 root 1286 root -> aws_instance.bar 1287 root -> aws_instance.bar (destroy)` 1288 1289 const testTerraformGraphStateStr = ` 1290 root: root 1291 aws_instance.old 1292 aws_instance.old -> provider.aws 1293 aws_instance.web 1294 aws_instance.web -> aws_security_group.firewall 1295 aws_instance.web -> provider.aws 1296 aws_load_balancer.weblb 1297 aws_load_balancer.weblb -> aws_instance.web 1298 aws_load_balancer.weblb -> provider.aws 1299 aws_security_group.firewall 1300 aws_security_group.firewall -> provider.aws 1301 openstack_floating_ip.random 1302 provider.aws 1303 provider.aws -> openstack_floating_ip.random 1304 root 1305 root -> aws_instance.old 1306 root -> aws_instance.web 1307 root -> aws_load_balancer.weblb 1308 root -> aws_security_group.firewall 1309 root -> openstack_floating_ip.random 1310 ` 1311 1312 const testTerraformGraphTaintedStr = ` 1313 root: root 1314 aws_instance.web 1315 aws_instance.web -> aws_instance.web (tainted #1) 1316 aws_instance.web -> aws_security_group.firewall 1317 aws_instance.web -> provider.aws 1318 aws_instance.web (tainted #1) 1319 aws_instance.web (tainted #1) -> provider.aws 1320 aws_security_group.firewall 1321 aws_security_group.firewall -> provider.aws 1322 provider.aws 1323 root 1324 root -> aws_instance.web 1325 root -> aws_instance.web (tainted #1) 1326 root -> aws_security_group.firewall 1327 ` 1328 1329 const testTerraformGraphTaintedMultiStr = ` 1330 root: root 1331 aws_instance.web 1332 aws_instance.web -> aws_instance.web (tainted #1) 1333 aws_instance.web -> aws_instance.web (tainted #2) 1334 aws_instance.web -> aws_security_group.firewall 1335 aws_instance.web -> provider.aws 1336 aws_instance.web (tainted #1) 1337 aws_instance.web (tainted #1) -> provider.aws 1338 aws_instance.web (tainted #2) 1339 aws_instance.web (tainted #2) -> provider.aws 1340 aws_security_group.firewall 1341 aws_security_group.firewall -> provider.aws 1342 provider.aws 1343 root 1344 root -> aws_instance.web 1345 root -> aws_instance.web (tainted #1) 1346 root -> aws_instance.web (tainted #2) 1347 root -> aws_security_group.firewall 1348 ` 1349 1350 const testTerraformGraphCountOrphanStr = ` 1351 root: root 1352 aws_instance.web 1353 aws_load_balancer.old 1354 aws_load_balancer.old -> aws_instance.web 1355 aws_load_balancer.weblb 1356 aws_load_balancer.weblb -> aws_instance.web 1357 root 1358 root -> aws_instance.web 1359 root -> aws_load_balancer.old 1360 root -> aws_load_balancer.weblb 1361 ` 1362 1363 const testTerraformGraphOrphanModuleDepsStr = ` 1364 root: root 1365 aws_instance.foo 1366 aws_instance.foo -> module.consul 1367 aws_instance.foo -> provider.aws 1368 aws_instance.web 1369 aws_instance.web -> aws_security_group.firewall 1370 aws_instance.web -> module.consul 1371 aws_instance.web -> provider.aws 1372 aws_security_group.firewall 1373 aws_security_group.firewall -> provider.aws 1374 module.consul 1375 module.consul -> aws_security_group.firewall 1376 module.consul -> provider.aws 1377 provider.aws 1378 root 1379 root -> aws_instance.foo 1380 root -> aws_instance.web 1381 root -> aws_security_group.firewall 1382 root -> module.consul 1383 ` 1384 1385 const testTerraformGraphResourceExpandStr = ` 1386 root: root 1387 aws_instance.web.0 1388 aws_instance.web.1 1389 aws_instance.web.2 1390 root 1391 root -> aws_instance.web.0 1392 root -> aws_instance.web.1 1393 root -> aws_instance.web.2 1394 ` 1395 1396 const testTerraformGraphResourceExpandProvDepsStr = ` 1397 root: root 1398 aws_instance.web.0 1399 aws_instance.web.1 1400 aws_instance.web.1 -> aws_instance.web.0 1401 aws_instance.web.2 1402 aws_instance.web.2 -> aws_instance.web.0 1403 root 1404 root -> aws_instance.web.0 1405 root -> aws_instance.web.1 1406 root -> aws_instance.web.2 1407 `