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

View File

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