replace command config with wrapper command.

This commit is contained in:
Matthew Hooker 2013-09-27 10:54:53 +00:00
parent cebbcc51a6
commit ac496a63dc
8 changed files with 74 additions and 72 deletions

View File

@ -29,12 +29,9 @@ type Config struct {
ChrootMounts [][]string `mapstructure:"chroot_mounts"` ChrootMounts [][]string `mapstructure:"chroot_mounts"`
CopyFiles []string `mapstructure:"copy_files"` CopyFiles []string `mapstructure:"copy_files"`
DevicePath string `mapstructure:"device_path"` DevicePath string `mapstructure:"device_path"`
MountCommand string `mapstructure:"mount_command"` CommandWrapper string `mapstructure:"command_wrapper"`
ChrootCommand string `mapstructure:"chroot_command"`
CopyCommand string `mapstructure:"copy_command"`
MountPath string `mapstructure:"mount_path"` MountPath string `mapstructure:"mount_path"`
SourceAmi string `mapstructure:"source_ami"` SourceAmi string `mapstructure:"source_ami"`
UnmountCommand string `mapstructure:"unmount_command"`
tpl *packer.ConfigTemplate tpl *packer.ConfigTemplate
} }
@ -80,26 +77,14 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.CopyFiles = []string{"/etc/resolv.conf"} b.config.CopyFiles = []string{"/etc/resolv.conf"}
} }
if b.config.MountCommand == "" { if b.config.CommandWrapper == "" {
b.config.MountCommand = "mount" b.config.CommandWrapper = "{{.Command}}"
}
if b.config.ChrootCommand == "" {
b.config.ChrootCommand = "chroot"
}
if b.config.CopyCommand == "" {
b.config.CopyCommand = "cp"
} }
if b.config.MountPath == "" { if b.config.MountPath == "" {
b.config.MountPath = "packer-amazon-chroot-volumes/{{.Device}}" b.config.MountPath = "packer-amazon-chroot-volumes/{{.Device}}"
} }
if b.config.UnmountCommand == "" {
b.config.UnmountCommand = "umount"
}
// Accumulate any errors // Accumulate any errors
errs := common.CheckUnusedConfig(md) errs := common.CheckUnusedConfig(md)
errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...)
@ -137,12 +122,8 @@ func (b *Builder) Prepare(raws ...interface{}) error {
} }
templates := map[string]*string{ templates := map[string]*string{
"device_path": &b.config.DevicePath, "device_path": &b.config.DevicePath,
"mount_command": &b.config.MountCommand, "source_ami": &b.config.SourceAmi,
"chroot_command": &b.config.ChrootCommand,
"copy_command": &b.config.CopyCommand,
"source_ami": &b.config.SourceAmi,
"unmount_command": &b.config.UnmountCommand,
} }
for n, ptr := range templates { for n, ptr := range templates {

View File

@ -1,5 +1,7 @@
package chroot package chroot
// pf := func () { somefunc("a str", 1) }
import ( import (
"fmt" "fmt"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
@ -12,18 +14,18 @@ import (
"syscall" "syscall"
) )
type Command func(string) *exec.Cmd
// Communicator is a special communicator that works by executing // Communicator is a special communicator that works by executing
// commands locally but within a chroot. // commands locally but within a chroot.
type Communicator struct { type Communicator struct {
Chroot string Chroot string
ChrootCommand string ChrootCmd Command
CopyCommand string wrappedCommand Command
} }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
localCmd := c.ChrootCmd(cmd.Command)
chrootCommand := fmt.Sprintf("%s %s %s", c.ChrootCommand, c.Chroot, cmd.Command)
localCmd := exec.Command("/bin/sh", "-c", chrootCommand)
localCmd.Stdin = cmd.Stdin localCmd.Stdin = cmd.Stdin
localCmd.Stdout = cmd.Stdout localCmd.Stdout = cmd.Stdout
localCmd.Stderr = cmd.Stderr localCmd.Stderr = cmd.Stderr
@ -64,7 +66,8 @@ func (c *Communicator) Upload(dst string, r io.Reader) error {
} }
defer os.Remove(tf.Name()) defer os.Remove(tf.Name())
io.Copy(tf, r) io.Copy(tf, r)
return copySingle(dst, tf.Name(), c.CopyCommand) cpCmd := fmt.Sprintf("cp %s %s", dst, tf.Name())
return (*c.ChrootCmd(cpCmd)).Run()
} }
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
@ -87,7 +90,8 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error
chrootDest := filepath.Join(c.Chroot, dst, path) chrootDest := filepath.Join(c.Chroot, dst, path)
log.Printf("Uploading to chroot dir: %s", dst) log.Printf("Uploading to chroot dir: %s", dst)
return copySingle(chrootDest, fullPath, c.CopyCommand) cpCmd := fmt.Sprintf("cp %s %s", chrootDest, fullPath)
return c.ChrootCmd(cpCmd).Run()
} }
log.Printf("Uploading directory '%s' to '%s'", src, dst) log.Printf("Uploading directory '%s' to '%s'", src, dst)

View File

@ -2,16 +2,17 @@ package chroot
import ( import (
"fmt" "fmt"
"os/exec"
"log" "log"
"os/exec"
) )
func copySingle(dest string, src string, copyCommand string) error { func ChrootCommand(chroot string, command string) *exec.Cmd {
cpCommand := fmt.Sprintf("%s %s %s", copyCommand, src, dest) chrootCommand := fmt.Sprintf("chroot %s %s", chroot, command)
localCmd := exec.Command("/bin/sh", "-c", cpCommand) return ShellCommand(chrootCommand)
log.Printf("Executing copy: %s %#v", localCmd.Path, localCmd.Args) }
if err := localCmd.Run(); err != nil {
return err func ShellCommand(command string) *exec.Cmd {
} cmd := exec.Command("/bin/sh", "-c", command)
return nil log.Printf("WrappedCommand(%s) -> #%v", command, cmd.Args)
return cmd
} }

View File

@ -4,6 +4,7 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"os/exec"
) )
// StepChrootProvision provisions the instance within a chroot. // StepChrootProvision provisions the instance within a chroot.
@ -11,17 +12,36 @@ type StepChrootProvision struct {
mounts []string mounts []string
} }
type WrappedCommandTemplate struct {
command string
}
func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction { func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction {
hook := state.Get("hook").(packer.Hook) hook := state.Get("hook").(packer.Hook)
mountPath := state.Get("mount_path").(string) mountPath := state.Get("mount_path").(string)
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
chrootCmd := func(command string) *exec.Cmd {
return ChrootCommand(mountPath, command)
}
wrappedCommand := func(command string) *exec.Cmd {
wrapped, err := config.tpl.Process(config.CommandWrapper, &WrappedCommandTemplate{
command: command,
})
if err != nil {
ui.Error(err.Error())
}
return ShellCommand(wrapped)
}
state.Put("chrootCmd", chrootCmd)
state.Put("wrappedCommand", wrappedCommand)
// Create our communicator // Create our communicator
comm := &Communicator{ comm := &Communicator{
Chroot: mountPath, Chroot: mountPath,
ChrootCommand: config.ChrootCommand, ChrootCmd: chrootCmd,
CopyCommand: config.CopyCommand, wrappedCommand: wrappedCommand,
} }
// Provision // Provision

View File

@ -5,7 +5,6 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"os/exec"
"path/filepath" "path/filepath"
) )
@ -22,6 +21,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
mountPath := state.Get("mount_path").(string) mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(Command)
s.files = make([]string, 0, len(config.CopyFiles)) s.files = make([]string, 0, len(config.CopyFiles))
if len(config.CopyFiles) > 0 { if len(config.CopyFiles) > 0 {
@ -31,7 +31,8 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction {
chrootPath := filepath.Join(mountPath, path) chrootPath := filepath.Join(mountPath, path)
log.Printf("Copying '%s' to '%s'", path, chrootPath) log.Printf("Copying '%s' to '%s'", path, chrootPath)
if err := copySingle(chrootPath, path, config.CopyCommand); err != nil { cmd := fmt.Sprintf("cp %s %s", chrootPath, path)
if err := wrappedCommand(cmd); err != nil {
err := fmt.Errorf("Error copying file: %s", err) err := fmt.Errorf("Error copying file: %s", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
@ -53,12 +54,13 @@ func (s *StepCopyFiles) Cleanup(state multistep.StateBag) {
} }
} }
func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error { func (s *StepCopyFiles) CleanupFunc(state multistep.StateBag) error {
wrappedCommand := state.Get("wrappedCommand").(Command)
if s.files != nil { if s.files != nil {
for _, file := range s.files { for _, file := range s.files {
log.Printf("Removing: %s", file) log.Printf("Removing: %s", file)
chrootCommand := fmt.Sprintf("rm %s", file) localCmd := wrappedCommand(fmt.Sprintf("rm -f %s", file))
localCmd := exec.Command("/bin/sh", "-c", chrootCommand) log.Println(localCmd.Args)
if err := localCmd.Run(); err != nil { if err := localCmd.Run(); err != nil {
return err return err
} }

View File

@ -7,7 +7,6 @@ import (
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
) )
@ -59,8 +58,9 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Mounting the root device...") ui.Say("Mounting the root device...")
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
mountCommand := fmt.Sprintf("%s %s %s", config.MountCommand, device, mountPath) mountCommand := fmt.Sprintf("mount %s %s", device, mountPath)
cmd := exec.Command("/bin/sh", "-c", mountCommand) wrappedCommand := state.Get("wrappedCommand").(Command)
cmd := wrappedCommand(mountCommand)
cmd.Stderr = stderr cmd.Stderr = stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
@ -90,12 +90,12 @@ func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error {
return nil return nil
} }
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ui.Say("Unmounting the root device...") ui.Say("Unmounting the root device...")
unmountCommand := fmt.Sprintf("%s %s", config.UnmountCommand, s.mountPath) unmountCommand := fmt.Sprintf("umount %s", s.mountPath)
cmd := exec.Command("/bin/sh", "-c", unmountCommand) wrappedCommand := state.Get("wrappedCommand").(Command)
cmd := wrappedCommand(unmountCommand)
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf("Error unmounting root device: %s", err) return fmt.Errorf("Error unmounting root device: %s", err)
} }

View File

@ -6,7 +6,6 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"os" "os"
"os/exec"
) )
// StepMountExtra mounts the attached device. // StepMountExtra mounts the attached device.
@ -21,6 +20,7 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
mountPath := state.Get("mount_path").(string) mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(Command)
s.mounts = make([]string, 0, len(config.ChrootMounts)) s.mounts = make([]string, 0, len(config.ChrootMounts))
@ -43,12 +43,11 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction {
ui.Message(fmt.Sprintf("Mounting: %s", mountInfo[2])) ui.Message(fmt.Sprintf("Mounting: %s", mountInfo[2]))
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
mountCommand := fmt.Sprintf( mountCommand := fmt.Sprintf(
"%s %s %s %s", "mount %s %s %s",
config.MountCommand,
flags, flags,
mountInfo[1], mountInfo[1],
innerPath) innerPath)
cmd := exec.Command("/bin/sh", "-c", mountCommand) cmd := wrappedCommand(mountCommand)
cmd.Stderr = stderr cmd.Stderr = stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
@ -79,15 +78,15 @@ func (s *StepMountExtra) CleanupFunc(state multistep.StateBag) error {
return nil return nil
} }
config := state.Get("config").(*Config) wrappedCommand := state.Get("wrappedCommand").(Command)
for len(s.mounts) > 0 { for len(s.mounts) > 0 {
var path string var path string
lastIndex := len(s.mounts) - 1 lastIndex := len(s.mounts) - 1
path, s.mounts = s.mounts[lastIndex], s.mounts[:lastIndex] path, s.mounts = s.mounts[lastIndex], s.mounts[:lastIndex]
unmountCommand := fmt.Sprintf("%s %s", config.UnmountCommand, path) unmountCommand := fmt.Sprintf("unmount %s", path)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
cmd := exec.Command("/bin/sh", "-c", unmountCommand) cmd := wrappedCommand(unmountCommand)
cmd.Stderr = stderr cmd.Stderr = stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
return fmt.Errorf( return fmt.Errorf(

View File

@ -111,9 +111,11 @@ Optional:
of the source AMI will be attached. This defaults to "" (empty string), of the source AMI will be attached. This defaults to "" (empty string),
which forces Packer to find an open device automatically. which forces Packer to find an open device automatically.
* `mount_command` (string) - The command to use to mount devices. This * `command_wrapper` (string) - How to run shell commands. This
defaults to "mount". This may be useful to set if you want to set defaults to "{{.Command}}". This may be useful to set if you want to set
environmental variables or perhaps run it with `sudo` or so on. environmental variables or perhaps run it with `sudo` or so on. This is a
configuration template where the `.Command` variable is replaced with the
command to be run..
* `mount_path` (string) - The path where the volume will be mounted. This is * `mount_path` (string) - The path where the volume will be mounted. This is
where the chroot environment will be. This defaults to where the chroot environment will be. This defaults to
@ -123,13 +125,6 @@ Optional:
* `tags` (object of key/value strings) - Tags applied to the AMI. * `tags` (object of key/value strings) - Tags applied to the AMI.
* `unmount_command` (string) - Just like `mount_command`, except this is
the command to unmount devices.
* `chroot_command` (string) - The command to use to create the chroot.
This defaults to "chroot", but like `mount_command`, it may be useful
to use `sudo` or variables.
## Basic Example ## Basic Example
Here is a basic example. It is completely valid except for the access keys: Here is a basic example. It is completely valid except for the access keys: