move to consul freeport implementation

This commit is contained in:
Alex Dadgar
2017-10-23 16:51:40 -07:00
parent 9b13904159
commit 8accabcd87
12 changed files with 169 additions and 77 deletions

View File

@@ -221,7 +221,6 @@ test: ## Run the Nomad test suite and/or the Nomad UI test suite
fi
.PHONY: test-nomad
test-nomad: LOCAL_PACKAGES = $(shell go list ./... | grep -v '/vendor/')
test-nomad: ## Run Nomad test suites
@echo "==> Running Nomad test suites:"
@NOMAD_TEST_RKT=1 \
@@ -229,7 +228,7 @@ test-nomad: ## Run Nomad test suites
-cover \
-timeout=900s \
-tags="nomad_test $(if $(HAS_LXC),lxc)" \
$(LOCAL_PACKAGES)
./...
.PHONY: clean
clean: GOPATH=$(shell go env GOPATH)

View File

@@ -11,12 +11,12 @@ import (
"testing"
"time"
"github.com/hashicorp/consul/lib/freeport"
memdb "github.com/hashicorp/go-memdb"
"github.com/hashicorp/nomad/client/config"
"github.com/hashicorp/nomad/client/fingerprint"
"github.com/hashicorp/nomad/command/agent/consul"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/helper/freeport"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad"
"github.com/hashicorp/nomad/nomad/mock"
@@ -75,12 +75,13 @@ func testServer(t *testing.T, cb func(*nomad.Config)) (*nomad.Server, string) {
}
for i := 10; i >= 0; i-- {
ports := freeport.GetT(t, 2)
config.RPCAddr = &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: freeport.Get(t),
Port: ports[0],
}
config.NodeName = fmt.Sprintf("Node %d", config.RPCAddr.Port)
config.SerfConfig.MemberlistConfig.BindPort = freeport.Get(t)
config.SerfConfig.MemberlistConfig.BindPort = ports[1]
// Create server
server, err := nomad.NewServer(config, catalog, logger)

View File

@@ -16,13 +16,13 @@ import (
"time"
docker "github.com/fsouza/go-dockerclient"
"github.com/hashicorp/consul/lib/freeport"
sockaddr "github.com/hashicorp/go-sockaddr"
"github.com/hashicorp/nomad/client/allocdir"
"github.com/hashicorp/nomad/client/config"
"github.com/hashicorp/nomad/client/driver/env"
cstructs "github.com/hashicorp/nomad/client/structs"
"github.com/hashicorp/nomad/client/testutil"
"github.com/hashicorp/nomad/helper/freeport"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
@@ -46,8 +46,9 @@ func dockerIsRemote(t *testing.T) bool {
// Returns a task with a reserved and dynamic port. The ports are returned
// respectively.
func dockerTask(t *testing.T) (*structs.Task, int, int) {
docker_reserved := freeport.Get(t)
docker_dynamic := freeport.Get(t)
ports := freeport.GetT(t, 2)
docker_reserved := ports[0]
docker_dynamic := ports[1]
return &structs.Task{
Name: "redis-demo",
Driver: "docker",

View File

@@ -16,9 +16,9 @@ import (
"github.com/mitchellh/go-testing-interface"
metrics "github.com/armon/go-metrics"
"github.com/hashicorp/consul/lib/freeport"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/client/fingerprint"
"github.com/hashicorp/nomad/helper/freeport"
"github.com/hashicorp/nomad/nomad"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
@@ -259,9 +259,10 @@ func (a *TestAgent) Client() *api.Client {
// Instead of relying on one set of ports to be sufficient we retry
// starting the agent with different ports on port conflict.
func (a *TestAgent) pickRandomPorts(c *Config) {
c.Ports.HTTP = freeport.Get(a.T)
c.Ports.RPC = freeport.Get(a.T)
c.Ports.Serf = freeport.Get(a.T)
ports := freeport.GetT(a.T, 3)
c.Ports.HTTP = ports[0]
c.Ports.RPC = ports[1]
c.Ports.Serf = ports[2]
if err := c.normalizeAddrs(); err != nil {
a.T.Fatalf("error normalizing config: %v", err)

View File

@@ -1,34 +0,0 @@
package freeport
import (
"fmt"
"net"
"github.com/mitchellh/go-testing-interface"
)
func Port() (int, error) {
ln, err := net.Listen("tcp", ":0")
if err != nil {
return -1, err
}
defer ln.Close()
addr, ok := ln.Addr().(*net.TCPAddr)
if !ok {
return -1, fmt.Errorf("unexpected address type: %T", ln.Addr())
}
return addr.Port, nil
}
func Get(t testing.T) int {
t.Helper()
p, err := Port()
if err != nil {
t.Fatalf("failed to get free port: %v", err)
}
return p
}

View File

@@ -1,10 +0,0 @@
package freeport
import "testing"
func TestFreePort_GetFreePort(t *testing.T) {
p := Get(t)
if p <= 0 {
t.Fatalf("bad port: %d", p)
}
}

View File

@@ -6,9 +6,9 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/lib/freeport"
"github.com/hashicorp/net-rpc-msgpackrpc"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/helper/freeport"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
@@ -129,7 +129,7 @@ func TestOperator_RaftRemovePeerByAddress(t *testing.T) {
// Try to remove a peer that's not there.
arg := structs.RaftPeerByAddressRequest{
Address: raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", freeport.Get(t))),
Address: raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", freeport.GetT(t, 1)[0])),
}
arg.Region = s1.config.Region
var reply struct{}
@@ -189,7 +189,7 @@ func TestOperator_RaftRemovePeerByAddress_ACL(t *testing.T) {
invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", mock.NodePolicy(acl.PolicyWrite))
arg := structs.RaftPeerByAddressRequest{
Address: raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", freeport.Get(t))),
Address: raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", freeport.GetT(t, 1)[0])),
}
arg.Region = s1.config.Region

View File

@@ -12,8 +12,8 @@ import (
"testing"
"time"
"github.com/hashicorp/consul/lib/freeport"
"github.com/hashicorp/nomad/command/agent/consul"
"github.com/hashicorp/nomad/helper/freeport"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
@@ -96,11 +96,12 @@ func testServer(t *testing.T, cb func(*Config)) *Server {
for i := 10; i >= 0; i-- {
// Get random ports
ports := freeport.GetT(t, 2)
config.RPCAddr = &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: freeport.Get(t),
Port: ports[0],
}
config.SerfConfig.MemberlistConfig.BindPort = freeport.Get(t)
config.SerfConfig.MemberlistConfig.BindPort = ports[1]
// Create server
server, err := NewServer(config, catalog, logger)

View File

@@ -20,16 +20,13 @@ import (
"net/http"
"os"
"os/exec"
"sync/atomic"
"github.com/hashicorp/consul/lib/freeport"
cleanhttp "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/nomad/helper/discover"
testing "github.com/mitchellh/go-testing-interface"
)
// offset is used to atomically increment the port numbers.
var offset uint64
// TestServerConfig is the main server configuration struct.
type TestServerConfig struct {
NodeName string `json:"name,omitempty"`
@@ -88,11 +85,10 @@ type ServerConfigCallback func(c *TestServerConfig)
// defaultServerConfig returns a new TestServerConfig struct
// with all of the listen ports incremented by one.
func defaultServerConfig() *TestServerConfig {
idx := int(atomic.AddUint64(&offset, 1))
func defaultServerConfig(t testing.T) *TestServerConfig {
ports := freeport.GetT(t, 3)
return &TestServerConfig{
NodeName: fmt.Sprintf("node%d", idx),
NodeName: fmt.Sprintf("node-%d", ports[0]),
DisableCheckpoint: true,
LogLevel: "DEBUG",
// Advertise can't be localhost
@@ -102,9 +98,9 @@ func defaultServerConfig() *TestServerConfig {
Serf: "169.254.42.42",
},
Ports: &PortsConfig{
HTTP: 20000 + idx,
RPC: 21000 + idx,
Serf: 22000 + idx,
HTTP: ports[0],
RPC: ports[1],
Serf: ports[2],
},
Server: &ServerConfig{
Enabled: true,
@@ -161,7 +157,7 @@ func NewTestServer(t testing.T, cb ServerConfigCallback) *TestServer {
}
defer configFile.Close()
nomadConfig := defaultServerConfig()
nomadConfig := defaultServerConfig(t)
nomadConfig.DataDir = dataDir
if cb != nil {

View File

@@ -7,7 +7,7 @@ import (
"os/exec"
"time"
"github.com/hashicorp/nomad/helper/freeport"
"github.com/hashicorp/consul/lib/freeport"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/structs/config"
vapi "github.com/hashicorp/vault/api"
@@ -37,7 +37,7 @@ type TestVault struct {
// NewTestVault returns a new TestVault instance that has yet to be started
func NewTestVault(t testing.T) *TestVault {
for i := 10; i >= 0; i-- {
port := freeport.Get(t)
port := freeport.GetT(t, 1)[0]
token := uuid.Generate()
bind := fmt.Sprintf("-dev-listen-address=127.0.0.1:%d", port)
http := fmt.Sprintf("http://127.0.0.1:%d", port)
@@ -118,7 +118,7 @@ func NewTestVault(t testing.T) *TestVault {
// Start must be called and it is the callers responsibility to deal with any
// port conflicts that may occur and retry accordingly.
func NewTestVaultDelayed(t testing.T) *TestVault {
port := freeport.Get(t)
port := freeport.GetT(t, 1)[0]
token := uuid.Generate()
bind := fmt.Sprintf("-dev-listen-address=127.0.0.1:%d", port)
http := fmt.Sprintf("http://127.0.0.1:%d", port)

View File

@@ -0,0 +1,131 @@
// Package freeport provides a helper for allocating free ports across multiple
// processes on the same machine.
package freeport
import (
"fmt"
"math/rand"
"net"
"sync"
"time"
"github.com/mitchellh/go-testing-interface"
)
const (
// blockSize is the size of the allocated port block. ports are given out
// consecutively from that block with roll-over for the lifetime of the
// application/test run.
blockSize = 500
// maxBlocks is the number of available port blocks.
// lowPort + maxBlocks * blockSize must be less than 65535.
maxBlocks = 30
// lowPort is the lowest port number that should be used.
lowPort = 10000
// attempts is how often we try to allocate a port block
// before giving up.
attempts = 10
)
var (
// firstPort is the first port of the allocated block.
firstPort int
// lockLn is the system-wide mutex for the port block.
lockLn net.Listener
// mu guards nextPort
mu sync.Mutex
// port is the last allocated port.
port int
)
func init() {
if lowPort+maxBlocks*blockSize > 65535 {
panic("freeport: block size too big or too many blocks requested")
}
rand.Seed(time.Now().UnixNano())
firstPort, lockLn = alloc()
}
// alloc reserves a port block for exclusive use for the lifetime of the
// application. lockLn serves as a system-wide mutex for the port block and is
// implemented as a TCP listener which is bound to the firstPort and which will
// be automatically released when the application terminates.
func alloc() (int, net.Listener) {
for i := 0; i < attempts; i++ {
block := int(rand.Int31n(int32(maxBlocks)))
firstPort := lowPort + block*blockSize
ln, err := net.ListenTCP("tcp", tcpAddr("127.0.0.1", firstPort))
if err != nil {
continue
}
// log.Printf("[DEBUG] freeport: allocated port block %d (%d-%d)", block, firstPort, firstPort+blockSize-1)
return firstPort, ln
}
panic("freeport: cannot allocate port block")
}
func tcpAddr(ip string, port int) *net.TCPAddr {
return &net.TCPAddr{IP: net.ParseIP(ip), Port: port}
}
// Get wraps the Free function and panics on any failure retrieving ports.
func Get(n int) (ports []int) {
ports, err := Free(n)
if err != nil {
panic(err)
}
return ports
}
// GetT is suitable for use when retrieving unused ports in tests. If there is
// an error retrieving free ports, the test will be failed.
func GetT(t testing.T, n int) (ports []int) {
ports, err := Free(n)
if err != nil {
t.Fatalf("Failed retrieving free port: %v", err)
}
return ports
}
// Free returns a list of free ports from the allocated port block. It is safe
// to call this method concurrently. Ports have been tested to be available on
// 127.0.0.1 TCP but there is no guarantee that they will remain free in the
// future.
func Free(n int) (ports []int, err error) {
mu.Lock()
defer mu.Unlock()
if n > blockSize-1 {
return nil, fmt.Errorf("freeport: block size too small")
}
for len(ports) < n {
port++
// roll-over the port
if port < firstPort+1 || port >= firstPort+blockSize {
port = firstPort + 1
}
// if the port is in use then skip it
ln, err := net.ListenTCP("tcp", tcpAddr("127.0.0.1", port))
if err != nil {
// log.Println("[DEBUG] freeport: port already in use: ", port)
continue
}
ln.Close()
ports = append(ports, port)
}
// log.Println("[DEBUG] freeport: free ports:", ports)
return ports, nil
}

6
vendor/vendor.json vendored
View File

@@ -760,6 +760,12 @@
"revision": "51ea240df8476e02215d53fbfad5838bf0d44d21",
"revisionTime": "2017-10-16T16:22:40Z"
},
{
"checksumSHA1": "XUc/5Wg49jT0dGHRv7FhzDosj2Q=",
"path": "github.com/hashicorp/consul/lib/freeport",
"revision": "be18f97531edb0b75a91e61c7e26a66224a46468",
"revisionTime": "2017-10-23T23:34:27Z"
},
{
"checksumSHA1": "5XjgqE4UIfwXvkq5VssGNc7uPhQ=",
"path": "github.com/hashicorp/consul/test/porter",