github.com/medzin/terraform@v0.11.11/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 if actual != strings.TrimSpace(ignoreChangesResourcesStr) { 855 t.Fatalf("bad:\n%s", actual) 856 } 857 858 // Check for the flag value 859 r := c.Resources[0] 860 if r.Name != "web" && r.Type != "aws_instance" { 861 t.Fatalf("Bad: %#v", r) 862 } 863 864 // Should populate ignore changes 865 if len(r.Lifecycle.IgnoreChanges) == 0 { 866 t.Fatalf("Bad: %#v", r) 867 } 868 869 r = c.Resources[1] 870 if r.Name != "bar" && r.Type != "aws_instance" { 871 t.Fatalf("Bad: %#v", r) 872 } 873 874 // Should not populate ignore changes 875 if len(r.Lifecycle.IgnoreChanges) > 0 { 876 t.Fatalf("Bad: %#v", r) 877 } 878 879 r = c.Resources[2] 880 if r.Name != "baz" && r.Type != "aws_instance" { 881 t.Fatalf("Bad: %#v", r) 882 } 883 884 // Should not populate ignore changes 885 if len(r.Lifecycle.IgnoreChanges) > 0 { 886 t.Fatalf("Bad: %#v", r) 887 } 888 } 889 890 func TestLoad_preventDestroyString(t *testing.T) { 891 c, err := LoadFile(filepath.Join(fixtureDir, "prevent-destroy-string.tf")) 892 if err != nil { 893 t.Fatalf("err: %s", err) 894 } 895 896 if c == nil { 897 t.Fatal("config should not be nil") 898 } 899 900 actual := resourcesStr(c.Resources) 901 if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) { 902 t.Fatalf("bad:\n%s", actual) 903 } 904 905 // Check for the flag value 906 r := c.Resources[0] 907 if r.Name != "web" && r.Type != "aws_instance" { 908 t.Fatalf("Bad: %#v", r) 909 } 910 911 // Should enable create before destroy 912 if !r.Lifecycle.PreventDestroy { 913 t.Fatalf("Bad: %#v", r) 914 } 915 916 r = c.Resources[1] 917 if r.Name != "bar" && r.Type != "aws_instance" { 918 t.Fatalf("Bad: %#v", r) 919 } 920 921 // Should not enable create before destroy 922 if r.Lifecycle.PreventDestroy { 923 t.Fatalf("Bad: %#v", r) 924 } 925 } 926 927 func TestLoad_temporary_files(t *testing.T) { 928 _, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files")) 929 if err == nil { 930 t.Fatalf("Expected to see an error stating no config files found") 931 } 932 } 933 934 func TestLoad_hclAttributes(t *testing.T) { 935 c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf")) 936 if err != nil { 937 t.Fatalf("Bad: %s", err) 938 } 939 940 if c == nil { 941 t.Fatal("config should not be nil") 942 } 943 944 actual := resourcesStr(c.Resources) 945 if actual != strings.TrimSpace(jsonAttributeStr) { 946 t.Fatalf("bad:\n%s", actual) 947 } 948 949 r := c.Resources[0] 950 if r.Name != "test" && r.Type != "cloudstack_firewall" { 951 t.Fatalf("Bad: %#v", r) 952 } 953 954 raw := r.RawConfig 955 if raw.Raw["ipaddress"] != "192.168.0.1" { 956 t.Fatalf("Bad: %s", raw.Raw["ipAddress"]) 957 } 958 959 rule := raw.Raw["rule"].([]map[string]interface{})[0] 960 if rule["protocol"] != "tcp" { 961 t.Fatalf("Bad: %s", rule["protocol"]) 962 } 963 964 if rule["source_cidr"] != "10.0.0.0/8" { 965 t.Fatalf("Bad: %s", rule["source_cidr"]) 966 } 967 968 ports := rule["ports"].([]interface{}) 969 970 if ports[0] != "80" { 971 t.Fatalf("Bad ports: %s", ports[0]) 972 } 973 if ports[1] != "1000-2000" { 974 t.Fatalf("Bad ports: %s", ports[1]) 975 } 976 } 977 978 func TestLoad_jsonAttributes(t *testing.T) { 979 c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf.json")) 980 if err != nil { 981 t.Fatalf("Bad: %s", err) 982 } 983 984 if c == nil { 985 t.Fatal("config should not be nil") 986 } 987 988 actual := resourcesStr(c.Resources) 989 if actual != strings.TrimSpace(jsonAttributeStr) { 990 t.Fatalf("bad:\n%s", actual) 991 } 992 993 r := c.Resources[0] 994 if r.Name != "test" && r.Type != "cloudstack_firewall" { 995 t.Fatalf("Bad: %#v", r) 996 } 997 998 raw := r.RawConfig 999 if raw.Raw["ipaddress"] != "192.168.0.1" { 1000 t.Fatalf("Bad: %s", raw.Raw["ipAddress"]) 1001 } 1002 1003 rule := raw.Raw["rule"].([]map[string]interface{})[0] 1004 if rule["protocol"] != "tcp" { 1005 t.Fatalf("Bad: %s", rule["protocol"]) 1006 } 1007 1008 if rule["source_cidr"] != "10.0.0.0/8" { 1009 t.Fatalf("Bad: %s", rule["source_cidr"]) 1010 } 1011 1012 ports := rule["ports"].([]interface{}) 1013 1014 if ports[0] != "80" { 1015 t.Fatalf("Bad ports: %s", ports[0]) 1016 } 1017 if ports[1] != "1000-2000" { 1018 t.Fatalf("Bad ports: %s", ports[1]) 1019 } 1020 } 1021 1022 func TestLoad_onlyOverride(t *testing.T) { 1023 c, err := LoadDir(filepath.Join(fixtureDir, "dir-only-override")) 1024 if err != nil { 1025 t.Fatalf("err: %s", err) 1026 } 1027 1028 if c == nil { 1029 t.Fatal("config should not be nil") 1030 } 1031 1032 actual := variablesStr(c.Variables) 1033 if actual != strings.TrimSpace(dirOnlyOverrideVariablesStr) { 1034 t.Fatalf("bad:\n%s", actual) 1035 } 1036 } 1037 1038 const jsonAttributeStr = ` 1039 cloudstack_firewall.test (x1) 1040 ipaddress 1041 rule 1042 ` 1043 1044 const windowsHeredocResourcesStr = ` 1045 aws_instance.test (x1) 1046 user_data 1047 ` 1048 1049 const heredocProvidersStr = ` 1050 aws 1051 access_key 1052 secret_key 1053 ` 1054 1055 const heredocResourcesStr = ` 1056 aws_iam_policy.policy (x1) 1057 description 1058 name 1059 path 1060 policy 1061 aws_instance.heredocwithnumbers (x1) 1062 ami 1063 provisioners 1064 local-exec 1065 command 1066 aws_instance.test (x1) 1067 ami 1068 provisioners 1069 remote-exec 1070 inline 1071 ` 1072 1073 const basicOutputsStr = ` 1074 web_id 1075 vars 1076 resource: aws_instance.web.id 1077 description 1078 The ID 1079 web_ip 1080 vars 1081 resource: aws_instance.web.private_ip 1082 ` 1083 1084 const basicLocalsStr = ` 1085 literal 1086 literal_list 1087 literal_map 1088 security_group_ids 1089 vars 1090 resource: aws_security_group.firewall.*.id 1091 web_ip 1092 vars 1093 resource: aws_instance.web.private_ip 1094 ` 1095 1096 const basicProvidersStr = ` 1097 aws 1098 access_key 1099 secret_key 1100 do 1101 api_key 1102 vars 1103 user: var.foo 1104 ` 1105 1106 const basicResourcesStr = ` 1107 aws_instance.db (x1) 1108 VPC 1109 security_groups 1110 provisioners 1111 file 1112 destination 1113 source 1114 dependsOn 1115 aws_instance.web 1116 vars 1117 resource: aws_security_group.firewall.*.id 1118 aws_instance.web (x1) 1119 ami 1120 network_interface 1121 security_groups 1122 provisioners 1123 file 1124 destination 1125 source 1126 vars 1127 resource: aws_security_group.firewall.foo 1128 user: var.foo 1129 aws_security_group.firewall (x5) 1130 data.do.depends (x1) 1131 dependsOn 1132 data.do.simple 1133 data.do.simple (x1) 1134 foo 1135 ` 1136 1137 const basicVariablesStr = ` 1138 bar (required) (string) 1139 <> 1140 <> 1141 baz (map) 1142 map[key:value] 1143 <> 1144 foo 1145 bar 1146 bar 1147 ` 1148 1149 const basicJsonNoNameResourcesStr = ` 1150 aws_security_group.allow_external_http_https (x1) 1151 tags 1152 ` 1153 1154 const dirBasicOutputsStr = ` 1155 web_ip 1156 vars 1157 resource: aws_instance.web.private_ip 1158 ` 1159 1160 const dirBasicProvidersStr = ` 1161 aws 1162 access_key 1163 secret_key 1164 do 1165 api_key 1166 vars 1167 user: var.foo 1168 ` 1169 1170 const dirBasicResourcesStr = ` 1171 aws_instance.db (x1) 1172 security_groups 1173 vars 1174 resource: aws_security_group.firewall.*.id 1175 aws_instance.web (x1) 1176 ami 1177 network_interface 1178 security_groups 1179 vars 1180 resource: aws_security_group.firewall.foo 1181 user: var.foo 1182 aws_security_group.firewall (x5) 1183 data.do.depends (x1) 1184 dependsOn 1185 data.do.simple 1186 data.do.simple (x1) 1187 foo 1188 ` 1189 1190 const dirBasicVariablesStr = ` 1191 foo 1192 bar 1193 bar 1194 ` 1195 1196 const dirOverrideOutputsStr = ` 1197 web_ip 1198 vars 1199 resource: aws_instance.web.private_ip 1200 ` 1201 1202 const dirOverrideProvidersStr = ` 1203 aws 1204 access_key 1205 secret_key 1206 do 1207 api_key 1208 vars 1209 user: var.foo 1210 ` 1211 1212 const dirOverrideResourcesStr = ` 1213 aws_instance.db (x1) 1214 ami 1215 security_groups 1216 aws_instance.web (x1) 1217 ami 1218 foo 1219 network_interface 1220 security_groups 1221 vars 1222 resource: aws_security_group.firewall.foo 1223 user: var.foo 1224 aws_security_group.firewall (x5) 1225 data.do.depends (x1) 1226 hello 1227 dependsOn 1228 data.do.simple 1229 data.do.simple (x1) 1230 foo 1231 ` 1232 1233 const dirOverrideVariablesStr = ` 1234 foo 1235 bar 1236 bar 1237 ` 1238 1239 const dirOverrideVarsVariablesStr = ` 1240 foo 1241 baz 1242 bar 1243 ` 1244 1245 const dirOnlyOverrideVariablesStr = ` 1246 foo 1247 bar 1248 bar 1249 ` 1250 1251 const importProvidersStr = ` 1252 aws 1253 bar 1254 foo 1255 ` 1256 1257 const importResourcesStr = ` 1258 aws_security_group.db (x1) 1259 aws_security_group.web (x1) 1260 ` 1261 1262 const importVariablesStr = ` 1263 bar (required) 1264 <> 1265 <> 1266 foo 1267 bar 1268 bar 1269 ` 1270 1271 const modulesModulesStr = ` 1272 bar 1273 source = baz 1274 memory 1275 ` 1276 1277 const provisionerResourcesStr = ` 1278 aws_instance.web (x1) 1279 ami 1280 security_groups 1281 provisioners 1282 shell 1283 path 1284 vars 1285 resource: aws_security_group.firewall.foo 1286 user: var.foo 1287 ` 1288 1289 const provisionerDestroyResourcesStr = ` 1290 aws_instance.web (x1) 1291 provisioners 1292 shell 1293 shell (destroy) 1294 path 1295 shell (destroy) 1296 on_failure = continue 1297 path 1298 ` 1299 1300 const connectionResourcesStr = ` 1301 aws_instance.web (x1) 1302 ami 1303 security_groups 1304 provisioners 1305 shell 1306 path 1307 shell 1308 path 1309 vars 1310 resource: aws_security_group.firewall.foo 1311 user: var.foo 1312 ` 1313 1314 const outputDependsOnStr = ` 1315 value 1316 dependsOn 1317 foo 1318 ` 1319 1320 const variablesVariablesStr = ` 1321 bar 1322 <> 1323 <> 1324 baz 1325 foo 1326 <> 1327 foo (required) 1328 <> 1329 <> 1330 ` 1331 1332 const createBeforeDestroyResourcesStr = ` 1333 aws_instance.bar (x1) 1334 ami 1335 aws_instance.web (x1) 1336 ami 1337 ` 1338 1339 const ignoreChangesResourcesStr = ` 1340 aws_instance.bar (x1) 1341 ami 1342 aws_instance.baz (x1) 1343 ami 1344 aws_instance.web (x1) 1345 ami 1346 `