github.com/trawler/terraform@v0.10.8-0.20171106022149-4b1c7a1d9b48/config/loader_test.go (about) 1 package config 2 3 import ( 4 "io/ioutil" 5 "path/filepath" 6 "reflect" 7 "strings" 8 "testing" 9 ) 10 11 func TestErrNoConfigsFound_impl(t *testing.T) { 12 var _ error = new(ErrNoConfigsFound) 13 } 14 15 func TestIsEmptyDir(t *testing.T) { 16 val, err := IsEmptyDir(fixtureDir) 17 if err != nil { 18 t.Fatalf("err: %s", err) 19 } 20 if val { 21 t.Fatal("should not be empty") 22 } 23 } 24 25 func TestIsEmptyDir_noExist(t *testing.T) { 26 val, err := IsEmptyDir(filepath.Join(fixtureDir, "nopenopenope")) 27 if err != nil { 28 t.Fatalf("err: %s", err) 29 } 30 if !val { 31 t.Fatal("should be empty") 32 } 33 } 34 35 func TestIsEmptyDir_noConfigs(t *testing.T) { 36 val, err := IsEmptyDir(filepath.Join(fixtureDir, "dir-empty")) 37 if err != nil { 38 t.Fatalf("err: %s", err) 39 } 40 if !val { 41 t.Fatal("should be empty") 42 } 43 } 44 45 func TestLoadFile_badType(t *testing.T) { 46 _, err := LoadFile(filepath.Join(fixtureDir, "bad_type.tf.nope")) 47 if err == nil { 48 t.Fatal("should have error") 49 } 50 } 51 52 func TestLoadFile_gitCrypt(t *testing.T) { 53 _, err := LoadFile(filepath.Join(fixtureDir, "git-crypt.tf")) 54 if err == nil { 55 t.Fatal("should have error") 56 } 57 58 t.Logf("err: %s", err) 59 } 60 61 func TestLoadFile_lifecycleKeyCheck(t *testing.T) { 62 _, err := LoadFile(filepath.Join(fixtureDir, "lifecycle_cbd_typo.tf")) 63 if err == nil { 64 t.Fatal("should have error") 65 } 66 67 t.Logf("err: %s", err) 68 } 69 70 func TestLoadFile_varInvalidKey(t *testing.T) { 71 _, err := LoadFile(filepath.Join(fixtureDir, "var-invalid-key.tf")) 72 if err == nil { 73 t.Fatal("should have error") 74 } 75 } 76 77 func TestLoadFile_resourceArityMistake(t *testing.T) { 78 _, err := LoadFile(filepath.Join(fixtureDir, "resource-arity-mistake.tf")) 79 if err == nil { 80 t.Fatal("should have error") 81 } 82 expected := "Error loading test-fixtures/resource-arity-mistake.tf: position 2:10: resource must be followed by exactly two strings, a type and a name" 83 if err.Error() != expected { 84 t.Fatalf("expected:\n%s\ngot:\n%s", expected, err) 85 } 86 } 87 88 func TestLoadFile_resourceMultiLifecycle(t *testing.T) { 89 _, err := LoadFile(filepath.Join(fixtureDir, "resource-multi-lifecycle.tf")) 90 if err == nil { 91 t.Fatal("should have error") 92 } 93 } 94 95 func TestLoadFile_dataSourceArityMistake(t *testing.T) { 96 _, err := LoadFile(filepath.Join(fixtureDir, "data-source-arity-mistake.tf")) 97 if err == nil { 98 t.Fatal("should have error") 99 } 100 expected := "Error loading test-fixtures/data-source-arity-mistake.tf: position 2:6: 'data' must be followed by exactly two strings: a type and a name" 101 if err.Error() != expected { 102 t.Fatalf("expected:\n%s\ngot:\n%s", expected, err) 103 } 104 } 105 106 func TestLoadFileWindowsLineEndings(t *testing.T) { 107 testFile := filepath.Join(fixtureDir, "windows-line-endings.tf") 108 109 contents, err := ioutil.ReadFile(testFile) 110 if err != nil { 111 t.Fatalf("err: %s", err) 112 } 113 if !strings.Contains(string(contents), "\r\n") { 114 t.Fatalf("Windows line endings test file %s contains no windows line endings - this may be an autocrlf related issue.", testFile) 115 } 116 117 c, err := LoadFile(testFile) 118 if err != nil { 119 t.Fatalf("err: %s", err) 120 } 121 122 if c == nil { 123 t.Fatal("config should not be nil") 124 } 125 126 if c.Dir != "" { 127 t.Fatalf("bad: %#v", c.Dir) 128 } 129 130 actual := resourcesStr(c.Resources) 131 if actual != strings.TrimSpace(windowsHeredocResourcesStr) { 132 t.Fatalf("bad:\n%s", actual) 133 } 134 } 135 136 func TestLoadFileHeredoc(t *testing.T) { 137 c, err := LoadFile(filepath.Join(fixtureDir, "heredoc.tf")) 138 if err != nil { 139 t.Fatalf("err: %s", err) 140 } 141 142 if c == nil { 143 t.Fatal("config should not be nil") 144 } 145 146 if c.Dir != "" { 147 t.Fatalf("bad: %#v", c.Dir) 148 } 149 150 actual := providerConfigsStr(c.ProviderConfigs) 151 if actual != strings.TrimSpace(heredocProvidersStr) { 152 t.Fatalf("bad:\n%s", actual) 153 } 154 155 actual = resourcesStr(c.Resources) 156 if actual != strings.TrimSpace(heredocResourcesStr) { 157 t.Fatalf("bad:\n%s", actual) 158 } 159 } 160 161 func TestLoadFileEscapedQuotes(t *testing.T) { 162 _, err := LoadFile(filepath.Join(fixtureDir, "escapedquotes.tf")) 163 if err == nil { 164 t.Fatalf("expected syntax error as escaped quotes are no longer supported") 165 } 166 167 if !strings.Contains(err.Error(), "parse error") { 168 t.Fatalf("expected \"syntax error\", got: %s", err) 169 } 170 } 171 172 func TestLoadFileBasic(t *testing.T) { 173 c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf")) 174 if err != nil { 175 t.Fatalf("err: %s", err) 176 } 177 178 if c == nil { 179 t.Fatal("config should not be nil") 180 } 181 182 if c.Dir != "" { 183 t.Fatalf("wrong dir %#v; want %#v", c.Dir, "") 184 } 185 186 expectedTF := &Terraform{RequiredVersion: "foo"} 187 if !reflect.DeepEqual(c.Terraform, expectedTF) { 188 t.Fatalf("wrong terraform block %#v; want %#v", c.Terraform, expectedTF) 189 } 190 191 expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"} 192 if !reflect.DeepEqual(c.Atlas, expectedAtlas) { 193 t.Fatalf("wrong atlas config %#v; want %#v", c.Atlas, expectedAtlas) 194 } 195 196 actual := variablesStr(c.Variables) 197 if actual != strings.TrimSpace(basicVariablesStr) { 198 t.Fatalf("bad:\n%s", actual) 199 } 200 201 actual = providerConfigsStr(c.ProviderConfigs) 202 if actual != strings.TrimSpace(basicProvidersStr) { 203 t.Fatalf("bad:\n%s", actual) 204 } 205 206 actual = resourcesStr(c.Resources) 207 if actual != strings.TrimSpace(basicResourcesStr) { 208 t.Fatalf("bad:\n%s", actual) 209 } 210 211 if actual, want := localsStr(c.Locals), strings.TrimSpace(basicLocalsStr); actual != want { 212 t.Fatalf("wrong locals:\n%s\nwant:\n%s", actual, want) 213 } 214 215 actual = outputsStr(c.Outputs) 216 if actual != strings.TrimSpace(basicOutputsStr) { 217 t.Fatalf("bad:\n%s", actual) 218 } 219 } 220 221 func TestLoadFileBasic_empty(t *testing.T) { 222 c, err := LoadFile(filepath.Join(fixtureDir, "empty.tf")) 223 if err != nil { 224 t.Fatalf("err: %s", err) 225 } 226 227 if c == nil { 228 t.Fatal("config should not be nil") 229 } 230 } 231 232 func TestLoadFileBasic_import(t *testing.T) { 233 // Skip because we disabled importing 234 t.Skip() 235 236 c, err := LoadFile(filepath.Join(fixtureDir, "import.tf")) 237 if err != nil { 238 t.Fatalf("err: %s", err) 239 } 240 241 if c == nil { 242 t.Fatal("config should not be nil") 243 } 244 245 actual := variablesStr(c.Variables) 246 if actual != strings.TrimSpace(importVariablesStr) { 247 t.Fatalf("bad:\n%s", actual) 248 } 249 250 actual = providerConfigsStr(c.ProviderConfigs) 251 if actual != strings.TrimSpace(importProvidersStr) { 252 t.Fatalf("bad:\n%s", actual) 253 } 254 255 actual = resourcesStr(c.Resources) 256 if actual != strings.TrimSpace(importResourcesStr) { 257 t.Fatalf("bad:\n%s", actual) 258 } 259 } 260 261 func TestLoadFileBasic_json(t *testing.T) { 262 c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf.json")) 263 if err != nil { 264 t.Fatalf("err: %s", err) 265 } 266 267 if c == nil { 268 t.Fatal("config should not be nil") 269 } 270 271 if c.Dir != "" { 272 t.Fatalf("bad: %#v", c.Dir) 273 } 274 275 expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"} 276 if !reflect.DeepEqual(c.Atlas, expectedAtlas) { 277 t.Fatalf("bad: %#v", c.Atlas) 278 } 279 280 actual := variablesStr(c.Variables) 281 if actual != strings.TrimSpace(basicVariablesStr) { 282 t.Fatalf("bad:\n%s", actual) 283 } 284 285 actual = providerConfigsStr(c.ProviderConfigs) 286 if actual != strings.TrimSpace(basicProvidersStr) { 287 t.Fatalf("bad:\n%s", actual) 288 } 289 290 actual = resourcesStr(c.Resources) 291 if actual != strings.TrimSpace(basicResourcesStr) { 292 t.Fatalf("bad:\n%s", actual) 293 } 294 295 if actual, want := localsStr(c.Locals), strings.TrimSpace(basicLocalsStr); actual != want { 296 t.Fatalf("wrong locals:\n%s\nwant:\n%s", actual, want) 297 } 298 299 actual = outputsStr(c.Outputs) 300 if actual != strings.TrimSpace(basicOutputsStr) { 301 t.Fatalf("bad:\n%s", actual) 302 } 303 } 304 305 func TestLoadFileBasic_modules(t *testing.T) { 306 c, err := LoadFile(filepath.Join(fixtureDir, "modules.tf")) 307 if err != nil { 308 t.Fatalf("err: %s", err) 309 } 310 311 if c == nil { 312 t.Fatal("config should not be nil") 313 } 314 315 if c.Dir != "" { 316 t.Fatalf("bad: %#v", c.Dir) 317 } 318 319 actual := modulesStr(c.Modules) 320 if actual != strings.TrimSpace(modulesModulesStr) { 321 t.Fatalf("bad:\n%s", actual) 322 } 323 } 324 325 func TestLoadFile_unnamedModule(t *testing.T) { 326 _, err := LoadFile(filepath.Join(fixtureDir, "module-unnamed.tf")) 327 if err == nil { 328 t.Fatalf("bad: expected error") 329 } 330 331 errorStr := err.Error() 332 if !strings.Contains(errorStr, `"module" must be followed`) { 333 t.Fatalf("bad: expected error has wrong text: %s", errorStr) 334 } 335 } 336 337 func TestLoadFile_outputDependsOn(t *testing.T) { 338 c, err := LoadFile(filepath.Join(fixtureDir, "output-depends-on.tf")) 339 if err != nil { 340 t.Fatalf("err: %s", err) 341 } 342 343 if c == nil { 344 t.Fatal("config should not be nil") 345 } 346 347 if c.Dir != "" { 348 t.Fatalf("bad: %#v", c.Dir) 349 } 350 351 actual := outputsStr(c.Outputs) 352 if actual != strings.TrimSpace(outputDependsOnStr) { 353 t.Fatalf("bad:\n%s", actual) 354 } 355 } 356 357 func TestLoadFile_terraformBackend(t *testing.T) { 358 c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend.tf")) 359 if err != nil { 360 t.Fatalf("err: %s", err) 361 } 362 363 if c == nil { 364 t.Fatal("config should not be nil") 365 } 366 367 if c.Dir != "" { 368 t.Fatalf("bad: %#v", c.Dir) 369 } 370 371 { 372 actual := terraformStr(c.Terraform) 373 expected := strings.TrimSpace(` 374 backend (s3) 375 foo`) 376 if actual != expected { 377 t.Fatalf("bad:\n%s", actual) 378 } 379 } 380 } 381 382 func TestLoadFile_terraformBackendJSON(t *testing.T) { 383 c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend.tf.json")) 384 if err != nil { 385 t.Fatalf("err: %s", err) 386 } 387 388 if c == nil { 389 t.Fatal("config should not be nil") 390 } 391 392 if c.Dir != "" { 393 t.Fatalf("bad: %#v", c.Dir) 394 } 395 396 { 397 actual := terraformStr(c.Terraform) 398 expected := strings.TrimSpace(` 399 backend (s3) 400 foo`) 401 if actual != expected { 402 t.Fatalf("bad:\n%s", actual) 403 } 404 } 405 } 406 407 // test that the alternate, more obvious JSON format also decodes properly 408 func TestLoadFile_terraformBackendJSON2(t *testing.T) { 409 c, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend-2.tf.json")) 410 if err != nil { 411 t.Fatalf("err: %s", err) 412 } 413 414 if c == nil { 415 t.Fatal("config should not be nil") 416 } 417 418 if c.Dir != "" { 419 t.Fatalf("bad: %#v", c.Dir) 420 } 421 422 { 423 actual := terraformStr(c.Terraform) 424 expected := strings.TrimSpace(` 425 backend (s3) 426 foo`) 427 if actual != expected { 428 t.Fatalf("bad:\n%s", actual) 429 } 430 } 431 } 432 433 func TestLoadFile_terraformBackendMulti(t *testing.T) { 434 _, err := LoadFile(filepath.Join(fixtureDir, "terraform-backend-multi.tf")) 435 if err == nil { 436 t.Fatal("expected error") 437 } 438 439 errorStr := err.Error() 440 if !strings.Contains(errorStr, "only one 'backend'") { 441 t.Fatalf("bad: expected error has wrong text: %s", errorStr) 442 } 443 } 444 445 func TestLoadJSONBasic(t *testing.T) { 446 raw, err := ioutil.ReadFile(filepath.Join(fixtureDir, "basic.tf.json")) 447 if err != nil { 448 t.Fatalf("err: %s", err) 449 } 450 451 c, err := LoadJSON(raw) 452 if err != nil { 453 t.Fatalf("err: %s", err) 454 } 455 456 if c == nil { 457 t.Fatal("config should not be nil") 458 } 459 460 if c.Dir != "" { 461 t.Fatalf("bad: %#v", c.Dir) 462 } 463 464 expectedAtlas := &AtlasConfig{Name: "mitchellh/foo"} 465 if !reflect.DeepEqual(c.Atlas, expectedAtlas) { 466 t.Fatalf("bad: %#v", c.Atlas) 467 } 468 469 actual := variablesStr(c.Variables) 470 if actual != strings.TrimSpace(basicVariablesStr) { 471 t.Fatalf("bad:\n%s", actual) 472 } 473 474 actual = providerConfigsStr(c.ProviderConfigs) 475 if actual != strings.TrimSpace(basicProvidersStr) { 476 t.Fatalf("bad:\n%s", actual) 477 } 478 479 actual = resourcesStr(c.Resources) 480 if actual != strings.TrimSpace(basicResourcesStr) { 481 t.Fatalf("bad:\n%s", actual) 482 } 483 484 actual = outputsStr(c.Outputs) 485 if actual != strings.TrimSpace(basicOutputsStr) { 486 t.Fatalf("bad:\n%s", actual) 487 } 488 } 489 490 func TestLoadJSONAmbiguous(t *testing.T) { 491 js := ` 492 { 493 "variable": { 494 "first": { 495 "default": { 496 "key": "val" 497 } 498 }, 499 "second": { 500 "description": "Described", 501 "default": { 502 "key": "val" 503 } 504 } 505 } 506 } 507 ` 508 509 c, err := LoadJSON([]byte(js)) 510 if err != nil { 511 t.Fatalf("err: %s", err) 512 } 513 514 if len(c.Variables) != 2 { 515 t.Fatal("config should have 2 variables, found", len(c.Variables)) 516 } 517 518 first := &Variable{ 519 Name: "first", 520 Default: map[string]interface{}{"key": "val"}, 521 } 522 second := &Variable{ 523 Name: "second", 524 Description: "Described", 525 Default: map[string]interface{}{"key": "val"}, 526 } 527 528 if !reflect.DeepEqual(first, c.Variables[0]) { 529 t.Fatalf("\nexpected: %#v\ngot: %#v", first, c.Variables[0]) 530 } 531 532 if !reflect.DeepEqual(second, c.Variables[1]) { 533 t.Fatalf("\nexpected: %#v\ngot: %#v", second, c.Variables[1]) 534 } 535 } 536 537 func TestLoadFileBasic_jsonNoName(t *testing.T) { 538 c, err := LoadFile(filepath.Join(fixtureDir, "resource-no-name.tf.json")) 539 if err != nil { 540 t.Fatalf("err: %s", err) 541 } 542 543 if c == nil { 544 t.Fatal("config should not be nil") 545 } 546 547 actual := resourcesStr(c.Resources) 548 if actual != strings.TrimSpace(basicJsonNoNameResourcesStr) { 549 t.Fatalf("bad:\n%s", actual) 550 } 551 } 552 553 func TestLoadFile_variables(t *testing.T) { 554 c, err := LoadFile(filepath.Join(fixtureDir, "variables.tf")) 555 if err != nil { 556 t.Fatalf("err: %s", err) 557 } 558 if c == nil { 559 t.Fatal("config should not be nil") 560 } 561 562 if c.Dir != "" { 563 t.Fatalf("bad: %#v", c.Dir) 564 } 565 566 actual := variablesStr(c.Variables) 567 if actual != strings.TrimSpace(variablesVariablesStr) { 568 t.Fatalf("bad:\n%s", actual) 569 } 570 } 571 572 func TestLoadDir_basic(t *testing.T) { 573 dir := filepath.Join(fixtureDir, "dir-basic") 574 c, err := LoadDir(dir) 575 if err != nil { 576 t.Fatalf("err: %s", err) 577 } 578 579 if c == nil { 580 t.Fatal("config should not be nil") 581 } 582 583 dirAbs, err := filepath.Abs(dir) 584 if err != nil { 585 t.Fatalf("err: %s", err) 586 } 587 if c.Dir != dirAbs { 588 t.Fatalf("bad: %#v", c.Dir) 589 } 590 591 actual := variablesStr(c.Variables) 592 if actual != strings.TrimSpace(dirBasicVariablesStr) { 593 t.Fatalf("bad:\n%s", actual) 594 } 595 596 actual = providerConfigsStr(c.ProviderConfigs) 597 if actual != strings.TrimSpace(dirBasicProvidersStr) { 598 t.Fatalf("bad:\n%s", actual) 599 } 600 601 actual = resourcesStr(c.Resources) 602 if actual != strings.TrimSpace(dirBasicResourcesStr) { 603 t.Fatalf("bad:\n%s", actual) 604 } 605 606 actual = outputsStr(c.Outputs) 607 if actual != strings.TrimSpace(dirBasicOutputsStr) { 608 t.Fatalf("bad:\n%s", actual) 609 } 610 } 611 612 func TestLoadDir_file(t *testing.T) { 613 _, err := LoadDir(filepath.Join(fixtureDir, "variables.tf")) 614 if err == nil { 615 t.Fatal("should error") 616 } 617 } 618 619 func TestLoadDir_noConfigs(t *testing.T) { 620 _, err := LoadDir(filepath.Join(fixtureDir, "dir-empty")) 621 if err == nil { 622 t.Fatal("should error") 623 } 624 } 625 626 func TestLoadDir_noMerge(t *testing.T) { 627 c, err := LoadDir(filepath.Join(fixtureDir, "dir-merge")) 628 if err != nil { 629 t.Fatalf("err: %s", err) 630 } 631 632 if c == nil { 633 t.Fatal("config should not be nil") 634 } 635 636 if err := c.Validate(); err == nil { 637 t.Fatal("should not be valid") 638 } 639 } 640 641 func TestLoadDir_override(t *testing.T) { 642 c, err := LoadDir(filepath.Join(fixtureDir, "dir-override")) 643 if err != nil { 644 t.Fatalf("err: %s", err) 645 } 646 647 if c == nil { 648 t.Fatal("config should not be nil") 649 } 650 651 actual := variablesStr(c.Variables) 652 if actual != strings.TrimSpace(dirOverrideVariablesStr) { 653 t.Fatalf("bad:\n%s", actual) 654 } 655 656 actual = providerConfigsStr(c.ProviderConfigs) 657 if actual != strings.TrimSpace(dirOverrideProvidersStr) { 658 t.Fatalf("bad:\n%s", actual) 659 } 660 661 actual = resourcesStr(c.Resources) 662 if actual != strings.TrimSpace(dirOverrideResourcesStr) { 663 t.Fatalf("bad:\n%s", actual) 664 } 665 666 actual = outputsStr(c.Outputs) 667 if actual != strings.TrimSpace(dirOverrideOutputsStr) { 668 t.Fatalf("bad:\n%s", actual) 669 } 670 } 671 672 func TestLoadDir_overrideVar(t *testing.T) { 673 c, err := LoadDir(filepath.Join(fixtureDir, "dir-override-var")) 674 if err != nil { 675 t.Fatalf("err: %s", err) 676 } 677 678 if c == nil { 679 t.Fatal("config should not be nil") 680 } 681 682 actual := variablesStr(c.Variables) 683 if actual != strings.TrimSpace(dirOverrideVarsVariablesStr) { 684 t.Fatalf("bad:\n%s", actual) 685 } 686 } 687 688 func TestLoadFile_mismatchedVariableTypes(t *testing.T) { 689 _, err := LoadFile(filepath.Join(fixtureDir, "variable-mismatched-type.tf")) 690 if err == nil { 691 t.Fatalf("bad: expected error") 692 } 693 694 errorStr := err.Error() 695 if !strings.Contains(errorStr, "'not_a_map' has a default value which is not of type 'string'") { 696 t.Fatalf("bad: expected error has wrong text: %s", errorStr) 697 } 698 } 699 700 func TestLoadFile_badVariableTypes(t *testing.T) { 701 _, err := LoadFile(filepath.Join(fixtureDir, "bad-variable-type.tf")) 702 if err == nil { 703 t.Fatalf("bad: expected error") 704 } 705 706 errorStr := err.Error() 707 if !strings.Contains(errorStr, "'bad_type' type must be one of") { 708 t.Fatalf("bad: expected error has wrong text: %s", errorStr) 709 } 710 } 711 712 func TestLoadFile_variableNoName(t *testing.T) { 713 _, err := LoadFile(filepath.Join(fixtureDir, "variable-no-name.tf")) 714 if err == nil { 715 t.Fatalf("bad: expected error") 716 } 717 718 errorStr := err.Error() 719 if !strings.Contains(errorStr, `"variable" must be followed`) { 720 t.Fatalf("bad: expected error has wrong text: %s", errorStr) 721 } 722 } 723 724 func TestLoadFile_provisioners(t *testing.T) { 725 c, err := LoadFile(filepath.Join(fixtureDir, "provisioners.tf")) 726 if err != nil { 727 t.Fatalf("err: %s", err) 728 } 729 730 if c == nil { 731 t.Fatal("config should not be nil") 732 } 733 734 actual := resourcesStr(c.Resources) 735 if actual != strings.TrimSpace(provisionerResourcesStr) { 736 t.Fatalf("bad:\n%s", actual) 737 } 738 } 739 740 func TestLoadFile_provisionersDestroy(t *testing.T) { 741 c, err := LoadFile(filepath.Join(fixtureDir, "provisioners-destroy.tf")) 742 if err != nil { 743 t.Fatalf("err: %s", err) 744 } 745 746 if c == nil { 747 t.Fatal("config should not be nil") 748 } 749 750 actual := resourcesStr(c.Resources) 751 if actual != strings.TrimSpace(provisionerDestroyResourcesStr) { 752 t.Fatalf("bad:\n%s", actual) 753 } 754 } 755 756 func TestLoadFile_unnamedOutput(t *testing.T) { 757 _, err := LoadFile(filepath.Join(fixtureDir, "output-unnamed.tf")) 758 if err == nil { 759 t.Fatalf("bad: expected error") 760 } 761 762 errorStr := err.Error() 763 if !strings.Contains(errorStr, `"output" must be followed`) { 764 t.Fatalf("bad: expected error has wrong text: %s", errorStr) 765 } 766 } 767 768 func TestLoadFile_connections(t *testing.T) { 769 c, err := LoadFile(filepath.Join(fixtureDir, "connection.tf")) 770 if err != nil { 771 t.Fatalf("err: %s", err) 772 } 773 774 if c == nil { 775 t.Fatal("config should not be nil") 776 } 777 778 actual := resourcesStr(c.Resources) 779 if actual != strings.TrimSpace(connectionResourcesStr) { 780 t.Fatalf("bad:\n%s", actual) 781 } 782 783 // Check for the connection info 784 r := c.Resources[0] 785 if r.Name != "web" && r.Type != "aws_instance" { 786 t.Fatalf("Bad: %#v", r) 787 } 788 789 p1 := r.Provisioners[0] 790 if p1.ConnInfo == nil || len(p1.ConnInfo.Raw) != 2 { 791 t.Fatalf("Bad: %#v", p1.ConnInfo) 792 } 793 if p1.ConnInfo.Raw["user"] != "nobody" { 794 t.Fatalf("Bad: %#v", p1.ConnInfo) 795 } 796 797 p2 := r.Provisioners[1] 798 if p2.ConnInfo == nil || len(p2.ConnInfo.Raw) != 2 { 799 t.Fatalf("Bad: %#v", p2.ConnInfo) 800 } 801 if p2.ConnInfo.Raw["user"] != "root" { 802 t.Fatalf("Bad: %#v", p2.ConnInfo) 803 } 804 } 805 806 func TestLoadFile_createBeforeDestroy(t *testing.T) { 807 c, err := LoadFile(filepath.Join(fixtureDir, "create-before-destroy.tf")) 808 if err != nil { 809 t.Fatalf("err: %s", err) 810 } 811 812 if c == nil { 813 t.Fatal("config should not be nil") 814 } 815 816 actual := resourcesStr(c.Resources) 817 if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) { 818 t.Fatalf("bad:\n%s", actual) 819 } 820 821 // Check for the flag value 822 r := c.Resources[0] 823 if r.Name != "web" && r.Type != "aws_instance" { 824 t.Fatalf("Bad: %#v", r) 825 } 826 827 // Should enable create before destroy 828 if !r.Lifecycle.CreateBeforeDestroy { 829 t.Fatalf("Bad: %#v", r) 830 } 831 832 r = c.Resources[1] 833 if r.Name != "bar" && r.Type != "aws_instance" { 834 t.Fatalf("Bad: %#v", r) 835 } 836 837 // Should not enable create before destroy 838 if r.Lifecycle.CreateBeforeDestroy { 839 t.Fatalf("Bad: %#v", r) 840 } 841 } 842 843 func TestLoadFile_ignoreChanges(t *testing.T) { 844 c, err := LoadFile(filepath.Join(fixtureDir, "ignore-changes.tf")) 845 if err != nil { 846 t.Fatalf("err: %s", err) 847 } 848 849 if c == nil { 850 t.Fatal("config should not be nil") 851 } 852 853 actual := resourcesStr(c.Resources) 854 print(actual) 855 if actual != strings.TrimSpace(ignoreChangesResourcesStr) { 856 t.Fatalf("bad:\n%s", actual) 857 } 858 859 // Check for the flag value 860 r := c.Resources[0] 861 if r.Name != "web" && r.Type != "aws_instance" { 862 t.Fatalf("Bad: %#v", r) 863 } 864 865 // Should populate ignore changes 866 if len(r.Lifecycle.IgnoreChanges) == 0 { 867 t.Fatalf("Bad: %#v", r) 868 } 869 870 r = c.Resources[1] 871 if r.Name != "bar" && r.Type != "aws_instance" { 872 t.Fatalf("Bad: %#v", r) 873 } 874 875 // Should not populate ignore changes 876 if len(r.Lifecycle.IgnoreChanges) > 0 { 877 t.Fatalf("Bad: %#v", r) 878 } 879 880 r = c.Resources[2] 881 if r.Name != "baz" && r.Type != "aws_instance" { 882 t.Fatalf("Bad: %#v", r) 883 } 884 885 // Should not populate ignore changes 886 if len(r.Lifecycle.IgnoreChanges) > 0 { 887 t.Fatalf("Bad: %#v", r) 888 } 889 } 890 891 func TestLoad_preventDestroyString(t *testing.T) { 892 c, err := LoadFile(filepath.Join(fixtureDir, "prevent-destroy-string.tf")) 893 if err != nil { 894 t.Fatalf("err: %s", err) 895 } 896 897 if c == nil { 898 t.Fatal("config should not be nil") 899 } 900 901 actual := resourcesStr(c.Resources) 902 if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) { 903 t.Fatalf("bad:\n%s", actual) 904 } 905 906 // Check for the flag value 907 r := c.Resources[0] 908 if r.Name != "web" && r.Type != "aws_instance" { 909 t.Fatalf("Bad: %#v", r) 910 } 911 912 // Should enable create before destroy 913 if !r.Lifecycle.PreventDestroy { 914 t.Fatalf("Bad: %#v", r) 915 } 916 917 r = c.Resources[1] 918 if r.Name != "bar" && r.Type != "aws_instance" { 919 t.Fatalf("Bad: %#v", r) 920 } 921 922 // Should not enable create before destroy 923 if r.Lifecycle.PreventDestroy { 924 t.Fatalf("Bad: %#v", r) 925 } 926 } 927 928 func TestLoad_temporary_files(t *testing.T) { 929 _, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files")) 930 if err == nil { 931 t.Fatalf("Expected to see an error stating no config files found") 932 } 933 } 934 935 func TestLoad_hclAttributes(t *testing.T) { 936 c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf")) 937 if err != nil { 938 t.Fatalf("Bad: %s", err) 939 } 940 941 if c == nil { 942 t.Fatal("config should not be nil") 943 } 944 945 actual := resourcesStr(c.Resources) 946 print(actual) 947 if actual != strings.TrimSpace(jsonAttributeStr) { 948 t.Fatalf("bad:\n%s", actual) 949 } 950 951 r := c.Resources[0] 952 if r.Name != "test" && r.Type != "cloudstack_firewall" { 953 t.Fatalf("Bad: %#v", r) 954 } 955 956 raw := r.RawConfig 957 if raw.Raw["ipaddress"] != "192.168.0.1" { 958 t.Fatalf("Bad: %s", raw.Raw["ipAddress"]) 959 } 960 961 rule := raw.Raw["rule"].([]map[string]interface{})[0] 962 if rule["protocol"] != "tcp" { 963 t.Fatalf("Bad: %s", rule["protocol"]) 964 } 965 966 if rule["source_cidr"] != "10.0.0.0/8" { 967 t.Fatalf("Bad: %s", rule["source_cidr"]) 968 } 969 970 ports := rule["ports"].([]interface{}) 971 972 if ports[0] != "80" { 973 t.Fatalf("Bad ports: %s", ports[0]) 974 } 975 if ports[1] != "1000-2000" { 976 t.Fatalf("Bad ports: %s", ports[1]) 977 } 978 } 979 980 func TestLoad_jsonAttributes(t *testing.T) { 981 c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf.json")) 982 if err != nil { 983 t.Fatalf("Bad: %s", err) 984 } 985 986 if c == nil { 987 t.Fatal("config should not be nil") 988 } 989 990 actual := resourcesStr(c.Resources) 991 print(actual) 992 if actual != strings.TrimSpace(jsonAttributeStr) { 993 t.Fatalf("bad:\n%s", actual) 994 } 995 996 r := c.Resources[0] 997 if r.Name != "test" && r.Type != "cloudstack_firewall" { 998 t.Fatalf("Bad: %#v", r) 999 } 1000 1001 raw := r.RawConfig 1002 if raw.Raw["ipaddress"] != "192.168.0.1" { 1003 t.Fatalf("Bad: %s", raw.Raw["ipAddress"]) 1004 } 1005 1006 rule := raw.Raw["rule"].([]map[string]interface{})[0] 1007 if rule["protocol"] != "tcp" { 1008 t.Fatalf("Bad: %s", rule["protocol"]) 1009 } 1010 1011 if rule["source_cidr"] != "10.0.0.0/8" { 1012 t.Fatalf("Bad: %s", rule["source_cidr"]) 1013 } 1014 1015 ports := rule["ports"].([]interface{}) 1016 1017 if ports[0] != "80" { 1018 t.Fatalf("Bad ports: %s", ports[0]) 1019 } 1020 if ports[1] != "1000-2000" { 1021 t.Fatalf("Bad ports: %s", ports[1]) 1022 } 1023 } 1024 1025 func TestLoad_onlyOverride(t *testing.T) { 1026 c, err := LoadDir(filepath.Join(fixtureDir, "dir-only-override")) 1027 if err != nil { 1028 t.Fatalf("err: %s", err) 1029 } 1030 1031 if c == nil { 1032 t.Fatal("config should not be nil") 1033 } 1034 1035 actual := variablesStr(c.Variables) 1036 if actual != strings.TrimSpace(dirOnlyOverrideVariablesStr) { 1037 t.Fatalf("bad:\n%s", actual) 1038 } 1039 } 1040 1041 const jsonAttributeStr = ` 1042 cloudstack_firewall.test (x1) 1043 ipaddress 1044 rule 1045 ` 1046 1047 const windowsHeredocResourcesStr = ` 1048 aws_instance.test (x1) 1049 user_data 1050 ` 1051 1052 const heredocProvidersStr = ` 1053 aws 1054 access_key 1055 secret_key 1056 ` 1057 1058 const heredocResourcesStr = ` 1059 aws_iam_policy.policy (x1) 1060 description 1061 name 1062 path 1063 policy 1064 aws_instance.heredocwithnumbers (x1) 1065 ami 1066 provisioners 1067 local-exec 1068 command 1069 aws_instance.test (x1) 1070 ami 1071 provisioners 1072 remote-exec 1073 inline 1074 ` 1075 1076 const basicOutputsStr = ` 1077 web_id 1078 vars 1079 resource: aws_instance.web.id 1080 description 1081 The ID 1082 web_ip 1083 vars 1084 resource: aws_instance.web.private_ip 1085 ` 1086 1087 const basicLocalsStr = ` 1088 literal 1089 literal_list 1090 literal_map 1091 security_group_ids 1092 vars 1093 resource: aws_security_group.firewall.*.id 1094 web_ip 1095 vars 1096 resource: aws_instance.web.private_ip 1097 ` 1098 1099 const basicProvidersStr = ` 1100 aws 1101 access_key 1102 secret_key 1103 do 1104 api_key 1105 vars 1106 user: var.foo 1107 ` 1108 1109 const basicResourcesStr = ` 1110 aws_instance.db (x1) 1111 VPC 1112 security_groups 1113 provisioners 1114 file 1115 destination 1116 source 1117 dependsOn 1118 aws_instance.web 1119 vars 1120 resource: aws_security_group.firewall.*.id 1121 aws_instance.web (x1) 1122 ami 1123 network_interface 1124 security_groups 1125 provisioners 1126 file 1127 destination 1128 source 1129 vars 1130 resource: aws_security_group.firewall.foo 1131 user: var.foo 1132 aws_security_group.firewall (x5) 1133 data.do.depends (x1) 1134 dependsOn 1135 data.do.simple 1136 data.do.simple (x1) 1137 foo 1138 ` 1139 1140 const basicVariablesStr = ` 1141 bar (required) (string) 1142 <> 1143 <> 1144 baz (map) 1145 map[key:value] 1146 <> 1147 foo 1148 bar 1149 bar 1150 ` 1151 1152 const basicJsonNoNameResourcesStr = ` 1153 aws_security_group.allow_external_http_https (x1) 1154 tags 1155 ` 1156 1157 const dirBasicOutputsStr = ` 1158 web_ip 1159 vars 1160 resource: aws_instance.web.private_ip 1161 ` 1162 1163 const dirBasicProvidersStr = ` 1164 aws 1165 access_key 1166 secret_key 1167 do 1168 api_key 1169 vars 1170 user: var.foo 1171 ` 1172 1173 const dirBasicResourcesStr = ` 1174 aws_instance.db (x1) 1175 security_groups 1176 vars 1177 resource: aws_security_group.firewall.*.id 1178 aws_instance.web (x1) 1179 ami 1180 network_interface 1181 security_groups 1182 vars 1183 resource: aws_security_group.firewall.foo 1184 user: var.foo 1185 aws_security_group.firewall (x5) 1186 data.do.depends (x1) 1187 dependsOn 1188 data.do.simple 1189 data.do.simple (x1) 1190 foo 1191 ` 1192 1193 const dirBasicVariablesStr = ` 1194 foo 1195 bar 1196 bar 1197 ` 1198 1199 const dirOverrideOutputsStr = ` 1200 web_ip 1201 vars 1202 resource: aws_instance.web.private_ip 1203 ` 1204 1205 const dirOverrideProvidersStr = ` 1206 aws 1207 access_key 1208 secret_key 1209 do 1210 api_key 1211 vars 1212 user: var.foo 1213 ` 1214 1215 const dirOverrideResourcesStr = ` 1216 aws_instance.db (x1) 1217 ami 1218 security_groups 1219 aws_instance.web (x1) 1220 ami 1221 foo 1222 network_interface 1223 security_groups 1224 vars 1225 resource: aws_security_group.firewall.foo 1226 user: var.foo 1227 aws_security_group.firewall (x5) 1228 data.do.depends (x1) 1229 hello 1230 dependsOn 1231 data.do.simple 1232 data.do.simple (x1) 1233 foo 1234 ` 1235 1236 const dirOverrideVariablesStr = ` 1237 foo 1238 bar 1239 bar 1240 ` 1241 1242 const dirOverrideVarsVariablesStr = ` 1243 foo 1244 baz 1245 bar 1246 ` 1247 1248 const dirOnlyOverrideVariablesStr = ` 1249 foo 1250 bar 1251 bar 1252 ` 1253 1254 const importProvidersStr = ` 1255 aws 1256 bar 1257 foo 1258 ` 1259 1260 const importResourcesStr = ` 1261 aws_security_group.db (x1) 1262 aws_security_group.web (x1) 1263 ` 1264 1265 const importVariablesStr = ` 1266 bar (required) 1267 <> 1268 <> 1269 foo 1270 bar 1271 bar 1272 ` 1273 1274 const modulesModulesStr = ` 1275 bar 1276 source = baz 1277 memory 1278 ` 1279 1280 const provisionerResourcesStr = ` 1281 aws_instance.web (x1) 1282 ami 1283 security_groups 1284 provisioners 1285 shell 1286 path 1287 vars 1288 resource: aws_security_group.firewall.foo 1289 user: var.foo 1290 ` 1291 1292 const provisionerDestroyResourcesStr = ` 1293 aws_instance.web (x1) 1294 provisioners 1295 shell 1296 shell (destroy) 1297 path 1298 shell (destroy) 1299 on_failure = continue 1300 path 1301 ` 1302 1303 const connectionResourcesStr = ` 1304 aws_instance.web (x1) 1305 ami 1306 security_groups 1307 provisioners 1308 shell 1309 path 1310 shell 1311 path 1312 vars 1313 resource: aws_security_group.firewall.foo 1314 user: var.foo 1315 ` 1316 1317 const outputDependsOnStr = ` 1318 value 1319 dependsOn 1320 foo 1321 ` 1322 1323 const variablesVariablesStr = ` 1324 bar 1325 <> 1326 <> 1327 baz 1328 foo 1329 <> 1330 foo (required) 1331 <> 1332 <> 1333 ` 1334 1335 const createBeforeDestroyResourcesStr = ` 1336 aws_instance.bar (x1) 1337 ami 1338 aws_instance.web (x1) 1339 ami 1340 ` 1341 1342 const ignoreChangesResourcesStr = ` 1343 aws_instance.bar (x1) 1344 ami 1345 aws_instance.baz (x1) 1346 ami 1347 aws_instance.web (x1) 1348 ami 1349 `