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  }