github.com/medzin/terraform@v0.11.11/config/module/tree_test.go (about) 1 package module 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/hashicorp/terraform/config" 13 "github.com/hashicorp/terraform/helper/copy" 14 ) 15 16 func TestTreeChild(t *testing.T) { 17 var nilTree *Tree 18 if nilTree.Child(nil) != nil { 19 t.Fatal("child should be nil") 20 } 21 22 storage := testStorage(t, nil) 23 storage.Mode = GetModeGet 24 tree := NewTree("", testConfig(t, "child")) 25 if err := tree.Load(storage); err != nil { 26 t.Fatalf("err: %s", err) 27 } 28 29 // Should be able to get the root child 30 if c := tree.Child([]string{}); c == nil { 31 t.Fatal("should not be nil") 32 } else if c.Name() != "root" { 33 t.Fatalf("bad: %#v", c.Name()) 34 } else if !reflect.DeepEqual(c.Path(), []string(nil)) { 35 t.Fatalf("bad: %#v", c.Path()) 36 } 37 38 // Should be able to get the root child 39 if c := tree.Child(nil); c == nil { 40 t.Fatal("should not be nil") 41 } else if c.Name() != "root" { 42 t.Fatalf("bad: %#v", c.Name()) 43 } else if !reflect.DeepEqual(c.Path(), []string(nil)) { 44 t.Fatalf("bad: %#v", c.Path()) 45 } 46 47 // Should be able to get the foo child 48 if c := tree.Child([]string{"foo"}); c == nil { 49 t.Fatal("should not be nil") 50 } else if c.Name() != "foo" { 51 t.Fatalf("bad: %#v", c.Name()) 52 } else if !reflect.DeepEqual(c.Path(), []string{"foo"}) { 53 t.Fatalf("bad: %#v", c.Path()) 54 } 55 56 // Should be able to get the nested child 57 if c := tree.Child([]string{"foo", "bar"}); c == nil { 58 t.Fatal("should not be nil") 59 } else if c.Name() != "bar" { 60 t.Fatalf("bad: %#v", c.Name()) 61 } else if !reflect.DeepEqual(c.Path(), []string{"foo", "bar"}) { 62 t.Fatalf("bad: %#v", c.Path()) 63 } 64 } 65 66 func TestTreeLoad(t *testing.T) { 67 storage := testStorage(t, nil) 68 tree := NewTree("", testConfig(t, "basic")) 69 70 if tree.Loaded() { 71 t.Fatal("should not be loaded") 72 } 73 74 // This should error because we haven't gotten things yet 75 if err := tree.Load(storage); err == nil { 76 t.Fatal("should error") 77 } 78 79 if tree.Loaded() { 80 t.Fatal("should not be loaded") 81 } 82 83 // This should get things 84 storage.Mode = GetModeGet 85 if err := tree.Load(storage); err != nil { 86 t.Fatalf("err: %s", err) 87 } 88 89 if !tree.Loaded() { 90 t.Fatal("should be loaded") 91 } 92 93 // This should no longer error 94 storage.Mode = GetModeNone 95 if err := tree.Load(storage); err != nil { 96 t.Fatalf("err: %s", err) 97 } 98 99 actual := strings.TrimSpace(tree.String()) 100 expected := strings.TrimSpace(treeLoadStr) 101 if actual != expected { 102 t.Fatalf("bad: \n\n%s", actual) 103 } 104 } 105 106 func TestTreeLoad_duplicate(t *testing.T) { 107 storage := testStorage(t, nil) 108 tree := NewTree("", testConfig(t, "dup")) 109 110 if tree.Loaded() { 111 t.Fatal("should not be loaded") 112 } 113 114 // This should get things 115 storage.Mode = GetModeGet 116 if err := tree.Load(storage); err == nil { 117 t.Fatalf("should error") 118 } 119 } 120 121 func TestTreeLoad_copyable(t *testing.T) { 122 dir := tempDir(t) 123 storage := &Storage{ 124 StorageDir: dir, 125 Mode: GetModeGet, 126 } 127 cfg := testConfig(t, "basic") 128 tree := NewTree("", cfg) 129 130 // This should get things 131 if err := tree.Load(storage); err != nil { 132 t.Fatalf("err: %s", err) 133 } 134 135 if !tree.Loaded() { 136 t.Fatal("should be loaded") 137 } 138 139 // This should no longer error 140 storage.Mode = GetModeNone 141 if err := tree.Load(storage); err != nil { 142 t.Fatalf("err: %s", err) 143 } 144 145 // Now we copy the directory, this COPIES symlink values, and 146 // doesn't create symlinks themselves. That is important. 147 dir2 := tempDir(t) 148 os.RemoveAll(dir2) 149 defer os.RemoveAll(dir2) 150 if err := copy.CopyDir(dir, dir2); err != nil { 151 t.Fatalf("err: %s", err) 152 } 153 154 // Now copy the configuration 155 cfgDir := tempDir(t) 156 os.RemoveAll(cfgDir) 157 defer os.RemoveAll(cfgDir) 158 if err := copy.CopyDir(cfg.Dir, cfgDir); err != nil { 159 t.Fatalf("err: %s", err) 160 } 161 162 { 163 cfg, err := config.LoadDir(cfgDir) 164 if err != nil { 165 t.Fatalf("err: %s", err) 166 } 167 168 tree := NewTree("", cfg) 169 storage := &Storage{ 170 StorageDir: dir2, 171 Mode: GetModeNone, 172 } 173 174 // This should not error since we already got it! 175 if err := tree.Load(storage); err != nil { 176 t.Fatalf("err: %s", err) 177 } 178 179 if !tree.Loaded() { 180 t.Fatal("should be loaded") 181 } 182 } 183 } 184 185 func TestTreeLoad_parentRef(t *testing.T) { 186 storage := testStorage(t, nil) 187 tree := NewTree("", testConfig(t, "basic-parent")) 188 189 if tree.Loaded() { 190 t.Fatal("should not be loaded") 191 } 192 193 // This should error because we haven't gotten things yet 194 storage.Mode = GetModeNone 195 if err := tree.Load(storage); err == nil { 196 t.Fatal("should error") 197 } 198 199 if tree.Loaded() { 200 t.Fatal("should not be loaded") 201 } 202 203 // This should get things 204 storage.Mode = GetModeGet 205 if err := tree.Load(storage); err != nil { 206 t.Fatalf("err: %s", err) 207 } 208 209 if !tree.Loaded() { 210 t.Fatal("should be loaded") 211 } 212 213 // This should no longer error 214 storage.Mode = GetModeNone 215 if err := tree.Load(storage); err != nil { 216 t.Fatalf("err: %s", err) 217 } 218 219 actual := strings.TrimSpace(tree.String()) 220 expected := strings.TrimSpace(treeLoadParentStr) 221 if actual != expected { 222 t.Fatalf("bad: \n\n%s", actual) 223 } 224 } 225 226 func TestTreeLoad_subdir(t *testing.T) { 227 fixtures := []string{ 228 "basic-subdir", 229 "basic-tar-subdir", 230 "tar-subdir-to-parent", 231 } 232 233 for _, tc := range fixtures { 234 t.Run(tc, func(t *testing.T) { 235 storage := testStorage(t, nil) 236 tree := NewTree("", testConfig(t, tc)) 237 238 if tree.Loaded() { 239 t.Fatal("should not be loaded") 240 } 241 242 // This should error because we haven't gotten things yet 243 storage.Mode = GetModeNone 244 if err := tree.Load(storage); err == nil { 245 t.Fatal("should error") 246 } 247 248 if tree.Loaded() { 249 t.Fatal("should not be loaded") 250 } 251 252 // This should get things 253 storage.Mode = GetModeGet 254 if err := tree.Load(storage); err != nil { 255 t.Fatalf("err: %s", err) 256 } 257 258 if !tree.Loaded() { 259 t.Fatal("should be loaded") 260 } 261 262 // This should no longer error 263 storage.Mode = GetModeNone 264 if err := tree.Load(storage); err != nil { 265 t.Fatalf("err: %s", err) 266 } 267 268 actual := strings.TrimSpace(tree.String()) 269 expected := strings.TrimSpace(treeLoadSubdirStr) 270 if actual != expected { 271 t.Fatalf("bad: \n\n%s", actual) 272 } 273 }) 274 } 275 } 276 277 func TestTree_recordManifest(t *testing.T) { 278 td, err := ioutil.TempDir("", "tf-module") 279 if err != nil { 280 t.Fatal(err) 281 } 282 defer os.RemoveAll(td) 283 284 storage := Storage{StorageDir: td} 285 286 dir := filepath.Join(td, "0131bf0fef686e090b16bdbab4910ddf") 287 288 subDir := "subDirName" 289 290 // record and read the subdir path 291 if err := storage.recordModuleRoot(dir, subDir); err != nil { 292 t.Fatal(err) 293 } 294 actual, err := storage.getModuleRoot(dir) 295 if err != nil { 296 t.Fatal(err) 297 } 298 299 if actual != subDir { 300 t.Fatalf("expected subDir %q, got %q", subDir, actual) 301 } 302 303 // overwrite the path, and nmake sure we get the new one 304 subDir = "newSubDir" 305 if err := storage.recordModuleRoot(dir, subDir); err != nil { 306 t.Fatal(err) 307 } 308 actual, err = storage.getModuleRoot(dir) 309 if err != nil { 310 t.Fatal(err) 311 } 312 313 if actual != subDir { 314 t.Fatalf("expected subDir %q, got %q", subDir, actual) 315 } 316 317 // create a fake entry 318 if err := ioutil.WriteFile(filepath.Join(td, manifestName), []byte("BAD DATA"), 0644); err != nil { 319 t.Fatal(err) 320 } 321 322 // this should fail because there aare now 2 entries 323 actual, err = storage.getModuleRoot(dir) 324 if err == nil { 325 t.Fatal("expected multiple subdir entries") 326 } 327 328 // writing the subdir entry should remove the incorrect value 329 if err := storage.recordModuleRoot(dir, subDir); err != nil { 330 t.Fatal(err) 331 } 332 actual, err = storage.getModuleRoot(dir) 333 if err != nil { 334 t.Fatal(err) 335 } 336 337 if actual != subDir { 338 t.Fatalf("expected subDir %q, got %q", subDir, actual) 339 } 340 } 341 342 func TestTreeModules(t *testing.T) { 343 tree := NewTree("", testConfig(t, "basic")) 344 actual := tree.Modules() 345 346 expected := []*Module{ 347 &Module{Name: "foo", Source: "./foo"}, 348 } 349 350 if !reflect.DeepEqual(actual, expected) { 351 t.Fatalf("bad: %#v", actual) 352 } 353 } 354 355 func TestTreeName(t *testing.T) { 356 tree := NewTree("", testConfig(t, "basic")) 357 actual := tree.Name() 358 359 if actual != RootName { 360 t.Fatalf("bad: %#v", actual) 361 } 362 } 363 364 // This is a table-driven test for tree validation. This is the preferred 365 // way to test Validate. Non table-driven tests exist historically but 366 // that style shouldn't be done anymore. 367 func TestTreeValidate_table(t *testing.T) { 368 cases := []struct { 369 Name string 370 Fixture string 371 Err string 372 }{ 373 { 374 "provider alias in child", 375 "validate-alias-good", 376 "", 377 }, 378 379 { 380 "undefined provider alias in child", 381 "validate-alias-bad", 382 "alias must be defined", 383 }, 384 385 { 386 "root module named root", 387 "validate-module-root", 388 "cannot contain module", 389 }, 390 391 { 392 "grandchild module named root", 393 "validate-module-root-grandchild", 394 "", 395 }, 396 } 397 398 for i, tc := range cases { 399 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 400 tree := NewTree("", testConfig(t, tc.Fixture)) 401 storage := testStorage(t, nil) 402 storage.Mode = GetModeGet 403 if err := tree.Load(storage); err != nil { 404 t.Fatalf("err: %s", err) 405 } 406 407 diags := tree.Validate() 408 if (diags.HasErrors()) != (tc.Err != "") { 409 t.Fatalf("err: %s", diags.Err()) 410 } 411 if len(diags) == 0 { 412 return 413 } 414 if !strings.Contains(diags.Err().Error(), tc.Err) { 415 t.Fatalf("err should contain %q: %s", tc.Err, diags.Err().Error()) 416 } 417 }) 418 } 419 } 420 421 func TestTreeValidate_badChild(t *testing.T) { 422 tree := NewTree("", testConfig(t, "validate-child-bad")) 423 424 storage := testStorage(t, nil) 425 storage.Mode = GetModeGet 426 if err := tree.Load(storage); err != nil { 427 t.Fatalf("err: %s", err) 428 } 429 430 if err := tree.Validate(); err == nil { 431 t.Fatal("should error") 432 } 433 } 434 435 func TestTreeValidate_badChildOutput(t *testing.T) { 436 tree := NewTree("", testConfig(t, "validate-bad-output")) 437 438 storage := testStorage(t, nil) 439 storage.Mode = GetModeGet 440 if err := tree.Load(storage); err != nil { 441 t.Fatalf("err: %s", err) 442 } 443 444 if err := tree.Validate(); err == nil { 445 t.Fatal("should error") 446 } 447 } 448 449 func TestTreeValidate_badChildOutputToModule(t *testing.T) { 450 tree := NewTree("", testConfig(t, "validate-bad-output-to-module")) 451 452 storage := testStorage(t, nil) 453 storage.Mode = GetModeGet 454 if err := tree.Load(storage); err != nil { 455 t.Fatalf("err: %s", err) 456 } 457 458 if err := tree.Validate(); err == nil { 459 t.Fatal("should error") 460 } 461 } 462 463 func TestTreeValidate_badChildVar(t *testing.T) { 464 tree := NewTree("", testConfig(t, "validate-bad-var")) 465 466 storage := testStorage(t, nil) 467 storage.Mode = GetModeGet 468 if err := tree.Load(storage); err != nil { 469 t.Fatalf("err: %s", err) 470 } 471 472 if err := tree.Validate(); err == nil { 473 t.Fatal("should error") 474 } 475 } 476 477 func TestTreeValidate_badRoot(t *testing.T) { 478 tree := NewTree("", testConfig(t, "validate-root-bad")) 479 480 storage := testStorage(t, nil) 481 storage.Mode = GetModeGet 482 if err := tree.Load(storage); err != nil { 483 t.Fatalf("err: %s", err) 484 } 485 486 if err := tree.Validate(); err == nil { 487 t.Fatal("should error") 488 } 489 } 490 491 func TestTreeValidate_good(t *testing.T) { 492 tree := NewTree("", testConfig(t, "validate-child-good")) 493 494 storage := testStorage(t, nil) 495 storage.Mode = GetModeGet 496 if err := tree.Load(storage); err != nil { 497 t.Fatalf("err: %s", err) 498 } 499 500 if err := tree.Validate(); err != nil { 501 t.Fatalf("err: %s", err) 502 } 503 } 504 505 func TestTreeValidate_notLoaded(t *testing.T) { 506 tree := NewTree("", testConfig(t, "basic")) 507 508 if err := tree.Validate(); err == nil { 509 t.Fatal("should error") 510 } 511 } 512 513 func TestTreeValidate_requiredChildVar(t *testing.T) { 514 tree := NewTree("", testConfig(t, "validate-required-var")) 515 516 storage := testStorage(t, nil) 517 storage.Mode = GetModeGet 518 if err := tree.Load(storage); err != nil { 519 t.Fatalf("err: %s", err) 520 } 521 522 diags := tree.Validate() 523 if !diags.HasErrors() { 524 t.Fatal("should error") 525 } 526 527 // ensure both variables are mentioned in the output 528 errMsg := diags.Err().Error() 529 for _, v := range []string{"feature", "memory"} { 530 if !strings.Contains(errMsg, v) { 531 t.Fatalf("no mention of missing variable %q", v) 532 } 533 } 534 } 535 536 func TestTreeValidate_unknownModule(t *testing.T) { 537 tree := NewTree("", testConfig(t, "validate-module-unknown")) 538 539 storage := testStorage(t, nil) 540 storage.Mode = GetModeNone 541 if err := tree.Load(storage); err != nil { 542 t.Fatalf("err: %s", err) 543 } 544 545 if err := tree.Validate(); err == nil { 546 t.Fatal("should error") 547 } 548 } 549 550 func TestTreeLoad_conflictingSubmoduleNames(t *testing.T) { 551 storage := testStorage(t, nil) 552 tree := NewTree("", testConfig(t, "conficting-submodule-names")) 553 554 storage.Mode = GetModeGet 555 if err := tree.Load(storage); err != nil { 556 t.Fatalf("load failed: %s", err) 557 } 558 559 if !tree.Loaded() { 560 t.Fatal("should be loaded") 561 } 562 563 // Try to reload 564 storage.Mode = GetModeNone 565 if err := tree.Load(storage); err != nil { 566 t.Fatalf("reload failed: %s", err) 567 } 568 569 // verify that the grand-children are correctly loaded 570 for _, c := range tree.Children() { 571 for _, gc := range c.Children() { 572 if len(gc.config.Resources) != 1 { 573 t.Fatalf("expected 1 resource in %s, got %d", gc.name, len(gc.config.Resources)) 574 } 575 res := gc.config.Resources[0] 576 switch gc.path[0] { 577 case "a": 578 if res.Name != "a-c" { 579 t.Fatal("found wrong resource in a/c:", res.Name) 580 } 581 case "b": 582 if res.Name != "b-c" { 583 t.Fatal("found wrong resource in b/c:", res.Name) 584 } 585 } 586 } 587 } 588 } 589 590 // changing the source for a module but not the module "path" 591 func TestTreeLoad_changeIntermediateSource(t *testing.T) { 592 // copy the config to our tempdir this time, since we're going to edit it 593 td, err := ioutil.TempDir("", "tf") 594 if err != nil { 595 t.Fatalf("err: %s", err) 596 } 597 defer os.RemoveAll(td) 598 599 if err := copyDir(td, filepath.Join(fixtureDir, "change-intermediate-source")); err != nil { 600 t.Fatal(err) 601 } 602 603 wd, err := os.Getwd() 604 if err != nil { 605 t.Fatal(err) 606 } 607 if err := os.Chdir(td); err != nil { 608 t.Fatal(err) 609 } 610 defer os.Chdir(wd) 611 612 if err := os.MkdirAll(".terraform/modules", 0777); err != nil { 613 t.Fatal(err) 614 } 615 storage := &Storage{StorageDir: ".terraform/modules"} 616 cfg, err := config.LoadDir("./") 617 if err != nil { 618 t.Fatal(err) 619 } 620 tree := NewTree("", cfg) 621 storage.Mode = GetModeGet 622 if err := tree.Load(storage); err != nil { 623 t.Fatalf("load failed: %s", err) 624 } 625 626 // now we change the source of our module, without changing its path 627 if err := os.Rename("main.tf.disabled", "main.tf"); err != nil { 628 t.Fatal(err) 629 } 630 631 // reload the tree 632 cfg, err = config.LoadDir("./") 633 if err != nil { 634 t.Fatal(err) 635 } 636 tree = NewTree("", cfg) 637 if err := tree.Load(storage); err != nil { 638 t.Fatalf("load failed: %s", err) 639 } 640 641 // check for our resource in b 642 for _, c := range tree.Children() { 643 for _, gc := range c.Children() { 644 if len(gc.config.Resources) != 1 { 645 t.Fatalf("expected 1 resource in %s, got %d", gc.name, len(gc.config.Resources)) 646 } 647 res := gc.config.Resources[0] 648 expected := "c-b" 649 if res.Name != expected { 650 t.Fatalf("expexted resource %q, got %q", expected, res.Name) 651 } 652 } 653 } 654 } 655 656 const treeLoadStr = ` 657 root 658 foo (path: foo) 659 ` 660 661 const treeLoadParentStr = ` 662 root 663 a (path: a) 664 b (path: a, b) 665 ` 666 const treeLoadSubdirStr = ` 667 root 668 foo (path: foo) 669 bar (path: foo, bar) 670 ` 671 672 const treeLoadRegistrySubdirStr = ` 673 root 674 foo (path: foo) 675 `