github.com/hashicorp/packer@v1.14.3/packer/core_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package packer 5 6 import ( 7 "context" 8 "errors" 9 "os" 10 "path/filepath" 11 "reflect" 12 "testing" 13 14 packersdk "github.com/hashicorp/packer-plugin-sdk/packer" 15 "github.com/hashicorp/packer-plugin-sdk/template" 16 configHelper "github.com/hashicorp/packer-plugin-sdk/template/config" 17 "github.com/hashicorp/packer/version" 18 ) 19 20 func TestCoreBuildNames(t *testing.T) { 21 cases := []struct { 22 File string 23 Vars map[string]string 24 Result []string 25 }{ 26 { 27 "build-names-basic.json", 28 nil, 29 []string{"something"}, 30 }, 31 32 { 33 "build-names-func.json", 34 nil, 35 []string{"TUBES"}, 36 }, 37 } 38 39 for _, tc := range cases { 40 tpl, err := template.ParseFile(fixtureDir(tc.File)) 41 if err != nil { 42 t.Fatalf("err: %s\n\n%s", tc.File, err) 43 } 44 45 core := NewCore(&CoreConfig{ 46 Template: tpl, 47 Variables: tc.Vars, 48 }) 49 diags := core.Initialize(InitializeOptions{}) 50 if diags.HasErrors() { 51 t.Fatalf("err: %s\n\n%s", tc.File, diags) 52 } 53 54 names := core.BuildNames(nil, nil) 55 if !reflect.DeepEqual(names, tc.Result) { 56 t.Fatalf("err: %s\n\n%#v", tc.File, names) 57 } 58 } 59 } 60 61 func TestCoreBuild_basic(t *testing.T) { 62 config := TestCoreConfig(t) 63 testCoreTemplate(t, config, fixtureDir("build-basic.json")) 64 b := TestBuilder(t, config, "test") 65 core := TestCore(t, config) 66 67 b.ArtifactId = "hello" 68 69 build, err := core.Build("test") 70 if err != nil { 71 t.Fatalf("err: %s", err) 72 } 73 74 if build.Name() != "test" { 75 t.Fatalf("bad: build name does not match expected: %q, got: %q", "test", build.Name()) 76 } 77 78 if _, err := build.Prepare(); err != nil { 79 t.Fatalf("err: %s", err) 80 } 81 82 artifact, err := build.Run(context.Background(), nil) 83 if err != nil { 84 t.Fatalf("err: %s", err) 85 } 86 if len(artifact) != 1 { 87 t.Fatalf("bad: %#v", artifact) 88 } 89 90 if artifact[0].Id() != b.ArtifactId { 91 t.Fatalf("bad: %s", artifact[0].Id()) 92 } 93 } 94 95 func TestCoreBuild_basicInterpolated(t *testing.T) { 96 config := TestCoreConfig(t) 97 testCoreTemplate(t, config, fixtureDir("build-basic-interpolated.json")) 98 b := TestBuilder(t, config, "test") 99 core := TestCore(t, config) 100 101 b.ArtifactId = "hello" 102 103 build, err := core.Build("NAME") 104 if err != nil { 105 t.Fatalf("err: %s", err) 106 } 107 108 if build.Name() != "test.NAME" { 109 t.Fatalf("bad: build name does not match expected: %q, got: %q", "NAME", build.Name()) 110 } 111 112 if _, err := build.Prepare(); err != nil { 113 t.Fatalf("err: %s", err) 114 } 115 116 artifact, err := build.Run(context.Background(), nil) 117 if err != nil { 118 t.Fatalf("err: %s", err) 119 } 120 if len(artifact) != 1 { 121 t.Fatalf("bad: %#v", artifact) 122 } 123 124 if artifact[0].Id() != b.ArtifactId { 125 t.Fatalf("bad: %s", artifact[0].Id()) 126 } 127 } 128 129 func TestCoreBuild_env(t *testing.T) { 130 t.Setenv("PACKER_TEST_ENV", "test") 131 132 config := TestCoreConfig(t) 133 testCoreTemplate(t, config, fixtureDir("build-env.json")) 134 b := TestBuilder(t, config, "test") 135 core := TestCore(t, config) 136 137 b.ArtifactId = "hello" 138 139 build, err := core.Build("test") 140 if err != nil { 141 t.Fatalf("err: %s", err) 142 } 143 144 if _, err := build.Prepare(); err != nil { 145 t.Fatalf("err: %s", err) 146 } 147 148 // Interpolate the config 149 var result map[string]interface{} 150 err = configHelper.Decode(&result, nil, b.PrepareConfig...) 151 if err != nil { 152 t.Fatalf("err: %s", err) 153 } 154 155 if result["value"] != "test" { 156 t.Fatalf("bad: %#v", result) 157 } 158 } 159 160 func TestCoreBuild_IgnoreTemplateVariables(t *testing.T) { 161 t.Setenv("PACKER_TEST_ENV", "test") 162 163 config := TestCoreConfig(t) 164 testCoreTemplate(t, config, fixtureDir("build-ignore-template-variable.json")) 165 core := TestCore(t, config) 166 167 if core.variables["http_ip"] != "{{ .HTTPIP }}" { 168 t.Fatalf("bad: User variable http_ip={{ .HTTPIP }} should not be interpolated") 169 } 170 171 if core.variables["var"] != "test_{{ .PACKER_TEST_TEMP }}" { 172 t.Fatalf("bad: User variable var should be half interpolated to var=test_{{ .PACKER_TEST_TEMP }} but was var=%s", core.variables["var"]) 173 } 174 175 if core.variables["array_var"] != "us-west-1,us-west-2" { 176 t.Fatalf("bad: User variable array_var should be \"us-west-1,us-west-2\" but was %s", core.variables["var"]) 177 } 178 179 build, err := core.Build("test") 180 if err != nil { 181 t.Fatalf("err: %s", err) 182 } 183 184 if _, err := build.Prepare(); err != nil { 185 t.Fatalf("err: %s", err) 186 } 187 } 188 189 func TestCoreBuild_buildNameVar(t *testing.T) { 190 config := TestCoreConfig(t) 191 testCoreTemplate(t, config, fixtureDir("build-var-build-name.json")) 192 b := TestBuilder(t, config, "test") 193 core := TestCore(t, config) 194 195 b.ArtifactId = "hello" 196 197 build, err := core.Build("test") 198 if err != nil { 199 t.Fatalf("err: %s", err) 200 } 201 202 if _, err := build.Prepare(); err != nil { 203 t.Fatalf("err: %s", err) 204 } 205 206 // Interpolate the config 207 var result map[string]interface{} 208 err = configHelper.Decode(&result, nil, b.PrepareConfig...) 209 if err != nil { 210 t.Fatalf("err: %s", err) 211 } 212 213 if result["value"] != "test" { 214 t.Fatalf("bad: %#v", result) 215 } 216 } 217 218 func TestCoreBuild_buildTypeVar(t *testing.T) { 219 config := TestCoreConfig(t) 220 testCoreTemplate(t, config, fixtureDir("build-var-build-type.json")) 221 b := TestBuilder(t, config, "test") 222 core := TestCore(t, config) 223 224 b.ArtifactId = "hello" 225 226 build, err := core.Build("test") 227 if err != nil { 228 t.Fatalf("err: %s", err) 229 } 230 231 if _, err := build.Prepare(); err != nil { 232 t.Fatalf("err: %s", err) 233 } 234 235 // Interpolate the config 236 var result map[string]interface{} 237 err = configHelper.Decode(&result, nil, b.PrepareConfig...) 238 if err != nil { 239 t.Fatalf("err: %s", err) 240 } 241 242 if result["value"] != "test" { 243 t.Fatalf("bad: %#v", result) 244 } 245 } 246 247 func TestCoreBuild_nonExist(t *testing.T) { 248 config := TestCoreConfig(t) 249 testCoreTemplate(t, config, fixtureDir("build-basic.json")) 250 TestBuilder(t, config, "test") 251 core := TestCore(t, config) 252 253 _, err := core.Build("nope") 254 if err == nil { 255 t.Fatal("should error") 256 } 257 } 258 259 func TestCoreBuild_prov(t *testing.T) { 260 config := TestCoreConfig(t) 261 testCoreTemplate(t, config, fixtureDir("build-prov.json")) 262 b := TestBuilder(t, config, "test") 263 p := TestProvisioner(t, config, "test") 264 core := TestCore(t, config) 265 266 b.ArtifactId = "hello" 267 268 build, err := core.Build("test") 269 if err != nil { 270 t.Fatalf("err: %s", err) 271 } 272 273 if _, err := build.Prepare(); err != nil { 274 t.Fatalf("err: %s", err) 275 } 276 277 artifact, err := build.Run(context.Background(), nil) 278 if err != nil { 279 t.Fatalf("err: %s", err) 280 } 281 if len(artifact) != 1 { 282 t.Fatalf("bad: %#v", artifact) 283 } 284 285 if artifact[0].Id() != b.ArtifactId { 286 t.Fatalf("bad: %s", artifact[0].Id()) 287 } 288 if !p.ProvCalled { 289 t.Fatal("provisioner not called") 290 } 291 } 292 293 func TestCoreBuild_provSkip(t *testing.T) { 294 config := TestCoreConfig(t) 295 testCoreTemplate(t, config, fixtureDir("build-prov-skip.json")) 296 b := TestBuilder(t, config, "test") 297 p := TestProvisioner(t, config, "test") 298 core := TestCore(t, config) 299 300 b.ArtifactId = "hello" 301 302 build, err := core.Build("test") 303 if err != nil { 304 t.Fatalf("err: %s", err) 305 } 306 307 if _, err := build.Prepare(); err != nil { 308 t.Fatalf("err: %s", err) 309 } 310 311 artifact, err := build.Run(context.Background(), nil) 312 if err != nil { 313 t.Fatalf("err: %s", err) 314 } 315 if len(artifact) != 1 { 316 t.Fatalf("bad: %#v", artifact) 317 } 318 319 if artifact[0].Id() != b.ArtifactId { 320 t.Fatalf("bad: %s", artifact[0].Id()) 321 } 322 if p.ProvCalled { 323 t.Fatal("provisioner should not be called") 324 } 325 } 326 327 func TestCoreBuild_provSkipInclude(t *testing.T) { 328 config := TestCoreConfig(t) 329 testCoreTemplate(t, config, fixtureDir("build-prov-skip-include.json")) 330 b := TestBuilder(t, config, "test") 331 p := TestProvisioner(t, config, "test") 332 core := TestCore(t, config) 333 334 b.ArtifactId = "hello" 335 336 build, err := core.Build("test") 337 if err != nil { 338 t.Fatalf("err: %s", err) 339 } 340 341 if _, err := build.Prepare(); err != nil { 342 t.Fatalf("err: %s", err) 343 } 344 345 artifact, err := build.Run(context.Background(), nil) 346 if err != nil { 347 t.Fatalf("err: %s", err) 348 } 349 if len(artifact) != 1 { 350 t.Fatalf("bad: %#v", artifact) 351 } 352 353 if artifact[0].Id() != b.ArtifactId { 354 t.Fatalf("bad: %s", artifact[0].Id()) 355 } 356 if !p.ProvCalled { 357 t.Fatal("provisioner should be called") 358 } 359 } 360 361 func TestCoreBuild_provOverride(t *testing.T) { 362 config := TestCoreConfig(t) 363 testCoreTemplate(t, config, fixtureDir("build-prov-override.json")) 364 b := TestBuilder(t, config, "test") 365 p := TestProvisioner(t, config, "test") 366 core := TestCore(t, config) 367 368 b.ArtifactId = "hello" 369 370 build, err := core.Build("test") 371 if err != nil { 372 t.Fatalf("err: %s", err) 373 } 374 375 if _, err := build.Prepare(); err != nil { 376 t.Fatalf("err: %s", err) 377 } 378 379 artifact, err := build.Run(context.Background(), nil) 380 if err != nil { 381 t.Fatalf("err: %s", err) 382 } 383 if len(artifact) != 1 { 384 t.Fatalf("bad: %#v", artifact) 385 } 386 387 if artifact[0].Id() != b.ArtifactId { 388 t.Fatalf("bad: %s", artifact[0].Id()) 389 } 390 if !p.ProvCalled { 391 t.Fatal("provisioner not called") 392 } 393 394 found := false 395 for _, raw := range p.PrepConfigs { 396 if m, ok := raw.(map[string]interface{}); ok { 397 if _, ok := m["foo"]; ok { 398 found = true 399 break 400 } 401 } 402 } 403 if !found { 404 t.Fatal("override not called") 405 } 406 } 407 408 func TestCoreBuild_postProcess(t *testing.T) { 409 config := TestCoreConfig(t) 410 testCoreTemplate(t, config, fixtureDir("build-pp.json")) 411 b := TestBuilder(t, config, "test") 412 p := TestPostProcessor(t, config, "test") 413 core := TestCore(t, config) 414 ui := TestUi(t) 415 416 b.ArtifactId = "hello" 417 p.ArtifactId = "goodbye" 418 419 build, err := core.Build("test") 420 if err != nil { 421 t.Fatalf("err: %s", err) 422 } 423 424 if _, err := build.Prepare(); err != nil { 425 t.Fatalf("err: %s", err) 426 } 427 428 artifact, err := build.Run(context.Background(), ui) 429 if err != nil { 430 t.Fatalf("err: %s", err) 431 } 432 if len(artifact) != 1 { 433 t.Fatalf("bad: %#v", artifact) 434 } 435 436 if artifact[0].Id() != p.ArtifactId { 437 t.Fatalf("bad: %s", artifact[0].Id()) 438 } 439 if p.PostProcessArtifact.Id() != b.ArtifactId { 440 t.Fatalf("bad: %s", p.PostProcessArtifact.Id()) 441 } 442 } 443 444 func TestCoreBuild_templatePath(t *testing.T) { 445 config := TestCoreConfig(t) 446 testCoreTemplate(t, config, fixtureDir("build-template-path.json")) 447 b := TestBuilder(t, config, "test") 448 core := TestCore(t, config) 449 450 expected, _ := filepath.Abs("./test-fixtures") 451 452 build, err := core.Build("test") 453 if err != nil { 454 t.Fatalf("err: %s", err) 455 } 456 457 if _, err := build.Prepare(); err != nil { 458 t.Fatalf("err: %s", err) 459 } 460 461 // Interpolate the config 462 var result map[string]interface{} 463 err = configHelper.Decode(&result, nil, b.PrepareConfig...) 464 if err != nil { 465 t.Fatalf("err: %s", err) 466 } 467 468 if result["value"] != expected { 469 t.Fatalf("bad: %#v", result) 470 } 471 } 472 473 func TestCoreValidate(t *testing.T) { 474 cases := []struct { 475 File string 476 Vars map[string]string 477 Err bool 478 }{ 479 {"validate-dup-builder.json", nil, true}, 480 481 // Required variable not set 482 {"validate-req-variable.json", nil, true}, 483 {"validate-req-variable.json", map[string]string{"foo": "bar"}, false}, 484 485 // Min version good 486 {"validate-min-version.json", map[string]string{"foo": "bar"}, false}, 487 {"validate-min-version-high.json", map[string]string{"foo": "bar"}, true}, 488 } 489 490 for _, tc := range cases { 491 f, err := os.Open(fixtureDir(tc.File)) 492 if err != nil { 493 t.Fatalf("err: %s", err) 494 } 495 496 tpl, err := template.Parse(f) 497 f.Close() 498 if err != nil { 499 t.Fatalf("err: %s\n\n%s", tc.File, err) 500 } 501 502 core := NewCore(&CoreConfig{ 503 Template: tpl, 504 Variables: tc.Vars, 505 Version: "1.0.0", 506 }) 507 diags := core.Initialize(InitializeOptions{}) 508 509 if diags.HasErrors() != tc.Err { 510 t.Fatalf("err: %s\n\n%s", tc.File, err) 511 } 512 } 513 } 514 515 // Tests that we can properly interpolate user variables defined within the 516 // packer template 517 func TestCore_InterpolateUserVars(t *testing.T) { 518 cases := []struct { 519 File string 520 Expected map[string]string 521 Err bool 522 }{ 523 { 524 "build-variables-interpolate.json", 525 map[string]string{ 526 "foo": "bar", 527 "bar": "bar", 528 "baz": "barbaz", 529 "bang": "bangbarbaz", 530 }, 531 false, 532 }, 533 { 534 "build-variables-interpolate2.json", 535 map[string]string{}, 536 true, 537 }, 538 } 539 for _, tc := range cases { 540 f, err := os.Open(fixtureDir(tc.File)) 541 if err != nil { 542 t.Fatalf("err: %s", err) 543 } 544 545 tpl, err := template.Parse(f) 546 f.Close() 547 if err != nil { 548 t.Fatalf("err: %s\n\n%s", tc.File, err) 549 } 550 551 ccf := NewCore(&CoreConfig{ 552 Template: tpl, 553 Version: "1.0.0", 554 }) 555 diags := ccf.Initialize(InitializeOptions{}) 556 557 if diags.HasErrors() != tc.Err { 558 if tc.Err == false { 559 t.Fatalf("Error interpolating %s: Expected no error, but got: %s", tc.File, diags) 560 } else { 561 t.Fatalf("Error interpolating %s: Expected an error, but got: %s", tc.File, diags) 562 } 563 564 } 565 if !tc.Err { 566 for k, v := range ccf.variables { 567 if tc.Expected[k] != v { 568 t.Fatalf("Expected %s but got %s", tc.Expected[k], v) 569 } 570 } 571 } 572 } 573 } 574 575 // Tests that we can properly interpolate user variables defined within a 576 // var-file provided alongside the Packer template 577 func TestCore_InterpolateUserVars_VarFile(t *testing.T) { 578 cases := []struct { 579 File string 580 Variables map[string]string 581 Expected map[string]string 582 Err bool 583 }{ 584 { 585 // tests that we can interpolate from var files when var isn't set in 586 // originating template 587 "build-basic-interpolated.json", 588 map[string]string{ 589 "name": "gotta-{{user `my_var`}}", 590 "my_var": "interpolate-em-all", 591 }, 592 map[string]string{ 593 "name": "gotta-interpolate-em-all", 594 "my_var": "interpolate-em-all"}, 595 false, 596 }, 597 { 598 // tests that we can interpolate from var files when var is set in 599 // originating template as required 600 "build-basic-interpolated-required.json", 601 map[string]string{ 602 "name": "gotta-{{user `my_var`}}", 603 "my_var": "interpolate-em-all", 604 }, 605 map[string]string{ 606 "name": "gotta-interpolate-em-all", 607 "my_var": "interpolate-em-all"}, 608 false, 609 }, 610 } 611 for _, tc := range cases { 612 f, err := os.Open(fixtureDir(tc.File)) 613 if err != nil { 614 t.Fatalf("err: %s", err) 615 } 616 617 tpl, err := template.Parse(f) 618 f.Close() 619 if err != nil { 620 t.Fatalf("err: %s\n\n%s", tc.File, err) 621 } 622 623 ccf := NewCore(&CoreConfig{ 624 Template: tpl, 625 Version: "1.0.0", 626 Variables: tc.Variables, 627 }) 628 diags := ccf.Initialize(InitializeOptions{}) 629 630 if diags.HasErrors() != tc.Err { 631 t.Fatalf("err: %s\n\n%s", tc.File, diags) 632 } 633 if !tc.Err { 634 for k, v := range ccf.variables { 635 if tc.Expected[k] != v { 636 t.Fatalf("Expected value %s for key %s but got %s", 637 tc.Expected[k], k, v) 638 } 639 } 640 } 641 } 642 } 643 644 func TestSensitiveVars(t *testing.T) { 645 cases := []struct { 646 File string 647 Vars map[string]string 648 SensitiveVars []string 649 Expected string 650 Err bool 651 }{ 652 // hardcoded 653 { 654 "sensitive-variables.json", 655 map[string]string{"foo": "bar_extra_sensitive_probably_a_password"}, 656 []string{"foo"}, 657 "the foo jumped over the <sensitive>", 658 false, 659 }, 660 // interpolated 661 { 662 "sensitive-variables.json", 663 map[string]string{"foo": "bar_extra_sensitive_probably_a_password", 664 "bang": "{{ user `foo`}}"}, 665 []string{"bang"}, 666 "the foo jumped over the <sensitive>", 667 false, 668 }, 669 } 670 671 for _, tc := range cases { 672 f, err := os.Open(fixtureDir(tc.File)) 673 if err != nil { 674 t.Fatalf("err: %s", err) 675 } 676 677 tpl, err := template.Parse(f) 678 f.Close() 679 if err != nil { 680 t.Fatalf("err: %s\n\n%s", tc.File, err) 681 } 682 683 ccf := NewCore(&CoreConfig{ 684 Template: tpl, 685 Variables: tc.Vars, 686 Version: "1.0.0", 687 }) 688 diags := ccf.Initialize(InitializeOptions{}) 689 690 if diags.HasErrors() != tc.Err { 691 t.Fatalf("err: %s\n\n%s", tc.File, diags) 692 } 693 // Check that filter correctly manipulates strings: 694 filtered := packersdk.LogSecretFilter.FilterString("the foo jumped over the bar_extra_sensitive_probably_a_password") 695 if filtered != tc.Expected { 696 t.Fatalf("not filtering sensitive vars; filtered is %#v", filtered) 697 } 698 } 699 } 700 701 // Normally I wouldn't test a little helper function, but it's regex. 702 func TestIsDoneInterpolating(t *testing.T) { 703 cases := []struct { 704 inputString string 705 expectedBool bool 706 expectedErr bool 707 }{ 708 // Many of these tests are just exercising the regex to make sure it 709 // doesn't get confused by different kinds of whitespace 710 {"charmander-{{ user `spacesaroundticks` }}", false, false}, 711 {"pidgey-{{ user `partyparrot`}}", false, false}, 712 {"jigglypuff-{{ user`notickspaaces`}}", false, false}, 713 {"eevee-{{user`nospaces`}}", false, false}, 714 {"staryu-{{ user `somanyspaces` }}", false, false}, 715 {"{{ user `somanyspaces` }}-{{isotime}}", false, false}, 716 // Make sure that we only flag on "user" when it's in the right set of 717 // brackets, in a properly declared template engine format 718 {"missingno-{{ user `missingbracket` }", true, false}, 719 {"missing2-{user ``missingopenbrackets }}", true, false}, 720 {"wat-userjustinname", true, false}, 721 // Any functions that aren't "user" should have already been properly 722 // interpolated by the time this is called, so these cases aren't 723 // realistic. That said, this makes it clear that this function doesn't 724 // care about anything but the user function 725 {"pokemon-{{ isotime }}", true, false}, 726 {"squirtle-{{ env `water`}}", true, false}, 727 {"bulbasaur-notinterpolated", true, false}, 728 {"extra-{{thisfunc `user`}}", true, false}, 729 } 730 for _, tc := range cases { 731 done, err := isDoneInterpolating(tc.inputString) 732 if (err != nil) != tc.expectedErr { 733 t.Fatalf("Test case failed. Error: %s expected error: "+ 734 "%t test string: %s", err, tc.expectedErr, tc.inputString) 735 } 736 if done != tc.expectedBool { 737 t.Fatalf("Test case failed. inputString: %s. "+ 738 "Expected done = %t but got done = %t", tc.inputString, 739 tc.expectedBool, done) 740 } 741 } 742 } 743 744 func TestEnvAndFileVars(t *testing.T) { 745 t.Setenv("INTERPOLATE_TEST_ENV_1", "bulbasaur") 746 t.Setenv("INTERPOLATE_TEST_ENV_3", "/path/to/nowhere") 747 t.Setenv("INTERPOLATE_TEST_ENV_2", "5") 748 t.Setenv("INTERPOLATE_TEST_ENV_4", "bananas") 749 750 f, err := os.Open(fixtureDir("complex-recursed-env-user-var-file.json")) 751 if err != nil { 752 t.Fatalf("err: %s", err) 753 } 754 755 tpl, err := template.Parse(f) 756 f.Close() 757 if err != nil { 758 t.Fatalf("err: %s\n\n%s", "complex-recursed-env-user-var-file.json", err) 759 } 760 761 ccf := NewCore(&CoreConfig{ 762 Template: tpl, 763 Version: "1.0.0", 764 Variables: map[string]string{ 765 "var_1": "partyparrot", 766 "var_2": "{{user `env_1`}}-{{user `env_2`}}{{user `env_3`}}-{{user `var_1`}}", 767 "final_var": "{{user `env_1`}}/{{user `env_2`}}/{{user `env_4`}}{{user `env_3`}}-{{user `var_1`}}/vmware/{{user `var_2`}}.vmx", 768 }, 769 }) 770 diags := ccf.Initialize(InitializeOptions{}) 771 772 expected := map[string]string{ 773 "var_1": "partyparrot", 774 "var_2": "bulbasaur-5/path/to/nowhere-partyparrot", 775 "final_var": "bulbasaur/5/bananas/path/to/nowhere-partyparrot/vmware/bulbasaur-5/path/to/nowhere-partyparrot.vmx", 776 "env_1": "bulbasaur", 777 "env_2": "5", 778 "env_3": "/path/to/nowhere", 779 "env_4": "bananas", 780 } 781 if diags.HasErrors() { 782 t.Fatalf("err: %s\n\n%s", "complex-recursed-env-user-var-file.json", diags) 783 } 784 for k, v := range ccf.variables { 785 if expected[k] != v { 786 t.Fatalf("Expected value %s for key %s but got %s", 787 expected[k], k, v) 788 } 789 } 790 } 791 792 func testCoreTemplate(t *testing.T, c *CoreConfig, p string) { 793 tpl, err := template.ParseFile(p) 794 if err != nil { 795 t.Fatalf("err: %s\n\n%s", p, err) 796 } 797 798 c.Template = tpl 799 } 800 801 func TestCoreBuild_provRetry(t *testing.T) { 802 config := TestCoreConfig(t) 803 testCoreTemplate(t, config, fixtureDir("build-prov-retry.json")) 804 b := TestBuilder(t, config, "test") 805 pString := new(packersdk.MockProvisioner) 806 pInt := new(packersdk.MockProvisioner) 807 config.Components.PluginConfig.Provisioners = MapOfProvisioner{ 808 "test-string": func() (packersdk.Provisioner, error) { return pString, nil }, 809 // backwards compatibility 810 "test-integer": func() (packersdk.Provisioner, error) { return pInt, nil }, 811 } 812 core := TestCore(t, config) 813 814 b.ArtifactId = "hello" 815 816 build, err := core.Build("test") 817 if err != nil { 818 t.Fatalf("err: %s", err) 819 } 820 821 if _, err := build.Prepare(); err != nil { 822 t.Fatalf("err: %s", err) 823 } 824 825 ui := testUi() 826 pInt.ProvFunc = func(ctx context.Context) error { 827 return errors.New("failed") 828 } 829 pString.ProvFunc = func(ctx context.Context) error { 830 return errors.New("failed") 831 } 832 833 artifact, err := build.Run(context.Background(), ui) 834 if err != nil { 835 t.Fatalf("err: %s", err) 836 } 837 if len(artifact) != 1 { 838 t.Fatalf("bad: %#v", artifact) 839 } 840 841 if artifact[0].Id() != b.ArtifactId { 842 t.Fatalf("bad: %s", artifact[0].Id()) 843 } 844 if !pString.ProvRetried { 845 t.Fatal("provisioner should retry for max_retries string value") 846 } 847 // backwards compatibility 848 if !pInt.ProvRetried { 849 t.Fatal("provisioner should retry for max_retries integer value") 850 } 851 } 852 853 func TestCoreBuild_packerVersion(t *testing.T) { 854 config := TestCoreConfig(t) 855 testCoreTemplate(t, config, fixtureDir("build-var-packer-version.json")) 856 b := TestBuilder(t, config, "test") 857 core := TestCore(t, config) 858 859 expected := version.FormattedVersion() 860 build, err := core.Build("test") 861 if err != nil { 862 t.Fatalf("err: %s", err) 863 } 864 865 if _, err := build.Prepare(); err != nil { 866 t.Fatalf("err: %s", err) 867 } 868 // Interpolate the config 869 var result map[string]interface{} 870 err = configHelper.Decode(&result, nil, b.PrepareConfig...) 871 if err != nil { 872 t.Fatalf("err: %s", err) 873 } 874 875 if result["value"] != expected { 876 t.Fatalf("bad: %#v", result) 877 } 878 } 879 880 func TestCoreBuild_buildNameIntepolation(t *testing.T) { 881 config := TestCoreConfig(t) 882 cases := []struct { 883 File string 884 InterpolatedName, Expected string 885 Vars map[string]string 886 }{ 887 { 888 File: "build-interpolated-name.json", 889 InterpolatedName: "mybuild-RandomToken", 890 Expected: "test.mybuild-RandomToken", 891 Vars: map[string]string{ 892 "build_name": "mybuild-RandomToken", 893 }, 894 }, 895 { 896 File: "build-interpolated-name.json", 897 InterpolatedName: "build-vardata", 898 Expected: "test.build-vardata", 899 Vars: map[string]string{ 900 "build_name": "build-vardata", 901 }, 902 }, 903 { 904 File: "build-interpolated-name.json", 905 InterpolatedName: "build-12345", 906 Expected: "test.build-12345", 907 Vars: map[string]string{ 908 "something": "build-12345", 909 "build_name": "{{user `something`}}", 910 }, 911 }, 912 { 913 // When no name attribute is provided in the config the builder type is the default name. 914 File: "build-basic.json", 915 InterpolatedName: "test", 916 Expected: "test", 917 }, 918 } 919 920 for _, tc := range cases { 921 config.Variables = tc.Vars 922 testCoreTemplate(t, config, fixtureDir(tc.File)) 923 core := TestCore(t, config) 924 diags := core.Initialize(InitializeOptions{}) 925 if diags.HasErrors() { 926 t.Fatalf("err: %s\n\n%s", tc.File, diags) 927 } 928 929 build, err := core.Build(tc.InterpolatedName) 930 if err != nil { 931 t.Fatalf("err for InterpolatedName(%q): %s", tc.InterpolatedName, err) 932 } 933 934 if build.Name() != tc.Expected { 935 t.Errorf("build type interpolation failed; expected %q, got %q", tc.Expected, build.Name()) 936 } 937 938 } 939 }