builder/vmware: configure VNC port randomly

This commit is contained in:
Mitchell Hashimoto 2013-06-07 14:48:59 -07:00
parent 068e94c3ee
commit 5947d77f05
4 changed files with 143 additions and 0 deletions

View File

@ -33,6 +33,8 @@ type config struct {
SSHUser string `mapstructure:"ssh_username"`
SSHPassword string `mapstructure:"ssh_password"`
SSHWaitTimeout time.Duration
VNCPortMin uint `mapstructure:"vnc_port_min"`
VNCPortMax uint `mapstructure:"vnc_port_max"`
RawBootWait string `mapstructure:"boot_wait"`
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
@ -53,6 +55,14 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
b.config.VMName = "packer"
}
if b.config.VNCPortMin == 0 {
b.config.VNCPortMin = 5900
}
if b.config.VNCPortMax == 0 {
b.config.VNCPortMax = 6000
}
if b.config.OutputDir == "" {
b.config.OutputDir = "vmware"
}
@ -93,6 +103,10 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
errs = append(errs, fmt.Errorf("Failed parsing ssh_wait_timeout: %s", err))
}
if b.config.VNCPortMin > b.config.VNCPortMax {
errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
}
b.driver, err = b.newDriver()
if err != nil {
errs = append(errs, fmt.Errorf("Failed creating VMware driver: %s", err))
@ -111,6 +125,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook) packer.Artifact {
&stepCreateDisk{},
&stepCreateVMX{},
&stepHTTPServer{},
&stepConfigureVNC{},
&stepRun{},
&stepTypeBootCommand{},
&stepWaitForSSH{},

View File

@ -0,0 +1,70 @@
package vmware
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"io/ioutil"
"math/rand"
"net"
"os"
)
// This step configures the VM to enable the VNC server.
//
// Uses:
// config *config
// ui packer.Ui
// vmx_path string
//
// Produces:
// vnc_port uint - The port that VNC is configured to listen on.
type stepConfigureVNC struct{}
func (stepConfigureVNC) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
vmxPath := state["vmx_path"].(string)
f, err := os.Open(vmxPath)
if err != nil {
ui.Error(fmt.Sprintf("Error while reading VMX data: %s", err))
return multistep.ActionHalt
}
vmxBytes, err := ioutil.ReadAll(f)
if err != nil {
ui.Error(fmt.Sprintf("Error reading VMX data: %s", err))
return multistep.ActionHalt
}
// Find an open VNC port. Note that this can still fail later on
// because we have to release the port at some point. But this does its
// best.
var vncPort uint
portRange := int(config.VNCPortMax - config.VNCPortMin)
for {
vncPort = uint(rand.Intn(portRange) + portRange)
l, err := net.Listen("tcp", fmt.Sprintf(":%d", vncPort))
if err == nil {
defer l.Close()
break
}
}
vmxData := ParseVMX(string(vmxBytes))
vmxData["RemoteDisplay.vnc.enabled"] = "TRUE"
vmxData["RemoteDisplay.vnc.port"] = fmt.Sprintf("%d", vncPort)
if err := WriteVMX(vmxPath, vmxData); err != nil {
ui.Error(fmt.Sprintf("Error writing VMX data: %s", err))
return multistep.ActionHalt
}
state["vnc_port"] = vncPort
return multistep.ActionContinue
}
func (stepConfigureVNC) Cleanup(map[string]interface{}) {
}

View File

@ -1,7 +1,12 @@
package vmware
import (
"bytes"
"fmt"
"io"
"os"
"regexp"
"sort"
"strings"
)
@ -23,3 +28,40 @@ func ParseVMX(contents string) map[string]string {
return results
}
// EncodeVMX takes a map and turns it into valid VMX contents.
func EncodeVMX(contents map[string]string) string {
var buf bytes.Buffer
i := 0
keys := make([]string, len(contents))
for k, _ := range contents {
keys[i] = k
i++
}
sort.Strings(keys)
for _, k := range keys {
buf.WriteString(fmt.Sprintf("%s = \"%s\"\n", k, contents[k]))
}
return buf.String()
}
// WriteVMX takes a path to a VMX file and contents in the form of a
// map and writes it out.
func WriteVMX(path string, data map[string]string) (err error) {
f, err := os.Create(path)
if err != nil {
return
}
defer f.Close()
var buf bytes.Buffer
buf.WriteString(EncodeVMX(data))
if _, err = io.Copy(f, &buf); err != nil {
return
}
return
}

View File

@ -21,3 +21,19 @@ config.version = "8"
t.Errorf("invalid config.version: %s", results["config.version"])
}
}
func TestEncodeVMX(t *testing.T) {
contents := map[string]string{
".encoding": "UTF-8",
"config.version": "8",
}
expected := `.encoding = "UTF-8"
config.version = "8"
`
result := EncodeVMX(contents)
if result != expected {
t.Errorf("invalid results: %s", result)
}
}