builder/docker: a non-working communicator
This commit is contained in:
parent
4e6d46bbd0
commit
4cdd532a93
|
@ -37,8 +37,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
|
|
||||||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
|
&StepTempDir{},
|
||||||
&StepPull{},
|
&StepPull{},
|
||||||
&StepRun{},
|
&StepRun{},
|
||||||
|
&StepProvision{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the state bag and initial state for the steps
|
// Setup the state bag and initial state for the steps
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if exitStatus != 0 {
|
||||||
|
return fmt.Errorf("Exit status: %d", 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", 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
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommunicator_impl(t *testing.T) {
|
||||||
|
var _ packer.Communicator = new(Communicator)
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepProvision struct{}
|
||||||
|
|
||||||
|
func (s *StepProvision) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
containerId := state.Get("container_id").(string)
|
||||||
|
hook := state.Get("hook").(packer.Hook)
|
||||||
|
tempDir := state.Get("temp_dir").(string)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
// Create the communicator that talks to Docker via various
|
||||||
|
// os/exec tricks.
|
||||||
|
comm := &Communicator{
|
||||||
|
ContainerId: containerId,
|
||||||
|
HostDir: tempDir,
|
||||||
|
ContainerDir: "/packer-files",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the provisioning hook
|
||||||
|
if err := hook.Run(packer.HookProvision, ui, comm, nil); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepProvision) Cleanup(state multistep.StateBag) {}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -15,14 +16,27 @@ type StepRun struct {
|
||||||
|
|
||||||
func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
config := state.Get("config").(*Config)
|
||||||
|
tempDir := state.Get("temp_dir").(string)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
ui.Say("Starting docker container with /bin/bash")
|
ui.Say("Starting docker container with /bin/bash")
|
||||||
|
|
||||||
|
// Args that we're going to pass to Docker
|
||||||
|
args := []string{
|
||||||
|
"run",
|
||||||
|
"-d", "-i", "-t",
|
||||||
|
"-v", fmt.Sprintf("%s:/packer-files", tempDir),
|
||||||
|
config.Image,
|
||||||
|
"/bin/bash",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the container
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
cmd := exec.Command("docker", "run", "-d", "-i", "-t", config.Image, "/bin/bash")
|
cmd := exec.Command("docker", args...)
|
||||||
cmd.Stdout = &stdout
|
cmd.Stdout = &stdout
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
log.Printf("Starting container with args: %v", args)
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
err := fmt.Errorf("Error running container: %s", err)
|
err := fmt.Errorf("Error running container: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
|
@ -31,15 +45,18 @@ func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
if err := cmd.Wait(); err != nil {
|
||||||
err := fmt.Errorf("Error running container: %s", err)
|
err := fmt.Errorf("Error running container: %s\nStderr: %s",
|
||||||
|
err, stderr.String())
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture the container ID, which is alone on stdout
|
||||||
s.containerId = strings.TrimSpace(stdout.String())
|
s.containerId = strings.TrimSpace(stdout.String())
|
||||||
ui.Message(fmt.Sprintf("Container ID: %s", s.containerId))
|
ui.Message(fmt.Sprintf("Container ID: %s", s.containerId))
|
||||||
|
|
||||||
|
state.Put("container_id", s.containerId)
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +65,8 @@ func (s *StepRun) Cleanup(state multistep.StateBag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(mitchellh): handle errors
|
// Kill the container. We don't handle errors because errors usually
|
||||||
|
// just mean that the container doesn't exist anymore, which isn't a
|
||||||
|
// big deal.
|
||||||
exec.Command("docker", "kill", s.containerId).Run()
|
exec.Command("docker", "kill", s.containerId).Run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StepTempDir creates a temporary directory that we use in order to
|
||||||
|
// share data with the docker container over the communicator.
|
||||||
|
type StepTempDir struct {
|
||||||
|
tempDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepTempDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
ui.Say("Creating a temporary directory for sharing data...")
|
||||||
|
td, err := ioutil.TempDir("", "packer-docker")
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error making temp dir: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.tempDir = td
|
||||||
|
state.Put("temp_dir", s.tempDir)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepTempDir) Cleanup(state multistep.StateBag) {
|
||||||
|
if s.tempDir != "" {
|
||||||
|
os.RemoveAll(s.tempDir)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue