mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-19 14:31:40 -04:00
* Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at https://hashi.co/license-faq, and details of the license at www.hashicorp.com/bsl. * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
503 lines
13 KiB
Go
503 lines
13 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package packer
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/packer-plugin-sdk/common"
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
"github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
|
|
"github.com/hashicorp/packer/version"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func boolPointer(tf bool) *bool {
|
|
return &tf
|
|
}
|
|
|
|
func testBuild() *CoreBuild {
|
|
return &CoreBuild{
|
|
Type: "test",
|
|
Builder: &packersdk.MockBuilder{ArtifactId: "b"},
|
|
BuilderConfig: 42,
|
|
BuilderType: "foo",
|
|
hooks: map[string][]packersdk.Hook{
|
|
"foo": {&packersdk.MockHook{}},
|
|
},
|
|
Provisioners: []CoreBuildProvisioner{
|
|
{
|
|
PType: "mock-provisioner",
|
|
Provisioner: &packersdk.MockProvisioner{},
|
|
config: []interface{}{42}},
|
|
},
|
|
PostProcessors: [][]CoreBuildPostProcessor{
|
|
{
|
|
{&MockPostProcessor{ArtifactId: "pp"}, "testPP", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(true)},
|
|
},
|
|
},
|
|
Variables: make(map[string]string),
|
|
onError: "cleanup",
|
|
}
|
|
}
|
|
|
|
func testDefaultPackerConfig() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
common.BuildNameConfigKey: "test",
|
|
common.BuilderTypeConfigKey: "foo",
|
|
common.CoreVersionConfigKey: version.FormattedVersion(),
|
|
common.DebugConfigKey: false,
|
|
common.ForceConfigKey: false,
|
|
common.OnErrorConfigKey: "cleanup",
|
|
common.TemplatePathKey: "",
|
|
common.UserVariablesConfigKey: make(map[string]string),
|
|
}
|
|
}
|
|
func TestBuild_Name(t *testing.T) {
|
|
build := testBuild()
|
|
if build.Name() != "test" {
|
|
t.Fatalf("bad: %s", build.Name())
|
|
}
|
|
}
|
|
|
|
func TestBuild_Prepare(t *testing.T) {
|
|
packerConfig := testDefaultPackerConfig()
|
|
|
|
build := testBuild()
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
|
|
build.Prepare()
|
|
if !builder.PrepareCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) {
|
|
t.Fatalf("bad: %#v", builder.PrepareConfig)
|
|
}
|
|
|
|
coreProv := build.Provisioners[0]
|
|
prov := coreProv.Provisioner.(*packersdk.MockProvisioner)
|
|
if !prov.PrepCalled {
|
|
t.Fatal("prep should be called")
|
|
}
|
|
if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, BasicPlaceholderData()}) {
|
|
t.Fatalf("bad: %#v", prov.PrepConfigs)
|
|
}
|
|
|
|
corePP := build.PostProcessors[0][0]
|
|
pp := corePP.PostProcessor.(*MockPostProcessor)
|
|
if !pp.ConfigureCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
if !reflect.DeepEqual(pp.ConfigureConfigs, []interface{}{make(map[string]interface{}), packerConfig, BasicPlaceholderData()}) {
|
|
t.Fatalf("bad: %#v", pp.ConfigureConfigs)
|
|
}
|
|
}
|
|
|
|
func TestBuild_Prepare_SkipWhenBuilderAlreadyInitialized(t *testing.T) {
|
|
build := testBuild()
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
|
|
build.Prepared = true
|
|
build.Prepare()
|
|
if builder.PrepareCalled {
|
|
t.Fatal("should not be called")
|
|
}
|
|
}
|
|
|
|
func TestBuild_Prepare_Twice(t *testing.T) {
|
|
build := testBuild()
|
|
warn, err := build.Prepare()
|
|
if len(warn) > 0 {
|
|
t.Fatalf("bad: %#v", warn)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("bad error: %s", err)
|
|
}
|
|
|
|
defer func() {
|
|
p := recover()
|
|
if p == nil {
|
|
t.Fatalf("should've paniced")
|
|
}
|
|
|
|
if p.(string) != "prepare already called" {
|
|
t.Fatalf("Invalid panic: %s", p)
|
|
}
|
|
}()
|
|
|
|
build.Prepare()
|
|
}
|
|
|
|
func TestBuildPrepare_BuilderWarnings(t *testing.T) {
|
|
expected := []string{"foo"}
|
|
|
|
build := testBuild()
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
builder.PrepareWarnings = expected
|
|
|
|
warn, err := build.Prepare()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if !reflect.DeepEqual(warn, expected) {
|
|
t.Fatalf("bad: %#v", warn)
|
|
}
|
|
}
|
|
|
|
func TestBuild_Prepare_Debug(t *testing.T) {
|
|
packerConfig := testDefaultPackerConfig()
|
|
packerConfig[common.DebugConfigKey] = true
|
|
|
|
build := testBuild()
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
|
|
build.SetDebug(true)
|
|
build.Prepare()
|
|
if !builder.PrepareCalled {
|
|
t.Fatalf("should be called")
|
|
}
|
|
if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) {
|
|
t.Fatalf("bad: %#v", builder.PrepareConfig)
|
|
}
|
|
|
|
coreProv := build.Provisioners[0]
|
|
prov := coreProv.Provisioner.(*packersdk.MockProvisioner)
|
|
if !prov.PrepCalled {
|
|
t.Fatal("prepare should be called")
|
|
}
|
|
if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, BasicPlaceholderData()}) {
|
|
t.Fatalf("bad: %#v", prov.PrepConfigs)
|
|
}
|
|
}
|
|
|
|
func TestBuildPrepare_variables_default(t *testing.T) {
|
|
packerConfig := testDefaultPackerConfig()
|
|
packerConfig[common.UserVariablesConfigKey] = map[string]string{
|
|
"foo": "bar",
|
|
}
|
|
|
|
build := testBuild()
|
|
build.Variables["foo"] = "bar"
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
|
|
warn, err := build.Prepare()
|
|
if len(warn) > 0 {
|
|
t.Fatalf("bad: %#v", warn)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
if !builder.PrepareCalled {
|
|
t.Fatal("prepare should be called")
|
|
}
|
|
|
|
if !reflect.DeepEqual(builder.PrepareConfig[1], packerConfig) {
|
|
t.Fatalf("prepare bad: %#v", builder.PrepareConfig[1])
|
|
}
|
|
}
|
|
|
|
func TestBuildPrepare_ProvisionerGetsGeneratedMap(t *testing.T) {
|
|
packerConfig := testDefaultPackerConfig()
|
|
|
|
build := testBuild()
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
builder.GeneratedVars = []string{"PartyVar"}
|
|
|
|
build.Prepare()
|
|
if !builder.PrepareCalled {
|
|
t.Fatalf("should be called")
|
|
}
|
|
if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) {
|
|
t.Fatalf("bad: %#v", builder.PrepareConfig)
|
|
}
|
|
|
|
coreProv := build.Provisioners[0]
|
|
prov := coreProv.Provisioner.(*packersdk.MockProvisioner)
|
|
if !prov.PrepCalled {
|
|
t.Fatal("prepare should be called")
|
|
}
|
|
|
|
generated := BasicPlaceholderData()
|
|
generated["PartyVar"] = "Build_PartyVar. " + packerbuilderdata.PlaceholderMsg
|
|
if !reflect.DeepEqual(prov.PrepConfigs, []interface{}{42, packerConfig, generated}) {
|
|
t.Fatalf("bad: %#v", prov.PrepConfigs)
|
|
}
|
|
}
|
|
|
|
func TestBuild_Run(t *testing.T) {
|
|
ui := testUi()
|
|
|
|
build := testBuild()
|
|
build.Prepare()
|
|
ctx := context.Background()
|
|
artifacts, err := build.Run(ctx, ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if len(artifacts) != 2 {
|
|
t.Fatalf("bad: %#v", artifacts)
|
|
}
|
|
|
|
// Verify builder was run
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
if !builder.RunCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
|
|
// Verify hooks are dispatchable
|
|
dispatchHook := builder.RunHook
|
|
dispatchHook.Run(ctx, "foo", nil, nil, 42)
|
|
|
|
hook := build.hooks["foo"][0].(*packersdk.MockHook)
|
|
if !hook.RunCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
if hook.RunData != 42 {
|
|
t.Fatalf("bad: %#v", hook.RunData)
|
|
}
|
|
|
|
// Verify provisioners run
|
|
err = dispatchHook.Run(ctx, packersdk.HookProvision, nil, new(packersdk.MockCommunicator), 42)
|
|
if err != nil {
|
|
t.Fatalf("should not have errored")
|
|
}
|
|
prov := build.Provisioners[0].Provisioner.(*packersdk.MockProvisioner)
|
|
if !prov.ProvCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
|
|
// Verify post-processor was run
|
|
pp := build.PostProcessors[0][0].PostProcessor.(*MockPostProcessor)
|
|
if !pp.PostProcessCalled {
|
|
t.Fatal("should be called")
|
|
}
|
|
}
|
|
|
|
func TestBuild_Run_Artifacts(t *testing.T) {
|
|
ui := testUi()
|
|
|
|
// Test case: Test that with no post-processors, we only get the
|
|
// main build.
|
|
build := testBuild()
|
|
build.PostProcessors = [][]CoreBuildPostProcessor{}
|
|
|
|
build.Prepare()
|
|
artifacts, err := build.Run(context.Background(), ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedIds := []string{"b"}
|
|
artifactIds := make([]string, len(artifacts))
|
|
for i, artifact := range artifacts {
|
|
artifactIds[i] = artifact.Id()
|
|
}
|
|
|
|
if !reflect.DeepEqual(artifactIds, expectedIds) {
|
|
t.Fatalf("unexpected ids: %#v", artifactIds)
|
|
}
|
|
|
|
// Test case: Test that with a single post-processor that doesn't keep
|
|
// inputs, only that post-processors results are returned.
|
|
build = testBuild()
|
|
build.PostProcessors = [][]CoreBuildPostProcessor{
|
|
{
|
|
{&MockPostProcessor{ArtifactId: "pp"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)},
|
|
},
|
|
}
|
|
|
|
build.Prepare()
|
|
artifacts, err = build.Run(context.Background(), ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedIds = []string{"pp"}
|
|
artifactIds = make([]string, len(artifacts))
|
|
for i, artifact := range artifacts {
|
|
artifactIds[i] = artifact.Id()
|
|
}
|
|
|
|
if !reflect.DeepEqual(artifactIds, expectedIds) {
|
|
t.Fatalf("unexpected ids: %#v", artifactIds)
|
|
}
|
|
|
|
// Test case: Test that with multiple post-processors, as long as one
|
|
// keeps the original, the original is kept.
|
|
build = testBuild()
|
|
build.PostProcessors = [][]CoreBuildPostProcessor{
|
|
{
|
|
{&MockPostProcessor{ArtifactId: "pp1"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)},
|
|
},
|
|
{
|
|
{&MockPostProcessor{ArtifactId: "pp2"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(true)},
|
|
},
|
|
}
|
|
|
|
build.Prepare()
|
|
artifacts, err = build.Run(context.Background(), ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedIds = []string{"b", "pp1", "pp2"}
|
|
artifactIds = make([]string, len(artifacts))
|
|
for i, artifact := range artifacts {
|
|
artifactIds[i] = artifact.Id()
|
|
}
|
|
|
|
if !reflect.DeepEqual(artifactIds, expectedIds) {
|
|
t.Fatalf("unexpected ids: %#v", artifactIds)
|
|
}
|
|
|
|
// Test case: Test that with sequences, intermediaries are kept if they
|
|
// want to be.
|
|
build = testBuild()
|
|
build.PostProcessors = [][]CoreBuildPostProcessor{
|
|
{
|
|
{&MockPostProcessor{ArtifactId: "pp1a"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)},
|
|
{&MockPostProcessor{ArtifactId: "pp1b"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(true)},
|
|
},
|
|
{
|
|
{&MockPostProcessor{ArtifactId: "pp2a"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)},
|
|
{&MockPostProcessor{ArtifactId: "pp2b"}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false)},
|
|
},
|
|
}
|
|
|
|
build.Prepare()
|
|
artifacts, err = build.Run(context.Background(), ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedIds = []string{"pp1a", "pp1b", "pp2b"}
|
|
artifactIds = make([]string, len(artifacts))
|
|
for i, artifact := range artifacts {
|
|
artifactIds[i] = artifact.Id()
|
|
}
|
|
|
|
if !reflect.DeepEqual(artifactIds, expectedIds) {
|
|
t.Fatalf("unexpected ids: %#v", artifactIds)
|
|
}
|
|
|
|
// Test case: Test that with a single post-processor that forcibly
|
|
// keeps inputs, that the artifacts are kept.
|
|
build = testBuild()
|
|
build.PostProcessors = [][]CoreBuildPostProcessor{
|
|
{
|
|
{
|
|
&MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: true}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false),
|
|
},
|
|
},
|
|
}
|
|
|
|
build.Prepare()
|
|
|
|
artifacts, err = build.Run(context.Background(), ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedIds = []string{"b", "pp"}
|
|
artifactIds = make([]string, len(artifacts))
|
|
for i, artifact := range artifacts {
|
|
artifactIds[i] = artifact.Id()
|
|
}
|
|
|
|
if !reflect.DeepEqual(artifactIds, expectedIds) {
|
|
t.Fatalf("unexpected ids: %#v", artifactIds)
|
|
}
|
|
|
|
// Test case: Test that with a single post-processor that non-forcibly
|
|
// keeps inputs, that the artifacts are discarded if user overrides.
|
|
build = testBuild()
|
|
build.PostProcessors = [][]CoreBuildPostProcessor{
|
|
{
|
|
{
|
|
&MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), boolPointer(false),
|
|
},
|
|
},
|
|
}
|
|
|
|
build.Prepare()
|
|
artifacts, err = build.Run(context.Background(), ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedIds = []string{"pp"}
|
|
artifactIds = make([]string, len(artifacts))
|
|
for i, artifact := range artifacts {
|
|
artifactIds[i] = artifact.Id()
|
|
}
|
|
|
|
if !reflect.DeepEqual(artifactIds, expectedIds) {
|
|
t.Fatalf("unexpected ids: %#v", artifactIds)
|
|
}
|
|
|
|
// Test case: Test that with a single post-processor that non-forcibly
|
|
// keeps inputs, that the artifacts are kept if user does not have preference.
|
|
build = testBuild()
|
|
build.PostProcessors = [][]CoreBuildPostProcessor{
|
|
{
|
|
{
|
|
&MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", "testPPName", cty.Value{}, make(map[string]interface{}), nil,
|
|
},
|
|
},
|
|
}
|
|
|
|
build.Prepare()
|
|
artifacts, err = build.Run(context.Background(), ui)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
expectedIds = []string{"b", "pp"}
|
|
artifactIds = make([]string, len(artifacts))
|
|
for i, artifact := range artifacts {
|
|
artifactIds[i] = artifact.Id()
|
|
}
|
|
|
|
if !reflect.DeepEqual(artifactIds, expectedIds) {
|
|
t.Fatalf("unexpected ids: %#v", artifactIds)
|
|
}
|
|
}
|
|
|
|
func TestBuild_RunBeforePrepare(t *testing.T) {
|
|
defer func() {
|
|
p := recover()
|
|
if p == nil {
|
|
t.Fatal("should panic")
|
|
}
|
|
|
|
if p.(string) != "Prepare must be called first" {
|
|
t.Fatalf("bad: %s", p.(string))
|
|
}
|
|
}()
|
|
|
|
testBuild().Run(context.Background(), testUi())
|
|
}
|
|
|
|
func TestBuild_Cancel(t *testing.T) {
|
|
build := testBuild()
|
|
|
|
build.Prepare()
|
|
|
|
topCtx, topCtxCancel := context.WithCancel(context.Background())
|
|
|
|
builder := build.Builder.(*packersdk.MockBuilder)
|
|
|
|
builder.RunFn = func(ctx context.Context) {
|
|
topCtxCancel()
|
|
}
|
|
|
|
_, err := build.Run(topCtx, testUi())
|
|
if err == nil {
|
|
t.Fatal("build should err")
|
|
}
|
|
}
|