Merge pull request #6033 from hashicorp/fix6032
handle errors uploading files more gracefully
This commit is contained in:
commit
7adb932e64
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue