|
|
|
|
@ -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
|
|
|
|
|
}
|
|
|
|
|
|