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