Skip to content

Commit

Permalink
Implemented docker_* options
Browse files Browse the repository at this point in the history
  • Loading branch information
rickard-von-essen committed Nov 27, 2016
1 parent 4722c53 commit a26a7f2
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 39 deletions.
6 changes: 5 additions & 1 deletion builder/docker/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
var driver Driver

if os.Getenv("PACKER_DOCKER_API") != "" {
driver = DockerApiDriverInit(&b.config.ctx, ui)
var err error
driver, err = DockerApiDriverInit(&b.config.ctx, &b.config.DockerHostConfig, ui)
if err != nil {
return nil, err
}
} else {
driver = &DockerDriver{Ctx: &b.config.ctx, Ui: ui}
}
Expand Down
61 changes: 48 additions & 13 deletions builder/docker/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package docker

import (
"fmt"
"log"
"os"
"path/filepath"

"github.com/mitchellh/mapstructure"
"github.com/mitchellh/packer/common"
Expand All @@ -13,22 +15,24 @@ import (
)

var (
errArtifactNotUsed = fmt.Errorf("No instructions given for handling the artifact; expected commit, discard, or export_path")
errArtifactUseConflict = fmt.Errorf("Cannot specify more than one of commit, discard, and export_path")
errExportPathNotFile = fmt.Errorf("export_path must be a file, not a directory")
errImageNotSpecified = fmt.Errorf("Image must be specified")
errArtifactNotUsed = fmt.Errorf("No instructions given for handling the artifact; expected commit, discard, or export_path")
errArtifactUseConflict = fmt.Errorf("Cannot specify more than one of commit, discard, and export_path")
errExportPathNotFile = fmt.Errorf("export_path must be a file, not a directory")
errDockerCertPathNotFound = fmt.Errorf("docker_cert_path could not be found")
errImageNotSpecified = fmt.Errorf("Image must be specified")
)

type Config struct {
common.PackerConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
DockerHostConfig DockerHostConfig `mapstructure:",squash"`

Commit bool
Discard bool
ExportPath string `mapstructure:"export_path"`
Image string
Pty bool
Pull bool
Pull *bool
RunCommand []string `mapstructure:"run_command"`
Volumes map[string]string
Privileged bool `mapstructure:"privileged"`
Expand All @@ -49,6 +53,29 @@ type Config struct {
ctx interpolate.Context
}

type DockerHostConfig struct {
Host string `mapstructure:"docker_host"`
TlsVerify *bool `mapstructure:"docker_tls_verify"`
CertPath string `mapstructure:"docker_cert_path"`
}

func (c *DockerHostConfig) Prepare() []error {
var errs []error

if c.CertPath != "" {
if fi, err := os.Stat(c.CertPath); err != nil && !fi.IsDir() {
errs = append(errs, errDockerCertPathNotFound)
}
files := []string{"ca.pem", "cert.pem", "key.pem"}
for i := range files {
if fi, err := os.Stat(filepath.Join(c.CertPath, files[i])); err != nil && fi.IsDir() {
errs = append(errs, fmt.Errorf("Could not read file: %s", files[i]))
}
}
}
return errs
}

func NewConfig(raws ...interface{}) (*Config, []string, error) {
c := new(Config)

Expand All @@ -66,23 +93,23 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
if err != nil {
return nil, nil, err
}
log.Printf("DockerHostConfig: %v", c.DockerHostConfig)

// Defaults
if len(c.RunCommand) == 0 {
c.RunCommand = []string{"-d", "-i", "-t", "{{.Image}}", "/bin/bash"}
}

// Default Pull if it wasn't set
hasPull := false
for _, k := range md.Keys {
if k == "Pull" {
hasPull = true
break
}
if c.Pull == nil {
t := true
c.Pull = &t
}

if !hasPull {
c.Pull = true
// Default Docker TLS Verify if it wasn't set
if c.DockerHostConfig.TlsVerify == nil {
t := true
c.DockerHostConfig.TlsVerify = &t
}

// Default to the normal Docker type
Expand All @@ -91,6 +118,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
}

var errs *packer.MultiError

if es := c.DockerHostConfig.Prepare(); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}

if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
Expand Down Expand Up @@ -120,5 +152,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
return nil, nil, errs
}

log.Printf("Config: %v", c)
log.Printf("Comm: %v", &c.Comm)
log.Printf("DockerHostConfig: %v", &c.DockerHostConfig)
return c, nil, nil
}
32 changes: 30 additions & 2 deletions builder/docker/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,43 @@ func TestConfigPrepare_pull(t *testing.T) {
delete(raw, "pull")
c, warns, errs := NewConfig(raw)
testConfigOk(t, warns, errs)
if !c.Pull {
if !*c.Pull {
t.Fatal("should pull by default")
}

// Pull set
raw["pull"] = false
c, warns, errs = NewConfig(raw)
testConfigOk(t, warns, errs)
if c.Pull {
if *c.Pull {
t.Fatal("should not pull")
}
}

func TestConfigPrepare_dockerTlsVerify(t *testing.T) {
raw := testConfig()

// No pull set
delete(raw, "docker_tls_verify")
c, warns, errs := NewConfig(raw)
testConfigOk(t, warns, errs)
if !*c.DockerHostConfig.TlsVerify {
t.Fatal("should verify TLS by default")
}

// Pull set
raw["docker_tls_verify"] = false
c, warns, errs = NewConfig(raw)
testConfigOk(t, warns, errs)
if *c.DockerHostConfig.TlsVerify {
t.Fatal("should not verify TLS")
}
}

func TestConfigPrepare_dockerHost(t *testing.T) {
raw := testConfig()

raw["docker_host"] = "unix:///var/run/docker.sock"
_, warns, errs := NewConfig(raw)
testConfigOk(t, warns, errs)
}
29 changes: 22 additions & 7 deletions builder/docker/driver_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"io"
"log"
"os"
"path/filepath"
"strings"
"sync"

godocker "github.com/fsouza/go-dockerclient"
"github.com/hashicorp/go-version"
Expand All @@ -18,22 +18,37 @@ type DockerApiDriver struct {
Ui packer.Ui
Ctx *interpolate.Context

l sync.Mutex
client *godocker.Client
auth godocker.AuthConfiguration
identityToken string
}

func DockerApiDriverInit(ctx *interpolate.Context, ui packer.Ui) DockerApiDriver {

// TODO Allow specefying DOCKER_
client, _ := godocker.NewClientFromEnv()
func DockerApiDriverInit(ctx *interpolate.Context, config *DockerHostConfig, ui packer.Ui) (DockerApiDriver, error) {

var client *godocker.Client
var err error

if config.Host == "" {
log.Println("Using Docker Host settings from environment variables.")
client, err = godocker.NewClientFromEnv()
} else {
if *config.TlsVerify {
log.Printf("Using Docker Host: %s with verified TLS.", config.Host)
client, err = godocker.NewTLSClient(config.Host,
filepath.Join(config.CertPath, "cert.pem"),
filepath.Join(config.CertPath, "key.pem"),
filepath.Join(config.CertPath, "ca.pem"))
} else {
log.Printf("Using Docker Host: %s", config.Host)
client, err = godocker.NewClient(config.Host)
}
}

return DockerApiDriver{
Ui: ui,
Ctx: ctx,
client: client,
}
}, err
}

func (d DockerApiDriver) DeleteImage(id string) error {
Expand Down
2 changes: 1 addition & 1 deletion builder/docker/step_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (s *StepPull) Run(state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)

if !config.Pull {
if !*config.Pull {
log.Println("Pull disabled, won't docker pull")
return multistep.ActionContinue
}
Expand Down
3 changes: 2 additions & 1 deletion builder/docker/step_pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ func TestStepPull_noPull(t *testing.T) {
defer step.Cleanup(state)

config := state.Get("config").(*Config)
config.Pull = false
p := false
config.Pull = &p

driver := state.Get("driver").(*MockDriver)

Expand Down
41 changes: 27 additions & 14 deletions post-processor/docker-push/post-processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import (
type Config struct {
common.PackerConfig `mapstructure:",squash"`

Login bool
LoginEmail string `mapstructure:"login_email"`
LoginUsername string `mapstructure:"login_username"`
LoginPassword string `mapstructure:"login_password"`
LoginServer string `mapstructure:"login_server"`
EcrLogin bool `mapstructure:"ecr_login"`
docker.AwsAccessConfig `mapstructure:",squash"`
Login bool
LoginEmail string `mapstructure:"login_email"`
LoginUsername string `mapstructure:"login_username"`
LoginPassword string `mapstructure:"login_password"`
LoginServer string `mapstructure:"login_server"`
EcrLogin bool `mapstructure:"ecr_login"`
docker.DockerHostConfig `mapstructure:",squash"`
docker.AwsAccessConfig `mapstructure:",squash"`

ctx interpolate.Context
}
Expand All @@ -45,8 +46,17 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
return err
}

var errs *packer.MultiError

if es := p.config.DockerHostConfig.Prepare(); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}

if p.config.EcrLogin && p.config.LoginServer == "" {
return fmt.Errorf("ECR login requires login server to be provided.")
errs = packer.MultiErrorAppend(fmt.Errorf("ECR login requires login server to be provided."))
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
Expand All @@ -61,14 +71,17 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
}

driver := p.Driver
if os.Getenv("PACKER_DOCKER_API") != "" {
driver = docker.DockerApiDriverInit(&p.config.ctx, ui)
} else {
driver = &docker.DockerDriver{Ctx: &p.config.ctx, Ui: ui}
}

if driver == nil {
// If no driver is set, then we use the real driver
if os.Getenv("PACKER_DOCKER_API") != "" {
var err error
driver, err = docker.DockerApiDriverInit(&p.config.ctx, &p.config.DockerHostConfig, ui)
if err != nil {
return nil, false, err
}
} else {
driver = &docker.DockerDriver{Ctx: &p.config.ctx, Ui: ui}
}
}

if p.config.EcrLogin {
Expand Down
11 changes: 11 additions & 0 deletions website/source/docs/builders/docker.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ You must specify (only) one of `commit`, `discard`, or `export_path`.
Example of instructions are `CMD`, `ENTRYPOINT`, `ENV`, and `EXPOSE`. Example:
`[ "USER ubuntu", "WORKDIR /app", "EXPOSE 8080" ]`

- `docker_cert_path` (string) - Path a directory containing "ca.pem", "cert.pem",
and "key.pem" needed to connet the Docker host. Will be ignored if `docker_host`
is not specified. Will be read from environment variable `DOCKER_CERT_PATH`.

- `docker_host` (string) - The Docker host to connect to. If unset environment
variable `DOCKER_HOST` will be used. Defaults to local host.

- `docker_tls_verify` (boolean) - Define if TLS certificates should be verified.
Will be ignored if `docker_host` is not specified. Will be read from environment
variable `DOCKER_TLS_VERIFY`. Defaults to true.

- `ecr_login` (boolean) - Defaults to false. If true, the builder will login in
order to pull the image from
[Amazon EC2 Container Registry (ECR)](https://aws.amazon.com/ecr/).
Expand Down

0 comments on commit a26a7f2

Please sign in to comment.