diff --git a/packer/build.go b/packer/build.go index 67f9b80e8..c038ccd5b 100644 --- a/packer/build.go +++ b/packer/build.go @@ -7,6 +7,7 @@ package packer type Build struct { name string builder Builder + rawConfig interface{} prepareCalled bool } @@ -43,9 +44,9 @@ func (NilBuilderFactory) CreateBuilder(name string) Builder { // Prepare prepares the build by doing some initialization for the builder // and any hooks. This _must_ be called prior to Run. -func (b *Build) Prepare(config interface{}) { +func (b *Build) Prepare() { b.prepareCalled = true - b.builder.Prepare(config) + b.builder.Prepare(b.rawConfig) } // Runs the actual build. Prepare must be called prior to running this. diff --git a/packer/build_test.go b/packer/build_test.go index 7359a05f2..fd1522a48 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -5,6 +5,14 @@ import ( "testing" ) +type hashBuilderFactory struct { + builderMap map[string]Builder +} + +func (bf *hashBuilderFactory) CreateBuilder(name string) Builder { + return bf.builderMap[name] +} + type TestBuilder struct { prepareCalled bool prepareConfig interface{} @@ -28,17 +36,25 @@ func testBuild() *Build { return &Build{ name: "test", builder: &TestBuilder{}, + rawConfig: 42, } } +func testBuilder() *TestBuilder { + return &TestBuilder{} +} + +func testBuildFactory(builderMap map[string]Builder) BuilderFactory { + return &hashBuilderFactory{builderMap} +} + func TestBuild_Prepare(t *testing.T) { assert := asserts.NewTestingAsserts(t, true) build := testBuild() - build.Prepare(42) - builder := build.builder.(*TestBuilder) + build.Prepare() assert.True(builder.prepareCalled, "prepare should be called") assert.Equal(builder.prepareConfig, 42, "prepare config should be 42") } @@ -49,7 +65,7 @@ func TestBuild_Run(t *testing.T) { ui := testUi() build := testBuild() - build.Prepare(nil) + build.Prepare() build.Run(ui) builder := build.builder.(*TestBuilder) diff --git a/packer/template.go b/packer/template.go index bdd170181..429599e0f 100644 --- a/packer/template.go +++ b/packer/template.go @@ -1,6 +1,9 @@ package packer -import "encoding/json" +import ( + "encoding/json" + "fmt" +) // The rawTemplate struct represents the structure of a template read // directly from a file. The builders and other components map just to @@ -74,3 +77,42 @@ func ParseTemplate(data []byte) (t *Template, err error) { return } + +// BuildNames returns a slice of the available names of builds that +// this template represents. +func (t *Template) BuildNames() []string { + names := make([]string, len(t.Builders)) + i := 0 + for name, _ := range t.Builders { + names[i] = name + i++ + } + + return names +} + +// Build returns a Build for the given name. +// +// If the build does not exist as part of this template, an error is +// returned. +func (t *Template) Build(name string, bf BuilderFactory) (b *Build, err error) { + builderConfig, ok := t.Builders[name] + if !ok { + err = fmt.Errorf("No such build found in template: %s", name) + return + } + + builder := bf.CreateBuilder(builderConfig.builderName) + if builder == nil { + err = fmt.Errorf("Builder could not be found: %s", builderConfig.builderName) + return + } + + b = &Build{ + name: name, + builder: builder, + rawConfig: builderConfig.rawConfig, + } + + return +} diff --git a/packer/template_test.go b/packer/template_test.go index b9adbe73b..f49b99d72 100644 --- a/packer/template_test.go +++ b/packer/template_test.go @@ -2,6 +2,7 @@ package packer import ( "cgl.tideland.biz/asserts" + "sort" "testing" ) @@ -87,3 +88,121 @@ func TestParseTemplate_BuilderWithName(t *testing.T) { assert.True(ok, "should have bob builder") assert.Equal(builder.builderName, "amazon-ebs", "builder should be amazon-ebs") } + +func TestTemplate_BuildNames(t *testing.T) { + assert := asserts.NewTestingAsserts(t, true) + + data := ` + { + "name": "my-image", + "builders": [ + { + "name": "bob", + "type": "amazon-ebs" + }, + { + "name": "chris", + "type": "another" + } + ] + } + ` + + result, err := ParseTemplate([]byte(data)) + assert.Nil(err, "should not error") + + buildNames := result.BuildNames() + sort.Strings(buildNames) + assert.Equal(buildNames, []string{"bob", "chris"}, "should have proper builds") +} + +func TestTemplate_BuildUnknown(t *testing.T) { + assert := asserts.NewTestingAsserts(t, true) + + data := ` + { + "name": "my-image", + "builders": [ + { + "name": "test1", + "type": "test-builder" + } + ] + } + ` + + template, err := ParseTemplate([]byte(data)) + assert.Nil(err, "should not error") + + build, err := template.Build("nope", nil) + assert.Nil(build, "build should be nil") + assert.NotNil(err, "should have error") +} + +func TestTemplate_BuildUnknownBuilder(t *testing.T) { + assert := asserts.NewTestingAsserts(t, true) + + data := ` + { + "name": "my-image", + "builders": [ + { + "name": "test1", + "type": "test-builder" + } + ] + } + ` + + template, err := ParseTemplate([]byte(data)) + assert.Nil(err, "should not error") + + builderFactory := testBuildFactory(map[string]Builder{}) + build, err := template.Build("test1", builderFactory) + assert.Nil(build, "build should be nil") + assert.NotNil(err, "should have error") +} + +func TestTemplate_Build(t *testing.T) { + assert := asserts.NewTestingAsserts(t, true) + + data := ` + { + "name": "my-image", + "builders": [ + { + "name": "test1", + "type": "test-builder" + } + ] + } + ` + + expectedConfig := map[string]interface{} { + "name": "test1", + "type": "test-builder", + } + + template, err := ParseTemplate([]byte(data)) + assert.Nil(err, "should not error") + + builder := testBuilder() + builderMap := map[string]Builder { + "test-builder": builder, + } + + builderFactory := testBuildFactory(builderMap) + + // Get the build, verifying we can get it without issue, but also + // that the proper builder was looked up and used for the build. + build, err := template.Build("test1", builderFactory) + assert.Nil(err, "should not error") + + build.Prepare() + build.Run(testUi()) + + assert.True(builder.prepareCalled, "prepare should be called") + assert.Equal(builder.prepareConfig, expectedConfig, "prepare config should be correct") + assert.True(builder.runCalled, "run should be called") + assert.Equal(builder.runBuild, build, "run should be called with build") +}