From b7f8c6ad9defd0ccf4292ff7ab5fafb7f7ca47a6 Mon Sep 17 00:00:00 2001 From: Marin Salinas Date: Fri, 22 Feb 2019 13:45:00 -0600 Subject: [PATCH] feature: add prepare device for chroot builder --- builder/osc/chroot/builder.go | 10 ++++ builder/osc/chroot/device.go | 70 +++++++++++++++++++++++ builder/osc/chroot/step_prepare_device.go | 46 +++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 builder/osc/chroot/device.go create mode 100644 builder/osc/chroot/step_prepare_device.go diff --git a/builder/osc/chroot/builder.go b/builder/osc/chroot/builder.go index 6843ad179..7c64edbc5 100644 --- a/builder/osc/chroot/builder.go +++ b/builder/osc/chroot/builder.go @@ -238,10 +238,20 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ) } + steps = append(steps, + &StepFlock{}, + &StepPrepareDevice{}, + ) + // Run! b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) b.runner.Run(state) + // If there was an error, return that + if rawErr, ok := state.GetOk("error"); ok { + return nil, rawErr.(error) + } + return nil, nil } diff --git a/builder/osc/chroot/device.go b/builder/osc/chroot/device.go new file mode 100644 index 000000000..4ae8bc789 --- /dev/null +++ b/builder/osc/chroot/device.go @@ -0,0 +1,70 @@ +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 { + device := fmt.Sprintf("/dev/%s%c", prefix, letter) + + // If the block device itself, i.e. /dev/sf, exists, then we + // can't use any of the numbers either. + if _, err := os.Stat(device); err == nil { + continue + } + + // To be able to build both Paravirtual and HVM images, the unnumbered + // device and the first numbered one must be available. + // E.g. /dev/xvdf and /dev/xvdf1 + numbered_device := fmt.Sprintf("%s%d", device, 1) + if _, err := os.Stat(numbered_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. The "vd" prefix appears on outscale images. +func devicePrefix() (string, error) { + available := []string{"sd", "xvd", "vd"} + + 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") +} diff --git a/builder/osc/chroot/step_prepare_device.go b/builder/osc/chroot/step_prepare_device.go new file mode 100644 index 000000000..0939d33cd --- /dev/null +++ b/builder/osc/chroot/step_prepare_device.go @@ -0,0 +1,46 @@ +package chroot + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +// StepPrepareDevice finds an available device and sets it. +type StepPrepareDevice struct { +} + +func (s *StepPrepareDevice) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + config := state.Get("config").(*Config) + ui := state.Get("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.Put("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.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + log.Printf("Device: %s", device) + state.Put("device", device) + return multistep.ActionContinue +} + +func (s *StepPrepareDevice) Cleanup(state multistep.StateBag) {}