builder/docker: StepExport tests
This commit is contained in:
parent
20f76c6ffc
commit
e88a0a57b9
|
@ -1,9 +1,16 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
// Driver is the interface that has to be implemented to communicate with
|
// Driver is the interface that has to be implemented to communicate with
|
||||||
// Docker. The Driver interface also allows the steps to be tested since
|
// Docker. The Driver interface also allows the steps to be tested since
|
||||||
// a mock driver can be shimmed in.
|
// a mock driver can be shimmed in.
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
|
// Export exports the container with the given ID to the given writer.
|
||||||
|
Export(id string, dst io.Writer) error
|
||||||
|
|
||||||
// Pull should pull down the given image.
|
// Pull should pull down the given image.
|
||||||
Pull(image string) error
|
Pull(image string) error
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -13,6 +14,26 @@ type DockerDriver struct {
|
||||||
Ui packer.Ui
|
Ui packer.Ui
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DockerDriver) Export(id string, dst io.Writer) error {
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd := exec.Command("docker", "export", id)
|
||||||
|
cmd.Stdout = dst
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
log.Printf("Exporting container: %s", id)
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
err = fmt.Errorf("Error exporting: %s\nStderr: %s",
|
||||||
|
err, stderr.String())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *DockerDriver) Pull(image string) error {
|
func (d *DockerDriver) Pull(image string) error {
|
||||||
cmd := exec.Command("docker", "pull", image)
|
cmd := exec.Command("docker", "pull", image)
|
||||||
return runAndStream(cmd, d.Ui)
|
return runAndStream(cmd, d.Ui)
|
||||||
|
|
|
@ -1,18 +1,40 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
// MockDriver is a driver implementation that can be used for tests.
|
// MockDriver is a driver implementation that can be used for tests.
|
||||||
type MockDriver struct {
|
type MockDriver struct {
|
||||||
PullError error
|
ExportReader io.Reader
|
||||||
StartID string
|
ExportError error
|
||||||
StartError error
|
PullError error
|
||||||
StopError error
|
StartID string
|
||||||
|
StartError error
|
||||||
|
StopError error
|
||||||
|
|
||||||
PullCalled bool
|
ExportCalled bool
|
||||||
PullImage string
|
ExportID string
|
||||||
StartCalled bool
|
PullCalled bool
|
||||||
StartConfig *ContainerConfig
|
PullImage string
|
||||||
StopCalled bool
|
StartCalled bool
|
||||||
StopID string
|
StartConfig *ContainerConfig
|
||||||
|
StopCalled bool
|
||||||
|
StopID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MockDriver) Export(id string, dst io.Writer) error {
|
||||||
|
d.ExportCalled = true
|
||||||
|
d.ExportID = id
|
||||||
|
|
||||||
|
if d.ExportReader != nil {
|
||||||
|
_, err := io.Copy(dst, d.ExportReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.ExportError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MockDriver) Pull(image string) error {
|
func (d *MockDriver) Pull(image string) error {
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// StepExport exports the container to a flat tar file.
|
// StepExport exports the container to a flat tar file.
|
||||||
|
@ -15,14 +12,10 @@ type StepExport struct{}
|
||||||
|
|
||||||
func (s *StepExport) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepExport) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
config := state.Get("config").(*Config)
|
||||||
|
driver := state.Get("driver").(Driver)
|
||||||
containerId := state.Get("container_id").(string)
|
containerId := state.Get("container_id").(string)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
ui.Say("Exporting the container")
|
|
||||||
|
|
||||||
// Args that we're going to pass to Docker
|
|
||||||
args := []string{"export", containerId}
|
|
||||||
|
|
||||||
// Open the file that we're going to write to
|
// Open the file that we're going to write to
|
||||||
f, err := os.Create(config.ExportPath)
|
f, err := os.Create(config.ExportPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -31,30 +24,18 @@ func (s *StepExport) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
// Export the thing, take stderr and point it to the file
|
ui.Say("Exporting the container")
|
||||||
var stderr bytes.Buffer
|
if err := driver.Export(containerId, f); err != nil {
|
||||||
cmd := exec.Command("docker", args...)
|
f.Close()
|
||||||
cmd.Stdout = f
|
os.Remove(f.Name())
|
||||||
cmd.Stderr = &stderr
|
|
||||||
|
|
||||||
log.Printf("Starting container with args: %v", args)
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
err := fmt.Errorf("Error exporting: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cmd.Wait(); err != nil {
|
|
||||||
err := fmt.Errorf("Error exporting: %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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Close()
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testStepExportState(t *testing.T) multistep.StateBag {
|
||||||
|
state := testState(t)
|
||||||
|
state.Put("container_id", "foo")
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepExport_impl(t *testing.T) {
|
||||||
|
var _ multistep.Step = new(StepExport)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepExport(t *testing.T) {
|
||||||
|
state := testStepExportState(t)
|
||||||
|
step := new(StepExport)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
// Create a tempfile for our output path
|
||||||
|
tf, err := ioutil.TempFile("", "packer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
tf.Close()
|
||||||
|
defer os.Remove(tf.Name())
|
||||||
|
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
config.ExportPath = tf.Name()
|
||||||
|
driver := state.Get("driver").(*MockDriver)
|
||||||
|
driver.ExportReader = bytes.NewReader([]byte("data!"))
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionContinue {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify we did the right thing
|
||||||
|
if !driver.ExportCalled {
|
||||||
|
t.Fatal("should've exported")
|
||||||
|
}
|
||||||
|
if driver.ExportID != "foo" {
|
||||||
|
t.Fatalf("bad: %#v", driver.ExportID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the data exported to the file
|
||||||
|
contents, err := ioutil.ReadFile(tf.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(contents) != "data!" {
|
||||||
|
t.Fatalf("bad: %#v", string(contents))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepExport_error(t *testing.T) {
|
||||||
|
state := testStepExportState(t)
|
||||||
|
step := new(StepExport)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
// Create a tempfile for our output path
|
||||||
|
tf, err := ioutil.TempFile("", "packer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
tf.Close()
|
||||||
|
|
||||||
|
if err := os.Remove(tf.Name()); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
config.ExportPath = tf.Name()
|
||||||
|
driver := state.Get("driver").(*MockDriver)
|
||||||
|
driver.ExportError = errors.New("foo")
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionHalt {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify we have an error
|
||||||
|
if _, ok := state.GetOk("error"); !ok {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify we didn't make that file
|
||||||
|
if _, err := os.Stat(tf.Name()); err == nil {
|
||||||
|
t.Fatal("export path shouldn't exist")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue