builder/vmware: StepCleanVMX

This commit is contained in:
Mitchell Hashimoto 2013-12-24 18:40:52 -07:00
parent 8f8ea60b4f
commit f01b21c610
5 changed files with 232 additions and 94 deletions

View File

@ -0,0 +1,78 @@
package common
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"regexp"
"strings"
)
// This step cleans up the VMX by removing or changing this prior to
// being ready for use.
//
// Uses:
// ui packer.Ui
// vmx_path string
//
// Produces:
// <nothing>
type StepCleanVMX struct{}
func (s StepCleanVMX) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
vmxPath := state.Get("vmx_path").(string)
ui.Say("Cleaning VMX prior to finishing up...")
vmxData, err := ReadVMX(vmxPath)
if err != nil {
state.Put("error", fmt.Errorf("Error reading VMX: %s", err))
return multistep.ActionHalt
}
if _, ok := state.GetOk("floppy_path"); ok {
// Delete the floppy0 entries so the floppy is no longer mounted
ui.Message("Unmounting floppy from VMX...")
for k, _ := range vmxData {
if strings.HasPrefix(k, "floppy0.") {
log.Printf("Deleting key: %s", k)
delete(vmxData, k)
}
}
vmxData["floppy0.present"] = "FALSE"
}
if isoPathRaw, ok := state.GetOk("iso_path"); ok {
isoPath := isoPathRaw.(string)
ui.Message("Detaching ISO from CD-ROM device...")
devRe := regexp.MustCompile(`^ide\d:\d\.`)
for k, _ := range vmxData {
match := devRe.FindString(k)
if match == "" {
continue
}
filenameKey := match + "filename"
if filename, ok := vmxData[filenameKey]; ok {
if filename == isoPath {
// Change the CD-ROM device back to auto-detect to eject
vmxData[filenameKey] = "auto detect"
vmxData[match+"devicetype"] = "cdrom-raw"
}
}
}
}
// Rewrite the VMX
if err := WriteVMX(vmxPath, vmxData); err != nil {
state.Put("error", fmt.Errorf("Error writing VMX: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (StepCleanVMX) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,142 @@
package common
import (
"io/ioutil"
"os"
"testing"
"github.com/mitchellh/multistep"
)
func TestStepCleanVMX_impl(t *testing.T) {
var _ multistep.Step = new(StepCleanVMX)
}
func TestStepCleanVMX(t *testing.T) {
state := testState(t)
step := new(StepCleanVMX)
vmxPath := testVMXFile(t)
defer os.Remove(vmxPath)
state.Put("vmx_path", vmxPath)
// 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")
}
}
func TestStepCleanVMX_floppyPath(t *testing.T) {
state := testState(t)
step := new(StepCleanVMX)
vmxPath := testVMXFile(t)
defer os.Remove(vmxPath)
if err := ioutil.WriteFile(vmxPath, []byte(testVMXFloppyPath), 0644); err != nil {
t.Fatalf("err: %s", err)
}
state.Put("floppy_path", "foo")
state.Put("vmx_path", vmxPath)
// 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 resulting data
vmxContents, err := ioutil.ReadFile(vmxPath)
if err != nil {
t.Fatalf("err: %s", err)
}
vmxData := ParseVMX(string(vmxContents))
cases := []struct {
Key string
Value string
}{
{"floppy0.present", "FALSE"},
{"floppy0.filetype", ""},
{"floppy0.filename", ""},
}
for _, tc := range cases {
if tc.Value == "" {
if _, ok := vmxData[tc.Key]; ok {
t.Fatalf("should not have key: %s", tc.Key)
}
} else {
if vmxData[tc.Key] != tc.Value {
t.Fatalf("bad: %s %#v", tc.Key, vmxData[tc.Key])
}
}
}
}
func TestStepCleanVMX_isoPath(t *testing.T) {
state := testState(t)
step := new(StepCleanVMX)
vmxPath := testVMXFile(t)
defer os.Remove(vmxPath)
if err := ioutil.WriteFile(vmxPath, []byte(testVMXISOPath), 0644); err != nil {
t.Fatalf("err: %s", err)
}
state.Put("iso_path", "foo")
state.Put("vmx_path", vmxPath)
// 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 resulting data
vmxContents, err := ioutil.ReadFile(vmxPath)
if err != nil {
t.Fatalf("err: %s", err)
}
vmxData := ParseVMX(string(vmxContents))
cases := []struct {
Key string
Value string
}{
{"ide0:0.filename", "auto detect"},
{"ide0:0.devicetype", "cdrom-raw"},
{"ide0:1.filename", "bar"},
{"foo", "bar"},
}
for _, tc := range cases {
if tc.Value == "" {
if _, ok := vmxData[tc.Key]; ok {
t.Fatalf("should not have key: %s", tc.Key)
}
} else {
if vmxData[tc.Key] != tc.Value {
t.Fatalf("bad: %s %#v", tc.Key, vmxData[tc.Key])
}
}
}
}
const testVMXFloppyPath = `
floppy0.present = "TRUE"
floppy0.filetype = "file"
`
const testVMXISOPath = `
ide0:0.filename = "foo"
ide0:1.filename = "bar"
foo = "bar"
`

View File

@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"regexp"
@ -68,3 +69,13 @@ func WriteVMX(path string, data map[string]string) (err error) {
return
}
// ReadVMX takes a path to a VMX file and reads it into a k/v mapping.
func ReadVMX(path string) (map[string]string, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
return ParseVMX(string(data)), nil
}

View File

@ -412,7 +412,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&common.StepProvision{},
&stepShutdown{},
&vmwcommon.StepCleanFiles{},
&stepCleanVMX{},
&vmwcommon.StepCleanVMX{},
&stepCompactDisk{},
}

View File

@ -1,93 +0,0 @@
package iso
import (
"fmt"
"github.com/mitchellh/multistep"
vmwcommon "github.com/mitchellh/packer/builder/vmware/common"
"github.com/mitchellh/packer/packer"
"io/ioutil"
"log"
"os"
"regexp"
"strings"
)
// This step cleans up the VMX by removing or changing this prior to
// being ready for use.
//
// Uses:
// ui packer.Ui
// vmx_path string
//
// Produces:
// <nothing>
type stepCleanVMX struct{}
func (s stepCleanVMX) Run(state multistep.StateBag) multistep.StepAction {
isoPath := state.Get("iso_path").(string)
ui := state.Get("ui").(packer.Ui)
vmxPath := state.Get("vmx_path").(string)
ui.Say("Cleaning VMX prior to finishing up...")
vmxData, err := s.readVMX(vmxPath)
if err != nil {
state.Put("error", fmt.Errorf("Error reading VMX: %s", err))
return multistep.ActionHalt
}
if _, ok := state.GetOk("floppy_path"); ok {
// Delete the floppy0 entries so the floppy is no longer mounted
ui.Message("Unmounting floppy from VMX...")
for k, _ := range vmxData {
if strings.HasPrefix(k, "floppy0.") {
log.Printf("Deleting key: %s", k)
delete(vmxData, k)
}
}
vmxData["floppy0.present"] = "FALSE"
}
ui.Message("Detaching ISO from CD-ROM device...")
devRe := regexp.MustCompile(`^ide\d:\d\.`)
for k, _ := range vmxData {
match := devRe.FindString(k)
if match == "" {
continue
}
filenameKey := match + "filename"
if filename, ok := vmxData[filenameKey]; ok {
if filename == isoPath {
// Change the CD-ROM device back to auto-detect to eject
vmxData[filenameKey] = "auto detect"
vmxData[match+"devicetype"] = "cdrom-raw"
}
}
}
// Rewrite the VMX
if err := vmwcommon.WriteVMX(vmxPath, vmxData); err != nil {
state.Put("error", fmt.Errorf("Error writing VMX: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (stepCleanVMX) Cleanup(multistep.StateBag) {}
func (stepCleanVMX) readVMX(vmxPath string) (map[string]string, error) {
vmxF, err := os.Open(vmxPath)
if err != nil {
return nil, err
}
defer vmxF.Close()
vmxBytes, err := ioutil.ReadAll(vmxF)
if err != nil {
return nil, err
}
return vmwcommon.ParseVMX(string(vmxBytes)), nil
}