diff --git a/helper/config/decode.go b/helper/config/decode.go index 1088fd19b..177f8f930 100644 --- a/helper/config/decode.go +++ b/helper/config/decode.go @@ -106,6 +106,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"` } @@ -117,6 +119,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}}" + }] +} 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. 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.