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