Commit ace53450 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: More efficient RemoteCommand.ExitChan

parent c6dd5476
...@@ -153,7 +153,6 @@ func (c *comm) Upload(path string, input io.Reader) error { ...@@ -153,7 +153,6 @@ func (c *comm) Upload(path string, input io.Reader) error {
return err return err
} }
log.Printf("scp stdout (length %d): %#v", stdout.Len(), stdout.Bytes()) log.Printf("scp stdout (length %d): %#v", stdout.Len(), stdout.Bytes())
log.Printf("scp stderr (length %d): %s", stderr.Len(), stderr.String()) log.Printf("scp stderr (length %d): %s", stderr.Len(), stderr.String())
......
...@@ -2,6 +2,7 @@ package packer ...@@ -2,6 +2,7 @@ package packer
import ( import (
"io" "io"
"log"
"sync" "sync"
"time" "time"
) )
...@@ -36,34 +37,55 @@ type RemoteCommand struct { ...@@ -36,34 +37,55 @@ type RemoteCommand struct {
Exited bool Exited bool
ExitStatus int ExitStatus int
exitChans []chan<- int
exitChanLock sync.Mutex exitChanLock sync.Mutex
} }
// StdoutStream returns a channel that will be sent all the output // 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. // of stdout as it comes. The output isn't guaranteed to be a full line.
// When the channel is closed, the process is exited. // When the channel is closed, the process is exited.
func (r *RemoteCommand) StdoutChan() (<-chan string) { func (r *RemoteCommand) StdoutChan() <-chan string {
return nil return nil
} }
// ExitChan returns a channel that will be sent the exit status once // 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 // the process exits. This can be used in cases such a select statement
// waiting on the process to end. // waiting on the process to end.
func (r *RemoteCommand) ExitChan() (<-chan int) { func (r *RemoteCommand) ExitChan() <-chan int {
// TODO(mitchellh): Something more efficient than multiple Wait() calls
r.exitChanLock.Lock() r.exitChanLock.Lock()
defer r.exitChanLock.Unlock() defer r.exitChanLock.Unlock()
// Make a single buffered channel so that the send doesn't block. // If we haven't made any channels yet, make that slice
exitChan := make(chan int, 1) if r.exitChans == nil {
r.exitChans = make([]chan<- int, 0, 5)
go func() { go func() {
defer close(exitChan) // Wait for the command to finish
r.Wait() r.Wait()
exitChan <- r.ExitStatus
// Grab the exit chan lock so we can iterate over it and
// message to each channel.
r.exitChanLock.Lock()
defer r.exitChanLock.Unlock()
for _, ch := range r.exitChans {
// Use a select so the send never blocks
select {
case ch <- r.ExitStatus:
default:
log.Println("remote command exit channel wouldn't blocked. Weird.")
}
close(ch)
}
r.exitChans = nil
}() }()
}
// Append our new channel onto it and return it
exitChan := make(chan int, 1)
r.exitChans = append(r.exitChans, exitChan)
return exitChan return exitChan
} }
......
package main package main
import ( import (
"github.com/mitchellh/packer/provisioner/shell"
"github.com/mitchellh/packer/packer/plugin" "github.com/mitchellh/packer/packer/plugin"
"github.com/mitchellh/packer/provisioner/shell"
) )
func main() { func main() {
......
...@@ -14,7 +14,7 @@ func TestProvisioner_Impl(t *testing.T) { ...@@ -14,7 +14,7 @@ func TestProvisioner_Impl(t *testing.T) {
} }
func TestProvisionerPrepare_Defaults(t *testing.T) { func TestProvisionerPrepare_Defaults(t *testing.T) {
raw := map[string]interface{} {} raw := map[string]interface{}{}
p := &Provisioner{} p := &Provisioner{}
p.Prepare(raw, nil) p.Prepare(raw, nil)
......
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