packer-cn/builder/docker/communicator.go

114 lines
2.5 KiB
Go

package docker
import (
"fmt"
"github.com/mitchellh/packer/packer"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"syscall"
"time"
)
type Communicator struct {
ContainerId string
HostDir string
ContainerDir string
}
func (c *Communicator) Start(remote *packer.RemoteCmd) error {
cmd := exec.Command("docker", "attach", c.ContainerId)
stdin_w, err := cmd.StdinPipe()
if err != nil {
return err
}
// TODO(mitchellh): We need to hijack the command to write the exit
// code to a temporary file in the shared folder so that we can read it
// out. Since we're going over a pty, we can't get the exit code another
// way.
cmd.Stdout = remote.Stdout
cmd.Stderr = remote.Stderr
log.Printf("Executing in container %s: %#v", c.ContainerId, remote.Command)
if err := cmd.Start(); err != nil {
return err
}
go func() {
defer stdin_w.Close()
// This sleep needs to be here because of the issue linked to below.
// Basically, without it, Docker will hang on reading stdin forever,
// and won't see what we write, for some reason.
//
// https://github.com/dotcloud/docker/issues/2628
time.Sleep(2 * time.Second)
stdin_w.Write([]byte(remote.Command + "\n"))
}()
var exitStatus int = 0
err = cmd.Wait()
if exitErr, ok := err.(*exec.ExitError); ok {
exitStatus = 1
// There is no process-independent way to get the REAL
// exit status so we just try to go deeper.
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
exitStatus = status.ExitStatus()
}
}
// Say that we ended!
remote.SetExited(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
}