github.com/hashicorp/packer@v1.14.3/packer/build_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 "reflect" 9 "testing" 10 11 "github.com/hashicorp/packer-plugin-sdk/common" 12 packersdk "github.com/hashicorp/packer-plugin-sdk/packer" 13 "github.com/hashicorp/packer-plugin-sdk/packerbuilderdata" 14 "github.com/hashicorp/packer/version" 15 "github.com/zclconf/go-cty/cty" 16 ) 17 18 func boolPointer(tf bool) *bool { 19 return &tf 20 } 21 22 func testBuild() *CoreBuild { 23 return &CoreBuild{ 24 Type: "test", 25 Builder: &packersdk.MockBuilder{ArtifactId: "b"}, 26 BuilderConfig: 42, 27 BuilderType: "foo", 28 hooks: map[string][]packersdk.Hook{ 29 "foo": {&packersdk.MockHook{}}, 30 }, 31 Provisioners: []CoreBuildProvisioner{ 32 { 33 PType: "mock-provisioner", 34 Provisioner: &packersdk.MockProvisioner{}, 35 config: []interface{}{42}}, 36 }, 37 PostProcessors: [][]CoreBuildPostProcessor{ 38 { 39 {&MockPostProcessor{ArtifactId: "pp"}, "testPP", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(true)}, 40 }, 41 }, 42 Variables: make(map[string]string), 43 onError: "cleanup", 44 SensitiveVars: []string{"sensitive_var"}, 45 } 46 } 47 48 func testDefaultPackerConfig() map[string]interface{} { 49 return map[string]interface{}{ 50 common.BuildNameConfigKey: "test", 51 common.BuilderTypeConfigKey: "foo", 52 common.CoreVersionConfigKey: version.FormattedVersion(), 53 common.DebugConfigKey: false, 54 common.ForceConfigKey: false, 55 common.OnErrorConfigKey: "cleanup", 56 common.TemplatePathKey: "", 57 common.UserVariablesConfigKey: make(map[string]string), 58 common.SensitiveVarsConfigKey: []string{"sensitive_var"}, 59 } 60 } 61 func TestBuild_Name(t *testing.T) { 62 build := testBuild() 63 if build.Name() != "test" { 64 t.Fatalf("bad: %s", build.Name()) 65 } 66 } 67 68 func TestBuild_Prepare(t *testing.T) { 69 packerConfig := testDefaultPackerConfig() 70 71 build := testBuild() 72 builder := build.Builder.(*packersdk.MockBuilder) 73 74 build.Prepare() 75 if !builder.PrepareCalled { 76 t.Fatal("should be called") 77 } 78 if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) { 79 t.Fatalf("bad: %#v", builder.PrepareConfig) 80 } 81 82 coreProv := build.Provisioners[0] 83 prov := coreProv.Provisioner.(*packersdk.MockProvisioner) 84 if !prov.PrepCalled { 85 t.Fatal("prep should be called") 86 } 87 if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, BasicPlaceholderData()}) { 88 t.Fatalf("bad: %#v", prov.PrepConfigs) 89 } 90 91 corePP := build.PostProcessors[0][0] 92 pp := corePP.PostProcessor.(*MockPostProcessor) 93 if !pp.ConfigureCalled { 94 t.Fatal("should be called") 95 } 96 if !reflect.DeepEqual(pp.ConfigureConfigs, []interface{}{make(map[string]interface{}), packerConfig, BasicPlaceholderData()}) { 97 t.Fatalf("bad: %#v", pp.ConfigureConfigs) 98 } 99 } 100 101 func TestBuild_Prepare_SkipWhenBuilderAlreadyInitialized(t *testing.T) { 102 build := testBuild() 103 builder := build.Builder.(*packersdk.MockBuilder) 104 105 build.Prepared = true 106 build.Prepare() 107 if builder.PrepareCalled { 108 t.Fatal("should not be called") 109 } 110 } 111 112 func TestBuild_Prepare_Twice(t *testing.T) { 113 build := testBuild() 114 warn, err := build.Prepare() 115 if len(warn) > 0 { 116 t.Fatalf("bad: %#v", warn) 117 } 118 if err != nil { 119 t.Fatalf("bad error: %s", err) 120 } 121 122 defer func() { 123 p := recover() 124 if p == nil { 125 t.Fatalf("should've paniced") 126 } 127 128 if p.(string) != "prepare already called" { 129 t.Fatalf("Invalid panic: %s", p) 130 } 131 }() 132 133 build.Prepare() 134 } 135 136 func TestBuildPrepare_BuilderWarnings(t *testing.T) { 137 expected := []string{"foo"} 138 139 build := testBuild() 140 builder := build.Builder.(*packersdk.MockBuilder) 141 builder.PrepareWarnings = expected 142 143 warn, err := build.Prepare() 144 if err != nil { 145 t.Fatalf("err: %s", err) 146 } 147 if !reflect.DeepEqual(warn, expected) { 148 t.Fatalf("bad: %#v", warn) 149 } 150 } 151 152 func TestBuild_Prepare_Debug(t *testing.T) { 153 packerConfig := testDefaultPackerConfig() 154 packerConfig[common.DebugConfigKey] = true 155 156 build := testBuild() 157 builder := build.Builder.(*packersdk.MockBuilder) 158 159 build.SetDebug(true) 160 build.Prepare() 161 if !builder.PrepareCalled { 162 t.Fatalf("should be called") 163 } 164 if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) { 165 t.Fatalf("bad: %#v", builder.PrepareConfig) 166 } 167 168 coreProv := build.Provisioners[0] 169 prov := coreProv.Provisioner.(*packersdk.MockProvisioner) 170 if !prov.PrepCalled { 171 t.Fatal("prepare should be called") 172 } 173 if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, BasicPlaceholderData()}) { 174 t.Fatalf("bad: %#v", prov.PrepConfigs) 175 } 176 } 177 178 func TestBuildPrepare_variables_default(t *testing.T) { 179 packerConfig := testDefaultPackerConfig() 180 packerConfig[common.UserVariablesConfigKey] = map[string]string{ 181 "foo": "bar", 182 } 183 184 build := testBuild() 185 build.Variables["foo"] = "bar" 186 builder := build.Builder.(*packersdk.MockBuilder) 187 188 warn, err := build.Prepare() 189 if len(warn) > 0 { 190 t.Fatalf("bad: %#v", warn) 191 } 192 if err != nil { 193 t.Fatalf("err: %s", err) 194 } 195 196 if !builder.PrepareCalled { 197 t.Fatal("prepare should be called") 198 } 199 200 if !reflect.DeepEqual(builder.PrepareConfig[1], packerConfig) { 201 t.Fatalf("prepare bad: %#v", builder.PrepareConfig[1]) 202 } 203 } 204 205 func TestBuildPrepare_ProvisionerGetsGeneratedMap(t *testing.T) { 206 packerConfig := testDefaultPackerConfig() 207 208 build := testBuild() 209 builder := build.Builder.(*packersdk.MockBuilder) 210 builder.GeneratedVars = []string{"PartyVar"} 211 212 build.Prepare() 213 if !builder.PrepareCalled { 214 t.Fatalf("should be called") 215 } 216 if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) { 217 t.Fatalf("bad: %#v", builder.PrepareConfig) 218 } 219 220 coreProv := build.Provisioners[0] 221 prov := coreProv.Provisioner.(*packersdk.MockProvisioner) 222 if !prov.PrepCalled { 223 t.Fatal("prepare should be called") 224 } 225 226 generated := BasicPlaceholderData() 227 generated["PartyVar"] = "Build_PartyVar. " + packerbuilderdata.PlaceholderMsg 228 if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, generated}) { 229 t.Fatalf("bad: %#v", prov.PrepConfigs) 230 } 231 } 232 233 func TestBuild_Run(t *testing.T) { 234 ui := testUi() 235 236 build := testBuild() 237 build.Prepare() 238 ctx := context.Background() 239 artifacts, err := build.Run(ctx, ui) 240 if err != nil { 241 t.Fatalf("err: %s", err) 242 } 243 if len(artifacts) != 2 { 244 t.Fatalf("bad: %#v", artifacts) 245 } 246 247 // Verify builder was run 248 builder := build.Builder.(*packersdk.MockBuilder) 249 if !builder.RunCalled { 250 t.Fatal("should be called") 251 } 252 253 // Verify hooks are dispatchable 254 dispatchHook := builder.RunHook 255 dispatchHook.Run(ctx, "foo", nil, nil, 42) 256 257 hook := build.hooks["foo"][0].(*packersdk.MockHook) 258 if !hook.RunCalled { 259 t.Fatal("should be called") 260 } 261 if hook.RunData != 42 { 262 t.Fatalf("bad: %#v", hook.RunData) 263 } 264 265 // Verify provisioners run 266 err = dispatchHook.Run(ctx, packersdk.HookProvision, nil, new(packersdk.MockCommunicator), 42) 267 if err != nil { 268 t.Fatalf("should not have errored") 269 } 270 prov := build.Provisioners[0].Provisioner.(*packersdk.MockProvisioner) 271 if !prov.ProvCalled { 272 t.Fatal("should be called") 273 } 274 275 // Verify post-processor was run 276 pp := build.PostProcessors[0][0].PostProcessor.(*MockPostProcessor) 277 if !pp.PostProcessCalled { 278 t.Fatal("should be called") 279 } 280 } 281 282 func TestBuild_Run_Artifacts(t *testing.T) { 283 ui := testUi() 284 285 // Test case: Test that with no post-processors, we only get the 286 // main build. 287 build := testBuild() 288 build.PostProcessors = [][]CoreBuildPostProcessor{} 289 290 build.Prepare() 291 artifacts, err := build.Run(context.Background(), ui) 292 if err != nil { 293 t.Fatalf("err: %s", err) 294 } 295 296 expectedIds := []string{"b"} 297 artifactIds := make([]string, len(artifacts)) 298 for i, artifact := range artifacts { 299 artifactIds[i] = artifact.Id() 300 } 301 302 if !reflect.DeepEqual(artifactIds, expectedIds) { 303 t.Fatalf("unexpected ids: %#v", artifactIds) 304 } 305 306 // Test case: Test that with a single post-processor that doesn't keep 307 // inputs, only that post-processors results are returned. 308 build = testBuild() 309 build.PostProcessors = [][]CoreBuildPostProcessor{ 310 { 311 {&MockPostProcessor{ArtifactId: "pp"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)}, 312 }, 313 } 314 315 build.Prepare() 316 artifacts, err = build.Run(context.Background(), ui) 317 if err != nil { 318 t.Fatalf("err: %s", err) 319 } 320 321 expectedIds = []string{"pp"} 322 artifactIds = make([]string, len(artifacts)) 323 for i, artifact := range artifacts { 324 artifactIds[i] = artifact.Id() 325 } 326 327 if !reflect.DeepEqual(artifactIds, expectedIds) { 328 t.Fatalf("unexpected ids: %#v", artifactIds) 329 } 330 331 // Test case: Test that with multiple post-processors, as long as one 332 // keeps the original, the original is kept. 333 build = testBuild() 334 build.PostProcessors = [][]CoreBuildPostProcessor{ 335 { 336 {&MockPostProcessor{ArtifactId: "pp1"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)}, 337 }, 338 { 339 {&MockPostProcessor{ArtifactId: "pp2"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(true)}, 340 }, 341 } 342 343 build.Prepare() 344 artifacts, err = build.Run(context.Background(), ui) 345 if err != nil { 346 t.Fatalf("err: %s", err) 347 } 348 349 expectedIds = []string{"b", "pp1", "pp2"} 350 artifactIds = make([]string, len(artifacts)) 351 for i, artifact := range artifacts { 352 artifactIds[i] = artifact.Id() 353 } 354 355 if !reflect.DeepEqual(artifactIds, expectedIds) { 356 t.Fatalf("unexpected ids: %#v", artifactIds) 357 } 358 359 // Test case: Test that with sequences, intermediaries are kept if they 360 // want to be. 361 build = testBuild() 362 build.PostProcessors = [][]CoreBuildPostProcessor{ 363 { 364 {&MockPostProcessor{ArtifactId: "pp1a"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)}, 365 {&MockPostProcessor{ArtifactId: "pp1b"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(true)}, 366 }, 367 { 368 {&MockPostProcessor{ArtifactId: "pp2a"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)}, 369 {&MockPostProcessor{ArtifactId: "pp2b"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)}, 370 }, 371 } 372 373 build.Prepare() 374 artifacts, err = build.Run(context.Background(), ui) 375 if err != nil { 376 t.Fatalf("err: %s", err) 377 } 378 379 expectedIds = []string{"pp1a", "pp1b", "pp2b"} 380 artifactIds = make([]string, len(artifacts)) 381 for i, artifact := range artifacts { 382 artifactIds[i] = artifact.Id() 383 } 384 385 if !reflect.DeepEqual(artifactIds, expectedIds) { 386 t.Fatalf("unexpected ids: %#v", artifactIds) 387 } 388 389 // Test case: Test that with a single post-processor that forcibly 390 // keeps inputs, that the artifacts are kept. 391 build = testBuild() 392 build.PostProcessors = [][]CoreBuildPostProcessor{ 393 { 394 { 395 &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: true}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false), 396 }, 397 }, 398 } 399 400 build.Prepare() 401 402 artifacts, err = build.Run(context.Background(), ui) 403 if err != nil { 404 t.Fatalf("err: %s", err) 405 } 406 407 expectedIds = []string{"b", "pp"} 408 artifactIds = make([]string, len(artifacts)) 409 for i, artifact := range artifacts { 410 artifactIds[i] = artifact.Id() 411 } 412 413 if !reflect.DeepEqual(artifactIds, expectedIds) { 414 t.Fatalf("unexpected ids: %#v", artifactIds) 415 } 416 417 // Test case: Test that with a single post-processor that non-forcibly 418 // keeps inputs, that the artifacts are discarded if user overrides. 419 build = testBuild() 420 build.PostProcessors = [][]CoreBuildPostProcessor{ 421 { 422 { 423 &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false), 424 }, 425 }, 426 } 427 428 build.Prepare() 429 artifacts, err = build.Run(context.Background(), ui) 430 if err != nil { 431 t.Fatalf("err: %s", err) 432 } 433 434 expectedIds = []string{"pp"} 435 artifactIds = make([]string, len(artifacts)) 436 for i, artifact := range artifacts { 437 artifactIds[i] = artifact.Id() 438 } 439 440 if !reflect.DeepEqual(artifactIds, expectedIds) { 441 t.Fatalf("unexpected ids: %#v", artifactIds) 442 } 443 444 // Test case: Test that with a single post-processor that non-forcibly 445 // keeps inputs, that the artifacts are kept if user does not have preference. 446 build = testBuild() 447 build.PostProcessors = [][]CoreBuildPostProcessor{ 448 { 449 { 450 &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), nil, 451 }, 452 }, 453 } 454 455 build.Prepare() 456 artifacts, err = build.Run(context.Background(), ui) 457 if err != nil { 458 t.Fatalf("err: %s", err) 459 } 460 461 expectedIds = []string{"b", "pp"} 462 artifactIds = make([]string, len(artifacts)) 463 for i, artifact := range artifacts { 464 artifactIds[i] = artifact.Id() 465 } 466 467 if !reflect.DeepEqual(artifactIds, expectedIds) { 468 t.Fatalf("unexpected ids: %#v", artifactIds) 469 } 470 } 471 472 func TestBuild_RunBeforePrepare(t *testing.T) { 473 defer func() { 474 p := recover() 475 if p == nil { 476 t.Fatal("should panic") 477 } 478 479 if p.(string) != "Prepare must be called first" { 480 t.Fatalf("bad: %s", p.(string)) 481 } 482 }() 483 484 testBuild().Run(context.Background(), testUi()) 485 } 486 487 func TestBuild_Cancel(t *testing.T) { 488 build := testBuild() 489 490 build.Prepare() 491 492 topCtx, topCtxCancel := context.WithCancel(context.Background()) 493 494 builder := build.Builder.(*packersdk.MockBuilder) 495 496 builder.RunFn = func(ctx context.Context) { 497 topCtxCancel() 498 } 499 500 _, err := build.Run(topCtx, testUi()) 501 if err == nil { 502 t.Fatal("build should err") 503 } 504 }