diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index ae73f41d1..9dad29b16 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -3,10 +3,12 @@ package ssh import ( "bytes" "code.google.com/p/go.crypto/ssh" + "fmt" "github.com/mitchellh/packer/packer" "io" "log" "net" + "path/filepath" ) type comm struct { @@ -72,7 +74,64 @@ func (c *comm) Start(cmd string) (remote *packer.RemoteCommand, err error) { return } -func (c *comm) Upload(string, io.Reader) error { +func (c *comm) Upload(path string, input io.Reader) error { + session, err := c.client.NewSession() + if err != nil { + return err + } + + defer session.Close() + + // Get a pipe to stdin so that we can send data down + w, err := session.StdinPipe() + if err != nil { + return err + } + + // We only want to close once, so we nil w after we close it, + // and only close in the defer if it hasn't been closed already. + defer func() { + if w != nil { + w.Close() + } + }() + + // The target directory and file for talking the SCP protocol + target_dir := filepath.Dir(path) + target_file := filepath.Base(path) + + // Start the sink mode on the other side + // TODO(mitchellh): There are probably issues with shell escaping the path + log.Println("Starting remote scp process in sink mode") + if err = session.Start("scp -t " + target_dir); err != nil { + return err + } + + // Determine the length of the upload content by copying it + // into an in-memory buffer. Note that this means what we upload + // must fit into memory. + log.Println("Copying input data into in-memory buffer so we can get the length") + input_memory := new(bytes.Buffer) + if _, err = io.Copy(input_memory, input); err != nil { + return err + } + + // Start the protocol + fmt.Fprintln(w, "C0644", input_memory.Len(), target_file) + io.Copy(w, input_memory) + + // Close the stdin, which sends an EOF, and then set w to nil so that + // our defer func doesn't close it again since that is unsafe with + // the Go SSH package. + w.Close() + w = nil + + // Wait for the SCP connection to close, meaning it has consumed all + // our data and has completed. Or has errored. + session.Wait() + + // TODO(mitchellh): Check for return data (expect a 0 or error) + return nil }