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"`
|
SSHUser string `mapstructure:"ssh_username"`
|
||||||
SSHPassword string `mapstructure:"ssh_password"`
|
SSHPassword string `mapstructure:"ssh_password"`
|
||||||
SSHWaitTimeout time.Duration
|
SSHWaitTimeout time.Duration
|
||||||
|
VNCPortMin uint `mapstructure:"vnc_port_min"`
|
||||||
|
VNCPortMax uint `mapstructure:"vnc_port_max"`
|
||||||
|
|
||||||
RawBootWait string `mapstructure:"boot_wait"`
|
RawBootWait string `mapstructure:"boot_wait"`
|
||||||
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
|
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
|
||||||
|
@ -53,6 +55,14 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
|
||||||
b.config.VMName = "packer"
|
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 == "" {
|
if b.config.OutputDir == "" {
|
||||||
b.config.OutputDir = "vmware"
|
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))
|
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()
|
b.driver, err = b.newDriver()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, fmt.Errorf("Failed creating VMware driver: %s", err))
|
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{},
|
&stepCreateDisk{},
|
||||||
&stepCreateVMX{},
|
&stepCreateVMX{},
|
||||||
&stepHTTPServer{},
|
&stepHTTPServer{},
|
||||||
|
&stepConfigureVNC{},
|
||||||
&stepRun{},
|
&stepRun{},
|
||||||
&stepTypeBootCommand{},
|
&stepTypeBootCommand{},
|
||||||
&stepWaitForSSH{},
|
&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
|
package vmware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,3 +28,40 @@ func ParseVMX(contents string) map[string]string {
|
||||||
|
|
||||||
return results
|
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"])
|
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