packer: builder prepare can return warnings

This commit is contained in:
Mitchell Hashimoto 2013-11-02 22:31:12 -05:00
parent 24db112b79
commit 336051e316
7 changed files with 82 additions and 76 deletions

View File

@ -38,8 +38,9 @@ type Build interface {
Name() string Name() string
// Prepare configures the various components of this build and reports // Prepare configures the various components of this build and reports
// any errors in doing so (such as syntax errors, validation errors, etc.) // any errors in doing so (such as syntax errors, validation errors, etc.).
Prepare(v map[string]string) error // It also reports any warnings.
Prepare(v map[string]string) ([]string, error)
// Run runs the actual builder, returning an artifact implementation // Run runs the actual builder, returning an artifact implementation
// of what is built. If anything goes wrong, an error is returned. // of what is built. If anything goes wrong, an error is returned.
@ -115,7 +116,7 @@ func (b *coreBuild) Name() string {
// Prepare prepares the build by doing some initialization for the builder // 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 // and any hooks. This _must_ be called prior to Run. The parameter is the
// overrides for the variables within the template (if any). // overrides for the variables within the template (if any).
func (b *coreBuild) Prepare(userVars map[string]string) (err error) { func (b *coreBuild) Prepare(userVars map[string]string) (warn []string, err error) {
b.l.Lock() b.l.Lock()
defer b.l.Unlock() defer b.l.Unlock()
@ -154,7 +155,7 @@ func (b *coreBuild) Prepare(userVars map[string]string) (err error) {
// If there were any problem with variables, return an error right // If there were any problem with variables, return an error right
// away because we can't be certain anything else will actually work. // away because we can't be certain anything else will actually work.
if len(varErrs) > 0 { if len(varErrs) > 0 {
return &MultiError{ return nil, &MultiError{
Errors: varErrs, Errors: varErrs,
} }
} }
@ -168,7 +169,7 @@ func (b *coreBuild) Prepare(userVars map[string]string) (err error) {
} }
// Prepare the builder // Prepare the builder
err = b.builder.Prepare(b.builderConfig, packerConfig) warn, err = b.builder.Prepare(b.builderConfig, packerConfig)
if err != nil { if err != nil {
log.Printf("Build '%s' prepare failure: %s\n", b.name, err) log.Printf("Build '%s' prepare failure: %s\n", b.name, err)
return return

View File

@ -8,7 +8,7 @@ import (
func testBuild() *coreBuild { func testBuild() *coreBuild {
return &coreBuild{ return &coreBuild{
name: "test", name: "test",
builder: &TestBuilder{artifactId: "b"}, builder: &MockBuilder{ArtifactId: "b"},
builderConfig: 42, builderConfig: 42,
builderType: "foo", builderType: "foo",
hooks: map[string][]Hook{ hooks: map[string][]Hook{
@ -26,10 +26,6 @@ func testBuild() *coreBuild {
} }
} }
func testBuilder() *TestBuilder {
return &TestBuilder{}
}
func testDefaultPackerConfig() map[string]interface{} { func testDefaultPackerConfig() map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
BuildNameConfigKey: "test", BuildNameConfigKey: "test",
@ -50,14 +46,14 @@ func TestBuild_Prepare(t *testing.T) {
packerConfig := testDefaultPackerConfig() packerConfig := testDefaultPackerConfig()
build := testBuild() build := testBuild()
builder := build.builder.(*TestBuilder) builder := build.builder.(*MockBuilder)
build.Prepare(nil) build.Prepare(nil)
if !builder.prepareCalled { if !builder.PrepareCalled {
t.Fatal("should be called") t.Fatal("should be called")
} }
if !reflect.DeepEqual(builder.prepareConfig, []interface{}{42, packerConfig}) { if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) {
t.Fatalf("bad: %#v", builder.prepareConfig) t.Fatalf("bad: %#v", builder.PrepareConfig)
} }
coreProv := build.provisioners[0] coreProv := build.provisioners[0]
@ -81,7 +77,11 @@ func TestBuild_Prepare(t *testing.T) {
func TestBuild_Prepare_Twice(t *testing.T) { func TestBuild_Prepare_Twice(t *testing.T) {
build := testBuild() build := testBuild()
if err := build.Prepare(nil); err != nil { warn, err := build.Prepare(nil)
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err != nil {
t.Fatalf("bad error: %s", err) t.Fatalf("bad error: %s", err)
} }
@ -99,20 +99,36 @@ func TestBuild_Prepare_Twice(t *testing.T) {
build.Prepare(nil) build.Prepare(nil)
} }
func TestBuildPrepare_BuilderWarniings(t *testing.T) {
expected := []string{"foo"}
build := testBuild()
builder := build.builder.(*MockBuilder)
builder.PrepareWarnings = expected
warn, err := build.Prepare(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(warn, expected) {
t.Fatalf("bad: %#v", warn)
}
}
func TestBuild_Prepare_Debug(t *testing.T) { func TestBuild_Prepare_Debug(t *testing.T) {
packerConfig := testDefaultPackerConfig() packerConfig := testDefaultPackerConfig()
packerConfig[DebugConfigKey] = true packerConfig[DebugConfigKey] = true
build := testBuild() build := testBuild()
builder := build.builder.(*TestBuilder) builder := build.builder.(*MockBuilder)
build.SetDebug(true) build.SetDebug(true)
build.Prepare(nil) build.Prepare(nil)
if !builder.prepareCalled { if !builder.PrepareCalled {
t.Fatalf("should be called") t.Fatalf("should be called")
} }
if !reflect.DeepEqual(builder.prepareConfig, []interface{}{42, packerConfig}) { if !reflect.DeepEqual(builder.PrepareConfig, []interface{}{42, packerConfig}) {
t.Fatalf("bad: %#v", builder.prepareConfig) t.Fatalf("bad: %#v", builder.PrepareConfig)
} }
coreProv := build.provisioners[0] coreProv := build.provisioners[0]
@ -133,19 +149,22 @@ func TestBuildPrepare_variables_default(t *testing.T) {
build := testBuild() build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"} build.variables["foo"] = coreBuildVariable{Default: "bar"}
builder := build.builder.(*TestBuilder) builder := build.builder.(*MockBuilder)
err := build.Prepare(nil) warn, err := build.Prepare(nil)
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
if !builder.prepareCalled { if !builder.PrepareCalled {
t.Fatal("prepare should be called") t.Fatal("prepare should be called")
} }
if !reflect.DeepEqual(builder.prepareConfig[1], packerConfig) { if !reflect.DeepEqual(builder.PrepareConfig[1], packerConfig) {
t.Fatalf("prepare bad: %#v", builder.prepareConfig[1]) t.Fatalf("prepare bad: %#v", builder.PrepareConfig[1])
} }
} }
@ -153,7 +172,10 @@ func TestBuildPrepare_variables_nonexist(t *testing.T) {
build := testBuild() build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"} build.variables["foo"] = coreBuildVariable{Default: "bar"}
err := build.Prepare(map[string]string{"bar": "baz"}) warn, err := build.Prepare(map[string]string{"bar": "baz"})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err == nil { if err == nil {
t.Fatal("should have had error") t.Fatal("should have had error")
} }
@ -167,19 +189,22 @@ func TestBuildPrepare_variables_override(t *testing.T) {
build := testBuild() build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"} build.variables["foo"] = coreBuildVariable{Default: "bar"}
builder := build.builder.(*TestBuilder) builder := build.builder.(*MockBuilder)
err := build.Prepare(map[string]string{"foo": "baz"}) warn, err := build.Prepare(map[string]string{"foo": "baz"})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
if !builder.prepareCalled { if !builder.PrepareCalled {
t.Fatal("prepare should be called") t.Fatal("prepare should be called")
} }
if !reflect.DeepEqual(builder.prepareConfig[1], packerConfig) { if !reflect.DeepEqual(builder.PrepareConfig[1], packerConfig) {
t.Fatalf("prepare bad: %#v", builder.prepareConfig[1]) t.Fatalf("prepare bad: %#v", builder.PrepareConfig[1])
} }
} }
@ -187,7 +212,10 @@ func TestBuildPrepare_variablesRequired(t *testing.T) {
build := testBuild() build := testBuild()
build.variables["foo"] = coreBuildVariable{Required: true} build.variables["foo"] = coreBuildVariable{Required: true}
err := build.Prepare(map[string]string{}) warn, err := build.Prepare(map[string]string{})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err == nil { if err == nil {
t.Fatal("should have had error") t.Fatal("should have had error")
} }
@ -195,7 +223,10 @@ func TestBuildPrepare_variablesRequired(t *testing.T) {
// Test with setting the value // Test with setting the value
build = testBuild() build = testBuild()
build.variables["foo"] = coreBuildVariable{Required: true} build.variables["foo"] = coreBuildVariable{Required: true}
err = build.Prepare(map[string]string{"foo": ""}) warn, err = build.Prepare(map[string]string{"foo": ""})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err != nil { if err != nil {
t.Fatalf("should not have error: %s", err) t.Fatalf("should not have error: %s", err)
} }
@ -216,13 +247,13 @@ func TestBuild_Run(t *testing.T) {
} }
// Verify builder was run // Verify builder was run
builder := build.builder.(*TestBuilder) builder := build.builder.(*MockBuilder)
if !builder.runCalled { if !builder.RunCalled {
t.Fatal("should be called") t.Fatal("should be called")
} }
// Verify hooks are disapatchable // Verify hooks are disapatchable
dispatchHook := builder.runHook dispatchHook := builder.RunHook
dispatchHook.Run("foo", nil, nil, 42) dispatchHook.Run("foo", nil, nil, 42)
hook := build.hooks["foo"][0].(*MockHook) hook := build.hooks["foo"][0].(*MockHook)
@ -402,8 +433,8 @@ func TestBuild_Cancel(t *testing.T) {
build := testBuild() build := testBuild()
build.Cancel() build.Cancel()
builder := build.builder.(*TestBuilder) builder := build.builder.(*MockBuilder)
if !builder.cancelCalled { if !builder.CancelCalled {
t.Fatal("cancel should be called") t.Fatal("cancel should be called")
} }
} }

View File

@ -22,7 +22,10 @@ type Builder interface {
// //
// Each of the configuration values should merge into the final // Each of the configuration values should merge into the final
// configuration. // configuration.
Prepare(...interface{}) error //
// Prepare should return a list of warnings along with any errors
// that occured while preparing.
Prepare(...interface{}) ([]string, error)
// Run is where the actual build should take place. It takes a Build and a Ui. // Run is where the actual build should take place. It takes a Build and a Ui.
Run(ui Ui, hook Hook, cache Cache) (Artifact, error) Run(ui Ui, hook Hook, cache Cache) (Artifact, error)

View File

@ -4,7 +4,8 @@ package packer
// You can set some fake return values and you can keep track of what // You can set some fake return values and you can keep track of what
// methods were called on the builder. It is fairly basic. // methods were called on the builder. It is fairly basic.
type MockBuilder struct { type MockBuilder struct {
ArtifactId string ArtifactId string
PrepareWarnings []string
PrepareCalled bool PrepareCalled bool
PrepareConfig []interface{} PrepareConfig []interface{}
@ -15,10 +16,10 @@ type MockBuilder struct {
CancelCalled bool CancelCalled bool
} }
func (tb *MockBuilder) Prepare(config ...interface{}) error { func (tb *MockBuilder) Prepare(config ...interface{}) ([]string, error) {
tb.PrepareCalled = true tb.PrepareCalled = true
tb.PrepareConfig = config tb.PrepareConfig = config
return nil return tb.PrepareWarnings, nil
} }
func (tb *MockBuilder) Run(ui Ui, h Hook, c Cache) (Artifact, error) { func (tb *MockBuilder) Run(ui Ui, h Hook, c Cache) (Artifact, error) {

View File

@ -1,31 +1 @@
package packer package packer
type TestBuilder struct {
artifactId string
prepareCalled bool
prepareConfig []interface{}
runCalled bool
runCache Cache
runHook Hook
runUi Ui
cancelCalled bool
}
func (tb *TestBuilder) Prepare(config ...interface{}) error {
tb.prepareCalled = true
tb.prepareConfig = config
return nil
}
func (tb *TestBuilder) Run(ui Ui, h Hook, c Cache) (Artifact, error) {
tb.runCalled = true
tb.runHook = h
tb.runUi = ui
tb.runCache = c
return &TestArtifact{id: tb.artifactId}, nil
}
func (tb *TestBuilder) Cancel() {
tb.cancelCalled = true
}

View File

@ -17,7 +17,7 @@ func init() {
} }
func testComponentFinder() *ComponentFinder { func testComponentFinder() *ComponentFinder {
builderFactory := func(n string) (Builder, error) { return testBuilder(), nil } builderFactory := func(n string) (Builder, error) { return new(MockBuilder), nil }
ppFactory := func(n string) (PostProcessor, error) { return new(TestPostProcessor), nil } ppFactory := func(n string) (PostProcessor, error) { return new(TestPostProcessor), nil }
provFactory := func(n string) (Provisioner, error) { return new(MockProvisioner), nil } provFactory := func(n string) (Provisioner, error) { return new(MockProvisioner), nil }
return &ComponentFinder{ return &ComponentFinder{
@ -97,7 +97,7 @@ func TestEnvironment_NilComponents(t *testing.T) {
} }
func TestEnvironment_Builder(t *testing.T) { func TestEnvironment_Builder(t *testing.T) {
builder := &TestBuilder{} builder := &MockBuilder{}
builders := make(map[string]Builder) builders := make(map[string]Builder)
builders["foo"] = builder builders["foo"] = builder

View File

@ -9,7 +9,7 @@ import (
) )
func testTemplateComponentFinder() *ComponentFinder { func testTemplateComponentFinder() *ComponentFinder {
builder := testBuilder() builder := new(MockBuilder)
pp := new(TestPostProcessor) pp := new(TestPostProcessor)
provisioner := &MockProvisioner{} provisioner := &MockProvisioner{}
@ -706,7 +706,7 @@ func TestTemplate_Build(t *testing.T) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
builder := testBuilder() builder := new(MockBuilder)
builderMap := map[string]Builder{ builderMap := map[string]Builder{
"test-builder": builder, "test-builder": builder,
} }
@ -1194,7 +1194,7 @@ func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
t.Fatalf("bad raw: %#v", RawConfig) t.Fatalf("bad raw: %#v", RawConfig)
} }
builder := testBuilder() builder := new(MockBuilder)
builderMap := map[string]Builder{ builderMap := map[string]Builder{
"test-builder": builder, "test-builder": builder,
} }