diff --git a/builder/amazon/chroot/communicator.go b/builder/amazon/chroot/communicator.go index 5df123971..ae42ee49f 100644 --- a/builder/amazon/chroot/communicator.go +++ b/builder/amazon/chroot/communicator.go @@ -60,7 +60,7 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { return nil } -func (c *Communicator) Upload(dst string, r io.Reader) error { +func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { dst = filepath.Join(c.Chroot, dst) log.Printf("Uploading to chroot dir: %s", dst) tf, err := ioutil.TempFile("", "packer-amazon-chroot") diff --git a/builder/amazon/instance/step_upload_x509_cert.go b/builder/amazon/instance/step_upload_x509_cert.go index d62ea8797..ee24cff98 100644 --- a/builder/amazon/instance/step_upload_x509_cert.go +++ b/builder/amazon/instance/step_upload_x509_cert.go @@ -45,5 +45,5 @@ func (s *StepUploadX509Cert) uploadSingle(comm packer.Communicator, dst, src str } defer f.Close() - return comm.Upload(dst, f) + return comm.Upload(dst, f, nil) } diff --git a/builder/docker/communicator.go b/builder/docker/communicator.go index 2cf538f4f..d5f0eb713 100644 --- a/builder/docker/communicator.go +++ b/builder/docker/communicator.go @@ -57,7 +57,7 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error { return nil } -func (c *Communicator) Upload(dst string, src io.Reader) error { +func (c *Communicator) Upload(dst string, src io.Reader, fi *os.FileInfo) error { // Create a temporary file to store the upload tempfile, err := ioutil.TempFile(c.HostDir, "upload") if err != nil { diff --git a/builder/parallels/common/step_upload_parallels_tools.go b/builder/parallels/common/step_upload_parallels_tools.go index 9c421a22b..ae511a1e8 100644 --- a/builder/parallels/common/step_upload_parallels_tools.go +++ b/builder/parallels/common/step_upload_parallels_tools.go @@ -62,7 +62,7 @@ func (s *StepUploadParallelsTools) Run(state multistep.StateBag) multistep.StepA ui.Say(fmt.Sprintf("Uploading Parallels Tools for '%s' to path: '%s'", s.ParallelsToolsFlavor, s.ParallelsToolsGuestPath)) - if err := comm.Upload(s.ParallelsToolsGuestPath, f); err != nil { + if err := comm.Upload(s.ParallelsToolsGuestPath, f, nil); err != nil { err := fmt.Errorf("Error uploading Parallels Tools: %s", err) state.Put("error", err) ui.Error(err.Error()) diff --git a/builder/parallels/common/step_upload_version.go b/builder/parallels/common/step_upload_version.go index f5e03c4f5..8bf5aa7dc 100644 --- a/builder/parallels/common/step_upload_version.go +++ b/builder/parallels/common/step_upload_version.go @@ -33,7 +33,7 @@ func (s *StepUploadVersion) Run(state multistep.StateBag) multistep.StepAction { ui.Say(fmt.Sprintf("Uploading Parallels version info (%s)", version)) var data bytes.Buffer data.WriteString(version) - if err := comm.Upload(s.Path, &data); err != nil { + if err := comm.Upload(s.Path, &data, nil); err != nil { state.Put("error", fmt.Errorf("Error uploading Parallels version: %s", err)) return multistep.ActionHalt } diff --git a/builder/virtualbox/common/step_upload_guest_additions.go b/builder/virtualbox/common/step_upload_guest_additions.go index 5e8b6a86c..04d2dc7cc 100644 --- a/builder/virtualbox/common/step_upload_guest_additions.go +++ b/builder/virtualbox/common/step_upload_guest_additions.go @@ -58,7 +58,7 @@ func (s *StepUploadGuestAdditions) Run(state multistep.StateBag) multistep.StepA } ui.Say("Uploading VirtualBox guest additions ISO...") - if err := comm.Upload(s.GuestAdditionsPath, f); err != nil { + if err := comm.Upload(s.GuestAdditionsPath, f, nil); err != nil { state.Put("error", fmt.Errorf("Error uploading guest additions: %s", err)) return multistep.ActionHalt } diff --git a/builder/virtualbox/common/step_upload_version.go b/builder/virtualbox/common/step_upload_version.go index 1bfd526a6..02566c8cb 100644 --- a/builder/virtualbox/common/step_upload_version.go +++ b/builder/virtualbox/common/step_upload_version.go @@ -33,7 +33,7 @@ func (s *StepUploadVersion) Run(state multistep.StateBag) multistep.StepAction { ui.Say(fmt.Sprintf("Uploading VirtualBox version info (%s)", version)) var data bytes.Buffer data.WriteString(version) - if err := comm.Upload(s.Path, &data); err != nil { + if err := comm.Upload(s.Path, &data, nil); err != nil { state.Put("error", fmt.Errorf("Error uploading VirtualBox version: %s", err)) return multistep.ActionHalt } diff --git a/builder/vmware/common/step_upload_tools.go b/builder/vmware/common/step_upload_tools.go index b714a3dc4..03387a13f 100644 --- a/builder/vmware/common/step_upload_tools.go +++ b/builder/vmware/common/step_upload_tools.go @@ -55,7 +55,7 @@ func (c *StepUploadTools) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - if err := comm.Upload(c.ToolsUploadPath, f); err != nil { + if err := comm.Upload(c.ToolsUploadPath, f, nil); err != nil { err := fmt.Errorf("Error uploading VMware Tools: %s", err) state.Put("error", err) ui.Error(err.Error()) diff --git a/builder/vmware/iso/driver_esx5.go b/builder/vmware/iso/driver_esx5.go index 211bc119e..6f35f5272 100644 --- a/builder/vmware/iso/driver_esx5.go +++ b/builder/vmware/iso/driver_esx5.go @@ -371,7 +371,7 @@ func (d *ESX5Driver) upload(dst, src string) error { return err } defer f.Close() - return d.comm.Upload(dst, f) + return d.comm.Upload(dst, f, nil) } func (d *ESX5Driver) verifyChecksum(ctype string, hash string, file string) bool { diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index e07f875c6..0b48eeb29 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -119,7 +119,7 @@ func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { return } -func (c *comm) Upload(path string, input io.Reader) error { +func (c *comm) Upload(path string, input io.Reader, fi *os.FileInfo) error { // The target directory and file for talking the SCP protocol target_dir := filepath.Dir(path) target_file := filepath.Base(path) @@ -130,7 +130,7 @@ func (c *comm) Upload(path string, input io.Reader) error { target_dir = filepath.ToSlash(target_dir) scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error { - return scpUploadFile(target_file, input, w, stdoutR) + return scpUploadFile(target_file, input, w, stdoutR, fi) } return c.scpSession("scp -vt "+target_dir, scpFunc) @@ -156,7 +156,11 @@ func (c *comm) UploadDir(dst string, src string, excl []string) error { if src[len(src)-1] != '/' { log.Printf("No trailing slash, creating the source directory name") - return scpUploadDirProtocol(filepath.Base(src), w, r, uploadEntries) + fi, err := os.Stat(src) + if err != nil { + return err + } + return scpUploadDirProtocol(filepath.Base(src), w, r, uploadEntries, fi) } else { // Trailing slash, so only upload the contents return uploadEntries() @@ -328,7 +332,7 @@ func checkSCPStatus(r *bufio.Reader) error { return nil } -func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader) error { +func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader, fi *os.FileInfo) error { // Create a temporary file where we can copy the contents of the src // so that we can determine the length, since SCP is length-prefixed. tf, err := ioutil.TempFile("", "packer-upload") @@ -338,30 +342,45 @@ func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader) erro defer os.Remove(tf.Name()) defer tf.Close() - log.Println("Copying input data into temporary file so we can read the length") - if _, err := io.Copy(tf, src); err != nil { - return err - } + var mode os.FileMode + var size int64 - // Sync the file so that the contents are definitely on disk, then - // read the length of it. - if err := tf.Sync(); err != nil { - return fmt.Errorf("Error creating temporary file for upload: %s", err) - } + if fi != nil { + mode = (*fi).Mode().Perm() + size = (*fi).Size() + } else { + mode = 0644 - // Seek the file to the beginning so we can re-read all of it - if _, err := tf.Seek(0, 0); err != nil { - return fmt.Errorf("Error creating temporary file for upload: %s", err) - } + log.Println("Copying input data into temporary file so we can read the length") + if _, err := io.Copy(tf, src); err != nil { + return err + } - fi, err := tf.Stat() - if err != nil { - return fmt.Errorf("Error creating temporary file for upload: %s", err) + // Sync the file so that the contents are definitely on disk, then + // read the length of it. + if err := tf.Sync(); err != nil { + return fmt.Errorf("Error creating temporary file for upload: %s", err) + } + + // Seek the file to the beginning so we can re-read all of it + if _, err := tf.Seek(0, 0); err != nil { + return fmt.Errorf("Error creating temporary file for upload: %s", err) + } + + tfi, err := tf.Stat() + if err != nil { + return fmt.Errorf("Error creating temporary file for upload: %s", err) + } + + size = tfi.Size() } // Start the protocol log.Println("Beginning file upload...") - fmt.Fprintln(w, "C0644", fi.Size(), dst) + + perms := fmt.Sprintf("C%04o", mode) + + fmt.Fprintln(w, perms, size, dst) if err := checkSCPStatus(r); err != nil { return err } @@ -378,9 +397,14 @@ func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader) erro return nil } -func scpUploadDirProtocol(name string, w io.Writer, r *bufio.Reader, f func() error) error { +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) - fmt.Fprintln(w, "D0755 0", name) + + mode := fi.Mode().Perm() + + perms := fmt.Sprintf("D%04o 0", mode) + + fmt.Fprintln(w, perms, name) err := checkSCPStatus(r) if err != nil { return err @@ -430,7 +454,7 @@ func scpUploadDir(root string, fs []os.FileInfo, w io.Writer, r *bufio.Reader) e err = func() error { defer f.Close() - return scpUploadFile(fi.Name(), f, w, r) + return scpUploadFile(fi.Name(), f, w, r, &fi) }() if err != nil { @@ -454,7 +478,7 @@ func scpUploadDir(root string, fs []os.FileInfo, w io.Writer, r *bufio.Reader) e } return scpUploadDir(realPath, entries, w, r) - }) + }, fi) if err != nil { return err } diff --git a/packer/communicator.go b/packer/communicator.go index 159e5e380..448e4aacf 100644 --- a/packer/communicator.go +++ b/packer/communicator.go @@ -3,6 +3,7 @@ package packer import ( "github.com/mitchellh/iochan" "io" + "os" "strings" "sync" ) @@ -57,7 +58,7 @@ type Communicator interface { // Upload uploads a file to the machine to the given path with the // contents coming from the given reader. This method will block until // it completes. - Upload(string, io.Reader) error + Upload(string, io.Reader, *os.FileInfo) error // UploadDir uploads the contents of a directory recursively to // the remote path. It also takes an optional slice of paths to diff --git a/packer/communicator_mock.go b/packer/communicator_mock.go index 30539f6f1..048fc3c3b 100644 --- a/packer/communicator_mock.go +++ b/packer/communicator_mock.go @@ -3,6 +3,7 @@ package packer import ( "bytes" "io" + "os" "sync" ) @@ -68,7 +69,7 @@ func (c *MockCommunicator) Start(rc *RemoteCmd) error { return nil } -func (c *MockCommunicator) Upload(path string, r io.Reader) error { +func (c *MockCommunicator) Upload(path string, r io.Reader, fi *os.FileInfo) error { c.UploadCalled = true c.UploadPath = path diff --git a/packer/rpc/communicator.go b/packer/rpc/communicator.go index e1d6cb649..abf841af8 100644 --- a/packer/rpc/communicator.go +++ b/packer/rpc/communicator.go @@ -6,6 +6,7 @@ import ( "io" "log" "net/rpc" + "os" ) // An implementation of packer.Communicator where the communicator is actually @@ -103,7 +104,7 @@ func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) { return } -func (c *communicator) Upload(path string, r io.Reader) (err error) { +func (c *communicator) Upload(path string, r io.Reader, fi *os.FileInfo) (err error) { // Pipe the reader through to the connection streamId := c.mux.NextId() go serveSingleCopy("uploadData", c.mux, streamId, nil, r) @@ -233,7 +234,7 @@ func (c *CommunicatorServer) Upload(args *CommunicatorUploadArgs, reply *interfa } defer readerC.Close() - err = c.c.Upload(args.Path, readerC) + err = c.c.Upload(args.Path, readerC, nil) return } diff --git a/packer/rpc/communicator_test.go b/packer/rpc/communicator_test.go index ca8239514..0e517aec9 100644 --- a/packer/rpc/communicator_test.go +++ b/packer/rpc/communicator_test.go @@ -82,7 +82,7 @@ func TestCommunicatorRPC(t *testing.T) { defer uploadW.Close() uploadW.Write([]byte("uploadfoo\n")) }() - err = remote.Upload("foo", uploadR) + err = remote.Upload("foo", uploadR, nil) if err != nil { t.Fatalf("err: %s", err) } diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 904abc57a..3188b625f 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -320,7 +320,7 @@ func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, sr } defer f.Close() - if err = comm.Upload(dst, f); err != nil { + if err = comm.Upload(dst, f, nil); err != nil { return fmt.Errorf("Error uploading %s: %s", src, err) } return nil diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index aba777e27..eff38dcb9 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -295,7 +295,7 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeN } remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "client.rb")) - if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString))); err != nil { + if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil { return "", err } @@ -324,7 +324,7 @@ func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string // Upload the bytes remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "first-boot.json")) - if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes)); err != nil { + if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes), nil); err != nil { return "", err } @@ -453,7 +453,7 @@ func (p *Provisioner) copyValidationKey(ui packer.Ui, comm packer.Communicator, } defer f.Close() - if err := comm.Upload(remotePath, f); err != nil { + if err := comm.Upload(remotePath, f, nil); err != nil { return err } diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index 2166a9d0c..3ee0e74c7 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -329,7 +329,7 @@ func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst str } defer f.Close() - return comm.Upload(dst, f) + return comm.Upload(dst, f, nil) } func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, localCookbooks []string, rolesPath string, dataBagsPath string, encryptedDataBagSecretPath string, environmentsPath string, chefEnvironment string) (string, error) { @@ -379,7 +379,7 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, local } remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "solo.rb")) - if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString))); err != nil { + if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil { return "", err } @@ -408,7 +408,7 @@ func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string // Upload the bytes remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "node.json")) - if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes)); err != nil { + if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes), nil); err != nil { return "", err } diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index c20cbd0f2..1cb69acd4 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -89,7 +89,12 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { } defer f.Close() - err = comm.Upload(p.config.Destination, f) + fi, err := f.Stat() + if err != nil { + return err + } + + err = comm.Upload(p.config.Destination, f, &fi) if err != nil { ui.Error(fmt.Sprintf("Upload failed: %s", err)) } diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index a290948fd..81ab34afa 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -301,7 +301,7 @@ func (p *Provisioner) uploadHieraConfig(ui packer.Ui, comm packer.Communicator) defer f.Close() path := fmt.Sprintf("%s/hiera.yaml", p.config.StagingDir) - if err := comm.Upload(path, f); err != nil { + if err := comm.Upload(path, f, nil); err != nil { return "", err } @@ -325,7 +325,7 @@ func (p *Provisioner) uploadManifests(ui packer.Ui, comm packer.Communicator) (s manifestFilename := filepath.Base(p.config.ManifestFile) remoteManifestFile := fmt.Sprintf("%s/%s", remoteManifestsPath, manifestFilename) - if err := comm.Upload(remoteManifestFile, f); err != nil { + if err := comm.Upload(remoteManifestFile, f, nil); err != nil { return "", err } diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index d13ca1f84..fdf9491a5 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -203,7 +203,7 @@ func uploadMinionConfig(comm packer.Communicator, dst string, src string) error } defer f.Close() - if err = comm.Upload(dst, f); err != nil { + if err = comm.Upload(dst, f, nil); err != nil { return fmt.Errorf("Error uploading minion config: %s", err) } diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index a5ec2e2bb..679b18100 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -269,7 +269,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { r = &UnixReader{Reader: r} } - if err := comm.Upload(p.config.RemotePath, r); err != nil { + if err := comm.Upload(p.config.RemotePath, r, nil); err != nil { return fmt.Errorf("Error uploading script: %s", err) }