post-processor/docker-import: have an Artifact
/cc @mmckeen
This commit is contained in:
parent
430963f400
commit
34dbf72142
33
builder/docker/artifact_import.go
Normal file
33
builder/docker/artifact_import.go
Normal file
@ -0,0 +1,33 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ImportArtifact is an Artifact implementation for when a container is
|
||||
// exported from docker into a single flat file.
|
||||
type ImportArtifact struct {
|
||||
BuilderIdValue string
|
||||
Driver Driver
|
||||
IdValue string
|
||||
}
|
||||
|
||||
func (a *ImportArtifact) BuilderId() string {
|
||||
return a.BuilderIdValue
|
||||
}
|
||||
|
||||
func (*ImportArtifact) Files() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ImportArtifact) Id() string {
|
||||
return a.IdValue
|
||||
}
|
||||
|
||||
func (a *ImportArtifact) String() string {
|
||||
return fmt.Sprintf("Imported Docker image: %s", a.Id())
|
||||
}
|
||||
|
||||
func (a *ImportArtifact) Destroy() error {
|
||||
return a.Driver.DeleteImage(a.Id())
|
||||
}
|
57
builder/docker/artifact_import_test.go
Normal file
57
builder/docker/artifact_import_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImportArtifact_impl(t *testing.T) {
|
||||
var _ packer.Artifact = new(ImportArtifact)
|
||||
}
|
||||
|
||||
func TestImportArtifactBuilderId(t *testing.T) {
|
||||
a := &ImportArtifact{BuilderIdValue: "foo"}
|
||||
if a.BuilderId() != "foo" {
|
||||
t.Fatalf("bad: %#v", a.BuilderId())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactFiles(t *testing.T) {
|
||||
a := &ImportArtifact{}
|
||||
if a.Files() != nil {
|
||||
t.Fatalf("bad: %#v", a.Files())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactId(t *testing.T) {
|
||||
a := &ImportArtifact{IdValue: "foo"}
|
||||
if a.Id() != "foo" {
|
||||
t.Fatalf("bad: %#v", a.Id())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactDestroy(t *testing.T) {
|
||||
d := new(MockDriver)
|
||||
a := &ImportArtifact{
|
||||
Driver: d,
|
||||
IdValue: "foo",
|
||||
}
|
||||
|
||||
// No error
|
||||
if err := a.Destroy(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if !d.DeleteImageCalled {
|
||||
t.Fatal("delete image should be called")
|
||||
}
|
||||
if d.DeleteImageId != "foo" {
|
||||
t.Fatalf("bad: %#v", d.DeleteImageId)
|
||||
}
|
||||
|
||||
// With an error
|
||||
d.DeleteImageErr = errors.New("foo")
|
||||
if err := a.Destroy(); err != d.DeleteImageErr {
|
||||
t.Fatalf("err: %#v", err)
|
||||
}
|
||||
}
|
@ -8,6 +8,9 @@ import (
|
||||
// Docker. The Driver interface also allows the steps to be tested since
|
||||
// a mock driver can be shimmed in.
|
||||
type Driver interface {
|
||||
// Delete an image that is imported into Docker
|
||||
DeleteImage(id string) error
|
||||
|
||||
// Export exports the container with the given ID to the given writer.
|
||||
Export(id string, dst io.Writer) error
|
||||
|
||||
|
@ -15,6 +15,25 @@ type DockerDriver struct {
|
||||
Tpl *packer.ConfigTemplate
|
||||
}
|
||||
|
||||
func (d *DockerDriver) DeleteImage(id string) error {
|
||||
var stderr bytes.Buffer
|
||||
cmd := exec.Command("docker", "rmi", id)
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
log.Printf("Deleting image: %s", id)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
err = fmt.Errorf("Error deleting image: %s\nStderr: %s",
|
||||
err, stderr.String())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DockerDriver) Export(id string, dst io.Writer) error {
|
||||
var stderr bytes.Buffer
|
||||
cmd := exec.Command("docker", "export", id)
|
||||
|
@ -6,6 +6,10 @@ import (
|
||||
|
||||
// MockDriver is a driver implementation that can be used for tests.
|
||||
type MockDriver struct {
|
||||
DeleteImageCalled bool
|
||||
DeleteImageId string
|
||||
DeleteImageErr error
|
||||
|
||||
ExportReader io.Reader
|
||||
ExportError error
|
||||
PullError error
|
||||
@ -25,6 +29,12 @@ type MockDriver struct {
|
||||
VerifyCalled bool
|
||||
}
|
||||
|
||||
func (d *MockDriver) DeleteImage(id string) error {
|
||||
d.DeleteImageCalled = true
|
||||
d.DeleteImageId = id
|
||||
return d.DeleteImageErr
|
||||
}
|
||||
|
||||
func (d *MockDriver) Export(id string, dst io.Writer) error {
|
||||
d.ExportCalled = true
|
||||
d.ExportID = id
|
||||
|
@ -1,18 +1,22 @@
|
||||
package dockerimport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/builder/docker"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const BuilderId = "packer.post-processor.docker-import"
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
Dockerfile string `mapstructure:"dockerfile"`
|
||||
Repository string `mapstructure:"repository"`
|
||||
Tag string `mapstructure:"tag"`
|
||||
|
||||
@ -39,7 +43,6 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
errs := new(packer.MultiError)
|
||||
|
||||
templates := map[string]*string{
|
||||
"dockerfile": &p.config.Dockerfile,
|
||||
"repository": &p.config.Repository,
|
||||
"tag": &p.config.Tag,
|
||||
}
|
||||
@ -71,7 +74,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
|
||||
importRepo += ":" + p.config.Tag
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
cmd := exec.Command("docker", "import", "-", importRepo)
|
||||
cmd.Stdout = &stdout
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
@ -96,38 +101,21 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
|
||||
io.Copy(stdin, file)
|
||||
}()
|
||||
|
||||
cmd.Wait()
|
||||
|
||||
// Process Dockerfile if provided
|
||||
if p.config.Dockerfile != "" {
|
||||
cmd := exec.Command("docker", "build", "-t="+importRepo, "-")
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// open Dockerfile
|
||||
file, err := os.Open(p.config.Dockerfile)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Couldn't open Dockerfile: %s", err)
|
||||
return nil, false, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
ui.Message("Running docker build with Dockerfile: " + p.config.Dockerfile)
|
||||
if err := cmd.Start(); err != nil {
|
||||
err = fmt.Errorf("Failed to start docker build: %s", err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer stdin.Close()
|
||||
io.Copy(stdin, file)
|
||||
}()
|
||||
|
||||
cmd.Wait()
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
err = fmt.Errorf("Error importing container: %s", err)
|
||||
return nil, false, err
|
||||
}
|
||||
return nil, false, nil
|
||||
|
||||
id := strings.TrimSpace(stdout.String())
|
||||
ui.Message("Imported ID: " + id)
|
||||
|
||||
// Build the artifact
|
||||
driver := &docker.DockerDriver{Tpl: p.config.tpl, Ui: ui}
|
||||
artifact = &docker.ImportArtifact{
|
||||
BuilderIdValue: BuilderId,
|
||||
Driver: driver,
|
||||
IdValue: id,
|
||||
}
|
||||
|
||||
return artifact, false, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user