Merge pull request #2731 from legal90/prl-compact-disk

Parallels: Add "CompactDisk" build step
This commit is contained in:
Rickard von Essen 2015-09-16 14:03:31 +02:00
commit 3f14b9d984
8 changed files with 209 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {}

View File

@ -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")
}
}

View File

@ -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,
},
&parallelscommon.StepCompactDisk{
Skip: b.config.SkipCompaction,
},
}
// Setup the state bag

View File

@ -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.

View File

@ -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