Commit 44a41451 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/docker: Communicator.Start doesn't block

parent d5ce8ddb
......@@ -11,6 +11,7 @@ import (
"os/exec"
"path/filepath"
"strconv"
"sync"
"syscall"
"time"
)
......@@ -19,6 +20,8 @@ type Communicator struct {
ContainerId string
HostDir string
ContainerDir string
lock sync.Mutex
}
func (c *Communicator) Start(remote *packer.RemoteCmd) error {
......@@ -38,6 +41,68 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
exitCodePath := outputFile.Name() + "-exit"
defer os.Remove(exitCodePath)
cmd := exec.Command("docker", "attach", c.ContainerId)
stdin_w, err := cmd.StdinPipe()
if err != nil {
return err
}
// Run the actual command in a goroutine so that Start doesn't block
go c.run(cmd, remote, stdin_w, outputFile, exitCodePath)
return nil
}
func (c *Communicator) Upload(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
}
defer os.Remove(tempfile.Name())
// Copy the contents to the temporary file
_, err = io.Copy(tempfile, src)
tempfile.Close()
if err != nil {
return err
}
// Copy the file into place by copying the temporary file we put
// into the shared folder into the proper location in the container
cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("cp %s/%s %s", c.ContainerDir,
filepath.Base(tempfile.Name()), dst),
}
if err := c.Start(cmd); err != nil {
return err
}
// Wait for the copy to complete
cmd.Wait()
if cmd.ExitStatus != 0 {
return fmt.Errorf("Upload failed with non-zero exit status: %d", cmd.ExitStatus)
}
return nil
}
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
return nil
}
func (c *Communicator) Download(src string, dst io.Writer) error {
return nil
}
// Runs the given command and blocks until completion
func (c *Communicator) run(cmd *exec.Cmd, remote *packer.RemoteCmd, stdin_w io.WriteCloser, outputFile *os.File, exitCodePath string) {
// For Docker, remote communication must be serialized since it
// only supports single execution.
c.lock.Lock()
defer c.lock.Unlock()
// Modify the remote command so that all the output of the commands
// go to a single file and so that the exit code is redirected to
// a single file. This lets us determine both when the command
......@@ -49,15 +114,12 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
filepath.Join(c.ContainerDir, filepath.Base(outputFile.Name())),
filepath.Join(c.ContainerDir, filepath.Base(exitCodePath)))
cmd := exec.Command("docker", "attach", c.ContainerId)
stdin_w, err := cmd.StdinPipe()
if err != nil {
return err
}
// Start the command
log.Printf("Executing in container %s: %#v", c.ContainerId, remoteCmd)
if err := cmd.Start(); err != nil {
return err
log.Printf("Error executing: %s", err)
remote.SetExited(254)
return
}
go func() {
......@@ -73,7 +135,7 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
stdin_w.Write([]byte(remoteCmd + "\n"))
}()
err = cmd.Wait()
err := cmd.Wait()
if exitErr, ok := err.(*exec.ExitError); ok {
exitStatus := 1
......@@ -86,7 +148,7 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
// Say that we ended, since if Docker itself failed, then
// the command must've not run, or so we assume
remote.SetExited(exitStatus)
return nil
return
}
// Wait for the exit code to appear in our file...
......@@ -103,19 +165,25 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
// Read the exit code
exitRaw, err := ioutil.ReadFile(exitCodePath)
if err != nil {
return err
log.Printf("Error executing: %s", err)
remote.SetExited(254)
return
}
exitStatus, err := strconv.ParseInt(string(bytes.TrimSpace(exitRaw)), 10, 0)
if err != nil {
return err
log.Printf("Error executing: %s", err)
remote.SetExited(254)
return
}
log.Printf("Executed command exit status: %d", exitStatus)
// Read the output
f, err := os.Open(outputFile.Name())
if err != nil {
return err
log.Printf("Error executing: %s", err)
remote.SetExited(254)
return
}
defer f.Close()
......@@ -124,7 +192,9 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
} else {
output, err := ioutil.ReadAll(f)
if err != nil {
return err
log.Printf("Error executing: %s", err)
remote.SetExited(254)
return
}
log.Printf("Command output: %s", string(output))
......@@ -132,48 +202,4 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
// Finally, we're done
remote.SetExited(int(exitStatus))
return nil
}
func (c *Communicator) Upload(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
}
defer os.Remove(tempfile.Name())
// Copy the contents to the temporary file
_, err = io.Copy(tempfile, src)
tempfile.Close()
if err != nil {
return err
}
// TODO(mitchellh): Copy the file into place
cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("cp %s/%s %s", c.ContainerDir,
filepath.Base(tempfile.Name()), dst),
}
if err := c.Start(cmd); err != nil {
return err
}
// Wait for the copy to complete
cmd.Wait()
if cmd.ExitStatus != 0 {
return fmt.Errorf("Upload failed with non-zero exit status: %d", cmd.ExitStatus)
}
return nil
}
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
return nil
}
func (c *Communicator) Download(src string, dst io.Writer) error {
return nil
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment