From 35ef7bce5ca489a6a74a92cb3386f34e637dedc3 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 15:35:03 -0700 Subject: [PATCH 1/8] implement dir check for winrm communicator --- communicator/winrm/communicator.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go index d1f7d2ab6..a725fe51f 100644 --- a/communicator/winrm/communicator.go +++ b/communicator/winrm/communicator.go @@ -127,6 +127,23 @@ func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) erro if err != nil { return err } + + // Get information about destination path + endpoint := winrm.NewEndpoint(c.endpoint.Host, c.endpoint.Port, c.config.Https, c.config.Insecure, nil, nil, nil, c.config.Timeout) + client, err := winrm.NewClient(endpoint, c.config.Username, c.config.Password) + if err != nil { + return fmt.Errorf("Was unable to create winrm client: %s", err) + } + stdout, _, _, err := client.RunWithString(fmt.Sprintf("powershell -Command \"(Get-Item %s) -is [System.IO.DirectoryInfo]\"", path), "") + if err != nil { + return fmt.Errorf("Couldn't determine whether destination was a folder or file: %s", err) + } + if strings.Contains(stdout, "True") { + // The path exists and is a directory. + // Upload file into the directory instead of overwriting. + path = filepath.Join(path, filepath.Base((*fi).Name())) + } + log.Printf("Uploading file to '%s'", path) return wcp.Write(path, input) } From a5e61348195383154b9f667c45d36d5270bf00cc Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 15:55:32 -0700 Subject: [PATCH 2/8] implement dir check for ssh communicator --- communicator/ssh/communicator.go | 23 +++++++++++++++++++++++ communicator/winrm/communicator.go | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index c10e97dcc..827925f2c 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -533,6 +533,29 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_dir := filepath.Dir(path) target_file := filepath.Base(path) + // find out if it's a directory + testDirectoryCommand := fmt.Sprintf("if [ -d \"%s\" ]; then echo directory; fi", path) + cmd := &packer.RemoteCmd{Command: testDirectoryCommand} + var buf, buf2 bytes.Buffer + cmd.Stdout = &buf + cmd.Stdout = io.MultiWriter(cmd.Stdout, &buf2) + + err := c.Start(cmd) + + if err != nil { + log.Printf("Unable to check whether remote path is a dir: %s", err) + return err + } + + stdoutToRead := buf2.String() + if strings.Contains(stdoutToRead, "directory") { + log.Printf("upload locale is a directory") + target_dir = path + target_file = filepath.Base((*fi).Name()) + } + + log.Printf("target_file was %s", target_file) + // On windows, filepath.Dir uses backslash seperators (ie. "\tmp"). // This does not work when the target host is unix. Switch to forward slash // which works for unix and windows diff --git a/communicator/winrm/communicator.go b/communicator/winrm/communicator.go index a725fe51f..3970d1af8 100644 --- a/communicator/winrm/communicator.go +++ b/communicator/winrm/communicator.go @@ -122,7 +122,7 @@ func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { } // Upload implementation of communicator.Communicator interface -func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) error { +func (c *Communicator) Upload(path string, input io.Reader, fi *os.FileInfo) error { wcp, err := c.newCopyClient() if err != nil { return err From b3cc90125dde7fbec29d6c9616df4167efe5f968 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 16:36:27 -0700 Subject: [PATCH 3/8] ssh communicator works --- communicator/ssh/communicator.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 827925f2c..3ec2d1aba 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -534,11 +534,8 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_file := filepath.Base(path) // find out if it's a directory - testDirectoryCommand := fmt.Sprintf("if [ -d \"%s\" ]; then echo directory; fi", path) + testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, path) cmd := &packer.RemoteCmd{Command: testDirectoryCommand} - var buf, buf2 bytes.Buffer - cmd.Stdout = &buf - cmd.Stdout = io.MultiWriter(cmd.Stdout, &buf2) err := c.Start(cmd) @@ -546,10 +543,9 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e log.Printf("Unable to check whether remote path is a dir: %s", err) return err } - - stdoutToRead := buf2.String() - if strings.Contains(stdoutToRead, "directory") { - log.Printf("upload locale is a directory") + cmd.Wait() + if cmd.ExitStatus == 0 { + log.Printf("path is a directory; copying file into directory.") target_dir = path target_file = filepath.Base((*fi).Name()) } From 8452ca898cbf5dcf3b8c6c0a7db975dffc18fee8 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 3 Oct 2017 17:06:33 -0700 Subject: [PATCH 4/8] implemented for docker communicator --- builder/docker/communicator.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builder/docker/communicator.go b/builder/docker/communicator.go index 13684b2f8..0c4a8833d 100644 --- a/builder/docker/communicator.go +++ b/builder/docker/communicator.go @@ -104,6 +104,21 @@ func (c *Communicator) uploadReader(dst string, src io.Reader) error { // uploadFile uses docker cp to copy the file from the host to the container func (c *Communicator) uploadFile(dst string, src io.Reader, fi *os.FileInfo) error { + // find out if it's a directory + testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, dst) + 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) + return err + } + cmd.Wait() + if cmd.ExitStatus == 0 { + log.Printf("path is a directory; copying file into directory.") + dst = filepath.Join(dst, filepath.Base((*fi).Name())) + } // command format: docker cp /path/to/infile containerid:/path/to/outfile log.Printf("Copying to %s on container %s.", dst, c.ContainerID) From 93bddb3e65101d553e596d975098949129dfba36 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 4 Oct 2017 13:35:15 -0700 Subject: [PATCH 5/8] implement directory fix for lxc file uploads --- builder/lxc/communicator.go | 18 ++++++++++++++++++ communicator/ssh/communicator.go | 2 -- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/builder/lxc/communicator.go b/builder/lxc/communicator.go index 8d9765979..77ea3c052 100644 --- a/builder/lxc/communicator.go +++ b/builder/lxc/communicator.go @@ -71,6 +71,24 @@ func (c *LxcAttachCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) return err } + if fi != nil { + tfDir := filepath.Dir(tf.Name()) + // rename tempfile to match original file name. This makes sure that if file is being + // moved into a directory, the filename is preserved instead of a temp name. + adjustedTempName := filepath.Join(tfDir, (*fi).Name()) + mvCmd, err := c.CmdWrapper(fmt.Sprintf("sudo mv %s %s", tf.Name(), adjustedTempName)) + if err != nil { + return err + } + defer os.Remove(adjustedTempName) + exitStatus := ShellCommand(mvCmd).Run() + // change cpCmd to use new file name as source + cpCmd, err = c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", adjustedTempName, dst)) + if err != nil { + return err + } + } + log.Printf("Running copy command: %s", dst) return ShellCommand(cpCmd).Run() diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 3ec2d1aba..8d5a388d5 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -550,8 +550,6 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_file = filepath.Base((*fi).Name()) } - log.Printf("target_file was %s", target_file) - // On windows, filepath.Dir uses backslash seperators (ie. "\tmp"). // This does not work when the target host is unix. Switch to forward slash // which works for unix and windows From e8cabc1e83b6aa99a678f5555a7218cffc20003c Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 4 Oct 2017 15:23:36 -0700 Subject: [PATCH 6/8] implemented for LXD --- builder/lxc/communicator.go | 2 +- builder/lxd/communicator.go | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/builder/lxc/communicator.go b/builder/lxc/communicator.go index 77ea3c052..bb940851a 100644 --- a/builder/lxc/communicator.go +++ b/builder/lxc/communicator.go @@ -81,7 +81,7 @@ func (c *LxcAttachCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) return err } defer os.Remove(adjustedTempName) - exitStatus := ShellCommand(mvCmd).Run() + ShellCommand(mvCmd).Run() // change cpCmd to use new file name as source cpCmd, err = c.CmdWrapper(fmt.Sprintf("sudo cp %s %s", adjustedTempName, dst)) if err != nil { diff --git a/builder/lxd/communicator.go b/builder/lxd/communicator.go index 8eaa47a5f..b59e83f82 100644 --- a/builder/lxd/communicator.go +++ b/builder/lxd/communicator.go @@ -54,7 +54,24 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { } func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { - cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file push - %s", filepath.Join(c.ContainerName, dst))) + fileDestination := filepath.Join(c.ContainerName, dst) + // find out if the place we are pushing to is a directory + testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, dst) + 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) + return err + } + cmd.Wait() + + if cmd.ExitStatus == 0 { + log.Printf("path is a directory; copying file into directory.") + fileDestination = filepath.Join(c.ContainerName, dst, (*fi).Name()) + } + + cpCmd, err := c.CmdWrapper(fmt.Sprintf("lxc file push - %s", fileDestination)) if err != nil { return err } From a79d5eff4ec3ad22d548a007d44d2bf6ad12cafd Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Oct 2017 10:44:18 -0700 Subject: [PATCH 7/8] implement sftp path --- communicator/ssh/communicator.go | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 8d5a388d5..3f668bb8e 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -377,15 +377,31 @@ func (c *comm) connectToAgent() { func (c *comm) sftpUploadSession(path string, input io.Reader, fi *os.FileInfo) error { sftpFunc := func(client *sftp.Client) error { - return sftpUploadFile(path, input, client, fi) + return c.sftpUploadFile(path, input, client, fi) } return c.sftpSession(sftpFunc) } -func sftpUploadFile(path string, input io.Reader, client *sftp.Client, fi *os.FileInfo) error { +func (c *comm) sftpUploadFile(path string, input io.Reader, client *sftp.Client, fi *os.FileInfo) error { log.Printf("[DEBUG] sftp: uploading %s", path) + // 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} + + err := c.Start(cmd) + + if err != nil { + log.Printf("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())) + } + f, err := client.Create(path) if err != nil { return err @@ -436,7 +452,7 @@ func (c *comm) sftpUploadDirSession(dst string, src string, excl []string) error return nil } - return sftpVisitFile(finalDst, path, info, client) + return c.sftpVisitFile(finalDst, path, info, client) } return filepath.Walk(src, walkFunc) @@ -445,7 +461,7 @@ func (c *comm) sftpUploadDirSession(dst string, src string, excl []string) error return c.sftpSession(sftpFunc) } -func sftpMkdir(path string, client *sftp.Client, fi os.FileInfo) error { +func (c *comm) sftpMkdir(path string, client *sftp.Client, fi os.FileInfo) error { log.Printf("[DEBUG] sftp: creating dir %s", path) if err := client.Mkdir(path); err != nil { @@ -463,16 +479,16 @@ func sftpMkdir(path string, client *sftp.Client, fi os.FileInfo) error { return nil } -func sftpVisitFile(dst string, src string, fi os.FileInfo, client *sftp.Client) error { +func (c *comm) sftpVisitFile(dst string, src string, fi os.FileInfo, client *sftp.Client) error { if !fi.IsDir() { f, err := os.Open(src) if err != nil { return err } defer f.Close() - return sftpUploadFile(dst, f, client, &fi) + return c.sftpUploadFile(dst, f, client, &fi) } else { - err := sftpMkdir(dst, client, fi) + err := c.sftpMkdir(dst, client, fi) return err } } @@ -533,7 +549,7 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e target_dir := filepath.Dir(path) target_file := filepath.Base(path) - // find out if it's a directory + // 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} From 52c0be2d82cf74f29e4d4d5a246001b5e559bcaf Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 5 Oct 2017 16:57:52 -0700 Subject: [PATCH 8/8] fix test --- communicator/winrm/communicator_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/communicator/winrm/communicator_test.go b/communicator/winrm/communicator_test.go index d5eb974ac..3f6b50ae1 100644 --- a/communicator/winrm/communicator_test.go +++ b/communicator/winrm/communicator_test.go @@ -48,6 +48,12 @@ func newMockWinRMServer(t *testing.T) *winrmtest.Remote { func(out, err io.Writer) int { return 0 }) + wrm.CommandFunc( + winrmtest.MatchText(`powershell -Command "(Get-Item C:/Temp/packer.cmd) -is [System.IO.DirectoryInfo]"`), + func(out, err io.Writer) int { + out.Write([]byte("False")) + return 0 + }) return wrm }