diff --git a/packer/build.go b/packer/build.go index b4d30fa56..bff34767e 100644 --- a/packer/build.go +++ b/packer/build.go @@ -23,6 +23,10 @@ const ( // This is the key in configurations that is set to "true" when Packer // force build is enabled. ForceConfigKey = "packer_force" + + // This key contains a map[string]string of the user variables for + // template processing. + UserVariablesConfigKey = "packer_user_variables" ) // A Build represents a single job within Packer that is responsible for @@ -105,7 +109,7 @@ func (b *coreBuild) Name() string { // Prepare prepares the build by doing some initialization for the builder // and any hooks. This _must_ be called prior to Run. The parameter is the // overrides for the variables within the template (if any). -func (b *coreBuild) Prepare(v map[string]string) (err error) { +func (b *coreBuild) Prepare(userVars map[string]string) (err error) { b.l.Lock() defer b.l.Unlock() @@ -115,11 +119,37 @@ func (b *coreBuild) Prepare(v map[string]string) (err error) { b.prepareCalled = true + // Compile the variables + variables := make(map[string]string) + for k, v := range b.variables { + variables[k] = v + } + + if userVars != nil { + errs := make([]error, 0) + for k, v := range userVars { + if _, ok := variables[k]; !ok { + errs = append( + errs, fmt.Errorf("Unknown user variable: %s", k)) + continue + } + + variables[k] = v + } + + if len(errs) > 0 { + return &MultiError{ + Errors: errs, + } + } + } + packerConfig := map[string]interface{}{ - BuildNameConfigKey: b.name, - BuilderTypeConfigKey: b.builderType, - DebugConfigKey: b.debug, - ForceConfigKey: b.force, + BuildNameConfigKey: b.name, + BuilderTypeConfigKey: b.builderType, + DebugConfigKey: b.debug, + ForceConfigKey: b.force, + UserVariablesConfigKey: variables, } // Prepare the builder diff --git a/packer/build_test.go b/packer/build_test.go index 00b77e6be..baf33cfa9 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -23,6 +23,7 @@ func testBuild() *coreBuild { coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp"}, "testPP", 42, true}, }, }, + variables: make(map[string]string), } } @@ -30,6 +31,15 @@ func testBuilder() *TestBuilder { return &TestBuilder{} } +func testDefaultPackerConfig() map[string]interface{} { + return map[string]interface{}{ + BuildNameConfigKey: "test", + BuilderTypeConfigKey: "foo", + DebugConfigKey: false, + ForceConfigKey: false, + UserVariablesConfigKey: make(map[string]string), + } +} func TestBuild_Name(t *testing.T) { assert := asserts.NewTestingAsserts(t, true) @@ -39,13 +49,7 @@ func TestBuild_Name(t *testing.T) { func TestBuild_Prepare(t *testing.T) { assert := asserts.NewTestingAsserts(t, true) - - packerConfig := map[string]interface{}{ - BuildNameConfigKey: "test", - BuilderTypeConfigKey: "foo", - DebugConfigKey: false, - ForceConfigKey: false, - } + packerConfig := testDefaultPackerConfig() build := testBuild() builder := build.builder.(*TestBuilder) @@ -88,12 +92,8 @@ func TestBuild_Prepare_Twice(t *testing.T) { func TestBuild_Prepare_Debug(t *testing.T) { assert := asserts.NewTestingAsserts(t, true) - packerConfig := map[string]interface{}{ - BuildNameConfigKey: "test", - BuilderTypeConfigKey: "foo", - DebugConfigKey: true, - ForceConfigKey: false, - } + packerConfig := testDefaultPackerConfig() + packerConfig[DebugConfigKey] = true build := testBuild() builder := build.builder.(*TestBuilder) @@ -109,6 +109,64 @@ func TestBuild_Prepare_Debug(t *testing.T) { assert.Equal(prov.prepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config") } +func TestBuildPrepare_variables_default(t *testing.T) { + packerConfig := testDefaultPackerConfig() + packerConfig[UserVariablesConfigKey] = map[string]string{ + "foo": "bar", + } + + build := testBuild() + build.variables["foo"] = "bar" + builder := build.builder.(*TestBuilder) + + err := build.Prepare(nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !builder.prepareCalled { + t.Fatal("prepare should be called") + } + + if !reflect.DeepEqual(builder.prepareConfig[1], packerConfig) { + t.Fatalf("prepare bad: %#v", builder.prepareConfig[1]) + } +} + +func TestBuildPrepare_variables_nonexist(t *testing.T) { + build := testBuild() + build.variables["foo"] = "bar" + + err := build.Prepare(map[string]string{"bar": "baz"}) + if err == nil { + t.Fatal("should have had error") + } +} + +func TestBuildPrepare_variables_override(t *testing.T) { + packerConfig := testDefaultPackerConfig() + packerConfig[UserVariablesConfigKey] = map[string]string{ + "foo": "baz", + } + + build := testBuild() + build.variables["foo"] = "bar" + builder := build.builder.(*TestBuilder) + + err := build.Prepare(map[string]string{"foo": "baz"}) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !builder.prepareCalled { + t.Fatal("prepare should be called") + } + + if !reflect.DeepEqual(builder.prepareConfig[1], packerConfig) { + t.Fatalf("prepare bad: %#v", builder.prepareConfig[1]) + } +} + func TestBuild_Run(t *testing.T) { assert := asserts.NewTestingAsserts(t, true)