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  }