builder/docker: a non-working communicator
This commit is contained in:
parent
2e080ece6d
commit
797c44bfc1
|
@ -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) {
|
||||
steps := []multistep.Step{
|
||||
&StepTempDir{},
|
||||
&StepPull{},
|
||||
&StepRun{},
|
||||
&StepProvision{},
|
||||
}
|
||||
|
||||
// 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"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
@ -15,14 +16,27 @@ type StepRun struct {
|
|||
|
||||
func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
tempDir := state.Get("temp_dir").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
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
|
||||
cmd := exec.Command("docker", "run", "-d", "-i", "-t", config.Image, "/bin/bash")
|
||||
cmd := exec.Command("docker", args...)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
log.Printf("Starting container with args: %v", args)
|
||||
if err := cmd.Start(); err != nil {
|
||||
err := fmt.Errorf("Error running container: %s", err)
|
||||
state.Put("error", err)
|
||||
|
@ -31,15 +45,18 @@ func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
|
|||
}
|
||||
|
||||
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)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Capture the container ID, which is alone on stdout
|
||||
s.containerId = strings.TrimSpace(stdout.String())
|
||||
ui.Message(fmt.Sprintf("Container ID: %s", s.containerId))
|
||||
|
||||
state.Put("container_id", s.containerId)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
@ -48,6 +65,8 @@ func (s *StepRun) Cleanup(state multistep.StateBag) {
|
|||
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()
|
||||
}
|
||||
|
|
|
@ -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