diff --git a/packer/command.go b/packer/command.go deleted file mode 100644 index d861347eb..000000000 --- a/packer/command.go +++ /dev/null @@ -1,23 +0,0 @@ -package packer - -// A command is a runnable sub-command of the `packer` application. -// When `packer` is called with the proper subcommand, this will be -// called. -// -// The mapping of command names to command interfaces is in the -// Environment struct. -type Command interface { - // Help should return long-form help text that includes the command-line - // usage, a brief few sentences explaining the function of the command, - // and the complete list of flags the command accepts. - Help() string - - // Run should run the actual command with the given environmet and - // command-line arguments. It should return the exit status when it is - // finished. - Run(env Environment, args []string) int - - // Synopsis should return a one-line, short synopsis of the command. - // This should be less than 50 characters ideally. - Synopsis() string -} diff --git a/packer/command_test.go b/packer/command_test.go deleted file mode 100644 index 06348fdac..000000000 --- a/packer/command_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package packer - -type TestCommand struct { - runArgs []string - runCalled bool - runEnv Environment -} - -func (tc *TestCommand) Help() string { - return "bar" -} - -func (tc *TestCommand) Run(env Environment, args []string) int { - tc.runCalled = true - tc.runArgs = args - tc.runEnv = env - return 0 -} - -func (tc *TestCommand) Synopsis() string { - return "foo" -} diff --git a/packer/environment.go b/packer/environment.go index 510b849c6..58585ffcf 100644 --- a/packer/environment.go +++ b/packer/environment.go @@ -4,19 +4,12 @@ package packer import ( "errors" "fmt" - "log" "os" - "sort" - "strings" - "sync" ) // The function type used to lookup Builder implementations. type BuilderFunc func(name string) (Builder, error) -// The function type used to lookup Command implementations. -type CommandFunc func(name string) (Command, error) - // The function type used to lookup Hook implementations. type HookFunc func(name string) (Hook, error) @@ -31,7 +24,6 @@ type ProvisionerFunc func(name string) (Provisioner, error) // commands, etc. type ComponentFinder struct { Builder BuilderFunc - Command CommandFunc Hook HookFunc PostProcessor PostProcessorFunc Provisioner ProvisionerFunc @@ -45,7 +37,6 @@ type ComponentFinder struct { type Environment interface { Builder(string) (Builder, error) Cache() Cache - Cli([]string) (int, error) Hook(string) (Hook, error) PostProcessor(string) (PostProcessor, error) Provisioner(string) (Provisioner, error) @@ -56,7 +47,6 @@ type Environment interface { // environment. type coreEnvironment struct { cache Cache - commands []string components ComponentFinder ui Ui } @@ -64,22 +54,14 @@ type coreEnvironment struct { // This struct configures new environments. type EnvironmentConfig struct { Cache Cache - Commands []string Components ComponentFinder Ui Ui } -type helpCommandEntry struct { - i int - key string - synopsis string -} - // DefaultEnvironmentConfig returns a default EnvironmentConfig that can // be used to create a new enviroment with NewEnvironment with sane defaults. func DefaultEnvironmentConfig() *EnvironmentConfig { config := &EnvironmentConfig{} - config.Commands = make([]string, 0) config.Ui = &BasicUi{ Reader: os.Stdin, Writer: os.Stdout, @@ -98,7 +80,6 @@ func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error env := &coreEnvironment{} env.cache = config.Cache - env.commands = config.Commands env.components = config.Components env.ui = config.Ui @@ -109,10 +90,6 @@ func NewEnvironment(config *EnvironmentConfig) (resultEnv Environment, err error env.components.Builder = func(string) (Builder, error) { return nil, nil } } - if env.components.Command == nil { - env.components.Command = func(string) (Command, error) { return nil, nil } - } - if env.components.Hook == nil { env.components.Hook = func(string) (Hook, error) { return nil, nil } } @@ -199,159 +176,6 @@ func (e *coreEnvironment) Provisioner(name string) (p Provisioner, err error) { return } -// Executes a command as if it was typed on the command-line interface. -// The return value is the exit code of the command. -func (e *coreEnvironment) Cli(args []string) (result int, err error) { - log.Printf("Environment.Cli: %#v\n", args) - - // If we have no arguments, just short-circuit here and print the help - if len(args) == 0 { - e.printHelp() - return 1, nil - } - - // This variable will track whether or not we're supposed to print - // the help or not. - isHelp := false - for _, arg := range args { - if arg == "-h" || arg == "--help" { - isHelp = true - break - } - } - - // Trim up to the command name - for i, v := range args { - if len(v) > 0 && v[0] != '-' { - args = args[i:] - break - } - } - - log.Printf("command + args: %#v", args) - - version := args[0] == "version" - if !version { - for _, arg := range args { - if arg == "--version" || arg == "-v" { - version = true - break - } - } - } - - var command Command - if version { - command = new(versionCommand) - } - - if command == nil { - command, err = e.components.Command(args[0]) - if err != nil { - return - } - - // If we still don't have a command, show the help. - if command == nil { - e.ui.Error(fmt.Sprintf("Unknown command: %s\n", args[0])) - e.printHelp() - return 1, nil - } - } - - // If we're supposed to print help, then print the help of the - // command rather than running it. - if isHelp { - e.ui.Say(command.Help()) - return 0, nil - } - - log.Printf("Executing command: %s\n", args[0]) - return command.Run(e, args[1:]), nil -} - -// Prints the CLI help to the UI. -func (e *coreEnvironment) printHelp() { - // Created a sorted slice of the map keys and record the longest - // command name so we can better format the output later. - maxKeyLen := 0 - for _, command := range e.commands { - if len(command) > maxKeyLen { - maxKeyLen = len(command) - } - } - - // Sort the keys - sort.Strings(e.commands) - - // Create the communication/sync mechanisms to get the synopsis' of - // the various commands. We do this in parallel since the overhead - // of the subprocess underneath is very expensive and this speeds things - // up an incredible amount. - var wg sync.WaitGroup - ch := make(chan *helpCommandEntry) - - for i, key := range e.commands { - wg.Add(1) - - // Get the synopsis in a goroutine since it may take awhile - // to subprocess out. - go func(i int, key string) { - defer wg.Done() - var synopsis string - command, err := e.components.Command(key) - if err != nil { - synopsis = fmt.Sprintf("Error loading command: %s", err.Error()) - } else if command == nil { - return - } else { - synopsis = command.Synopsis() - } - - // Pad the key with spaces so that they're all the same width - key = fmt.Sprintf("%s%s", key, strings.Repeat(" ", maxKeyLen-len(key))) - - // Output the command and the synopsis - ch <- &helpCommandEntry{ - i: i, - key: key, - synopsis: synopsis, - } - }(i, key) - } - - e.ui.Say("usage: packer [--version] [--help] []\n") - e.ui.Say("Available commands are:") - - // Make a goroutine that just waits for all the synopsis gathering - // to complete, and then output it. - synopsisDone := make(chan struct{}) - go func() { - defer close(synopsisDone) - entries := make([]string, len(e.commands)) - - for entry := range ch { - e.ui.Machine("command", entry.key, entry.synopsis) - message := fmt.Sprintf(" %s %s", entry.key, entry.synopsis) - entries[entry.i] = message - } - - for _, message := range entries { - if message != "" { - e.ui.Say(message) - } - } - }() - - // Wait to complete getting the synopsis' then close the channel - wg.Wait() - close(ch) - <-synopsisDone - - e.ui.Say("\nGlobally recognized options:") - e.ui.Say(" -machine-readable Machine-readable output format.") -} - // Returns the UI for the environment. The UI is the interface that should // be used for all communication with the outside world. func (e *coreEnvironment) Ui() Ui { diff --git a/packer/environment_test.go b/packer/environment_test.go index c293c2a9a..80edab58e 100644 --- a/packer/environment_test.go +++ b/packer/environment_test.go @@ -6,8 +6,6 @@ import ( "io/ioutil" "log" "os" - "reflect" - "strings" "testing" ) @@ -43,13 +41,6 @@ func testEnvironment() Environment { return env } -func TestEnvironment_DefaultConfig_Commands(t *testing.T) { - config := DefaultEnvironmentConfig() - if len(config.Commands) != 0 { - t.Fatalf("bad: %#v", config.Commands) - } -} - func TestEnvironment_DefaultConfig_Ui(t *testing.T) { config := DefaultEnvironmentConfig() if config.Ui == nil { @@ -91,7 +82,6 @@ func TestEnvironment_NilComponents(t *testing.T) { // anything but if there is a panic in the test then yeah, something // went wrong. env.Builder("foo") - env.Cli([]string{"foo"}) env.Hook("foo") env.PostProcessor("foo") env.Provisioner("foo") @@ -154,117 +144,6 @@ func TestEnvironment_Cache(t *testing.T) { } } -func TestEnvironment_Cli_Error(t *testing.T) { - config := DefaultEnvironmentConfig() - config.Components.Command = func(n string) (Command, error) { return nil, errors.New("foo") } - - env, _ := NewEnvironment(config) - _, err := env.Cli([]string{"foo"}) - if err == nil { - t.Fatal("should have error") - } - if err.Error() != "foo" { - t.Fatalf("bad: %s", err) - } -} - -func TestEnvironment_Cli_CallsRun(t *testing.T) { - command := &TestCommand{} - commands := make(map[string]Command) - commands["foo"] = command - - config := &EnvironmentConfig{} - config.Commands = []string{"foo"} - config.Components.Command = func(n string) (Command, error) { return commands[n], nil } - - env, _ := NewEnvironment(config) - exitCode, err := env.Cli([]string{"foo", "bar", "baz"}) - if err != nil { - t.Fatalf("err: %s", err) - } - if exitCode != 0 { - t.Fatalf("bad: %d", exitCode) - } - if !command.runCalled { - t.Fatal("command should be run") - } - if command.runEnv != env { - t.Fatalf("bad env: %#v", command.runEnv) - } - if !reflect.DeepEqual(command.runArgs, []string{"bar", "baz"}) { - t.Fatalf("bad: %#v", command.runArgs) - } -} - -func TestEnvironment_DefaultCli_Empty(t *testing.T) { - defaultEnv := testEnvironment() - - // Test with no args - exitCode, _ := defaultEnv.Cli([]string{}) - if exitCode != 1 { - t.Fatalf("bad: %d", exitCode) - } - - // Test with only blank args - exitCode, _ = defaultEnv.Cli([]string{""}) - if exitCode != 1 { - t.Fatalf("bad: %d", exitCode) - } -} - -func TestEnvironment_DefaultCli_Help(t *testing.T) { - defaultEnv := testEnvironment() - - // A little lambda to help us test the output actually contains help - testOutput := func() { - buffer := defaultEnv.Ui().(*BasicUi).Writer.(*bytes.Buffer) - output := buffer.String() - buffer.Reset() - if !strings.Contains(output, "usage: packer") { - t.Fatalf("should contain help: %#v", output) - } - } - - // Test "--help" - exitCode, _ := defaultEnv.Cli([]string{"--help"}) - if exitCode != 1 { - t.Fatalf("bad: %d", exitCode) - } - testOutput() - - // Test "-h" - exitCode, _ = defaultEnv.Cli([]string{"--help"}) - if exitCode != 1 { - t.Fatalf("bad: %d", exitCode) - } - testOutput() -} - -func TestEnvironment_DefaultCli_Version(t *testing.T) { - defaultEnv := testEnvironment() - - versionCommands := []string{"version", "--version", "-v"} - for _, command := range versionCommands { - exitCode, _ := defaultEnv.Cli([]string{command}) - if exitCode != 0 { - t.Fatalf("bad: %d", exitCode) - } - - // Test the --version and -v can appear anywhere - exitCode, _ = defaultEnv.Cli([]string{"bad", command}) - - if command != "version" { - if exitCode != 0 { - t.Fatalf("bad: %d", exitCode) - } - } else { - if exitCode != 1 { - t.Fatalf("bad: %d", exitCode) - } - } - } -} - func TestEnvironment_Hook(t *testing.T) { hook := &MockHook{} hooks := make(map[string]Hook)