diff --git a/TODO.md b/TODO.md index 6e30376f9..45932c6bd 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,7 @@ * communicator/ssh: Ability to re-establish connection * communicator/ssh: Download() * packer: Communicator should have Close() method +* packer: RemoteCommand.ExitChan() should be more efficient * packer: Ui input * packer/plugin: Better error messages/detection if plugin crashes * packer/plugin: Testing of client struct/methods diff --git a/packer/communicator.go b/packer/communicator.go index e57c8ec38..6ca2957c1 100644 --- a/packer/communicator.go +++ b/packer/communicator.go @@ -36,6 +36,32 @@ type RemoteCommand struct { ExitStatus int } +// StdoutStream returns a channel that will be sent all the output +// of stdout as it comes. The output isn't guaranteed to be a full line. +// When the channel is closed, the process is exited. +func (r *RemoteCommand) StdoutChan() (<-chan string) { + return nil +} + +// ExitChan returns a channel that will be sent the exit status once +// the process exits. This can be used in cases such a select statement +// waiting on the process to end. +func (r *RemoteCommand) ExitChan() (<-chan int) { + // TODO(mitchellh): lock + // TODO(mitchellh): Something more efficient than multiple Wait() calls + + // Make a single buffered channel so that the send doesn't block. + exitChan := make(chan int, 1) + + go func() { + defer close(exitChan) + r.Wait() + exitChan <- r.ExitStatus + }() + + return exitChan +} + // Wait waits for the command to exit. func (r *RemoteCommand) Wait() { // Busy wait on being exited. We put a sleep to be kind to the diff --git a/packer/communicator_test.go b/packer/communicator_test.go index bba6addbd..b7d374ed3 100644 --- a/packer/communicator_test.go +++ b/packer/communicator_test.go @@ -5,6 +5,31 @@ import ( "time" ) +func TestRemoteCommand_ExitChan(t *testing.T) { + t.Parallel() + + rc := &RemoteCommand{} + exitChan := rc.ExitChan() + + // Set the exit data so that it is sent + rc.ExitStatus = 42 + rc.Exited = true + + select { + case exitCode := <-exitChan: + if exitCode != 42 { + t.Fatal("invalid exit code") + } + + _, ok := <-exitChan + if ok { + t.Fatal("exit channel should be closed") + } + case <-time.After(500 * time.Millisecond): + t.Fatal("exit channel never sent") + } +} + func TestRemoteCommand_WaitBlocks(t *testing.T) { t.Parallel()