From f1b3c8a7ae49c4acdbb74c862956eb22538cfd17 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 13 Jun 2015 16:48:35 -0400 Subject: [PATCH 1/3] template/interpolate: build_name and build_type functions --- template/interpolate/funcs.go | 22 +++++++++++++ template/interpolate/funcs_test.go | 50 ++++++++++++++++++++++++++++++ template/interpolate/i.go | 15 ++++++--- 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index 6092707b8..e5d01b455 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -24,6 +24,8 @@ func init() { // Funcs are the interpolation funcs that are available within interpolations. var FuncGens = map[string]FuncGenerator{ + "build_name": funcGenBuildName, + "build_type": funcGenBuildType, "env": funcGenEnv, "isotime": funcGenIsotime, "pwd": funcGenPwd, @@ -56,6 +58,26 @@ func Funcs(ctx *Context) template.FuncMap { return template.FuncMap(result) } +func funcGenBuildName(ctx *Context) interface{} { + return func() (string, error) { + if ctx == nil || ctx.BuildName == "" { + return "", errors.New("build_name not available") + } + + return ctx.BuildName, nil + } +} + +func funcGenBuildType(ctx *Context) interface{} { + return func() (string, error) { + if ctx == nil || ctx.BuildType == "" { + return "", errors.New("build_name not available") + } + + return ctx.BuildType, nil + } +} + func funcGenEnv(ctx *Context) interface{} { return func(k string) (string, error) { if !ctx.EnableEnv { diff --git a/template/interpolate/funcs_test.go b/template/interpolate/funcs_test.go index ff877f13e..065942c93 100644 --- a/template/interpolate/funcs_test.go +++ b/template/interpolate/funcs_test.go @@ -8,6 +8,56 @@ import ( "time" ) +func TestFuncBuildName(t *testing.T) { + cases := []struct { + Input string + Output string + }{ + { + `{{build_name}}`, + "foo", + }, + } + + ctx := &Context{BuildName: "foo"} + for _, tc := range cases { + i := &I{Value: tc.Input} + result, err := i.Render(ctx) + if err != nil { + t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err) + } + + if result != tc.Output { + t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result) + } + } +} + +func TestFuncBuildType(t *testing.T) { + cases := []struct { + Input string + Output string + }{ + { + `{{build_type}}`, + "foo", + }, + } + + ctx := &Context{BuildType: "foo"} + for _, tc := range cases { + i := &I{Value: tc.Input} + result, err := i.Render(ctx) + if err != nil { + t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err) + } + + if result != tc.Output { + t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result) + } + } +} + func TestFuncEnv(t *testing.T) { cases := []struct { Input string diff --git a/template/interpolate/i.go b/template/interpolate/i.go index d5f7c8413..02f56197a 100644 --- a/template/interpolate/i.go +++ b/template/interpolate/i.go @@ -14,16 +14,23 @@ type Context struct { // Funcs are extra functions available in the template Funcs map[string]interface{} - // TemplatePath is the path to the template that this is being - // rendered within. - TemplatePath string - // UserVariables is the mapping of user variables that the // "user" function reads from. UserVariables map[string]string // EnableEnv enables the env function EnableEnv bool + + // All the fields below are used for built-in functions. + // + // BuildName and BuildType are the name and type, respectively, + // of the builder being used. + // + // TemplatePath is the path to the template that this is being + // rendered within. + BuildName string + BuildType string + TemplatePath string } // Render is shorthand for constructing an I and calling Render. From 472b060394622a1b9c25c0570718a0cba3d691a7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 13 Jun 2015 16:51:29 -0400 Subject: [PATCH 2/3] packer: build_name and build_type work + tests --- helper/config/decode.go | 4 ++ packer/core_test.go | 58 +++++++++++++++++++ .../test-fixtures/build-var-build-name.json | 6 ++ .../test-fixtures/build-var-build-type.json | 6 ++ 4 files changed, 74 insertions(+) create mode 100644 packer/test-fixtures/build-var-build-name.json create mode 100644 packer/test-fixtures/build-var-build-type.json diff --git a/helper/config/decode.go b/helper/config/decode.go index 0148ad27c..ccb71ac7f 100644 --- a/helper/config/decode.go +++ b/helper/config/decode.go @@ -104,6 +104,8 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { // detecting things like user variables from the raw configuration params. func DetectContext(raws ...interface{}) (*interpolate.Context, error) { var s struct { + BuildName string `mapstructure:"packer_build_name"` + BuildType string `mapstructure:"packer_builder_type"` TemplatePath string `mapstructure:"packer_template_path"` Vars map[string]string `mapstructure:"packer_user_variables"` } @@ -115,6 +117,8 @@ func DetectContext(raws ...interface{}) (*interpolate.Context, error) { } return &interpolate.Context{ + BuildName: s.BuildName, + BuildType: s.BuildType, TemplatePath: s.TemplatePath, UserVariables: s.Vars, }, nil diff --git a/packer/core_test.go b/packer/core_test.go index f11242d0c..cc958356e 100644 --- a/packer/core_test.go +++ b/packer/core_test.go @@ -142,6 +142,64 @@ func TestCoreBuild_env(t *testing.T) { } } +func TestCoreBuild_buildNameVar(t *testing.T) { + config := TestCoreConfig(t) + testCoreTemplate(t, config, fixtureDir("build-var-build-name.json")) + b := TestBuilder(t, config, "test") + core := TestCore(t, config) + + b.ArtifactId = "hello" + + build, err := core.Build("test") + if err != nil { + t.Fatalf("err: %s", err) + } + + if _, err := build.Prepare(); err != nil { + t.Fatalf("err: %s", err) + } + + // Interpolate the config + var result map[string]interface{} + err = configHelper.Decode(&result, nil, b.PrepareConfig...) + if err != nil { + t.Fatalf("err: %s", err) + } + + if result["value"] != "test" { + t.Fatalf("bad: %#v", result) + } +} + +func TestCoreBuild_buildTypeVar(t *testing.T) { + config := TestCoreConfig(t) + testCoreTemplate(t, config, fixtureDir("build-var-build-type.json")) + b := TestBuilder(t, config, "test") + core := TestCore(t, config) + + b.ArtifactId = "hello" + + build, err := core.Build("test") + if err != nil { + t.Fatalf("err: %s", err) + } + + if _, err := build.Prepare(); err != nil { + t.Fatalf("err: %s", err) + } + + // Interpolate the config + var result map[string]interface{} + err = configHelper.Decode(&result, nil, b.PrepareConfig...) + if err != nil { + t.Fatalf("err: %s", err) + } + + if result["value"] != "test" { + t.Fatalf("bad: %#v", result) + } +} + func TestCoreBuild_nonExist(t *testing.T) { config := TestCoreConfig(t) testCoreTemplate(t, config, fixtureDir("build-basic.json")) diff --git a/packer/test-fixtures/build-var-build-name.json b/packer/test-fixtures/build-var-build-name.json new file mode 100644 index 000000000..da43d9ad9 --- /dev/null +++ b/packer/test-fixtures/build-var-build-name.json @@ -0,0 +1,6 @@ +{ + "builders": [{ + "type": "test", + "value": "{{build_name}}" + }] +} diff --git a/packer/test-fixtures/build-var-build-type.json b/packer/test-fixtures/build-var-build-type.json new file mode 100644 index 000000000..3d925e406 --- /dev/null +++ b/packer/test-fixtures/build-var-build-type.json @@ -0,0 +1,6 @@ +{ + "builders": [{ + "type": "test", + "value": "{{build_type}}" + }] +} From a978bbf781ec999a93f94431f3d1d7f0a99babf0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 15 Jun 2015 09:40:11 -0700 Subject: [PATCH 3/3] website: update docs for new functions --- .../source/docs/templates/configuration-templates.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/source/docs/templates/configuration-templates.html.markdown b/website/source/docs/templates/configuration-templates.html.markdown index 514bf7820..cef1385d3 100644 --- a/website/source/docs/templates/configuration-templates.html.markdown +++ b/website/source/docs/templates/configuration-templates.html.markdown @@ -55,6 +55,8 @@ While some configuration settings have local variables specific to only that configuration, a set of functions are available globally for use in _any string_ in Packer templates. These are listed below for reference. +* `build_name` - The name of the build being run. +* `build_type` - The type of the builder being used currently. * `isotime [FORMAT]` - UTC time, which can be [formatted](http://golang.org/pkg/time/#example_Time_Format). See more examples below. * `lower` - Lowercases the string.