From 23fa3107a3f1cd8a3b330c32b8030fad6f9ddb57 Mon Sep 17 00:00:00 2001 From: Sylvia Moss Date: Thu, 13 Feb 2020 17:35:23 +0100 Subject: [PATCH] Render variables one time on prepare method (#8727) --- builder/null/artifact_export.go | 5 +++ builder/null/builder.go | 1 + command/build_test.go | 32 ++++++++++++++++++- .../build-variable-sharing/template.json | 25 +++++++++++++++ packer/provisioner.go | 7 ++++ template/interpolate/i.go | 7 +++- template/interpolate/render.go | 2 +- 7 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 command/test-fixtures/build-variable-sharing/template.json diff --git a/builder/null/artifact_export.go b/builder/null/artifact_export.go index 3962358e0..e3d23236d 100644 --- a/builder/null/artifact_export.go +++ b/builder/null/artifact_export.go @@ -25,6 +25,11 @@ func (a *NullArtifact) String() string { } func (a *NullArtifact) State(name string) interface{} { + if name == "generated_data" { + return map[interface{}]interface{}{ + "ID": "Null", + } + } return nil } diff --git a/builder/null/builder.go b/builder/null/builder.go index e0568ce05..1d2fc40e4 100644 --- a/builder/null/builder.go +++ b/builder/null/builder.go @@ -47,6 +47,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state := new(multistep.BasicStateBag) state.Put("hook", hook) state.Put("ui", ui) + state.Put("instance_id", "Null") // Run! b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) diff --git a/command/build_test.go b/command/build_test.go index ca8252715..76e49d65a 100644 --- a/command/build_test.go +++ b/command/build_test.go @@ -111,6 +111,33 @@ func TestBuildOnlyFileMultipleFlags(t *testing.T) { } } +func TestBuildProvisionAndPosProcessWithBuildVariablesSharing(t *testing.T) { + c := &BuildCommand{ + Meta: testMetaFile(t), + } + + args := []string{ + filepath.Join(testFixture("build-variable-sharing"), "template.json"), + } + + files := []string{ + "provisioner.Null.txt", + "post-processor.Null.txt", + } + + defer cleanup(files...) + + if code := c.Run(args); code != 0 { + fatalCommand(t, c.Meta) + } + + for _, f := range files { + if !fileExists(f) { + t.Errorf("Expected to find %s", f) + } + } +} + func TestBuildEverything(t *testing.T) { c := &BuildCommand{ Meta: testMetaFile(t), @@ -231,7 +258,7 @@ func testMetaFile(t *testing.T) Meta { } } -func cleanup() { +func cleanup(moreFiles ...string) { os.RemoveAll("chocolate.txt") os.RemoveAll("vanilla.txt") os.RemoveAll("cherry.txt") @@ -245,6 +272,9 @@ func cleanup() { os.RemoveAll("lilas.txt") os.RemoveAll("campanules.txt") os.RemoveAll("ducky.txt") + for _, file := range moreFiles { + os.RemoveAll(file) + } } func TestBuildCommand_ParseArgs(t *testing.T) { diff --git a/command/test-fixtures/build-variable-sharing/template.json b/command/test-fixtures/build-variable-sharing/template.json new file mode 100644 index 000000000..ac1c410b5 --- /dev/null +++ b/command/test-fixtures/build-variable-sharing/template.json @@ -0,0 +1,25 @@ +{ + "builders": [ + { + "name": "chocolate", + "type": "null", + "communicator": "none" + } + ], + "provisioners": [ + { + "type": "shell-local", + "inline": [ + "echo hi > provisioner.{{ build `ID`}}.txt" + ] + } + ], + "post-processors": [ + { + "type": "shell-local", + "inline": [ + "echo hi > post-processor.{{ build `ID`}}.txt" + ] + } + ] +} \ No newline at end of file diff --git a/packer/provisioner.go b/packer/provisioner.go index fce6355f3..7b7fdaa60 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -80,6 +80,13 @@ func BasicPlaceholderData() map[string]string { } func CastDataToMap(data interface{}) map[string]interface{} { + + if interMap, ok := data.(map[string]interface{}); ok { + // null and file builder sometimes don't use a communicator and + // therefore don't go through RPC + return interMap + } + // Provisioners expect a map[string]interface{} in their data field, but // it gets converted into a map[interface]interface on the way over the // RPC. Check that data can be cast into such a form, and cast it. diff --git a/template/interpolate/i.go b/template/interpolate/i.go index 9dcdc4ba8..54d1e516e 100644 --- a/template/interpolate/i.go +++ b/template/interpolate/i.go @@ -44,7 +44,12 @@ func NewContext() *Context { return &Context{} } -// Render is shorthand for constructing an I and calling Render. +// RenderOnce is shorthand for constructing an I and calling Render one time. +func RenderOnce(v string, ctx *Context) (string, error) { + return (&I{Value: v}).Render(ctx) +} + +// Render is shorthand for constructing an I and calling Render until all variables are rendered. func Render(v string, ctx *Context) (rendered string, err error) { // Keep interpolating until all variables are done // Sometimes a variable can been inside another one diff --git a/template/interpolate/render.go b/template/interpolate/render.go index 4c49d43b8..7893e8745 100644 --- a/template/interpolate/render.go +++ b/template/interpolate/render.go @@ -57,7 +57,7 @@ func RenderMap(v interface{}, ctx *Context, f *RenderFilter) (map[string]interfa // RenderInterface renders any value and returns the resulting value. func RenderInterface(v interface{}, ctx *Context) (interface{}, error) { f := func(v string) (string, error) { - return Render(v, ctx) + return RenderOnce(v, ctx) } walker := &renderWalker{