commit
5dc32df1fb
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in New Issue