Commit 85ab8621 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/vmware: Randomize HTTP port to avoid collisions

parent 56108f2b
...@@ -26,6 +26,8 @@ type config struct { ...@@ -26,6 +26,8 @@ type config struct {
VMName string `mapstructure:"vm_name"` VMName string `mapstructure:"vm_name"`
OutputDir string `mapstructure:"output_directory"` OutputDir string `mapstructure:"output_directory"`
HTTPDir string `mapstructure:"http_directory"` HTTPDir string `mapstructure:"http_directory"`
HTTPPortMin uint `mapstructure:"http_port_min"`
HTTPPortMax uint `mapstructure:"http_port_max"`
BootCommand []string `mapstructure:"boot_command"` BootCommand []string `mapstructure:"boot_command"`
BootWait time.Duration BootWait time.Duration
ShutdownCommand string `mapstructure:"shutdown_command"` ShutdownCommand string `mapstructure:"shutdown_command"`
...@@ -55,6 +57,14 @@ func (b *Builder) Prepare(raw interface{}) (err error) { ...@@ -55,6 +57,14 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
b.config.VMName = "packer" b.config.VMName = "packer"
} }
if b.config.HTTPPortMin == 0 {
b.config.HTTPPortMin = 8000
}
if b.config.HTTPPortMax == 0 {
b.config.HTTPPortMax = 9000
}
if b.config.VNCPortMin == 0 { if b.config.VNCPortMin == 0 {
b.config.VNCPortMin = 5900 b.config.VNCPortMin = 5900
} }
...@@ -70,6 +80,10 @@ func (b *Builder) Prepare(raw interface{}) (err error) { ...@@ -70,6 +80,10 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
// Accumulate any errors // Accumulate any errors
errs := make([]error, 0) errs := make([]error, 0)
if b.config.HTTPPortMin > b.config.HTTPPortMax {
errs = append(errs, errors.New("http_port_min must be less than http_port_max"))
}
if b.config.ISOUrl == "" { if b.config.ISOUrl == "" {
errs = append(errs, errors.New("An iso_url must be specified.")) errs = append(errs, errors.New("An iso_url must be specified."))
} }
......
...@@ -65,6 +65,34 @@ func TestBuilderPrepare_Defaults(t *testing.T) { ...@@ -65,6 +65,34 @@ func TestBuilderPrepare_Defaults(t *testing.T) {
} }
} }
func TestBuilderPrepare_HTTPPort(t *testing.T) {
var b Builder
config := testConfig()
// Bad
config["http_port_min"] = 1000
config["http_port_max"] = 500
err := b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
// Bad
config["http_port_min"] = -500
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
// Good
config["http_port_min"] = 500
config["http_port_max"] = 1000
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_ISOUrl(t *testing.T) { func TestBuilderPrepare_ISOUrl(t *testing.T) {
var b Builder var b Builder
config := testConfig() config := testConfig()
......
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log"
"math/rand"
"net" "net"
"net/http" "net/http"
) )
...@@ -25,27 +27,32 @@ func (s *stepHTTPServer) Run(state map[string]interface{}) multistep.StepAction ...@@ -25,27 +27,32 @@ func (s *stepHTTPServer) Run(state map[string]interface{}) multistep.StepAction
config := state["config"].(*config) config := state["config"].(*config)
ui := state["ui"].(packer.Ui) ui := state["ui"].(packer.Ui)
httpPort := 0 var httpPort uint = 0
if config.HTTPDir != "" { if config.HTTPDir == "" {
httpPort = 8080 state["http_port"] = httpPort
httpAddr := fmt.Sprintf(":%d", httpPort) }
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
// Start the TCP listener // Find an available TCP port for our HTTP server
var httpAddr string
portRange := int(config.HTTPPortMax - config.HTTPPortMin)
for {
var err error var err error
httpPort = uint(rand.Intn(portRange)) + config.HTTPPortMin
httpAddr = fmt.Sprintf(":%d", httpPort)
log.Printf("Trying port: %d", httpPort)
s.l, err = net.Listen("tcp", httpAddr) s.l, err = net.Listen("tcp", httpAddr)
if err != nil { if err == nil {
ui.Error(fmt.Sprintf("Error starting HTTP server: %s", err)) break
return multistep.ActionHalt
} }
// Start the HTTP server and run it in the background
fileServer := http.FileServer(http.Dir(config.HTTPDir))
server := &http.Server{Addr: httpAddr, Handler: fileServer}
go server.Serve(s.l)
} }
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
// Start the HTTP server and run it in the background
fileServer := http.FileServer(http.Dir(config.HTTPDir))
server := &http.Server{Addr: httpAddr, Handler: fileServer}
go server.Serve(s.l)
// Save the address into the state so it can be accessed in the future // Save the address into the state so it can be accessed in the future
state["http_port"] = httpPort state["http_port"] = httpPort
......
...@@ -19,7 +19,7 @@ const KeyLeftShift uint32 = 0xFFE1 ...@@ -19,7 +19,7 @@ const KeyLeftShift uint32 = 0xFFE1
type bootCommandTemplateData struct { type bootCommandTemplateData struct {
HTTPIP string HTTPIP string
HTTPPort int HTTPPort uint
Name string Name string
} }
...@@ -37,7 +37,7 @@ type stepTypeBootCommand struct{} ...@@ -37,7 +37,7 @@ type stepTypeBootCommand struct{}
func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAction { func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config) config := state["config"].(*config)
httpPort := state["http_port"].(int) httpPort := state["http_port"].(uint)
ui := state["ui"].(packer.Ui) ui := state["ui"].(packer.Ui)
vncPort := state["vnc_port"].(uint) vncPort := state["vnc_port"].(uint)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment