Factor out common port-finding code

Fixes bug where we would infinite-loop trying to find an HTTP server
port when all ports were unavailable (e.g. range [80,80] and no root
permission)

Also fixes bug where SSH port forwarding would try ports up to but not
including HostPortMax.

Also add clarification in error messages for which port range to expand
This commit is contained in:
Cheng Sun 2014-12-10 14:18:18 +00:00
parent 602255e9c1
commit b433754c83
3 changed files with 35 additions and 45 deletions

View File

@ -0,0 +1,25 @@
package xenserver
import (
"fmt"
"log"
"net"
)
// FindPort finds and starts listening on a port in the range [portMin, portMax]
// returns the listener and the port number on success, or nil, 0 on failure
func FindPort(portMin uint, portMax uint) (net.Listener, uint) {
log.Printf("Looking for an available port between %d and %d", portMin, portMax)
for port := portMin; port <= portMax; port++ {
log.Printf("Trying port: %d", port)
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err == nil {
return l, port
} else {
log.Printf("Port %d unavailable: %s", port, err.Error())
}
}
return nil, 0
}

View File

@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log"
"net"
) )
type stepForwardPortOverSSH struct { type stepForwardPortOverSSH struct {
@ -25,32 +23,15 @@ func (self *stepForwardPortOverSSH) Run(state multistep.StateBag) multistep.Step
// Find a free local port: // Find a free local port:
log.Printf("Looking for an available port between %d and %d", l, sshHostPort := FindPort(self.HostPortMin, self.HostPortMax)
self.HostPortMin,
self.HostPortMax)
var sshHostPort uint if l == nil || sshHostPort == 0 {
var foundPort bool ui.Error("Error: unable to find free host port. Try providing a larger range [host_port_min, host_port_max]")
foundPort = false
for i := self.HostPortMin; i < self.HostPortMax; i++ {
sshHostPort = i
log.Printf("Trying port: %d", sshHostPort)
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", sshHostPort))
if err == nil {
l.Close()
foundPort = true
break
}
}
if !foundPort {
ui.Error("Error: unable to find free host port. Try providing a larger range")
return multistep.ActionHalt return multistep.ActionHalt
} }
l.Close()
ui.Say(fmt.Sprintf("Creating a local port forward over SSH on local port %d", sshHostPort)) ui.Say(fmt.Sprintf("Creating a local port forward over SSH on local port %d", sshHostPort))
remotePort, _ := self.RemotePort(state) remotePort, _ := self.RemotePort(state)

View File

@ -6,8 +6,6 @@ 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"
) )
@ -36,32 +34,18 @@ func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue return multistep.ActionContinue
} }
// Find an available TCP port for our HTTP server s.l, httpPort = FindPort(config.HTTPPortMin, config.HTTPPortMax)
var httpAddr string
portRange := int(config.HTTPPortMax - config.HTTPPortMin)
for {
var err error
var offset uint = 0
if portRange > 0 { if s.l == nil || httpPort == 0 {
// Intn will panic if portRange == 0, so we do a check. ui.Error("Error: unable to find free HTTP server port. Try providing a larger range [http_port_min, http_port_max]")
offset = uint(rand.Intn(portRange)) return multistep.ActionHalt
}
httpPort = offset + 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)) ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
// Start the HTTP server and run it in the background // Start the HTTP server and run it in the background
fileServer := http.FileServer(http.Dir(config.HTTPDir)) fileServer := http.FileServer(http.Dir(config.HTTPDir))
server := &http.Server{Addr: httpAddr, Handler: fileServer} server := &http.Server{Addr: fmt.Sprintf(":%d", httpPort), Handler: fileServer}
go server.Serve(s.l) 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