Merge pull request #2150 from mitchellh/f-docker

Docker bug fixes
This commit is contained in:
Mitchell Hashimoto 2015-05-29 11:13:45 -07:00
commit 5dc32df1fb
7 changed files with 76 additions and 6 deletions

View File

@ -1,10 +1,11 @@
package docker
import (
"log"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"log"
)
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
}
version, err := driver.Version()
if err != nil {
return nil, err
}
log.Printf("[DEBUG] Docker version: %s", version.String())
steps := []multistep.Step{
&StepTempDir{},
&StepPull{},
@ -70,8 +77,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, rawErr.(error)
}
var artifact packer.Artifact
// If it was cancelled, then just return
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return nil, nil
}
// No errors, must've worked
var artifact packer.Artifact
if b.config.Commit {
artifact = &ImportArtifact{
IdValue: state.Get("image_id").(string),
@ -81,6 +93,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
} else {
artifact = &ExportArtifact{path: b.config.ExportPath}
}
return artifact, nil
}

View File

@ -15,6 +15,7 @@ import (
"time"
"github.com/ActiveState/tail"
"github.com/hashicorp/go-version"
"github.com/mitchellh/packer/packer"
)
@ -22,6 +23,7 @@ type Communicator struct {
ContainerId string
HostDir string
ContainerDir string
Version *version.Version
lock sync.Mutex
}
@ -41,7 +43,13 @@ func (c *Communicator) Start(remote *packer.RemoteCmd) error {
// This file will store the exit code of the command once it is complete.
exitCodePath := outputFile.Name() + "-exit"
cmd := exec.Command("docker", "attach", c.ContainerId)
var cmd *exec.Cmd
if c.canExec() {
cmd = exec.Command("docker", "exec", "-i", c.ContainerId, "/bin/sh")
} else {
cmd = exec.Command("docker", "attach", c.ContainerId)
}
stdin_w, err := cmd.StdinPipe()
if err != nil {
// We have to do some cleanup since run was never called
@ -117,7 +125,7 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error
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)
if err != nil {
@ -186,6 +194,15 @@ func (c *Communicator) Download(src string, dst io.Writer) error {
panic("not implemented")
}
// canExec tells us whether `docker exec` is supported
func (c *Communicator) canExec() bool {
execConstraint, err := version.NewConstraint(">= 1.4.0")
if err != nil {
panic(err)
}
return execConstraint.Check(c.Version)
}
// 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

View File

@ -30,7 +30,7 @@ type Config struct {
}
func NewConfig(raws ...interface{}) (*Config, []string, error) {
c := new(Config)
var c Config
var md mapstructure.Metadata
err := config.Decode(&c, &config.DecodeOpts{
@ -83,5 +83,5 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
return nil, nil, errs
}
return c, nil, nil
return &c, nil, nil
}

View File

@ -2,6 +2,8 @@ package docker
import (
"io"
"github.com/hashicorp/go-version"
)
// 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() error
// Version reads the Docker version
Version() (*version.Version, error)
}
// ContainerConfig is the configuration used to start a container.

View File

@ -7,9 +7,11 @@ import (
"log"
"os"
"os/exec"
"regexp"
"strings"
"sync"
"github.com/hashicorp/go-version"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
@ -263,3 +265,17 @@ func (d *DockerDriver) Verify() error {
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 (
"io"
"github.com/hashicorp/go-version"
)
// MockDriver is a driver implementation that can be used for tests.
@ -63,6 +65,9 @@ type MockDriver struct {
StopCalled bool
StopID string
VerifyCalled bool
VersionCalled bool
VersionVersion string
}
func (d *MockDriver) Commit(id string) (string, error) {
@ -162,3 +167,8 @@ func (d *MockDriver) Verify() error {
d.VerifyCalled = true
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 {
containerId := state.Get("container_id").(string)
driver := state.Get("driver").(Driver)
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
// os/exec tricks.
comm := &Communicator{
ContainerId: containerId,
HostDir: tempDir,
ContainerDir: "/packer-files",
Version: version,
}
prov := common.StepProvision{Comm: comm}