docker: stream uploads over stdin

Only write to a tempfile if we don't have a stat struct
This commit is contained in:
Matthew Hooker 2017-08-23 14:09:39 -07:00
parent c8db128dc5
commit 4edbd5322c
No known key found for this signature in database
GPG Key ID: 7B5F933D9CE8C6A1
2 changed files with 70 additions and 23 deletions

View File

@ -60,39 +60,85 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
return nil
}
// Upload uploads a file to the docker container
func (c *Communicator) Upload(dst string, src io.Reader, fi *os.FileInfo) error {
if fi == nil {
return c.uploadReader(dst, src)
}
return c.uploadFile(dst, src, fi)
}
// uploadReader writes an io.Reader to a temporary file before uploading
func (c *Communicator) uploadReader(dst string, src io.Reader) error {
// Create a temporary file to store the upload
tempfile, err := ioutil.TempFile(c.HostDir, "upload")
if err != nil {
return err
return fmt.Errorf("Failed to open temp file for writing: %s", err)
}
defer os.Remove(tempfile.Name())
defer tempfile.Close()
// Copy the contents to the temporary file
_, err = io.Copy(tempfile, src)
if _, err := io.Copy(tempfile, src); err != nil {
return fmt.Errorf("Failed to copy upload file to tempfile: %s", err)
}
tempfile.Seek(0, 0)
fi, err := tempfile.Stat()
if err != nil {
return fmt.Errorf("Error getting tempfile info: %s", err)
}
return c.uploadFile(dst, tempfile, &fi)
}
// 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 {
// command format: docker cp /path/to/infile containerid:/path/to/outfile
log.Printf("Copying to %s on container %s.", dst, c.ContainerId)
localCmd := exec.Command("docker", "cp", "-",
fmt.Sprintf("%s:%s", c.ContainerId, filepath.Dir(dst)))
stderrP, err := localCmd.StderrPipe()
if err != nil {
return fmt.Errorf("Failed to open pipe: %s", err)
}
stdin, err := localCmd.StdinPipe()
if err != nil {
return fmt.Errorf("Failed to open pipe: %s", err)
}
if err := localCmd.Start(); err != nil {
return err
}
archive := tar.NewWriter(stdin)
header, err := tar.FileInfoHeader(*fi, "")
if err != nil {
return err
}
header.Name = filepath.Base(dst)
archive.WriteHeader(header)
numBytes, err := io.Copy(archive, src)
if err != nil {
return fmt.Errorf("Failed to pipe upload: %s", err)
}
log.Printf("Copied %d bytes for %s", numBytes, dst)
if err := archive.Close(); err != nil {
return fmt.Errorf("Failed to close archive: %s", err)
}
if err := stdin.Close(); err != nil {
return fmt.Errorf("Failed to close stdin: %s", err)
}
stderrOut, err := ioutil.ReadAll(stderrP)
if err != nil {
return err
}
if fi != nil {
tempfile.Chmod((*fi).Mode())
}
tempfile.Close()
// Use docker cp to copy the file from the host to the container
// command format: docker cp /path/to/infile containerid:/path/to/outfile
cmd := exec.Command("docker", "cp", tempfile.Name(), fmt.Sprintf("%s:%s", c.ContainerId, dst))
log.Printf("Copying %s to %s on container %s.", tempfile.Name(), dst, c.ContainerId)
if err := cmd.Start(); err != nil {
return err
}
if err := cmd.Wait(); err != nil {
err = fmt.Errorf("Error uploading %s: %s",
tempfile.Name(),
err)
return err
if err := localCmd.Wait(); err != nil {
return fmt.Errorf("Failed to upload to '%s' in container: %s. %s.", dst, stderrOut, err)
}
return nil

View File

@ -2,9 +2,10 @@ package none
import (
"errors"
"github.com/hashicorp/packer/packer"
"io"
"os"
"github.com/hashicorp/packer/packer"
)
type comm struct {