Get rid of BuilderFactory

This commit is contained in:
Mitchell Hashimoto 2013-05-05 14:47:17 -07:00
parent 3370c55e2c
commit 14c568a9d2
6 changed files with 45 additions and 97 deletions

View File

@ -33,22 +33,6 @@ type Builder interface {
Run(build Build, ui Ui) Run(build Build, ui Ui)
} }
// This factory is responsible for returning Builders for the given name.
//
// CreateBuilder is called with the string name of a builder and should
// return a Builder implementation by that name or nil if no builder can be
// found.
type BuilderFactory interface {
CreateBuilder(name string) Builder
}
// This implements BuilderFactory to return nil for every builder.
type NilBuilderFactory byte
func (NilBuilderFactory) CreateBuilder(name string) Builder {
return nil
}
// 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. // and any hooks. This _must_ be called prior to Run.
func (b *coreBuild) Prepare() { func (b *coreBuild) Prepare() {

View File

@ -5,14 +5,6 @@ import (
"testing" "testing"
) )
type hashBuilderFactory struct {
builderMap map[string]Builder
}
func (bf *hashBuilderFactory) CreateBuilder(name string) Builder {
return bf.builderMap[name]
}
type TestBuilder struct { type TestBuilder struct {
prepareCalled bool prepareCalled bool
prepareConfig interface{} prepareConfig interface{}
@ -44,10 +36,6 @@ func testBuilder() *TestBuilder {
return &TestBuilder{} return &TestBuilder{}
} }
func testBuildFactory(builderMap map[string]Builder) BuilderFactory {
return &hashBuilderFactory{builderMap}
}
func TestBuild_Prepare(t *testing.T) { func TestBuild_Prepare(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)

View File

@ -9,13 +9,16 @@ import (
"strings" "strings"
) )
type BuilderFunc func(name string) Builder
type CommandFunc func(name string) Command
// The environment interface provides access to the configuration and // The environment interface provides access to the configuration and
// state of a single Packer run. // state of a single Packer run.
// //
// It allows for things such as executing CLI commands, getting the // It allows for things such as executing CLI commands, getting the
// list of available builders, and more. // list of available builders, and more.
type Environment interface { type Environment interface {
BuilderFactory() BuilderFactory
Cli(args []string) int Cli(args []string) int
Ui() Ui Ui() Ui
} }
@ -23,15 +26,17 @@ type Environment interface {
// An implementation of an Environment that represents the Packer core // An implementation of an Environment that represents the Packer core
// environment. // environment.
type coreEnvironment struct { type coreEnvironment struct {
builderFactory BuilderFactory builderFunc BuilderFunc
command map[string]Command commands []string
commandFunc CommandFunc
ui Ui ui Ui
} }
// This struct configures new environments. // This struct configures new environments.
type EnvironmentConfig struct { type EnvironmentConfig struct {
BuilderFactory BuilderFactory BuilderFunc BuilderFunc
Command map[string]Command CommandFunc CommandFunc
Commands []string
Ui Ui Ui Ui
} }
@ -39,8 +44,9 @@ type EnvironmentConfig struct {
// be used to create a new enviroment with NewEnvironment with sane defaults. // be used to create a new enviroment with NewEnvironment with sane defaults.
func DefaultEnvironmentConfig() *EnvironmentConfig { func DefaultEnvironmentConfig() *EnvironmentConfig {
config := &EnvironmentConfig{} config := &EnvironmentConfig{}
config.BuilderFactory = new(NilBuilderFactory) config.BuilderFunc = func(string) Builder { return nil }
config.Command = make(map[string]Command) config.CommandFunc = func(string) Command { return nil }
config.Commands = make([]string, 0)
config.Ui = &ReaderWriterUi{os.Stdin, os.Stdout} config.Ui = &ReaderWriterUi{os.Stdin, os.Stdout}
return config return config
} }
@ -53,28 +59,15 @@ func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error
} }
env := &coreEnvironment{} env := &coreEnvironment{}
env.builderFactory = config.BuilderFactory env.builderFunc = config.BuilderFunc
env.command = make(map[string]Command) env.commandFunc = config.CommandFunc
env.commands = config.Commands
env.ui = config.Ui env.ui = config.Ui
for k, v := range config.Command {
env.command[k] = v
}
// TODO: Should "version" be allowed to be overriden?
if _, ok := env.command["version"]; !ok {
env.command["version"] = new(versionCommand)
}
resultEnv = env resultEnv = env
return return
} }
// Returns the BuilderFactory associated with this Environment.
func (e *coreEnvironment) BuilderFactory() BuilderFactory {
return e.builderFactory
}
// Executes a command as if it was typed on the command-line interface. // Executes a command as if it was typed on the command-line interface.
// The return value is the exit code of the command. // The return value is the exit code of the command.
func (e *coreEnvironment) Cli(args []string) int { func (e *coreEnvironment) Cli(args []string) int {
@ -83,16 +76,23 @@ func (e *coreEnvironment) Cli(args []string) int {
return 1 return 1
} }
command, ok := e.command[args[0]] version := args[0] == "version"
if !ok { if !version {
// The command was not found. In this case, let's go through
// the arguments and see if the user is requesting the version.
for _, arg := range args { for _, arg := range args {
if arg == "--version" || arg == "-v" { if arg == "--version" || arg == "-v" {
command = e.command["version"] version = true
break break
} }
} }
}
var command Command
if version {
command = new(versionCommand)
}
if command == nil {
command = e.commandFunc(args[0])
// If we still don't have a command, show the help. // If we still don't have a command, show the help.
if command == nil { if command == nil {
@ -108,25 +108,23 @@ func (e *coreEnvironment) Cli(args []string) int {
func (e *coreEnvironment) printHelp() { func (e *coreEnvironment) printHelp() {
// Created a sorted slice of the map keys and record the longest // Created a sorted slice of the map keys and record the longest
// command name so we can better format the output later. // command name so we can better format the output later.
commandKeys := make([]string, len(e.command))
i := 0 i := 0
maxKeyLen := 0 maxKeyLen := 0
for key, _ := range e.command { for _, command := range e.commands {
commandKeys[i] = key if len(command) > maxKeyLen {
if len(key) > maxKeyLen { maxKeyLen = len(command)
maxKeyLen = len(key)
} }
i++ i++
} }
// Sort the keys // Sort the keys
sort.Strings(commandKeys) sort.Strings(e.commands)
e.ui.Say("usage: packer [--version] [--help] <command> [<args>]\n\n") e.ui.Say("usage: packer [--version] [--help] <command> [<args>]\n\n")
e.ui.Say("Available commands are:\n") e.ui.Say("Available commands are:\n")
for _, key := range commandKeys { for _, key := range e.commands {
command := e.command[key] command := e.commandFunc(key)
// Pad the key with spaces so that they're all the same width // Pad the key with spaces so that they're all the same width
key = fmt.Sprintf("%v%v", key, strings.Repeat(" ", maxKeyLen-len(key))) key = fmt.Sprintf("%v%v", key, strings.Repeat(" ", maxKeyLen-len(key)))

View File

@ -3,14 +3,13 @@ package packer
import ( import (
"bytes" "bytes"
"cgl.tideland.biz/asserts" "cgl.tideland.biz/asserts"
"encoding/gob"
"os" "os"
"strings" "strings"
"testing" "testing"
) )
func testEnvironment() Environment { func testEnvironment() Environment {
config := &EnvironmentConfig{} config := DefaultEnvironmentConfig()
config.Ui = &ReaderWriterUi{ config.Ui = &ReaderWriterUi{
new(bytes.Buffer), new(bytes.Buffer),
new(bytes.Buffer), new(bytes.Buffer),
@ -24,25 +23,11 @@ func testEnvironment() Environment {
return env return env
} }
// This is just a sanity test to prove that our coreEnvironment can't func TestEnvironment_DefaultConfig_Commands(t *testing.T) {
// encode or decode using gobs. That is fine, and expected, but I just like
// to be sure.
func TestEnvironment_CoreEnvironmentCantEncode(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
env := testEnvironment()
b := new(bytes.Buffer)
e := gob.NewEncoder(b)
err := e.Encode(env)
assert.NotNil(err, "encoding should fail")
}
func TestEnvironment_DefaultConfig_Command(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig() config := DefaultEnvironmentConfig()
assert.NotNil(config.Command, "default Command should not be nil") assert.Empty(config.Commands, "should have no commands")
} }
func TestEnvironment_DefaultConfig_Ui(t *testing.T) { func TestEnvironment_DefaultConfig_Ui(t *testing.T) {
@ -65,23 +50,16 @@ func TestNewEnvironment_NoConfig(t *testing.T) {
assert.NotNil(err, "should be an error") assert.NotNil(err, "should be an error")
} }
func TestEnvironment_GetBuilderFactory(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
config := DefaultEnvironmentConfig()
env, _ := NewEnvironment(config)
assert.Equal(env.BuilderFactory(), config.BuilderFactory, "should match factories")
}
func TestEnvironment_Cli_CallsRun(t *testing.T) { func TestEnvironment_Cli_CallsRun(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
command := &TestCommand{} command := &TestCommand{}
commands := make(map[string]Command)
commands["foo"] = command
config := &EnvironmentConfig{} config := &EnvironmentConfig{}
config.Command = make(map[string]Command) config.Commands = []string{"foo"}
config.Command["foo"] = command config.CommandFunc = func(n string) Command { return commands[n] }
env, _ := NewEnvironment(config) env, _ := NewEnvironment(config)
assert.Equal(env.Cli([]string{"foo", "bar", "baz"}), 0, "runs foo command") assert.Equal(env.Cli([]string{"foo", "bar", "baz"}), 0, "runs foo command")

View File

@ -95,14 +95,14 @@ func (t *Template) BuildNames() []string {
// //
// If the build does not exist as part of this template, an error is // If the build does not exist as part of this template, an error is
// returned. // returned.
func (t *Template) Build(name string, bf BuilderFactory) (b Build, err error) { func (t *Template) Build(name string, bf BuilderFunc) (b Build, err error) {
builderConfig, ok := t.Builders[name] builderConfig, ok := t.Builders[name]
if !ok { if !ok {
err = fmt.Errorf("No such build found in template: %s", name) err = fmt.Errorf("No such build found in template: %s", name)
return return
} }
builder := bf.CreateBuilder(builderConfig.builderName) builder := bf(builderConfig.builderName)
if builder == nil { if builder == nil {
err = fmt.Errorf("Builder could not be found: %s", builderConfig.builderName) err = fmt.Errorf("Builder could not be found: %s", builderConfig.builderName)
return return

View File

@ -157,7 +157,7 @@ func TestTemplate_BuildUnknownBuilder(t *testing.T) {
template, err := ParseTemplate([]byte(data)) template, err := ParseTemplate([]byte(data))
assert.Nil(err, "should not error") assert.Nil(err, "should not error")
builderFactory := testBuildFactory(map[string]Builder{}) builderFactory := func(string) Builder { return nil }
build, err := template.Build("test1", builderFactory) build, err := template.Build("test1", builderFactory)
assert.Nil(build, "build should be nil") assert.Nil(build, "build should be nil")
assert.NotNil(err, "should have error") assert.NotNil(err, "should have error")
@ -191,7 +191,7 @@ func TestTemplate_Build(t *testing.T) {
"test-builder": builder, "test-builder": builder,
} }
builderFactory := testBuildFactory(builderMap) builderFactory := func(n string) Builder { return builderMap[n] }
// Get the build, verifying we can get it without issue, but also // Get the build, verifying we can get it without issue, but also
// that the proper builder was looked up and used for the build. // that the proper builder was looked up and used for the build.