2019-03-27 15:09:08 -04:00
|
|
|
package docker
|
|
|
|
|
|
|
|
import (
|
2019-03-28 12:38:17 -04:00
|
|
|
"bytes"
|
2019-03-27 15:09:08 -04:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
)
|
|
|
|
|
|
|
|
type WindowsContainerCommunicator struct {
|
2019-03-28 12:38:17 -04:00
|
|
|
Communicator
|
2019-03-27 15:09:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *WindowsContainerCommunicator) Start(remote *packer.RemoteCmd) error {
|
|
|
|
dockerArgs := []string{
|
|
|
|
"exec",
|
|
|
|
"-i",
|
|
|
|
c.ContainerID,
|
|
|
|
"powershell",
|
|
|
|
fmt.Sprintf("(%s)", remote.Command),
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Config.Pty {
|
|
|
|
dockerArgs = append(dockerArgs[:2], append([]string{"-t"}, dockerArgs[2:]...)...)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Config.ExecUser != "" {
|
|
|
|
dockerArgs = append(dockerArgs[:2],
|
|
|
|
append([]string{"-u", c.Config.ExecUser}, dockerArgs[2:]...)...)
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := exec.Command("docker", dockerArgs...)
|
|
|
|
|
|
|
|
var (
|
|
|
|
stdin_w io.WriteCloser
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
stdin_w, err = cmd.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
stderr_r, err := cmd.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
stdout_r, err := cmd.StdoutPipe()
|
|
|
|
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, stdout_r, stderr_r)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Upload uses docker exec to copy the file from the host to the container
|
|
|
|
func (c *WindowsContainerCommunicator) 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 {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer os.Remove(tempfile.Name())
|
|
|
|
|
|
|
|
// Copy the contents to the temporary file
|
|
|
|
_, err = io.Copy(tempfile, src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if fi != nil {
|
|
|
|
tempfile.Chmod((*fi).Mode())
|
|
|
|
}
|
|
|
|
tempfile.Close()
|
|
|
|
|
|
|
|
// 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("Copy-Item -Path %s/%s -Destination %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 *WindowsContainerCommunicator) UploadDir(dst string, src string, exclude []string) error {
|
|
|
|
// Create the temporary directory that will store the contents of "src"
|
|
|
|
// for copying into the container.
|
|
|
|
td, err := ioutil.TempDir(c.HostDir, "dirupload")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
|
|
|
|
walkFn := func(path string, info os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
relpath, err := filepath.Rel(src, path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
hostpath := filepath.Join(td, relpath)
|
|
|
|
|
|
|
|
// If it is a directory, just create it
|
|
|
|
if info.IsDir() {
|
|
|
|
return os.MkdirAll(hostpath, info.Mode())
|
|
|
|
}
|
|
|
|
|
|
|
|
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
|
|
|
dest, err := os.Readlink(path)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.Symlink(dest, hostpath)
|
|
|
|
}
|
|
|
|
|
|
|
|
// It is a file, copy it over, including mode.
|
|
|
|
src, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer src.Close()
|
|
|
|
|
|
|
|
dst, err := os.Create(hostpath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer dst.Close()
|
|
|
|
|
|
|
|
if _, err := io.Copy(dst, src); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-28 12:38:17 -04:00
|
|
|
return nil
|
2019-03-27 15:09:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the entire directory tree to the temporary directory
|
|
|
|
if err := filepath.Walk(src, walkFn); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the destination directory
|
|
|
|
containerSrc := filepath.Join(c.ContainerDir, filepath.Base(td))
|
|
|
|
containerDst := dst
|
|
|
|
if src[len(src)-1] != '/' {
|
|
|
|
containerDst = filepath.Join(dst, filepath.Base(src))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the directory, then copy into it
|
|
|
|
cmd := &packer.RemoteCmd{
|
2019-03-28 12:38:17 -04:00
|
|
|
Command: fmt.Sprintf("Copy-Item %s -Destination %s -Recurse",
|
|
|
|
containerSrc, containerDst),
|
2019-03-27 15:09:08 -04:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-03-28 12:38:17 -04:00
|
|
|
// Download pulls a file out of a container using `docker cp`. We have a source
|
|
|
|
// path and want to write to an io.Writer
|
|
|
|
func (c *WindowsContainerCommunicator) Download(src string, dst io.Writer) error {
|
|
|
|
log.Printf("Downloading file from container: %s:%s", c.ContainerID, src)
|
|
|
|
// Copy file onto temp file on mounted volume inside container
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
cmd := &packer.RemoteCmd{
|
|
|
|
Command: fmt.Sprintf("Copy-Item -Path %s -Destination %s/%s", src, c.ContainerDir,
|
|
|
|
filepath.Base(src)),
|
|
|
|
Stdout: &stdout,
|
|
|
|
Stderr: &stderr,
|
2019-03-27 15:09:08 -04:00
|
|
|
}
|
2019-03-28 12:38:17 -04:00
|
|
|
if err := c.Start(cmd); err != nil {
|
2019-03-27 15:09:08 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-28 12:38:17 -04:00
|
|
|
// Wait for the copy to complete
|
|
|
|
cmd.Wait()
|
2019-03-27 15:09:08 -04:00
|
|
|
|
2019-03-28 12:38:17 -04:00
|
|
|
if cmd.ExitStatus != 0 {
|
|
|
|
return fmt.Errorf("Failed to copy file to shared drive: %s, %s, %d", stderr.String(), stdout.String(), cmd.ExitStatus)
|
2019-03-27 15:09:08 -04:00
|
|
|
}
|
|
|
|
|
2019-03-28 12:38:17 -04:00
|
|
|
// Read that copied file into a new file opened on host machine
|
|
|
|
fsrc, err := os.Open(filepath.Join(c.HostDir, filepath.Base(src)))
|
2019-03-27 15:09:08 -04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-28 12:38:17 -04:00
|
|
|
defer fsrc.Close()
|
|
|
|
defer os.Remove(fsrc.Name())
|
2019-03-27 15:09:08 -04:00
|
|
|
|
2019-03-28 12:38:17 -04:00
|
|
|
_, err = io.Copy(dst, fsrc)
|
2019-03-27 15:09:08 -04:00
|
|
|
if err != nil {
|
2019-03-28 12:38:17 -04:00
|
|
|
return err
|
2019-03-27 15:09:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *WindowsContainerCommunicator) DownloadDir(src string, dst string, exclude []string) error {
|
|
|
|
return fmt.Errorf("DownloadDir is not implemented for docker")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Runs the given command and blocks until completion
|
|
|
|
func (c *WindowsContainerCommunicator) run(cmd *exec.Cmd, remote *packer.RemoteCmd, stdin io.WriteCloser, stdout, stderr io.ReadCloser) {
|
|
|
|
// For Docker, remote communication must be serialized since it
|
|
|
|
// only supports single execution.
|
2019-03-28 12:38:17 -04:00
|
|
|
c.Communicator.run(cmd, remote, stdin, stdout, stderr)
|
2019-03-27 15:09:08 -04:00
|
|
|
}
|