builder/docker: use exec for v1.4+

This commit is contained in:
Mitchell Hashimoto 2015-05-29 09:29:59 -07:00
parent 966d70148e
commit 6570b53c4a
6 changed files with 59 additions and 29 deletions

View File

@ -1,10 +1,11 @@
package docker package docker
import ( import (
"log"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common" "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log"
) )
const BuilderId = "packer.docker" const BuilderId = "packer.docker"
@ -31,6 +32,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, err return nil, err
} }
version, err := driver.Version()
if err != nil {
return nil, err
}
log.Printf("[DEBUG] Docker version: %s", version.String())
steps := []multistep.Step{ steps := []multistep.Step{
&StepTempDir{}, &StepTempDir{},
&StepPull{}, &StepPull{},

View File

@ -9,7 +9,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"strconv" "strconv"
"sync" "sync"
"syscall" "syscall"
@ -24,36 +23,20 @@ type Communicator struct {
ContainerId string ContainerId string
HostDir string HostDir string
ContainerDir string ContainerDir string
Version *version.Version
lock sync.Mutex lock sync.Mutex
} }
var dockerVersion *version.Version
var useDockerExec bool
func init() {
execConstraint, _ := version.NewConstraint(">= 1.4.0")
versionExtractor := regexp.MustCompile(version.VersionRegexpRaw)
dockerVersionOutput, err := exec.Command("docker", "-v").Output()
extractedVersion := versionExtractor.FindSubmatch(dockerVersionOutput)
if extractedVersion != nil {
dockerVersionString := string(extractedVersion[0])
dockerVersion, err = version.NewVersion(dockerVersionString)
}
if dockerVersion == nil {
log.Printf("Could not determine docker version: %v", err)
log.Printf("Assuming no `exec` capability, using `attatch`")
useDockerExec = false
} else {
log.Printf("Docker version detected as %s", dockerVersion)
useDockerExec = execConstraint.Check(dockerVersion)
}
}
func (c *Communicator) Start(remote *packer.RemoteCmd) error { func (c *Communicator) Start(remote *packer.RemoteCmd) error {
// Determine if we're using docker exec or not
useExec := false
execConstraint, err := version.NewConstraint(">= 1.4.0")
if err != nil {
return err
}
useExec = execConstraint.Check(c.Version)
// Create a temporary file to store the output. Because of a bug in // Create a temporary file to store the output. Because of a bug in
// Docker, sometimes all the output doesn't properly show up. This // Docker, sometimes all the output doesn't properly show up. This
// file will capture ALL of the output, and we'll read that. // file will capture ALL of the output, and we'll read that.
@ -69,7 +52,7 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
exitCodePath := outputFile.Name() + "-exit" exitCodePath := outputFile.Name() + "-exit"
var cmd *exec.Cmd var cmd *exec.Cmd
if useDockerExec { if useExec {
cmd = exec.Command("docker", "exec", "-i", c.ContainerId, "/bin/sh") cmd = exec.Command("docker", "exec", "-i", c.ContainerId, "/bin/sh")
} else { } else {
cmd = exec.Command("docker", "attach", c.ContainerId) cmd = exec.Command("docker", "attach", c.ContainerId)
@ -150,7 +133,7 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error
return os.MkdirAll(hostpath, info.Mode()) return os.MkdirAll(hostpath, info.Mode())
} }
if info.Mode() & os.ModeSymlink == os.ModeSymlink { if info.Mode()&os.ModeSymlink == os.ModeSymlink {
dest, err := os.Readlink(path) dest, err := os.Readlink(path)
if err != nil { if err != nil {

View File

@ -2,6 +2,8 @@ package docker
import ( import (
"io" "io"
"github.com/hashicorp/go-version"
) )
// Driver is the interface that has to be implemented to communicate with // Driver is the interface that has to be implemented to communicate with
@ -48,6 +50,9 @@ type Driver interface {
// Verify verifies that the driver can run // Verify verifies that the driver can run
Verify() error Verify() error
// Version reads the Docker version
Version() (*version.Version, error)
} }
// ContainerConfig is the configuration used to start a container. // ContainerConfig is the configuration used to start a container.

View File

@ -7,9 +7,11 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"regexp"
"strings" "strings"
"sync" "sync"
"github.com/hashicorp/go-version"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate" "github.com/mitchellh/packer/template/interpolate"
) )
@ -263,3 +265,17 @@ func (d *DockerDriver) Verify() error {
return nil return nil
} }
func (d *DockerDriver) Version() (*version.Version, error) {
output, err := exec.Command("docker", "-v").Output()
if err != nil {
return nil, err
}
match := regexp.MustCompile(version.VersionRegexpRaw).FindSubmatch(output)
if match == nil {
return nil, fmt.Errorf("unknown version: %s", output)
}
return version.NewVersion(string(match[0]))
}

View File

@ -2,6 +2,8 @@ package docker
import ( import (
"io" "io"
"github.com/hashicorp/go-version"
) )
// MockDriver is a driver implementation that can be used for tests. // MockDriver is a driver implementation that can be used for tests.
@ -63,6 +65,9 @@ type MockDriver struct {
StopCalled bool StopCalled bool
StopID string StopID string
VerifyCalled bool VerifyCalled bool
VersionCalled bool
VersionVersion string
} }
func (d *MockDriver) Commit(id string) (string, error) { func (d *MockDriver) Commit(id string) (string, error) {
@ -162,3 +167,8 @@ func (d *MockDriver) Verify() error {
d.VerifyCalled = true d.VerifyCalled = true
return d.VerifyError return d.VerifyError
} }
func (d *MockDriver) Version() (*version.Version, error) {
d.VersionCalled = true
return version.NewVersion(d.VersionVersion)
}

View File

@ -9,14 +9,23 @@ type StepProvision struct{}
func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction { func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction {
containerId := state.Get("container_id").(string) containerId := state.Get("container_id").(string)
driver := state.Get("driver").(Driver)
tempDir := state.Get("temp_dir").(string) tempDir := state.Get("temp_dir").(string)
// Get the version so we can pass it to the communicator
version, err := driver.Version()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Create the communicator that talks to Docker via various // Create the communicator that talks to Docker via various
// os/exec tricks. // os/exec tricks.
comm := &Communicator{ comm := &Communicator{
ContainerId: containerId, ContainerId: containerId,
HostDir: tempDir, HostDir: tempDir,
ContainerDir: "/packer-files", ContainerDir: "/packer-files",
Version: version,
} }
prov := common.StepProvision{Comm: comm} prov := common.StepProvision{Comm: comm}