diff --git a/builder/vmware/builder.go b/builder/vmware/builder.go index 19b97c667..9bf6637a5 100644 --- a/builder/vmware/builder.go +++ b/builder/vmware/builder.go @@ -26,6 +26,8 @@ type config struct { VMName string `mapstructure:"vm_name"` OutputDir string `mapstructure:"output_directory"` HTTPDir string `mapstructure:"http_directory"` + HTTPPortMin uint `mapstructure:"http_port_min"` + HTTPPortMax uint `mapstructure:"http_port_max"` BootCommand []string `mapstructure:"boot_command"` BootWait time.Duration ShutdownCommand string `mapstructure:"shutdown_command"` @@ -55,6 +57,14 @@ func (b *Builder) Prepare(raw interface{}) (err error) { 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 { b.config.VNCPortMin = 5900 } @@ -70,6 +80,10 @@ func (b *Builder) Prepare(raw interface{}) (err error) { // Accumulate any errors 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 == "" { errs = append(errs, errors.New("An iso_url must be specified.")) } diff --git a/builder/vmware/builder_test.go b/builder/vmware/builder_test.go index 3d01b73cd..a81ce5006 100644 --- a/builder/vmware/builder_test.go +++ b/builder/vmware/builder_test.go @@ -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) { var b Builder config := testConfig() diff --git a/builder/vmware/step_http_server.go b/builder/vmware/step_http_server.go index 2c39d84df..9366ca75e 100644 --- a/builder/vmware/step_http_server.go +++ b/builder/vmware/step_http_server.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" + "log" + "math/rand" "net" "net/http" ) @@ -25,27 +27,32 @@ func (s *stepHTTPServer) Run(state map[string]interface{}) multistep.StepAction config := state["config"].(*config) ui := state["ui"].(packer.Ui) - httpPort := 0 - if config.HTTPDir != "" { - httpPort = 8080 - httpAddr := fmt.Sprintf(":%d", httpPort) - - ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort)) - - // Start the TCP listener - var err error - s.l, err = net.Listen("tcp", httpAddr) - if err != nil { - ui.Error(fmt.Sprintf("Error starting HTTP server: %s", err)) - 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) + var httpPort uint = 0 + if config.HTTPDir == "" { + state["http_port"] = httpPort } + // Find an available TCP port for our HTTP server + var httpAddr string + portRange := int(config.HTTPPortMax - config.HTTPPortMin) + for { + 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) + if err == nil { + break + } + } + + 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 state["http_port"] = httpPort diff --git a/builder/vmware/step_type_boot_command.go b/builder/vmware/step_type_boot_command.go index e340ae4d6..0eb09b561 100644 --- a/builder/vmware/step_type_boot_command.go +++ b/builder/vmware/step_type_boot_command.go @@ -19,7 +19,7 @@ const KeyLeftShift uint32 = 0xFFE1 type bootCommandTemplateData struct { HTTPIP string - HTTPPort int + HTTPPort uint Name string } @@ -37,7 +37,7 @@ type stepTypeBootCommand struct{} func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAction { config := state["config"].(*config) - httpPort := state["http_port"].(int) + httpPort := state["http_port"].(uint) ui := state["ui"].(packer.Ui) vncPort := state["vnc_port"].(uint)