/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.
118 lines
2.9 KiB
Go
118 lines
2.9 KiB
Go
package chroot
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/mitchellh/multistep"
|
|
"github.com/mitchellh/packer/packer"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
type mountPathData struct {
|
|
Device string
|
|
}
|
|
|
|
// StepMountDevice mounts the attached device.
|
|
//
|
|
// Produces:
|
|
// mount_path string - The location where the volume was mounted.
|
|
// mount_device_cleanup CleanupFunc - To perform early cleanup
|
|
type StepMountDevice struct {
|
|
mountPath string
|
|
}
|
|
|
|
func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
|
|
config := state.Get("config").(*Config)
|
|
ui := state.Get("ui").(packer.Ui)
|
|
device := state.Get("device").(string)
|
|
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
|
|
|
mountPath, err := config.tpl.Process(config.MountPath, &mountPathData{
|
|
Device: filepath.Base(device),
|
|
})
|
|
|
|
if err != nil {
|
|
err := fmt.Errorf("Error preparing mount directory: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
mountPath, err = filepath.Abs(mountPath)
|
|
if err != nil {
|
|
err := fmt.Errorf("Error preparing mount directory: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
log.Printf("Mount path: %s", mountPath)
|
|
|
|
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
|
err := fmt.Errorf("Error creating mount directory: %s", err)
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
ui.Say("Mounting the root device...")
|
|
stderr := new(bytes.Buffer)
|
|
mountCommand, err := wrappedCommand(
|
|
fmt.Sprintf("mount %s %s", device, mountPath))
|
|
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
|
|
if err := cmd.Run(); err != nil {
|
|
err := fmt.Errorf(
|
|
"Error mounting root volume: %s\nStderr: %s", err, stderr.String())
|
|
state.Put("error", err)
|
|
ui.Error(err.Error())
|
|
return multistep.ActionHalt
|
|
}
|
|
|
|
// Set the mount path so we remember to unmount it later
|
|
s.mountPath = mountPath
|
|
state.Put("mount_path", s.mountPath)
|
|
state.Put("mount_device_cleanup", s)
|
|
|
|
return multistep.ActionContinue
|
|
}
|
|
|
|
func (s *StepMountDevice) Cleanup(state multistep.StateBag) {
|
|
ui := state.Get("ui").(packer.Ui)
|
|
if err := s.CleanupFunc(state); err != nil {
|
|
ui.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error {
|
|
if s.mountPath == "" {
|
|
return nil
|
|
}
|
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
|
|
|
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)
|
|
}
|
|
|
|
cmd := ShellCommand(unmountCommand)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("Error unmounting root device: %s", err)
|
|
}
|
|
|
|
s.mountPath = ""
|
|
return nil
|
|
}
|