Commit b554a0dd authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/amazon/chroot: CommandWrapper

/cc @mwhooker - I changed the interface up a bit to return an error,
since things should return errors in Go (the ui.Error bit was kind of
ghetto because it had no way to bubble that error up except through the
UI).

Using this, I made it so that the communicator uses both a
CommandWrapper and ShellCommand with chroot so that the chroot commannd
is also wrapped (it wasn't before).

I think the functionality of all this is the same but I'd love if you
could look it over and make sure.
parent 535888d9
...@@ -13,7 +13,6 @@ import ( ...@@ -13,7 +13,6 @@ import (
"github.com/mitchellh/packer/common" "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"os/exec"
"runtime" "runtime"
) )
...@@ -165,15 +164,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -165,15 +164,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ec2conn := ec2.New(auth, region) ec2conn := ec2.New(auth, region)
wrappedCommand := func(command string) *exec.Cmd { wrappedCommand := func(command string) (string, error) {
wrapped, err := b.config.tpl.Process( return b.config.tpl.Process(
b.config.CommandWrapper, &wrappedCommandTemplate{ b.config.CommandWrapper, &wrappedCommandTemplate{
Command: command, Command: command,
}) })
if err != nil {
ui.Error(err.Error())
}
return ShellCommand(wrapped)
} }
// Setup the state bag and initial state for the steps // Setup the state bag and initial state for the steps
...@@ -182,7 +177,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -182,7 +177,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
state.Put("ec2", ec2conn) state.Put("ec2", ec2conn)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("ui", ui) state.Put("ui", ui)
state.Put("wrappedCommand", Command(wrappedCommand)) state.Put("wrappedCommand", CommandWrapper(wrappedCommand))
// Build the steps // Build the steps
steps := []multistep.Step{ steps := []multistep.Step{
......
package chroot
import (
"os/exec"
)
// CommandWrapper is a type that given a command, will possibly modify that
// command in-flight. This might return an error.
type CommandWrapper func(string) (string, error)
// ShellCommand takes a command string and returns an *exec.Cmd to execute
// it within the context of a shell (/bin/sh).
func ShellCommand(command string) *exec.Cmd {
return exec.Command("/bin/sh", "-c", command)
}
...@@ -14,18 +14,21 @@ import ( ...@@ -14,18 +14,21 @@ 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
ChrootCmd Command CmdWrapper CommandWrapper
WrappedCommand Command
} }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
localCmd := c.ChrootCmd(cmd.Command) command, err := c.CmdWrapper(
fmt.Sprintf("sudo chroot %s '%s'", c.Chroot, cmd.Command))
if err != nil {
return err
}
localCmd := ShellCommand(command)
localCmd.Stdin = cmd.Stdin localCmd.Stdin = cmd.Stdin
localCmd.Stdout = cmd.Stdout localCmd.Stdout = cmd.Stdout
localCmd.Stderr = cmd.Stderr localCmd.Stderr = cmd.Stderr
...@@ -66,41 +69,25 @@ func (c *Communicator) Upload(dst string, r io.Reader) error { ...@@ -66,41 +69,25 @@ 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)
cpCmd := fmt.Sprintf("cp %s %s", tf.Name(), dst)
return (c.WrappedCommand(cpCmd)).Run()
}
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
/*
walkFn := func(fullPath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
path, err := filepath.Rel(src, fullPath) cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp %s %s", tf.Name(), dst))
if err != nil { if err != nil {
return err return err
} }
for _, e := range exclude { return ShellCommand(cpCmd).Run()
if e == path { }
log.Printf("Skipping excluded file: %s", path)
return nil
}
}
chrootDest := filepath.Join(c.Chroot, dst, path)
log.Printf("Uploading dir %s to chroot dir: %s", src, dst)
cpCmd := fmt.Sprintf("cp %s %s", fullPath, chrootDest)
return c.WrappedCommand(cpCmd).Run()
}
*/
func (c *Communicator) UploadDir(dst string, src string, exclude []string) error {
// TODO: remove any file copied if it appears in `exclude` // TODO: remove any file copied if it appears in `exclude`
chrootDest := filepath.Join(c.Chroot, dst) chrootDest := filepath.Join(c.Chroot, dst)
log.Printf("Uploading directory '%s' to '%s'", src, chrootDest) log.Printf("Uploading directory '%s' to '%s'", src, chrootDest)
cpCmd := fmt.Sprintf("cp -R %s* %s", src, chrootDest) cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp -R %s* %s", src, chrootDest))
return c.WrappedCommand(cpCmd).Run() if err != nil {
return err
}
return ShellCommand(cpCmd).Run()
} }
func (c *Communicator) Download(src string, w io.Writer) error { func (c *Communicator) Download(src string, w io.Writer) error {
......
package chroot package chroot
import (
"fmt"
"log"
"os/exec"
)
func ChrootCommand(chroot string, command string) *exec.Cmd {
cmd := fmt.Sprintf("sudo chroot %s", chroot)
return ShellCommand(cmd, command)
}
func ShellCommand(commands ...string) *exec.Cmd {
cmds := append([]string{"-c"}, commands...)
cmd := exec.Command("/bin/sh", cmds...)
log.Printf("ShellCommand: %s %v", cmd.Path, cmd.Args[1:])
return cmd
}
...@@ -4,7 +4,6 @@ import ( ...@@ -4,7 +4,6 @@ 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.
...@@ -16,16 +15,12 @@ func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction ...@@ -16,16 +15,12 @@ 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)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(Command) wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
chrootCmd := func(command string) *exec.Cmd {
return ChrootCommand(mountPath, command)
}
// Create our communicator // Create our communicator
comm := &Communicator{ comm := &Communicator{
Chroot: mountPath, Chroot: mountPath,
ChrootCmd: chrootCmd, CmdWrapper: wrappedCommand,
WrappedCommand: wrappedCommand,
} }
// Provision // Provision
......
...@@ -22,7 +22,7 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { ...@@ -22,7 +22,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) wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
s.files = make([]string, 0, len(config.CopyFiles)) s.files = make([]string, 0, len(config.CopyFiles))
...@@ -33,8 +33,16 @@ func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction { ...@@ -33,8 +33,16 @@ 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)
cmd := wrappedCommand(fmt.Sprintf("cp %s %s", path, chrootPath)) cmdText, err := wrappedCommand(fmt.Sprintf("cp %s %s", path, chrootPath))
if err != nil {
err := fmt.Errorf("Error building copy command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
stderr.Reset() stderr.Reset()
cmd := ShellCommand(cmdText)
cmd.Stderr = stderr cmd.Stderr = stderr
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
err := fmt.Errorf( err := fmt.Errorf(
...@@ -60,11 +68,16 @@ func (s *StepCopyFiles) Cleanup(state multistep.StateBag) { ...@@ -60,11 +68,16 @@ func (s *StepCopyFiles) Cleanup(state multistep.StateBag) {
} }
func (s *StepCopyFiles) CleanupFunc(state multistep.StateBag) error { func (s *StepCopyFiles) CleanupFunc(state multistep.StateBag) error {
wrappedCommand := state.Get("wrappedCommand").(Command) wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
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)
localCmd := wrappedCommand(fmt.Sprintf("rm -f %s", file)) localCmdText, err := wrappedCommand(fmt.Sprintf("rm -f %s", file))
if err != nil {
return err
}
localCmd := ShellCommand(localCmdText)
if err := localCmd.Run(); err != nil { if err := localCmd.Run(); err != nil {
return err return err
} }
......
...@@ -27,6 +27,7 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction { ...@@ -27,6 +27,7 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
device := state.Get("device").(string) device := state.Get("device").(string)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
mountPath, err := config.tpl.Process(config.MountPath, &mountPathData{ mountPath, err := config.tpl.Process(config.MountPath, &mountPathData{
Device: filepath.Base(device), Device: filepath.Base(device),
...@@ -58,9 +59,16 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction { ...@@ -58,9 +59,16 @@ 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("mount %s %s", device, mountPath) mountCommand, err := wrappedCommand(
wrappedCommand := state.Get("wrappedCommand").(Command) fmt.Sprintf("mount %s %s", device, mountPath))
cmd := wrappedCommand(mountCommand) if err != nil {
err := fmt.Errorf("Error creating mount command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
cmd := ShellCommand(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(
...@@ -91,11 +99,15 @@ func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error { ...@@ -91,11 +99,15 @@ func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error {
} }
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
ui.Say("Unmounting the root device...") ui.Say("Unmounting the root device...")
unmountCommand, err := wrappedCommand(fmt.Sprintf("umount %s", s.mountPath))
if err != nil {
return fmt.Errorf("Error creating unmount command: %s", err)
}
unmountCommand := fmt.Sprintf("umount %s", s.mountPath) cmd := ShellCommand(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)
} }
......
...@@ -20,7 +20,7 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction { ...@@ -20,7 +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) wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
s.mounts = make([]string, 0, len(config.ChrootMounts)) s.mounts = make([]string, 0, len(config.ChrootMounts))
...@@ -42,12 +42,19 @@ func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction { ...@@ -42,12 +42,19 @@ 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, err := wrappedCommand(fmt.Sprintf(
"mount %s %s %s", "mount %s %s %s",
flags, flags,
mountInfo[1], mountInfo[1],
innerPath) innerPath))
cmd := wrappedCommand(mountCommand) if err != nil {
err := fmt.Errorf("Error creating mount command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
cmd := ShellCommand(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(
...@@ -78,15 +85,18 @@ func (s *StepMountExtra) CleanupFunc(state multistep.StateBag) error { ...@@ -78,15 +85,18 @@ func (s *StepMountExtra) CleanupFunc(state multistep.StateBag) error {
return nil return nil
} }
wrappedCommand := state.Get("wrappedCommand").(Command) wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
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("umount %s", path) unmountCommand, err := wrappedCommand(fmt.Sprintf("umount %s", path))
if err != nil {
return fmt.Errorf("Error creating unmount command: %s", err)
}
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
cmd := wrappedCommand(unmountCommand) cmd := ShellCommand(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(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment