builder/vmware: configure VNC port randomly
This commit is contained in:
parent
068e94c3ee
commit
5947d77f05
|
@ -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{},
|
||||
|
|
|
@ -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{}) {
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue