Commit 86973282 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer, packer/rpc: Make command/builderFunc support errors

parent fbc2013b
...@@ -9,9 +9,9 @@ import ( ...@@ -9,9 +9,9 @@ import (
"strings" "strings"
) )
type BuilderFunc func(name string) Builder type BuilderFunc func(name string) (Builder, error)
type CommandFunc func(name string) Command type CommandFunc func(name string) (Command, error)
// 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.
...@@ -19,8 +19,8 @@ type CommandFunc func(name string) Command ...@@ -19,8 +19,8 @@ type CommandFunc func(name string) Command
// 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 {
Builder(name string) Builder Builder(name string) (Builder, error)
Cli(args []string) int Cli(args []string) (int, error)
Ui() Ui Ui() Ui
} }
...@@ -45,8 +45,8 @@ type EnvironmentConfig struct { ...@@ -45,8 +45,8 @@ 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.BuilderFunc = func(string) Builder { return nil } config.BuilderFunc = func(string) (Builder, error) { return nil, nil }
config.CommandFunc = func(string) Command { return nil } config.CommandFunc = func(string) (Command, error) { return nil, nil }
config.Commands = make([]string, 0) config.Commands = make([]string, 0)
config.Ui = &ReaderWriterUi{os.Stdin, os.Stdout} config.Ui = &ReaderWriterUi{os.Stdin, os.Stdout}
return config return config
...@@ -71,16 +71,25 @@ func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error ...@@ -71,16 +71,25 @@ func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error
// Returns a builder of the given name that is registered with this // Returns a builder of the given name that is registered with this
// environment. // environment.
func (e *coreEnvironment) Builder(name string) Builder { func (e *coreEnvironment) Builder(name string) (b Builder, err error) {
return e.builderFunc(name) b, err = e.builderFunc(name)
if err != nil {
return
}
if b == nil {
err = fmt.Errorf("No builder returned for name: %s", name)
}
return
} }
// 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) (result int, err error) {
if len(args) == 0 || args[0] == "--help" || args[0] == "-h" { if len(args) == 0 || args[0] == "--help" || args[0] == "-h" {
e.printHelp() e.printHelp()
return 1 return 1, nil
} }
version := args[0] == "version" version := args[0] == "version"
...@@ -99,16 +108,19 @@ func (e *coreEnvironment) Cli(args []string) int { ...@@ -99,16 +108,19 @@ func (e *coreEnvironment) Cli(args []string) int {
} }
if command == nil { if command == nil {
command = e.commandFunc(args[0]) command, err = e.commandFunc(args[0])
if err != nil {
return
}
// 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 {
e.printHelp() e.printHelp()
return 1 return 1, nil
} }
} }
return command.Run(e, args[1:]) return command.Run(e, args[1:]), nil
} }
// Prints the CLI help to the UI. // Prints the CLI help to the UI.
...@@ -131,13 +143,20 @@ func (e *coreEnvironment) printHelp() { ...@@ -131,13 +143,20 @@ func (e *coreEnvironment) printHelp() {
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 e.commands { for _, key := range e.commands {
command := e.commandFunc(key) var synopsis string
command, err := e.commandFunc(key)
if err != nil {
synopsis = fmt.Sprintf("Error loading command: %s", err.Error())
} else {
synopsis = command.Synopsis()
}
// 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)))
// Output the command and the synopsis // Output the command and the synopsis
e.ui.Say(" %v %v\n", key, command.Synopsis()) e.ui.Say(" %v %v\n", key, synopsis)
} }
} }
......
...@@ -3,6 +3,7 @@ package packer ...@@ -3,6 +3,7 @@ package packer
import ( import (
"bytes" "bytes"
"cgl.tideland.biz/asserts" "cgl.tideland.biz/asserts"
"fmt"
"os" "os"
"strings" "strings"
"testing" "testing"
...@@ -58,10 +59,12 @@ func TestEnvironment_Builder(t *testing.T) { ...@@ -58,10 +59,12 @@ func TestEnvironment_Builder(t *testing.T) {
builders["foo"] = builder builders["foo"] = builder
config := DefaultEnvironmentConfig() config := DefaultEnvironmentConfig()
config.BuilderFunc = func(n string) Builder { return builders[n] } config.BuilderFunc = func(n string) (Builder, error) { return builders[n], nil }
env, _ := NewEnvironment(config) env, _ := NewEnvironment(config)
assert.Equal(env.Builder("foo"), builder, "should return correct builder") returnedBuilder, err := env.Builder("foo")
assert.Nil(err, "should be no error")
assert.Equal(returnedBuilder, builder, "should return correct builder")
} }
func TestEnvironment_Cli_CallsRun(t *testing.T) { func TestEnvironment_Cli_CallsRun(t *testing.T) {
...@@ -73,10 +76,12 @@ func TestEnvironment_Cli_CallsRun(t *testing.T) { ...@@ -73,10 +76,12 @@ func TestEnvironment_Cli_CallsRun(t *testing.T) {
config := &EnvironmentConfig{} config := &EnvironmentConfig{}
config.Commands = []string{"foo"} config.Commands = []string{"foo"}
config.CommandFunc = func(n string) Command { return commands[n] } config.CommandFunc = func(n string) (Command, error) { return commands[n], nil }
env, _ := NewEnvironment(config) env, _ := NewEnvironment(config)
assert.Equal(env.Cli([]string{"foo", "bar", "baz"}), 0, "runs foo command") exitCode, err := env.Cli([]string{"foo", "bar", "baz"})
assert.Nil(err, "should be no error")
assert.Equal(exitCode, 0, "runs foo command")
assert.True(command.runCalled, "run should've been called") assert.True(command.runCalled, "run should've been called")
assert.Equal(command.runEnv, env, "should've ran with env") assert.Equal(command.runEnv, env, "should've ran with env")
assert.Equal(command.runArgs, []string{"bar", "baz"}, "should have right args") assert.Equal(command.runArgs, []string{"bar", "baz"}, "should have right args")
...@@ -87,7 +92,8 @@ func TestEnvironment_DefaultCli_Empty(t *testing.T) { ...@@ -87,7 +92,8 @@ func TestEnvironment_DefaultCli_Empty(t *testing.T) {
defaultEnv := testEnvironment() defaultEnv := testEnvironment()
assert.Equal(defaultEnv.Cli([]string{}), 1, "CLI with no args") exitCode, _ := defaultEnv.Cli([]string{})
assert.Equal(exitCode, 1, "CLI with no args")
} }
func TestEnvironment_DefaultCli_Help(t *testing.T) { func TestEnvironment_DefaultCli_Help(t *testing.T) {
...@@ -104,11 +110,13 @@ func TestEnvironment_DefaultCli_Help(t *testing.T) { ...@@ -104,11 +110,13 @@ func TestEnvironment_DefaultCli_Help(t *testing.T) {
} }
// Test "--help" // Test "--help"
assert.Equal(defaultEnv.Cli([]string{"--help"}), 1, "--help should print") exitCode, _ := defaultEnv.Cli([]string{"--help"})
assert.Equal(exitCode, 1, "--help should print")
testOutput() testOutput()
// Test "-h" // Test "-h"
assert.Equal(defaultEnv.Cli([]string{"-h"}), 1, "--help should print") exitCode, _ = defaultEnv.Cli([]string{"--help"})
assert.Equal(exitCode, 1, "--help should print")
testOutput() testOutput()
} }
...@@ -117,17 +125,20 @@ func TestEnvironment_DefaultCli_Version(t *testing.T) { ...@@ -117,17 +125,20 @@ func TestEnvironment_DefaultCli_Version(t *testing.T) {
defaultEnv := testEnvironment() defaultEnv := testEnvironment()
// Test the basic version options versionCommands := []string{"version", "--version", "-v"}
assert.Equal(defaultEnv.Cli([]string{"version"}), 0, "version should work") for _, command := range versionCommands {
assert.Equal(defaultEnv.Cli([]string{"--version"}), 0, "--version should work") exitCode, _ := defaultEnv.Cli([]string{command})
assert.Equal(defaultEnv.Cli([]string{"-v"}), 0, "-v should work") assert.Equal(exitCode, 0, fmt.Sprintf("%s should work", command))
// Test the --version and -v can appear anywhere // Test the --version and -v can appear anywhere
assert.Equal(defaultEnv.Cli([]string{"bad", "-v"}), 0, "-v should work anywhere") exitCode, _ = defaultEnv.Cli([]string{"bad", command})
assert.Equal(defaultEnv.Cli([]string{"bad", "--version"}), 0, "--version should work anywhere")
// Test that "version" can't appear anywhere if command != "version" {
assert.Equal(defaultEnv.Cli([]string{"bad", "version"}), 1, "version should NOT work anywhere") assert.Equal(exitCode, 0, fmt.Sprintf("%s should work anywhere", command))
} else {
assert.Equal(exitCode, 1, fmt.Sprintf("%s should NOT work anywhere", command))
}
}
} }
func TestEnvironment_SettingUi(t *testing.T) { func TestEnvironment_SettingUi(t *testing.T) {
......
...@@ -21,18 +21,25 @@ type EnvironmentCliArgs struct { ...@@ -21,18 +21,25 @@ type EnvironmentCliArgs struct {
Args []string Args []string
} }
func (e *Environment) Builder(name string) packer.Builder { func (e *Environment) Builder(name string) (b packer.Builder, err error) {
var reply string var reply string
e.client.Call("Environment.Builder", name, &reply) err = e.client.Call("Environment.Builder", name, &reply)
if err != nil {
return
}
// TODO: error handling client, err := rpc.Dial("tcp", reply)
client, _ := rpc.Dial("tcp", reply) if err != nil {
return &Builder{client} return
}
b = &Builder{client}
return
} }
func (e *Environment) Cli(args []string) (result int) { func (e *Environment) Cli(args []string) (result int, err error) {
rpcArgs := &EnvironmentCliArgs{args} rpcArgs := &EnvironmentCliArgs{args}
e.client.Call("Environment.Cli", rpcArgs, &result) err = e.client.Call("Environment.Cli", rpcArgs, &result)
return return
} }
...@@ -46,7 +53,10 @@ func (e *Environment) Ui() packer.Ui { ...@@ -46,7 +53,10 @@ func (e *Environment) Ui() packer.Ui {
} }
func (e *EnvironmentServer) Builder(name *string, reply *string) error { func (e *EnvironmentServer) Builder(name *string, reply *string) error {
builder := e.env.Builder(*name) builder, err := e.env.Builder(*name)
if err != nil {
return err
}
// Wrap it // Wrap it
server := rpc.NewServer() server := rpc.NewServer()
...@@ -56,9 +66,9 @@ func (e *EnvironmentServer) Builder(name *string, reply *string) error { ...@@ -56,9 +66,9 @@ func (e *EnvironmentServer) Builder(name *string, reply *string) error {
return nil return nil
} }
func (e *EnvironmentServer) Cli(args *EnvironmentCliArgs, reply *int) error { func (e *EnvironmentServer) Cli(args *EnvironmentCliArgs, reply *int) (err error) {
*reply = e.env.Cli(args.Args) *reply, err = e.env.Cli(args.Args)
return nil return
} }
func (e *EnvironmentServer) Ui(args *interface{}, reply *string) error { func (e *EnvironmentServer) Ui(args *interface{}, reply *string) error {
......
...@@ -18,16 +18,16 @@ type testEnvironment struct { ...@@ -18,16 +18,16 @@ type testEnvironment struct {
uiCalled bool uiCalled bool
} }
func (e *testEnvironment) Builder(name string) packer.Builder { func (e *testEnvironment) Builder(name string) (packer.Builder, error) {
e.builderCalled = true e.builderCalled = true
e.builderName = name e.builderName = name
return testEnvBuilder return testEnvBuilder, nil
} }
func (e *testEnvironment) Cli(args []string) int { func (e *testEnvironment) Cli(args []string) (int, error) {
e.cliCalled = true e.cliCalled = true
e.cliArgs = args e.cliArgs = args
return 42 return 42, nil
} }
func (e *testEnvironment) Ui() packer.Ui { func (e *testEnvironment) Ui() packer.Ui {
...@@ -52,7 +52,7 @@ func TestEnvironmentRPC(t *testing.T) { ...@@ -52,7 +52,7 @@ func TestEnvironmentRPC(t *testing.T) {
eClient := &Environment{client} eClient := &Environment{client}
// Test Builder // Test Builder
builder := eClient.Builder("foo") builder, _ := eClient.Builder("foo")
assert.True(e.builderCalled, "Builder should be called") assert.True(e.builderCalled, "Builder should be called")
assert.Equal(e.builderName, "foo", "Correct name for Builder") assert.Equal(e.builderName, "foo", "Correct name for Builder")
...@@ -61,7 +61,7 @@ func TestEnvironmentRPC(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestEnvironmentRPC(t *testing.T) {
// Test Cli // Test Cli
cliArgs := []string{"foo", "bar"} cliArgs := []string{"foo", "bar"}
result := eClient.Cli(cliArgs) result, _ := eClient.Cli(cliArgs)
assert.True(e.cliCalled, "CLI should be called") assert.True(e.cliCalled, "CLI should be called")
assert.Equal(e.cliArgs, cliArgs, "args should match") assert.Equal(e.cliArgs, cliArgs, "args should match")
assert.Equal(result, 42, "result shuld be 42") assert.Equal(result, 42, "result shuld be 42")
......
...@@ -102,9 +102,13 @@ func (t *Template) Build(name string, bf BuilderFunc) (b Build, err error) { ...@@ -102,9 +102,13 @@ func (t *Template) Build(name string, bf BuilderFunc) (b Build, err error) {
return return
} }
builder := bf(builderConfig.builderName) builder, err := bf(builderConfig.builderName)
if err != nil {
return
}
if builder == nil { if builder == nil {
err = fmt.Errorf("Builder could not be found: %s", builderConfig.builderName) err = fmt.Errorf("Builder not found: %s", name)
return return
} }
......
...@@ -157,7 +157,7 @@ func TestTemplate_BuildUnknownBuilder(t *testing.T) { ...@@ -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 := func(string) Builder { return nil } builderFactory := func(string) (Builder, error) { return nil, 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) { ...@@ -191,7 +191,7 @@ func TestTemplate_Build(t *testing.T) {
"test-builder": builder, "test-builder": builder,
} }
builderFactory := func(n string) Builder { return builderMap[n] } builderFactory := func(n string) (Builder, error) { return builderMap[n], nil }
// 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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment