Turn Environment into an interface

This commit is contained in:
Mitchell Hashimoto 2013-05-02 14:03:55 -07:00
parent fefd2ae208
commit 520503e10c
5 changed files with 35 additions and 26 deletions

View File

@ -6,7 +6,7 @@ import (
type buildCommand byte type buildCommand byte
func (buildCommand) Run(env *Environment, args []string) int { func (buildCommand) Run(env Environment, args []string) int {
if len(args) != 1 { if len(args) != 1 {
// TODO: Error message // TODO: Error message
return 1 return 1

View File

@ -1,5 +1,7 @@
package packer package packer
import "net/rpc"
// A command is a runnable sub-command of the `packer` application. // A command is a runnable sub-command of the `packer` application.
// When `packer` is called with the proper subcommand, this will be // When `packer` is called with the proper subcommand, this will be
// called. // called.
@ -14,7 +16,12 @@ package packer
// Synopsis should return a one-line, short synopsis of the command. // Synopsis should return a one-line, short synopsis of the command.
// This should be less than 50 characters ideally. // This should be less than 50 characters ideally.
type Command interface { type Command interface {
Run(env *Environment, args []string) int Run(env Environment, args []string) int
Synopsis() string Synopsis() string
} }
// An RPCCommand is an implementation of the Command interface where the
// command is actually executed over an RPC connection.
type RPCCommand struct {
client *rpc.Client
}

View File

@ -9,14 +9,20 @@ import (
"strings" "strings"
) )
// The environment struct contains all the state necessary for a single // The environment interface provides access to the configuration and
// instance of Packer. // state of a single Packer run.
// //
// It is *not* a singleton, but generally a single environment is created // It allows for things such as executing CLI commands, getting the
// when Packer starts running to represent that Packer run. Technically, // list of available builders, and more.
// if you're building a custom Packer binary, you could instantiate multiple type Environment interface {
// environments and run them in parallel. BuilderFactory() BuilderFactory
type Environment struct { Cli(args []string) int
Ui() Ui
}
// An implementation of an Environment that represents the Packer core
// environment.
type coreEnvironment struct {
builderFactory BuilderFactory builderFactory BuilderFactory
command map[string]Command command map[string]Command
ui Ui ui Ui
@ -40,13 +46,13 @@ func DefaultEnvironmentConfig() *EnvironmentConfig {
} }
// This creates a new environment // This creates a new environment
func NewEnvironment(config *EnvironmentConfig) (env *Environment, err error) { func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error) {
if config == nil { if config == nil {
err = errors.New("config must be given to initialize environment") err = errors.New("config must be given to initialize environment")
return return
} }
env = &Environment{} env := &coreEnvironment{}
env.builderFactory = config.BuilderFactory env.builderFactory = config.BuilderFactory
env.command = make(map[string]Command) env.command = make(map[string]Command)
env.ui = config.Ui env.ui = config.Ui
@ -60,19 +66,20 @@ func NewEnvironment(config *EnvironmentConfig) (env *Environment, err error) {
env.command["version"] = new(versionCommand) env.command["version"] = new(versionCommand)
} }
resultEnv = env
return return
} }
// Returns the BuilderFactory associated with this Environment. // Returns the BuilderFactory associated with this Environment.
func (e *Environment) BuilderFactory() BuilderFactory { func (e *coreEnvironment) BuilderFactory() BuilderFactory {
return e.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 *Environment) Cli(args []string) int { func (e *coreEnvironment) Cli(args []string) int {
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
} }
@ -89,7 +96,7 @@ func (e *Environment) Cli(args []string) int {
// 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
} }
} }
@ -98,7 +105,7 @@ func (e *Environment) Cli(args []string) int {
} }
// Prints the CLI help to the UI. // Prints the CLI help to the UI.
func (e *Environment) 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)) commandKeys := make([]string, len(e.command))
@ -131,6 +138,6 @@ func (e *Environment) PrintHelp() {
// Returns the UI for the environment. The UI is the interface that should // Returns the UI for the environment. The UI is the interface that should
// be used for all communication with the outside world. // be used for all communication with the outside world.
func (e *Environment) Ui() Ui { func (e *coreEnvironment) Ui() Ui {
return e.ui return e.ui
} }

View File

@ -11,10 +11,10 @@ import (
type TestCommand struct { type TestCommand struct {
runArgs []string runArgs []string
runCalled bool runCalled bool
runEnv *Environment runEnv Environment
} }
func (tc *TestCommand) Run(env *Environment, args []string) int { func (tc *TestCommand) Run(env Environment, args []string) int {
tc.runCalled = true tc.runCalled = true
tc.runArgs = args tc.runArgs = args
tc.runEnv = env tc.runEnv = env
@ -25,7 +25,7 @@ func (tc *TestCommand) Synopsis() string {
return "" return ""
} }
func testEnvironment() *Environment { func testEnvironment() Environment {
config := &EnvironmentConfig{} config := &EnvironmentConfig{}
config.Ui = &ReaderWriterUi{ config.Ui = &ReaderWriterUi{
new(bytes.Buffer), new(bytes.Buffer),
@ -140,11 +140,6 @@ func TestEnvironment_DefaultCli_Version(t *testing.T) {
assert.Equal(defaultEnv.Cli([]string{"bad", "version"}), 1, "version should NOT work anywhere") assert.Equal(defaultEnv.Cli([]string{"bad", "version"}), 1, "version should NOT work anywhere")
} }
func TestEnvironment_PrintHelp(t *testing.T) {
// Just call the function and verify that no panics occur
testEnvironment().PrintHelp()
}
func TestEnvironment_SettingUi(t *testing.T) { func TestEnvironment_SettingUi(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)

View File

@ -6,7 +6,7 @@ const Version = "0.1.0.dev"
type versionCommand byte type versionCommand byte
// Implement the Command interface by simply showing the version // Implement the Command interface by simply showing the version
func (versionCommand) Run(env *Environment, args []string) int { func (versionCommand) Run(env Environment, args []string) int {
env.Ui().Say("Packer v%v\n", Version) env.Ui().Say("Packer v%v\n", Version)
return 0 return 0
} }