Merge pull request #2731 from legal90/prl-compact-disk
Parallels: Add "CompactDisk" build step
This commit is contained in:
commit
3f14b9d984
|
@ -15,9 +15,15 @@ import (
|
|||
// versions out of the builder steps, so sometimes the methods are
|
||||
// extremely specific.
|
||||
type Driver interface {
|
||||
// Compact a virtual disk image.
|
||||
CompactDisk(string) error
|
||||
|
||||
// Adds new CD/DVD drive to the VM and returns name of this device
|
||||
DeviceAddCdRom(string, string) (string, error)
|
||||
|
||||
// Get path to the first virtual disk image
|
||||
DiskPath(string) (string, error)
|
||||
|
||||
// Import a VM
|
||||
Import(string, string, string, bool) error
|
||||
|
||||
|
|
|
@ -102,6 +102,33 @@ func getAppPath(bundleId string) (string, error) {
|
|||
return pathOutput, nil
|
||||
}
|
||||
|
||||
func (d *Parallels9Driver) CompactDisk(diskPath string) error {
|
||||
prlDiskToolPath, err := exec.LookPath("prl_disk_tool")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Analyze the disk content and remove unused blocks
|
||||
command := []string{
|
||||
"compact",
|
||||
"--hdd", diskPath,
|
||||
}
|
||||
if err := exec.Command(prlDiskToolPath, command...).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove null blocks
|
||||
command = []string{
|
||||
"compact", "--buildmap",
|
||||
"--hdd", diskPath,
|
||||
}
|
||||
if err := exec.Command(prlDiskToolPath, command...).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Parallels9Driver) DeviceAddCdRom(name string, image string) (string, error) {
|
||||
command := []string{
|
||||
"set", name,
|
||||
|
@ -125,6 +152,23 @@ func (d *Parallels9Driver) DeviceAddCdRom(name string, image string) (string, er
|
|||
return device_name, nil
|
||||
}
|
||||
|
||||
func (d *Parallels9Driver) DiskPath(name string) (string, error) {
|
||||
out, err := exec.Command(d.PrlctlPath, "list", "-i", name).Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hddRe := regexp.MustCompile("hdd0.* image='(.*)' type=*")
|
||||
matches := hddRe.FindStringSubmatch(string(out))
|
||||
if matches == nil {
|
||||
return "", fmt.Errorf(
|
||||
"Could not determine hdd image path in the output:\n%s", string(out))
|
||||
}
|
||||
|
||||
hdd_path := matches[1]
|
||||
return hdd_path, nil
|
||||
}
|
||||
|
||||
func (d *Parallels9Driver) IsRunning(name string) (bool, error) {
|
||||
var stdout bytes.Buffer
|
||||
|
||||
|
|
|
@ -5,12 +5,21 @@ import "sync"
|
|||
type DriverMock struct {
|
||||
sync.Mutex
|
||||
|
||||
CompactDiskCalled bool
|
||||
CompactDiskPath string
|
||||
CompactDiskErr error
|
||||
|
||||
DeviceAddCdRomCalled bool
|
||||
DeviceAddCdRomName string
|
||||
DeviceAddCdRomImage string
|
||||
DeviceAddCdRomResult string
|
||||
DeviceAddCdRomErr error
|
||||
|
||||
DiskPathCalled bool
|
||||
DiskPathName string
|
||||
DiskPathResult string
|
||||
DiskPathErr error
|
||||
|
||||
ImportCalled bool
|
||||
ImportName string
|
||||
ImportSrcPath string
|
||||
|
@ -54,6 +63,12 @@ type DriverMock struct {
|
|||
IpAddressError error
|
||||
}
|
||||
|
||||
func (d *DriverMock) CompactDisk(path string) error {
|
||||
d.CompactDiskCalled = true
|
||||
d.CompactDiskPath = path
|
||||
return d.CompactDiskErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) DeviceAddCdRom(name string, image string) (string, error) {
|
||||
d.DeviceAddCdRomCalled = true
|
||||
d.DeviceAddCdRomName = name
|
||||
|
@ -61,6 +76,12 @@ func (d *DriverMock) DeviceAddCdRom(name string, image string) (string, error) {
|
|||
return d.DeviceAddCdRomResult, d.DeviceAddCdRomErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) DiskPath(name string) (string, error) {
|
||||
d.DiskPathCalled = true
|
||||
d.DiskPathName = name
|
||||
return d.DiskPathResult, d.DiskPathErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) Import(name, srcPath, dstPath string, reassignMac bool) error {
|
||||
d.ImportCalled = true
|
||||
d.ImportName = name
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
// This step removes all empty blocks from expanding Parallels virtual disks
|
||||
// and reduces the result disk size
|
||||
//
|
||||
// Uses:
|
||||
// driver Driver
|
||||
// vmName string
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// <nothing>
|
||||
type StepCompactDisk struct {
|
||||
Skip bool
|
||||
}
|
||||
|
||||
func (s *StepCompactDisk) Run(state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(Driver)
|
||||
vmName := state.Get("vmName").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if s.Skip {
|
||||
ui.Say("Skipping disk compaction step...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ui.Say("Compacting the disk image")
|
||||
diskPath, err := driver.DiskPath(vmName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error detecting virtual disk path: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err := driver.CompactDisk(diskPath); err != nil {
|
||||
state.Put("error", fmt.Errorf("Error compacting disk: %s", err))
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (*StepCompactDisk) Cleanup(multistep.StateBag) {}
|
|
@ -0,0 +1,73 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/multistep"
|
||||
)
|
||||
|
||||
func TestStepCompactDisk_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCompactDisk)
|
||||
}
|
||||
|
||||
func TestStepCompactDisk(t *testing.T) {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
tf.Close()
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
state := testState(t)
|
||||
step := new(StepCompactDisk)
|
||||
|
||||
state.Put("vmName", "foo")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Mock results
|
||||
driver.DiskPathResult = tf.Name()
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if !driver.CompactDiskCalled {
|
||||
t.Fatal("should've called")
|
||||
}
|
||||
|
||||
path, _ := driver.DiskPath("foo")
|
||||
if path != tf.Name() {
|
||||
t.Fatal("should call with right path")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCompactDisk_skip(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCompactDisk)
|
||||
step.Skip = true
|
||||
|
||||
state.Put("vmName", "foo")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if driver.CompactDiskCalled {
|
||||
t.Fatal("should not have called")
|
||||
}
|
||||
}
|
|
@ -45,6 +45,7 @@ type Config struct {
|
|||
ISOChecksum string `mapstructure:"iso_checksum"`
|
||||
ISOChecksumType string `mapstructure:"iso_checksum_type"`
|
||||
ISOUrls []string `mapstructure:"iso_urls"`
|
||||
SkipCompaction bool `mapstructure:"skip_compaction"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
RawSingleISOUrl string `mapstructure:"iso_url"`
|
||||
|
@ -271,6 +272,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
Commands: b.config.PrlctlPost,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
¶llelscommon.StepCompactDisk{
|
||||
Skip: b.config.SkipCompaction,
|
||||
},
|
||||
}
|
||||
|
||||
// Setup the state bag
|
||||
|
|
|
@ -196,6 +196,11 @@ builder.
|
|||
doesn't shut down in this time, it is an error. By default, the timeout is
|
||||
"5m", or five minutes.
|
||||
|
||||
- `skip_compaction` (boolean) - Virtual disk image is compacted at the end of
|
||||
the build process using `prl_disk_tool` utility. In certain rare cases, this
|
||||
might corrupt the resulting disk image. If you find this to be the case,
|
||||
you can disable compaction using this configuration value.
|
||||
|
||||
- `vm_name` (string) - This is the name of the PVM directory for the new
|
||||
virtual machine, without the file extension. By default this is
|
||||
"packer-BUILDNAME", where "BUILDNAME" is the name of the build.
|
||||
|
|
|
@ -142,6 +142,11 @@ builder.
|
|||
doesn't shut down in this time, it is an error. By default, the timeout is
|
||||
"5m", or five minutes.
|
||||
|
||||
- `skip_compaction` (boolean) - Virtual disk image is compacted at the end of
|
||||
the build process using `prl_disk_tool` utility. In certain rare cases, this
|
||||
might corrupt the resulting disk image. If you find this to be the case,
|
||||
you can disable compaction using this configuration value.
|
||||
|
||||
- `vm_name` (string) - This is the name of the virtual machine when it is
|
||||
imported as well as the name of the PVM directory when the virtual machine
|
||||
is exported. By default this is "packer-BUILDNAME", where "BUILDNAME" is the
|
||||
|
|
Loading…
Reference in New Issue