diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index d81ab750a..218c11be6 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -105,7 +105,7 @@ func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { } } - log.Printf("starting remote command: %s", cmd.Command) + log.Printf("[DEBUG] starting remote command: %s", cmd.Command) err = session.Start(cmd.Command + "\n") if err != nil { return @@ -136,12 +136,12 @@ func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { switch err.(type) { case *ssh.ExitError: exitStatus = err.(*ssh.ExitError).ExitStatus() - log.Printf("Remote command exited with '%d': %s", exitStatus, cmd.Command) + log.Printf("[ERROR] Remote command exited with '%d': %s", exitStatus, cmd.Command) case *ssh.ExitMissingError: - log.Printf("Remote command exited without exit status or exit signal.") + log.Printf("[ERROR] Remote command exited without exit status or exit signal.") exitStatus = packer.CmdDisconnect default: - log.Printf("Error occurred waiting for ssh session: %s", err.Error()) + log.Printf("[ERROR] Error occurred waiting for ssh session: %s", err.Error()) } } cmd.SetExited(exitStatus) @@ -158,7 +158,7 @@ func (c *comm) Upload(path string, input io.Reader, fi *os.FileInfo) error { } func (c *comm) UploadDir(dst string, src string, excl []string) error { - log.Printf("Upload dir '%s' to '%s'", src, dst) + log.Printf("[DEBUG] Upload dir '%s' to '%s'", src, dst) if c.config.UseSftp { return c.sftpUploadDirSession(dst, src, excl) } else { @@ -167,7 +167,7 @@ func (c *comm) UploadDir(dst string, src string, excl []string) error { } func (c *comm) DownloadDir(src string, dst string, excl []string) error { - log.Printf("Download dir '%s' to '%s'", src, dst) + log.Printf("[DEBUG] Download dir '%s' to '%s'", src, dst) scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error { dirStack := []string{dst} for { @@ -202,7 +202,7 @@ func (c *comm) DownloadDir(src string, dst string, excl []string) error { var mode int64 var size int64 var name string - log.Printf("Download dir str:%s", fi) + log.Printf("[DEBUG] Download dir str:%s", fi) n, err := fmt.Sscanf(fi[1:], "%o %d %s", &mode, &size, &name) if err != nil || n != 3 { return fmt.Errorf("can't parse server response (%s)", fi) @@ -211,7 +211,7 @@ func (c *comm) DownloadDir(src string, dst string, excl []string) error { return fmt.Errorf("negative file size") } - log.Printf("Download dir mode:%0o size:%d name:%s", mode, size, name) + log.Printf("[DEBUG] Download dir mode:%0o size:%d name:%s", mode, size, name) dst = filepath.Join(dirStack...) switch fi[0] { @@ -246,7 +246,7 @@ func (c *comm) Download(path string, output io.Writer) error { } func (c *comm) newSession() (session *ssh.Session, err error) { - log.Println("opening new ssh session") + log.Println("[DEBUG] Opening new ssh session") if c.client == nil { err = errors.New("client not available") } else { @@ -254,7 +254,7 @@ func (c *comm) newSession() (session *ssh.Session, err error) { } if err != nil { - log.Printf("ssh session open error: '%s', attempting reconnect", err) + log.Printf("[ERROR] ssh session open error: '%s', attempting reconnect", err) if err := c.reconnect(); err != nil { return nil, err } @@ -279,7 +279,7 @@ func (c *comm) reconnect() (err error) { c.conn = nil c.client = nil - log.Printf("reconnecting to TCP connection for SSH") + log.Printf("[DEBUG] reconnecting to TCP connection for SSH") c.conn, err = c.config.Connection() if err != nil { // Explicitly set this to the REAL nil. Connection() can return @@ -290,7 +290,7 @@ func (c *comm) reconnect() (err error) { // http://golang.org/doc/faq#nil_error c.conn = nil - log.Printf("reconnection error: %s", err) + log.Printf("[ERROR] reconnection error: %s", err) return } @@ -298,7 +298,7 @@ func (c *comm) reconnect() (err error) { c.conn = &timeoutConn{c.conn, c.config.Timeout, c.config.Timeout} } - log.Printf("handshaking with SSH") + log.Printf("[DEBUG] handshaking with SSH") // Default timeout to 1 minute if it wasn't specified (zero value). For // when you need to handshake from low orbit. @@ -335,10 +335,10 @@ func (c *comm) reconnect() (err error) { } if err != nil { - log.Printf("handshake error: %s", err) + log.Printf("[ERROR] handshake error: %s", err) return } - log.Printf("handshake complete!") + log.Printf("[DEBUG] handshake complete!") if sshConn != nil { c.client = ssh.NewClient(sshConn, sshChan, req) } @@ -413,18 +413,22 @@ func (c *comm) sftpUploadFile(path string, input io.Reader, client *sftp.Client, // find out if destination is a directory (this is to replicate rsync behavior) testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) - cmd := &packer.RemoteCmd{Command: testDirectoryCommand} + + cmd := &packer.RemoteCmd{ + Command: testDirectoryCommand, + } err := c.Start(cmd) if err != nil { - log.Printf("Unable to check whether remote path is a dir: %s", err) + log.Printf("[ERROR] Unable to check whether remote path is a dir: %s", err) return err } cmd.Wait() if cmd.ExitStatus == 0 { - log.Printf("path is a directory; copying file into directory.") - path = filepath.Join(path, filepath.Base((*fi).Name())) + return fmt.Errorf( + "Destination path (%s) is a directory that already exists. "+ + "Please ensure the destination is writable.", path) } f, err := client.Create(path) @@ -452,7 +456,7 @@ func (c *comm) sftpUploadDirSession(dst string, src string, excl []string) error sftpFunc := func(client *sftp.Client) error { rootDst := dst if src[len(src)-1] != '/' { - log.Printf("No trailing slash, creating the source directory name") + log.Printf("[DEBUG] No trailing slash, creating the source directory name") rootDst = filepath.Join(dst, filepath.Base(src)) } walkFunc := func(path string, info os.FileInfo, err error) error { @@ -539,7 +543,7 @@ func (c *comm) sftpDownloadSession(path string, output io.Writer) error { func (c *comm) sftpSession(f func(*sftp.Client) error) error { client, err := c.newSftpClient() if err != nil { - return err + return fmt.Errorf("sftpSession error: %s", err.Error()) } defer client.Close() @@ -565,7 +569,15 @@ func (c *comm) newSftpClient() (*sftp.Client, error) { return nil, err } - return sftp.NewClientPipe(pr, pw) + // Capture stdout so we can return errors to the user + var stdout bytes.Buffer + tee := io.TeeReader(pr, &stdout) + client, err := sftp.NewClientPipe(tee, pw) + if err != nil && stdout.Len() > 0 { + log.Printf("[ERROR] Upload failed: %s", stdout.Bytes()) + } + + return client, err } func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) error { @@ -576,19 +588,30 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e // find out if destination is a directory (this is to replicate rsync behavior) testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) - cmd := &packer.RemoteCmd{Command: testDirectoryCommand} + var stdout, stderr bytes.Buffer + cmd := &packer.RemoteCmd{ + Command: testDirectoryCommand, + Stdout: &stdout, + Stderr: &stderr, + } err := c.Start(cmd) if err != nil { - log.Printf("Unable to check whether remote path is a dir: %s", err) + log.Printf("[ERROR] Unable to check whether remote path is a dir: %s", err) return err } cmd.Wait() + if stdout.Len() > 0 { + return fmt.Errorf("%s", stdout.Bytes()) + } + if stderr.Len() > 0 { + return fmt.Errorf("%s", stderr.Bytes()) + } if cmd.ExitStatus == 0 { - log.Printf("path is a directory; copying file into directory.") - target_dir = path - target_file = filepath.Base((*fi).Name()) + return fmt.Errorf( + "Destination path (%s) is a directory that already exists. "+ + "Please ensure the destination is writable.", path) } // On windows, filepath.Dir uses backslash separators (ie. "\tmp"). @@ -621,7 +644,7 @@ func (c *comm) scpUploadDirSession(dst string, src string, excl []string) error } if src[len(src)-1] != '/' { - log.Printf("No trailing slash, creating the source directory name") + log.Printf("[DEBUG] No trailing slash, creating the source directory name") fi, err := os.Stat(src) if err != nil { return err @@ -722,7 +745,7 @@ func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) er // 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: ", scpCommand) + log.Println("[DEBUG] Starting remote scp process: ", scpCommand) if err := session.Start(scpCommand); err != nil { return err } @@ -730,7 +753,7 @@ func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) er // Call our callback that executes in the context of SCP. We ignore // EOF errors if they occur because it usually means that SCP prematurely // ended on the other side. - log.Println("Started SCP session, beginning transfers...") + log.Println("[DEBUG] Started SCP session, beginning transfers...") if err := f(stdinW, stdoutR); err != nil && err != io.EOF { return err } @@ -738,24 +761,24 @@ func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) er // 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. - log.Println("SCP session complete, closing stdin pipe.") + log.Println("[DEBUG] SCP session complete, closing stdin pipe.") stdinW.Close() stdinW = nil // Wait for the SCP connection to close, meaning it has consumed all // our data and has completed. Or has errored. - log.Println("Waiting for SSH session to complete.") + log.Println("[DEBUG] Waiting for SSH session to complete.") err = session.Wait() if err != nil { if exitErr, ok := err.(*ssh.ExitError); ok { // Otherwise, we have an ExitError, meaning we can just read // the exit status - log.Printf("non-zero exit status: %d", exitErr.ExitStatus()) + log.Printf("[DEBUG] non-zero exit status: %d", exitErr.ExitStatus()) stdoutB, err := ioutil.ReadAll(stdoutR) if err != nil { return err } - log.Printf("scp output: %s", stdoutB) + log.Printf("[DEBUG] scp output: %s", stdoutB) // If we exited with status 127, it means SCP isn't available. // Return a more descriptive error for that. @@ -769,7 +792,7 @@ func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) er return err } - log.Printf("scp stderr (length %d): %s", stderr.Len(), stderr.String()) + log.Printf("[DEBUG] scp stderr (length %d): %s", stderr.Len(), stderr.String()) return nil } @@ -826,7 +849,7 @@ func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader, fi * mode = 0644 - log.Println("Copying input data into temporary file so we can read the length") + log.Println("[DEBUG] Copying input data into temporary file so we can read the length") if _, err := io.Copy(tf, src); err != nil { return err } @@ -869,7 +892,7 @@ func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader, fi * } func scpUploadDirProtocol(name string, w io.Writer, r *bufio.Reader, f func() error, fi os.FileInfo) error { - log.Printf("SCP: starting directory upload: %s", name) + log.Printf("[DEBUG] SCP: starting directory upload: %s", name) mode := fi.Mode().Perm() diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index 8dd5796ce..bfecd27ff 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -230,6 +230,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return fmt.Errorf("Error opening shell script: %s", err) } defer f.Close() + info, _ := f.Stat() // Compile the command p.config.ctx.Data = &ExecuteCommandTemplate{ @@ -257,7 +258,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { r = &UnixReader{Reader: r} } - if err := comm.Upload(p.config.RemotePath, r, nil); err != nil { + if err := comm.Upload(p.config.RemotePath, r, &info); err != nil { return fmt.Errorf("Error uploading script: %s", err) }