Use a random port instead of idx in testutil

The testutil server uses an atomic incrementer to generate unique port
numbers. This works great until tests are run in parallel, _across
packages_. Because each package starts at the same "offset" idx, they
collide.

One way to overcome this is to run each packages' test in isolation, but
that makes the test suite much longer as it does not maximize
parallelization. Alternatively, instead of having "predictable" ports,
we can let the OS choose a random open port automatically.

This still has a (albeit smaller) race condition in that the OS could
return an open port twice, before the server has a chance to actually
start and occupy said port. In practice, I have not been able to hit
this race condition, so it either doesn't happen or it happens far less
frequently that the existing implementation.

I'm not sure how I feel about the panic, but this is just test code, so
I'm including to say it's okay?
This commit is contained in:
Seth Vargo 2016-12-01 17:24:26 +02:00
parent a6bb339621
commit 9010b0e0f4
No known key found for this signature in database
GPG Key ID: 905A90C2949E8787
1 changed files with 17 additions and 13 deletions

View File

@ -23,15 +23,11 @@ import (
"os"
"os/exec"
"strings"
"sync/atomic"
"github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/go-cleanhttp"
)
// offset is used to atomically increment the port numbers.
var offset uint64
// TestPerformanceConfig configures the performance parameters.
type TestPerformanceConfig struct {
RaftMultiplier uint `json:"raft_multiplier,omitempty"`
@ -82,10 +78,8 @@ 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))
return &TestServerConfig{
NodeName: fmt.Sprintf("node%d", idx),
NodeName: fmt.Sprintf("node%d", randomPort()),
DisableCheckpoint: true,
Performance: &TestPerformanceConfig{
RaftMultiplier: 1,
@ -96,16 +90,26 @@ func defaultServerConfig() *TestServerConfig {
Bind: "127.0.0.1",
Addresses: &TestAddressConfig{},
Ports: &TestPortConfig{
DNS: 20000 + idx,
HTTP: 21000 + idx,
RPC: 22000 + idx,
SerfLan: 23000 + idx,
SerfWan: 24000 + idx,
Server: 25000 + idx,
DNS: randomPort(),
HTTP: randomPort(),
RPC: randomPort(),
SerfLan: randomPort(),
SerfWan: randomPort(),
Server: randomPort(),
},
}
}
// randomPort asks the kernel for a random port to use.
func randomPort() int {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port
}
// TestService is used to serialize a service definition.
type TestService struct {
ID string `json:",omitempty"`