builder/docker: customize run command [GH-648]

This commit is contained in:
Mitchell Hashimoto 2013-12-27 10:17:45 -07:00
parent be7861080b
commit 037a744be5
7 changed files with 49 additions and 11 deletions

View File

@ -45,6 +45,8 @@ IMPROVEMENTS:
"Packer Builder" so that they are easily recognizable. [GH-642] "Packer Builder" so that they are easily recognizable. [GH-642]
* builder/amazon/all: Copying AMIs to multiple regions now happens * builder/amazon/all: Copying AMIs to multiple regions now happens
in parallel. [GH-495] in parallel. [GH-495]
* builder/docker: A "run\_command" can be specified, configuring how
the container is started. [GH-648]
* builder/openstack: In debug mode, the generated SSH keypair is saved * builder/openstack: In debug mode, the generated SSH keypair is saved
so you can SSH into the machine. [GH-746] so you can SSH into the machine. [GH-746]
* builder/qemu: Floppy files are supported. [GH-686] * builder/qemu: Floppy files are supported. [GH-686]

View File

@ -25,7 +25,7 @@ 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) {
driver := &DockerDriver{Ui: ui} driver := &DockerDriver{Tpl: b.config.tpl, Ui: ui}
if err := driver.Verify(); err != nil { if err := driver.Verify(); err != nil {
return nil, err return nil, err
} }

View File

@ -12,6 +12,7 @@ type Config struct {
ExportPath string `mapstructure:"export_path"` ExportPath string `mapstructure:"export_path"`
Image string Image string
Pull bool Pull bool
RunCommand []string `mapstructure:"run_command"`
tpl *packer.ConfigTemplate tpl *packer.ConfigTemplate
} }
@ -28,6 +29,17 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
return nil, nil, err return nil, nil, err
} }
// Defaults
if len(c.RunCommand) == 0 {
c.RunCommand = []string{
"run",
"-d", "-i", "-t",
"-v", "{{.Volumes}}",
"{{.Image}}",
"/bin/bash",
}
}
// Default Pull if it wasn't set // Default Pull if it wasn't set
hasPull := false hasPull := false
for _, k := range md.Keys { for _, k := range md.Keys {

View File

@ -27,6 +27,13 @@ type Driver interface {
// ContainerConfig is the configuration used to start a container. // ContainerConfig is the configuration used to start a container.
type ContainerConfig struct { type ContainerConfig struct {
Image string Image string
Volumes map[string]string RunCommand []string
Volumes map[string]string
}
// This is the template that is used for the RunCommand in the ContainerConfig.
type startContainerTemplate struct {
Image string
Volumes string
} }

View File

@ -11,7 +11,8 @@ import (
) )
type DockerDriver struct { type DockerDriver struct {
Ui packer.Ui Ui packer.Ui
Tpl *packer.ConfigTemplate
} }
func (d *DockerDriver) Export(id string, dst io.Writer) error { func (d *DockerDriver) Export(id string, dst io.Writer) error {
@ -40,19 +41,29 @@ func (d *DockerDriver) Pull(image string) error {
} }
func (d *DockerDriver) StartContainer(config *ContainerConfig) (string, error) { func (d *DockerDriver) StartContainer(config *ContainerConfig) (string, error) {
// Args that we're going to pass to Docker // Build up the template data
args := []string{"run", "-d", "-i", "-t"} var tplData startContainerTemplate
tplData.Image = config.Image
if len(config.Volumes) > 0 { if len(config.Volumes) > 0 {
volumes := make([]string, 0, len(config.Volumes)) volumes := make([]string, 0, len(config.Volumes))
for host, guest := range config.Volumes { for host, guest := range config.Volumes {
volumes = append(volumes, fmt.Sprintf("%s:%s", host, guest)) volumes = append(volumes, fmt.Sprintf("%s:%s", host, guest))
} }
args = append(args, "-v", strings.Join(volumes, ",")) tplData.Volumes = strings.Join(volumes, ",")
} }
args = append(args, config.Image, "/bin/bash") // Args that we're going to pass to Docker
args := config.RunCommand
for i, v := range args {
var err error
args[i], err = d.Tpl.Process(v, &tplData)
if err != nil {
return "", err
}
}
d.Ui.Message(fmt.Sprintf(
"Run command: docker %s", strings.Join(args, " ")))
// Start the container // Start the container
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer

View File

@ -17,13 +17,14 @@ func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
runConfig := ContainerConfig{ runConfig := ContainerConfig{
Image: config.Image, Image: config.Image,
RunCommand: config.RunCommand,
Volumes: map[string]string{ Volumes: map[string]string{
tempDir: "/packer-files", tempDir: "/packer-files",
}, },
} }
ui.Say("Starting docker container with /bin/bash") ui.Say("Starting docker container...")
containerId, err := driver.StartContainer(&runConfig) containerId, err := driver.StartContainer(&runConfig)
if err != nil { if err != nil {
err := fmt.Errorf("Error running container: %s", err) err := fmt.Errorf("Error running container: %s", err)

View File

@ -59,6 +59,11 @@ Optional:
`docker pull` prior to use. Otherwise, it is assumed the image already `docker pull` prior to use. Otherwise, it is assumed the image already
exists and can be used. This defaults to true if not set. exists and can be used. This defaults to true if not set.
* `run_command` (array of strings) - An array of arguments to pass to
`docker` in order to run the container. By default this is set to
`["run", "-d", "-i", "-t", "-v", "{{.Volumes}}", "{{.Image}}", "/bin/bash"]`.
As you can see, you have a couple template variables to customize, as well.
## Using the generated artifact ## Using the generated artifact
Once the tar artifact has been generated, you will likely want to import, tag, Once the tar artifact has been generated, you will likely want to import, tag,