builder/amazon/chroot: find available device

This commit is contained in:
Mitchell Hashimoto 2013-07-30 21:19:57 -07:00
parent 7857406f3e
commit b75bd29bfd
4 changed files with 120 additions and 17 deletions

View File

@ -24,14 +24,13 @@ type Config struct {
common.PackerConfig `mapstructure:",squash"`
awscommon.AccessConfig `mapstructure:",squash"`
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
CopyFiles []string `mapstructure:"copy_files"`
DevicePath string `mapstructure:"device_path"`
DevicePrefix string `mapstructure:"device_prefix"`
MountCommand string `mapstructure:"mount_command"`
MountPath string `mapstructure:"mount_path"`
SourceAmi string `mapstructure:"source_ami"`
UnmountCommand string `mapstructure:"unmount_command"`
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
CopyFiles []string `mapstructure:"copy_files"`
DevicePath string `mapstructure:"device_path"`
MountCommand string `mapstructure:"mount_command"`
MountPath string `mapstructure:"mount_path"`
SourceAmi string `mapstructure:"source_ami"`
UnmountCommand string `mapstructure:"unmount_command"`
}
type Builder struct {
@ -68,10 +67,6 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.CopyFiles = []string{"/etc/resolv.conf"}
}
if b.config.DevicePath == "" {
b.config.DevicePath = "/dev/sdh"
}
if b.config.MountCommand == "" {
b.config.MountCommand = "mount"
}
@ -128,6 +123,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
steps := []multistep.Step{
&StepInstanceInfo{},
&StepSourceAMIInfo{},
&StepPrepareDevice{},
&StepCreateVolume{},
&StepAttachVolume{},
&StepMountDevice{},

View File

@ -0,0 +1,61 @@
package chroot
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
)
// AvailableDevice finds an available device and returns it. Note that
// you should externally hold a flock or something in order to guarantee
// that this device is available across processes.
func AvailableDevice() (string, error) {
prefix, err := devicePrefix()
if err != nil {
return "", err
}
letters := "fghijklmnop"
for _, letter := range letters {
for i := 1; i < 16; i++ {
device := fmt.Sprintf("/dev/%s%c%d", prefix, letter, i)
if _, err := os.Stat(device); err != nil {
return device, nil
}
}
}
return "", errors.New("available device could not be found")
}
// devicePrefix returns the prefix ("sd" or "xvd" or so on) of the devices
// on the system.
func devicePrefix() (string, error) {
available := []string{"sd", "xvd"}
f, err := os.Open("/sys/block")
if err != nil {
return "", err
}
defer f.Close()
dirs, err := f.Readdirnames(-1)
if dirs != nil && len(dirs) > 0 {
for _, dir := range dirs {
dirBase := filepath.Base(dir)
for _, prefix := range available {
if strings.HasPrefix(dirBase, prefix) {
return prefix, nil
}
}
}
}
if err != nil {
return "", err
}
return "", errors.New("device prefix could not be detected")
}

View File

@ -7,6 +7,7 @@ import (
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
"strings"
)
// StepAttachVolume attaches the previously created volume to an
@ -21,16 +22,17 @@ type StepAttachVolume struct {
}
func (s *StepAttachVolume) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
ec2conn := state["ec2"].(*ec2.EC2)
device := state["device"].(string)
instance := state["instance"].(*ec2.Instance)
ui := state["ui"].(packer.Ui)
volumeId := state["volume_id"].(string)
device := config.DevicePath
// For the API call, it expects "sd" prefixed devices.
attachVolume := strings.Replace(device, "/xvd", "/sd", 1)
ui.Say("Attaching the root volume...")
_, err := ec2conn.AttachVolume(volumeId, instance.InstanceId, device)
ui.Say(fmt.Sprintf("Attaching the root volume to %s", attachVolume))
_, err := ec2conn.AttachVolume(volumeId, instance.InstanceId, attachVolume)
if err != nil {
err := fmt.Errorf("Error attaching volume: %s", err)
state["error"] = err
@ -70,7 +72,6 @@ func (s *StepAttachVolume) Run(state map[string]interface{}) multistep.StepActio
return multistep.ActionHalt
}
state["device"] = device
state["attach_cleanup"] = s
return multistep.ActionContinue
}

View File

@ -0,0 +1,45 @@
package chroot
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
)
// StepPrepareDevice finds an available device and sets it.
type StepPrepareDevice struct {
mounts []string
}
func (s *StepPrepareDevice) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
ui := state["ui"].(packer.Ui)
device := config.DevicePath
if device == "" {
var err error
log.Println("Device path not specified, searching for available device...")
device, err = AvailableDevice()
if err != nil {
err := fmt.Errorf("Error finding available device: %s", err)
state["error"] = err
ui.Error(err.Error())
return multistep.ActionHalt
}
}
if _, err := os.Stat(device); err == nil {
err := fmt.Errorf("Device is in use: %s", device)
state["error"] = err
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Printf("Device: %s", device)
state["device"] = device
return multistep.ActionContinue
}
func (s *StepPrepareDevice) Cleanup(state map[string]interface{}) {}