ci: swap freeport for portal in packages (#15661)
This commit is contained in:
parent
9771281ecd
commit
7214e21402
|
@ -0,0 +1,20 @@
|
||||||
|
package ci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/shoenig/test/portal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fatalTester struct{}
|
||||||
|
|
||||||
|
func (t *fatalTester) Fatalf(msg string, args ...any) {
|
||||||
|
panic(fmt.Sprintf(msg, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortAllocator is used to acquire unused ports for testing real network
|
||||||
|
// listeners.
|
||||||
|
var PortAllocator = portal.New(
|
||||||
|
new(fatalTester),
|
||||||
|
portal.WithAddress("127.0.0.1"),
|
||||||
|
)
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/testlog"
|
"github.com/hashicorp/nomad/helper/testlog"
|
||||||
"github.com/hashicorp/nomad/nomad/mock"
|
"github.com/hashicorp/nomad/nomad/mock"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
|
@ -410,8 +409,7 @@ func TestChecker_Do_TCP(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ports := freeport.MustTake(3)
|
ports := ci.PortAllocator.Grab(3)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
client "github.com/hashicorp/nomad/client/config"
|
client "github.com/hashicorp/nomad/client/config"
|
||||||
"github.com/hashicorp/nomad/client/testutil"
|
"github.com/hashicorp/nomad/client/testutil"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/pointer"
|
"github.com/hashicorp/nomad/helper/pointer"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
"github.com/hashicorp/nomad/nomad/structs/config"
|
"github.com/hashicorp/nomad/nomad/structs/config"
|
||||||
|
@ -660,8 +659,7 @@ func TestConfig_Listener(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Works with valid inputs
|
// Works with valid inputs
|
||||||
ports := freeport.MustTake(2)
|
ports := ci.PortAllocator.Grab(2)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
ln, err := config.Listener("tcp", "127.0.0.1", ports[0])
|
ln, err := config.Listener("tcp", "127.0.0.1", ports[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -15,10 +15,10 @@ import (
|
||||||
metrics "github.com/armon/go-metrics"
|
metrics "github.com/armon/go-metrics"
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/nomad/api"
|
"github.com/hashicorp/nomad/api"
|
||||||
|
"github.com/hashicorp/nomad/ci"
|
||||||
client "github.com/hashicorp/nomad/client/config"
|
client "github.com/hashicorp/nomad/client/config"
|
||||||
"github.com/hashicorp/nomad/client/fingerprint"
|
"github.com/hashicorp/nomad/client/fingerprint"
|
||||||
"github.com/hashicorp/nomad/helper"
|
"github.com/hashicorp/nomad/helper"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/testlog"
|
"github.com/hashicorp/nomad/helper/testlog"
|
||||||
"github.com/hashicorp/nomad/nomad"
|
"github.com/hashicorp/nomad/nomad"
|
||||||
"github.com/hashicorp/nomad/nomad/mock"
|
"github.com/hashicorp/nomad/nomad/mock"
|
||||||
|
@ -269,8 +269,6 @@ func (a *TestAgent) Shutdown() {
|
||||||
}
|
}
|
||||||
a.shutdown = true
|
a.shutdown = true
|
||||||
|
|
||||||
defer freeport.Return(a.ports)
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if a.DataDir != "" {
|
if a.DataDir != "" {
|
||||||
_ = os.RemoveAll(a.DataDir)
|
_ = os.RemoveAll(a.DataDir)
|
||||||
|
@ -332,7 +330,7 @@ func (a *TestAgent) Client() *api.Client {
|
||||||
// Instead of relying on one set of ports to be sufficient we retry
|
// Instead of relying on one set of ports to be sufficient we retry
|
||||||
// starting the agent with different ports on port conflict.
|
// starting the agent with different ports on port conflict.
|
||||||
func (a *TestAgent) pickRandomPorts(c *Config) {
|
func (a *TestAgent) pickRandomPorts(c *Config) {
|
||||||
ports := freeport.MustTake(3)
|
ports := ci.PortAllocator.Grab(3)
|
||||||
a.ports = append(a.ports, ports...)
|
a.ports = append(a.ports, ports...)
|
||||||
|
|
||||||
c.Ports.HTTP = ports[0]
|
c.Ports.HTTP = ports[0]
|
||||||
|
|
|
@ -30,8 +30,7 @@ func TestHTTP_Variables(t *testing.T) {
|
||||||
|
|
||||||
httpTest(t, cb, func(s *TestAgent) {
|
httpTest(t, cb, func(s *TestAgent) {
|
||||||
// These tests are run against the same running server in order to reduce
|
// These tests are run against the same running server in order to reduce
|
||||||
// the costs of server startup and allow as much parallelization as possible
|
// the costs of server startup and allow as much parallelization as possible.
|
||||||
// given the port reuse issue that we have seen with the current freeport
|
|
||||||
t.Run("error_badverb_list", func(t *testing.T) {
|
t.Run("error_badverb_list", func(t *testing.T) {
|
||||||
req, err := http.NewRequest("LOLWUT", "/v1/vars", nil)
|
req, err := http.NewRequest("LOLWUT", "/v1/vars", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -14,12 +14,11 @@ Each unit test should meet a few criteria:
|
||||||
- Undo any changes to the environment
|
- Undo any changes to the environment
|
||||||
- Set environment variables must be unset (use `t.Setenv`)
|
- Set environment variables must be unset (use `t.Setenv`)
|
||||||
- Scratch files/dirs must be removed (use `t.TempDir`)
|
- Scratch files/dirs must be removed (use `t.TempDir`)
|
||||||
- Consumed ports must be freed (e.g. `TestServer.Cleanup`, `freeport.Return`)
|
|
||||||
|
|
||||||
- Able to run in parallel
|
- Able to run in parallel
|
||||||
- All package level `Test*` functions should start with `ci.Parallel`
|
- All package level `Test*` functions should start with `ci.Parallel`
|
||||||
- Always use dynamic scratch dirs, files
|
- Always use dynamic scratch dirs, files
|
||||||
- Always get ports from helpers (`TestServer`, `TestClient`, `TestAgent`, `freeport.Get`)
|
- Always get ports via `ci.PortAllocator.Grab()`
|
||||||
|
|
||||||
- Log control
|
- Log control
|
||||||
- Logging must go through the `testing.T` (use `helper/testlog.HCLogger`)
|
- Logging must go through the `testing.T` (use `helper/testlog.HCLogger`)
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/client/testutil"
|
"github.com/hashicorp/nomad/client/testutil"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/pointer"
|
"github.com/hashicorp/nomad/helper/pointer"
|
||||||
tu "github.com/hashicorp/nomad/testutil"
|
tu "github.com/hashicorp/nomad/testutil"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -55,8 +54,7 @@ func TestDockerDriver_PluginConfig_PidsLimit(t *testing.T) {
|
||||||
driver := dh.Impl().(*Driver)
|
driver := dh.Impl().(*Driver)
|
||||||
driver.config.PidsLimit = 5
|
driver.config.PidsLimit = 5
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
cfg.PidsLimit = 7
|
cfg.PidsLimit = 7
|
||||||
|
@ -75,8 +73,7 @@ func TestDockerDriver_PidsLimit(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.PidsLimit = 1
|
cfg.PidsLimit = 1
|
||||||
cfg.Command = "/bin/sh"
|
cfg.Command = "/bin/sh"
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/client/taskenv"
|
"github.com/hashicorp/nomad/client/taskenv"
|
||||||
"github.com/hashicorp/nomad/client/testutil"
|
"github.com/hashicorp/nomad/client/testutil"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/pluginutils/hclspecutils"
|
"github.com/hashicorp/nomad/helper/pluginutils/hclspecutils"
|
||||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||||
"github.com/hashicorp/nomad/helper/pluginutils/loader"
|
"github.com/hashicorp/nomad/helper/pluginutils/loader"
|
||||||
|
@ -73,10 +72,9 @@ var (
|
||||||
busyboxLongRunningCmd = []string{"nc", "-l", "-p", "3000", "127.0.0.1"}
|
busyboxLongRunningCmd = []string{"nc", "-l", "-p", "3000", "127.0.0.1"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Returns a task with a reserved and dynamic port. The ports are returned
|
// Returns a task with a reserved and dynamic port.
|
||||||
// respectively, and should be reclaimed with freeport.Return at the end of a test.
|
|
||||||
func dockerTask(t *testing.T) (*drivers.TaskConfig, *TaskConfig, []int) {
|
func dockerTask(t *testing.T) (*drivers.TaskConfig, *TaskConfig, []int) {
|
||||||
ports := freeport.MustTake(2)
|
ports := ci.PortAllocator.Grab(2)
|
||||||
dockerReserved := ports[0]
|
dockerReserved := ports[0]
|
||||||
dockerDynamic := ports[1]
|
dockerDynamic := ports[1]
|
||||||
|
|
||||||
|
@ -640,14 +638,11 @@ func TestDockerDriver_StartN(t *testing.T) {
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
task1, _, ports1 := dockerTask(t)
|
task1, _, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports1)
|
|
||||||
|
|
||||||
task2, _, ports2 := dockerTask(t)
|
task2, _, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports2)
|
|
||||||
|
|
||||||
task3, _, ports3 := dockerTask(t)
|
task3, _, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports3)
|
|
||||||
|
|
||||||
taskList := []*drivers.TaskConfig{task1, task2, task3}
|
taskList := []*drivers.TaskConfig{task1, task2, task3}
|
||||||
|
|
||||||
|
@ -694,22 +689,22 @@ func TestDockerDriver_StartNVersions(t *testing.T) {
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
task1, cfg1, ports1 := dockerTask(t)
|
task1, cfg1, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports1)
|
|
||||||
tcfg1 := newTaskConfig("", []string{"echo", "hello"})
|
tcfg1 := newTaskConfig("", []string{"echo", "hello"})
|
||||||
cfg1.Image = tcfg1.Image
|
cfg1.Image = tcfg1.Image
|
||||||
cfg1.LoadImage = tcfg1.LoadImage
|
cfg1.LoadImage = tcfg1.LoadImage
|
||||||
require.NoError(task1.EncodeConcreteDriverConfig(cfg1))
|
require.NoError(task1.EncodeConcreteDriverConfig(cfg1))
|
||||||
|
|
||||||
task2, cfg2, ports2 := dockerTask(t)
|
task2, cfg2, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports2)
|
|
||||||
tcfg2 := newTaskConfig("musl", []string{"echo", "hello"})
|
tcfg2 := newTaskConfig("musl", []string{"echo", "hello"})
|
||||||
cfg2.Image = tcfg2.Image
|
cfg2.Image = tcfg2.Image
|
||||||
cfg2.LoadImage = tcfg2.LoadImage
|
cfg2.LoadImage = tcfg2.LoadImage
|
||||||
require.NoError(task2.EncodeConcreteDriverConfig(cfg2))
|
require.NoError(task2.EncodeConcreteDriverConfig(cfg2))
|
||||||
|
|
||||||
task3, cfg3, ports3 := dockerTask(t)
|
task3, cfg3, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports3)
|
|
||||||
tcfg3 := newTaskConfig("glibc", []string{"echo", "hello"})
|
tcfg3 := newTaskConfig("glibc", []string{"echo", "hello"})
|
||||||
cfg3.Image = tcfg3.Image
|
cfg3.Image = tcfg3.Image
|
||||||
cfg3.LoadImage = tcfg3.LoadImage
|
cfg3.LoadImage = tcfg3.LoadImage
|
||||||
|
@ -759,8 +754,7 @@ func TestDockerDriver_Labels(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.Labels = map[string]string{
|
cfg.Labels = map[string]string{
|
||||||
"label1": "value1",
|
"label1": "value1",
|
||||||
|
@ -788,8 +782,7 @@ func TestDockerDriver_ExtraLabels(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -823,8 +816,7 @@ func TestDockerDriver_LoggingConfiguration(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -850,9 +842,9 @@ func TestDockerDriver_HealthchecksDisable(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
cfg.Healthchecks.Disable = true
|
cfg.Healthchecks.Disable = true
|
||||||
defer freeport.Return(ports)
|
|
||||||
must.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
must.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
client, d, handle, cleanup := dockerSetup(t, task, nil)
|
client, d, handle, cleanup := dockerSetup(t, task, nil)
|
||||||
|
@ -870,8 +862,7 @@ func TestDockerDriver_ForcePull(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.ForcePull = true
|
cfg.ForcePull = true
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -894,8 +885,8 @@ func TestDockerDriver_ForcePull_RepoDigest(t *testing.T) {
|
||||||
}
|
}
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.LoadImage = ""
|
cfg.LoadImage = ""
|
||||||
cfg.Image = "library/busybox@sha256:58ac43b2cc92c687a32c8be6278e50a063579655fe3090125dcb2af0ff9e1a64"
|
cfg.Image = "library/busybox@sha256:58ac43b2cc92c687a32c8be6278e50a063579655fe3090125dcb2af0ff9e1a64"
|
||||||
localDigest := "sha256:8ac48589692a53a9b8c2d1ceaa6b402665aa7fe667ba51ccc03002300856d8c7"
|
localDigest := "sha256:8ac48589692a53a9b8c2d1ceaa6b402665aa7fe667ba51ccc03002300856d8c7"
|
||||||
|
@ -920,8 +911,8 @@ func TestDockerDriver_SecurityOptUnconfined(t *testing.T) {
|
||||||
}
|
}
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.SecurityOpt = []string{"seccomp=unconfined"}
|
cfg.SecurityOpt = []string{"seccomp=unconfined"}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -944,8 +935,8 @@ func TestDockerDriver_SecurityOptFromFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.SecurityOpt = []string{"seccomp=./test-resources/docker/seccomp.json"}
|
cfg.SecurityOpt = []string{"seccomp=./test-resources/docker/seccomp.json"}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -963,8 +954,8 @@ func TestDockerDriver_Runtime(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Runtime = "runc"
|
cfg.Runtime = "runc"
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -983,8 +974,8 @@ func TestDockerDriver_Runtime(t *testing.T) {
|
||||||
func TestDockerDriver_CreateContainerConfig(t *testing.T) {
|
func TestDockerDriver_CreateContainerConfig(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
opt := map[string]string{"size": "120G"}
|
opt := map[string]string{"size": "120G"}
|
||||||
|
|
||||||
cfg.StorageOpt = opt
|
cfg.StorageOpt = opt
|
||||||
|
@ -1007,8 +998,8 @@ func TestDockerDriver_CreateContainerConfig(t *testing.T) {
|
||||||
func TestDockerDriver_CreateContainerConfig_RuntimeConflict(t *testing.T) {
|
func TestDockerDriver_CreateContainerConfig_RuntimeConflict(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
task.DeviceEnv["NVIDIA_VISIBLE_DEVICES"] = "GPU_UUID_1"
|
task.DeviceEnv["NVIDIA_VISIBLE_DEVICES"] = "GPU_UUID_1"
|
||||||
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1046,8 +1037,8 @@ func TestDockerDriver_CreateContainerConfig_ChecksAllowRuntimes(t *testing.T) {
|
||||||
"custom",
|
"custom",
|
||||||
}
|
}
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
for _, runtime := range allowRuntime {
|
for _, runtime := range allowRuntime {
|
||||||
|
@ -1071,8 +1062,8 @@ func TestDockerDriver_CreateContainerConfig_ChecksAllowRuntimes(t *testing.T) {
|
||||||
func TestDockerDriver_CreateContainerConfig_User(t *testing.T) {
|
func TestDockerDriver_CreateContainerConfig_User(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
task.User = "random-user-1"
|
task.User = "random-user-1"
|
||||||
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1089,8 +1080,8 @@ func TestDockerDriver_CreateContainerConfig_User(t *testing.T) {
|
||||||
func TestDockerDriver_CreateContainerConfig_Labels(t *testing.T) {
|
func TestDockerDriver_CreateContainerConfig_Labels(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
task.AllocID = uuid.Generate()
|
task.AllocID = uuid.Generate()
|
||||||
task.JobName = "redis-demo-job"
|
task.JobName = "redis-demo-job"
|
||||||
|
|
||||||
|
@ -1180,8 +1171,7 @@ func TestDockerDriver_CreateContainerConfig_Logging(t *testing.T) {
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.Logging = c.loggingConfig
|
cfg.Logging = c.loggingConfig
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1202,8 +1192,7 @@ func TestDockerDriver_CreateContainerConfig_Logging(t *testing.T) {
|
||||||
func TestDockerDriver_CreateContainerConfig_Mounts(t *testing.T) {
|
func TestDockerDriver_CreateContainerConfig_Mounts(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.Mounts = []DockerMount{
|
cfg.Mounts = []DockerMount{
|
||||||
{
|
{
|
||||||
|
@ -1317,8 +1306,7 @@ func TestDockerDriver_CreateContainerConfigWithRuntimes(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
dh := dockerDriverHarness(t, map[string]interface{}{
|
dh := dockerDriverHarness(t, map[string]interface{}{
|
||||||
"allow_runtimes": []string{"runc", "nvidia", "nvidia-runtime-modified-name"},
|
"allow_runtimes": []string{"runc", "nvidia", "nvidia-runtime-modified-name"},
|
||||||
|
@ -1408,8 +1396,7 @@ func TestDockerDriver_Capabilities(t *testing.T) {
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.Name, func(t *testing.T) {
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
client := newTestDockerClient(t)
|
client := newTestDockerClient(t)
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
if len(tc.CapAdd) > 0 {
|
if len(tc.CapAdd) > 0 {
|
||||||
cfg.CapAdd = tc.CapAdd
|
cfg.CapAdd = tc.CapAdd
|
||||||
|
@ -1486,8 +1473,8 @@ func TestDockerDriver_DNS(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
task.DNS = c.cfg
|
task.DNS = c.cfg
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -1509,8 +1496,7 @@ func TestDockerDriver_Init(t *testing.T) {
|
||||||
t.Skip("Windows does not support init.")
|
t.Skip("Windows does not support init.")
|
||||||
}
|
}
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.Init = true
|
cfg.Init = true
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1550,8 +1536,7 @@ func TestDockerDriver_CPUSetCPUs(t *testing.T) {
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.Name, func(t *testing.T) {
|
t.Run(testCase.Name, func(t *testing.T) {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.CPUSetCPUs = testCase.CPUSetCPUs
|
cfg.CPUSetCPUs = testCase.CPUSetCPUs
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1575,8 +1560,7 @@ func TestDockerDriver_MemoryHardLimit(t *testing.T) {
|
||||||
t.Skip("Windows does not support MemoryReservation")
|
t.Skip("Windows does not support MemoryReservation")
|
||||||
}
|
}
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
cfg.MemoryHardLimit = 300
|
cfg.MemoryHardLimit = 300
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1599,8 +1583,8 @@ func TestDockerDriver_MACAddress(t *testing.T) {
|
||||||
t.Skip("Windows docker does not support setting MacAddress")
|
t.Skip("Windows docker does not support setting MacAddress")
|
||||||
}
|
}
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.MacAddress = "00:16:3e:00:00:00"
|
cfg.MacAddress = "00:16:3e:00:00:00"
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -1618,8 +1602,8 @@ func TestDockerWorkDir(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.WorkDir = "/some/path"
|
cfg.WorkDir = "/some/path"
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -1646,7 +1630,6 @@ func TestDockerDriver_PortsNoMap(t *testing.T) {
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, _, ports := dockerTask(t)
|
task, _, ports := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
res := ports[0]
|
res := ports[0]
|
||||||
dyn := ports[1]
|
dyn := ports[1]
|
||||||
|
|
||||||
|
@ -1688,7 +1671,6 @@ func TestDockerDriver_PortsMapping(t *testing.T) {
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, ports := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
res := ports[0]
|
res := ports[0]
|
||||||
dyn := ports[1]
|
dyn := ports[1]
|
||||||
cfg.PortMap = map[string]int{
|
cfg.PortMap = map[string]int{
|
||||||
|
@ -1737,7 +1719,6 @@ func TestDockerDriver_CreateContainerConfig_Ports(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, ports := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
hostIP := "127.0.0.1"
|
hostIP := "127.0.0.1"
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
hostIP = ""
|
hostIP = ""
|
||||||
|
@ -1780,7 +1761,6 @@ func TestDockerDriver_CreateContainerConfig_PortsMapping(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, ports := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
res := ports[0]
|
res := ports[0]
|
||||||
dyn := ports[1]
|
dyn := ports[1]
|
||||||
cfg.PortMap = map[string]int{
|
cfg.PortMap = map[string]int{
|
||||||
|
@ -1816,8 +1796,7 @@ func TestDockerDriver_CleanupContainer(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Command = "echo"
|
cfg.Command = "echo"
|
||||||
cfg.Args = []string{"hello"}
|
cfg.Args = []string{"hello"}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1854,8 +1833,7 @@ func TestDockerDriver_EnableImageGC(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Command = "echo"
|
cfg.Command = "echo"
|
||||||
cfg.Args = []string{"hello"}
|
cfg.Args = []string{"hello"}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1921,8 +1899,7 @@ func TestDockerDriver_DisableImageGC(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Command = "echo"
|
cfg.Command = "echo"
|
||||||
cfg.Args = []string{"hello"}
|
cfg.Args = []string{"hello"}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -1984,8 +1961,8 @@ func TestDockerDriver_MissingContainer_Cleanup(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Command = "echo"
|
cfg.Command = "echo"
|
||||||
cfg.Args = []string{"hello"}
|
cfg.Args = []string{"hello"}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -2051,8 +2028,8 @@ func TestDockerDriver_Stats(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Command = "sleep"
|
cfg.Command = "sleep"
|
||||||
cfg.Args = []string{"1000"}
|
cfg.Args = []string{"1000"}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -2273,8 +2250,8 @@ func TestDockerDriver_Mounts(t *testing.T) {
|
||||||
driver.config.Volumes.Enabled = true
|
driver.config.Volumes.Enabled = true
|
||||||
|
|
||||||
// Build the task
|
// Build the task
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Command = "sleep"
|
cfg.Command = "sleep"
|
||||||
cfg.Args = []string{"10000"}
|
cfg.Args = []string{"10000"}
|
||||||
cfg.Mounts = c.Mounts
|
cfg.Mounts = c.Mounts
|
||||||
|
@ -2472,7 +2449,7 @@ func TestDockerDriver_Devices_IsInvalidConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
cfg.Devices = tc.deviceConfig
|
cfg.Devices = tc.deviceConfig
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
d := dockerDriverHarness(t, nil)
|
d := dockerDriverHarness(t, nil)
|
||||||
|
@ -2483,7 +2460,6 @@ func TestDockerDriver_Devices_IsInvalidConfig(t *testing.T) {
|
||||||
_, _, err := d.StartTask(task)
|
_, _, err := d.StartTask(task)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), tc.err.Error())
|
require.Contains(t, err.Error(), tc.err.Error())
|
||||||
freeport.Return(ports)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2509,8 +2485,8 @@ func TestDockerDriver_Device_Success(t *testing.T) {
|
||||||
ContainerPath: containerPath,
|
ContainerPath: containerPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Devices = []DockerDevice{config}
|
cfg.Devices = []DockerDevice{config}
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -2530,8 +2506,8 @@ func TestDockerDriver_Entrypoint(t *testing.T) {
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
entrypoint := []string{"sh", "-c"}
|
entrypoint := []string{"sh", "-c"}
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Entrypoint = entrypoint
|
cfg.Entrypoint = entrypoint
|
||||||
cfg.Command = strings.Join(busyboxLongRunningCmd, " ")
|
cfg.Command = strings.Join(busyboxLongRunningCmd, " ")
|
||||||
cfg.Args = []string{}
|
cfg.Args = []string{}
|
||||||
|
@ -2558,8 +2534,8 @@ func TestDockerDriver_ReadonlyRootfs(t *testing.T) {
|
||||||
t.Skip("Windows Docker does not support root filesystem in read-only mode")
|
t.Skip("Windows Docker does not support root filesystem in read-only mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.ReadonlyRootfs = true
|
cfg.ReadonlyRootfs = true
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -2596,8 +2572,8 @@ func TestDockerDriver_VolumeError(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
_, cfg, ports := dockerTask(t)
|
_, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
driver := dockerDriverHarness(t, nil)
|
driver := dockerDriverHarness(t, nil)
|
||||||
|
|
||||||
// assert volume error is recoverable
|
// assert volume error is recoverable
|
||||||
|
@ -2611,8 +2587,8 @@ func TestDockerDriver_AdvertiseIPv6Address(t *testing.T) {
|
||||||
|
|
||||||
expectedPrefix := "2001:db8:1::242:ac11"
|
expectedPrefix := "2001:db8:1::242:ac11"
|
||||||
expectedAdvertise := true
|
expectedAdvertise := true
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.AdvertiseIPv6Addr = expectedAdvertise
|
cfg.AdvertiseIPv6Addr = expectedAdvertise
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
|
@ -2718,8 +2694,8 @@ func TestDockerDriver_CreationIdempotent(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
client := newTestDockerClient(t)
|
client := newTestDockerClient(t)
|
||||||
|
@ -2786,8 +2762,7 @@ func TestDockerDriver_CreationIdempotent(t *testing.T) {
|
||||||
func TestDockerDriver_CreateContainerConfig_CPUHardLimit(t *testing.T) {
|
func TestDockerDriver_CreateContainerConfig_CPUHardLimit(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
task, _, ports := dockerTask(t)
|
task, _, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
dh := dockerDriverHarness(t, nil)
|
dh := dockerDriverHarness(t, nil)
|
||||||
driver := dh.Impl().(*Driver)
|
driver := dh.Impl().(*Driver)
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/client/allocdir"
|
"github.com/hashicorp/nomad/client/allocdir"
|
||||||
"github.com/hashicorp/nomad/client/testutil"
|
"github.com/hashicorp/nomad/client/testutil"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/uuid"
|
"github.com/hashicorp/nomad/helper/uuid"
|
||||||
"github.com/hashicorp/nomad/plugins/drivers"
|
"github.com/hashicorp/nomad/plugins/drivers"
|
||||||
dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils"
|
dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils"
|
||||||
|
@ -31,8 +30,8 @@ func TestDockerDriver_User(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
task.User = "alice"
|
task.User = "alice"
|
||||||
cfg.Command = "/bin/sleep"
|
cfg.Command = "/bin/sleep"
|
||||||
cfg.Args = []string{"10000"}
|
cfg.Args = []string{"10000"}
|
||||||
|
@ -148,8 +147,8 @@ func TestDockerDriver_CPUCFSPeriod(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.CPUHardLimit = true
|
cfg.CPUHardLimit = true
|
||||||
cfg.CPUCFSPeriod = 1000000
|
cfg.CPUCFSPeriod = 1000000
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
@ -169,8 +168,8 @@ func TestDockerDriver_Sysctl_Ulimit(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
expectedUlimits := map[string]string{
|
expectedUlimits := map[string]string{
|
||||||
"nproc": "4242",
|
"nproc": "4242",
|
||||||
"nofile": "2048:4096",
|
"nofile": "2048:4096",
|
||||||
|
@ -240,19 +239,18 @@ func TestDockerDriver_Sysctl_Ulimit_Errors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
cfg.Ulimit = tc.ulimitConfig
|
cfg.Ulimit = tc.ulimitConfig
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
d := dockerDriverHarness(t, nil)
|
d := dockerDriverHarness(t, nil)
|
||||||
cleanup := d.MkAllocDir(task, true)
|
cleanup := d.MkAllocDir(task, true)
|
||||||
defer cleanup()
|
t.Cleanup(cleanup)
|
||||||
copyImage(t, task.TaskDir(), "busybox.tar")
|
copyImage(t, task.TaskDir(), "busybox.tar")
|
||||||
|
|
||||||
_, _, err := d.StartTask(task)
|
_, _, err := d.StartTask(task)
|
||||||
require.NotNil(t, err, "Expected non nil error")
|
require.NotNil(t, err, "Expected non nil error")
|
||||||
require.Contains(t, err.Error(), tc.err.Error())
|
require.Contains(t, err.Error(), tc.err.Error())
|
||||||
freeport.Return(ports)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,8 +337,7 @@ func TestDockerDriver_BindMountsHonorVolumesEnabledFlag(t *testing.T) {
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.VolumeDriver = c.volumeDriver
|
cfg.VolumeDriver = c.volumeDriver
|
||||||
cfg.Volumes = c.volumes
|
cfg.Volumes = c.volumes
|
||||||
|
|
||||||
|
@ -366,8 +363,7 @@ func TestDockerDriver_BindMountsHonorVolumesEnabledFlag(t *testing.T) {
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.VolumeDriver = c.volumeDriver
|
cfg.VolumeDriver = c.volumeDriver
|
||||||
cfg.Volumes = c.volumes
|
cfg.Volumes = c.volumes
|
||||||
|
|
||||||
|
@ -515,8 +511,7 @@ func TestDockerDriver_MountsSerialization(t *testing.T) {
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Mounts = c.passedMounts
|
cfg.Mounts = c.passedMounts
|
||||||
|
|
||||||
task.AllocDir = allocDir
|
task.AllocDir = allocDir
|
||||||
|
@ -538,8 +533,8 @@ func TestDockerDriver_MountsSerialization(t *testing.T) {
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
cfg.Mounts = c.passedMounts
|
cfg.Mounts = c.passedMounts
|
||||||
|
|
||||||
task.AllocDir = allocDir
|
task.AllocDir = allocDir
|
||||||
|
@ -567,8 +562,7 @@ func TestDockerDriver_CreateContainerConfig_MountsCombined(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
task.Devices = []*drivers.DeviceConfig{
|
task.Devices = []*drivers.DeviceConfig{
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/client/testutil"
|
"github.com/hashicorp/nomad/client/testutil"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/uuid"
|
"github.com/hashicorp/nomad/helper/uuid"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -63,8 +62,7 @@ func TestDanglingContainerRemoval(t *testing.T) {
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
// start two containers: one tracked nomad container, and one unrelated container
|
// start two containers: one tracked nomad container, and one unrelated container
|
||||||
task, cfg, ports := dockerTask(t)
|
task, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
require.NoError(t, task.EncodeConcreteDriverConfig(cfg))
|
||||||
|
|
||||||
client, d, handle, cleanup := dockerSetup(t, task, nil)
|
client, d, handle, cleanup := dockerSetup(t, task, nil)
|
||||||
|
@ -166,8 +164,7 @@ func TestDanglingContainerRemoval_Stopped(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
testutil.DockerCompatible(t)
|
testutil.DockerCompatible(t)
|
||||||
|
|
||||||
_, cfg, ports := dockerTask(t)
|
_, cfg, _ := dockerTask(t)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
client := newTestDockerClient(t)
|
client := newTestDockerClient(t)
|
||||||
container, err := client.CreateContainer(docker.CreateContainerOptions{
|
container, err := client.CreateContainer(docker.CreateContainerOptions{
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
//go:build darwin
|
|
||||||
// +build darwin
|
|
||||||
|
|
||||||
package freeport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
|
|
||||||
net.inet.ip.portrange.first: 49152
|
|
||||||
net.inet.ip.portrange.last: 65535
|
|
||||||
*/
|
|
||||||
|
|
||||||
const (
|
|
||||||
ephPortFirst = "net.inet.ip.portrange.first"
|
|
||||||
ephPortLast = "net.inet.ip.portrange.last"
|
|
||||||
command = "sysctl"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ephPortRe = regexp.MustCompile(`^\s*(\d+)\s+(\d+)\s*$`)
|
|
||||||
|
|
||||||
func getEphemeralPortRange() (int, int, error) {
|
|
||||||
cmd := exec.Command(command, "-n", ephPortFirst, ephPortLast)
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
val := string(out)
|
|
||||||
|
|
||||||
m := ephPortRe.FindStringSubmatch(val)
|
|
||||||
if m != nil {
|
|
||||||
min, err1 := strconv.Atoi(m[1])
|
|
||||||
max, err2 := strconv.Atoi(m[2])
|
|
||||||
|
|
||||||
if err1 == nil && err2 == nil {
|
|
||||||
return min, max, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, 0, fmt.Errorf("unexpected sysctl value %q for keys %q %q", val, ephPortFirst, ephPortLast)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
//go:build darwin
|
|
||||||
// +build darwin
|
|
||||||
|
|
||||||
package freeport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetEphemeralPortRange(t *testing.T) {
|
|
||||||
min, max, err := getEphemeralPortRange()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
if min <= 0 || max <= 0 || min > max {
|
|
||||||
t.Fatalf("unexpected values: min=%d, max=%d", min, max)
|
|
||||||
}
|
|
||||||
t.Logf("min=%d, max=%d", min, max)
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
//go:build freebsd
|
|
||||||
// +build freebsd
|
|
||||||
|
|
||||||
package freeport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
|
|
||||||
net.inet.ip.portrange.first: 49152
|
|
||||||
net.inet.ip.portrange.last: 65535
|
|
||||||
*/
|
|
||||||
|
|
||||||
const (
|
|
||||||
ephPortFirst = "net.inet.ip.portrange.first"
|
|
||||||
ephPortLast = "net.inet.ip.portrange.last"
|
|
||||||
command = "sysctl"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ephPortRe = regexp.MustCompile(`^\s*(\d+)\s+(\d+)\s*$`)
|
|
||||||
|
|
||||||
func getEphemeralPortRange() (int, int, error) {
|
|
||||||
cmd := exec.Command(command, "-n", ephPortFirst, ephPortLast)
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
val := string(out)
|
|
||||||
|
|
||||||
m := ephPortRe.FindStringSubmatch(val)
|
|
||||||
if m != nil {
|
|
||||||
min, err1 := strconv.Atoi(m[1])
|
|
||||||
max, err2 := strconv.Atoi(m[2])
|
|
||||||
|
|
||||||
if err1 == nil && err2 == nil {
|
|
||||||
return min, max, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, 0, fmt.Errorf("unexpected sysctl value %q for keys %q %q", val, ephPortFirst, ephPortLast)
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
//go:build linux
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package freeport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
$ sysctl -n net.ipv4.ip_local_port_range
|
|
||||||
32768 60999
|
|
||||||
*/
|
|
||||||
|
|
||||||
const ephemeralPortRangeSysctlKey = "net.ipv4.ip_local_port_range"
|
|
||||||
|
|
||||||
var ephemeralPortRangePatt = regexp.MustCompile(`^\s*(\d+)\s+(\d+)\s*$`)
|
|
||||||
|
|
||||||
func getEphemeralPortRange() (int, int, error) {
|
|
||||||
cmd := exec.Command("sysctl", "-n", ephemeralPortRangeSysctlKey)
|
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
val := string(out)
|
|
||||||
|
|
||||||
m := ephemeralPortRangePatt.FindStringSubmatch(val)
|
|
||||||
if m != nil {
|
|
||||||
min, err1 := strconv.Atoi(m[1])
|
|
||||||
max, err2 := strconv.Atoi(m[2])
|
|
||||||
|
|
||||||
if err1 == nil && err2 == nil {
|
|
||||||
return min, max, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, 0, fmt.Errorf("unexpected sysctl value %q for key %q", val, ephemeralPortRangeSysctlKey)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
//go:build linux
|
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package freeport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetEphemeralPortRange(t *testing.T) {
|
|
||||||
min, max, err := getEphemeralPortRange()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
if min <= 0 || max <= 0 || min > max {
|
|
||||||
t.Fatalf("unexpected values: min=%d, max=%d", min, max)
|
|
||||||
}
|
|
||||||
t.Logf("min=%d, max=%d", min, max)
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package freeport
|
|
||||||
|
|
||||||
// For now we hard-code the Windows ephemeral port range, which is documented by
|
|
||||||
// Microsoft to be in this range for Vista / Server 2008 and newer.
|
|
||||||
//
|
|
||||||
// https://support.microsoft.com/en-us/help/832017/service-overview-and-network-port-requirements-for-windows
|
|
||||||
func getEphemeralPortRange() (int, int, error) {
|
|
||||||
return 49152, 65535, nil
|
|
||||||
}
|
|
|
@ -1,297 +0,0 @@
|
||||||
// Copied from github.com/hashicorp/consul/sdk/freeport
|
|
||||||
//
|
|
||||||
// and tweaked for use by Nomad.
|
|
||||||
package freeport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// todo(shoenig)
|
|
||||||
// There is a conflict between this copy of the updated sdk/freeport package
|
|
||||||
// and the lib/freeport package that is vendored as of nomad v0.10.x, which
|
|
||||||
// means we need to be careful to avoid the ports that transitive dependency
|
|
||||||
// is going to use (i.e. 10,000+). For now, we use the 9XXX port range with
|
|
||||||
// small blocks which means some tests will have to wait, and we need to be
|
|
||||||
// very careful not to leak ports.
|
|
||||||
|
|
||||||
const (
|
|
||||||
// blockSize is the size of the allocated port block. ports are given out
|
|
||||||
// consecutively from that block and after that point in a LRU fashion.
|
|
||||||
// blockSize = 1500
|
|
||||||
blockSize = 100 // todo(shoenig) revert once consul dependency is updated
|
|
||||||
|
|
||||||
// maxBlocks is the number of available port blocks before exclusions.
|
|
||||||
// maxBlocks = 30
|
|
||||||
maxBlocks = 10 // todo(shoenig) revert once consul dependency is updated
|
|
||||||
|
|
||||||
// lowPort is the lowest port number that should be used.
|
|
||||||
// lowPort = 10000
|
|
||||||
lowPort = 9000 // todo(shoenig) revert once consul dependency is updated
|
|
||||||
|
|
||||||
// attempts is how often we try to allocate a port block
|
|
||||||
// before giving up.
|
|
||||||
attempts = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// effectiveMaxBlocks is the number of available port blocks.
|
|
||||||
// lowPort + effectiveMaxBlocks * blockSize must be less than 65535.
|
|
||||||
effectiveMaxBlocks int
|
|
||||||
|
|
||||||
// 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:
|
|
||||||
// - pendingPorts
|
|
||||||
// - freePorts
|
|
||||||
// - total
|
|
||||||
mu sync.Mutex
|
|
||||||
|
|
||||||
// once is used to do the initialization on the first call to retrieve free
|
|
||||||
// ports
|
|
||||||
once sync.Once
|
|
||||||
|
|
||||||
// condNotEmpty is a condition variable to wait for freePorts to be not
|
|
||||||
// empty. Linked to 'mu'
|
|
||||||
condNotEmpty *sync.Cond
|
|
||||||
|
|
||||||
// freePorts is a FIFO of all currently free ports. Take from the front,
|
|
||||||
// and return to the back.
|
|
||||||
freePorts *list.List
|
|
||||||
|
|
||||||
// pendingPorts is a FIFO of recently freed ports that have not yet passed
|
|
||||||
// the not-in-use check.
|
|
||||||
pendingPorts *list.List
|
|
||||||
|
|
||||||
// total is the total number of available ports in the block for use.
|
|
||||||
total int
|
|
||||||
)
|
|
||||||
|
|
||||||
// initialize is used to initialize freeport.
|
|
||||||
func initialize() {
|
|
||||||
var err error
|
|
||||||
effectiveMaxBlocks, err = adjustMaxBlocks()
|
|
||||||
if err != nil {
|
|
||||||
panic("freeport: ephemeral port range detection failed: " + err.Error())
|
|
||||||
}
|
|
||||||
if effectiveMaxBlocks < 0 {
|
|
||||||
panic("freeport: no blocks of ports available outside of ephemeral range")
|
|
||||||
}
|
|
||||||
if lowPort+effectiveMaxBlocks*blockSize > 65535 {
|
|
||||||
panic("freeport: block size too big or too many blocks requested")
|
|
||||||
}
|
|
||||||
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
firstPort, lockLn = alloc()
|
|
||||||
|
|
||||||
condNotEmpty = sync.NewCond(&mu)
|
|
||||||
freePorts = list.New()
|
|
||||||
pendingPorts = list.New()
|
|
||||||
|
|
||||||
// fill with all available free ports
|
|
||||||
for port := firstPort + 1; port < firstPort+blockSize; port++ {
|
|
||||||
if used := isPortInUse(port); !used {
|
|
||||||
freePorts.PushBack(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
total = freePorts.Len()
|
|
||||||
|
|
||||||
go checkFreedPorts()
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkFreedPorts() {
|
|
||||||
ticker := time.NewTicker(250 * time.Millisecond)
|
|
||||||
for {
|
|
||||||
<-ticker.C
|
|
||||||
checkFreedPortsOnce()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkFreedPortsOnce() {
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
pending := pendingPorts.Len()
|
|
||||||
remove := make([]*list.Element, 0, pending)
|
|
||||||
for elem := pendingPorts.Front(); elem != nil; elem = elem.Next() {
|
|
||||||
port := elem.Value.(int)
|
|
||||||
if used := isPortInUse(port); !used {
|
|
||||||
freePorts.PushBack(port)
|
|
||||||
remove = append(remove, elem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
retained := pending - len(remove)
|
|
||||||
|
|
||||||
if retained > 0 {
|
|
||||||
logf("WARN", "%d out of %d pending ports are still in use; something probably didn't wait around for the port to be closed!", retained, pending)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(remove) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, elem := range remove {
|
|
||||||
pendingPorts.Remove(elem)
|
|
||||||
}
|
|
||||||
|
|
||||||
condNotEmpty.Broadcast()
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjustMaxBlocks avoids having the allocation ranges overlap the ephemeral
|
|
||||||
// port range.
|
|
||||||
func adjustMaxBlocks() (int, error) {
|
|
||||||
ephemeralPortMin, ephemeralPortMax, err := getEphemeralPortRange()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ephemeralPortMin <= 0 || ephemeralPortMax <= 0 {
|
|
||||||
logf("INFO", "ephemeral port range detection not configured for GOOS=%q", runtime.GOOS)
|
|
||||||
return maxBlocks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
logf("INFO", "detected ephemeral port range of [%d, %d]", ephemeralPortMin, ephemeralPortMax)
|
|
||||||
for block := 0; block < maxBlocks; block++ {
|
|
||||||
min := lowPort + block*blockSize
|
|
||||||
max := min + blockSize
|
|
||||||
overlap := intervalOverlap(min, max-1, ephemeralPortMin, ephemeralPortMax)
|
|
||||||
if overlap {
|
|
||||||
logf("INFO", "reducing max blocks from %d to %d to avoid the ephemeral port range", maxBlocks, block)
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return maxBlocks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(effectiveMaxBlocks)))
|
|
||||||
firstPort := lowPort + block*blockSize
|
|
||||||
ln, err := net.ListenTCP("tcp", tcpAddr("127.0.0.1", firstPort))
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// logf("DEBUG", "allocated port block %d (%d-%d)", block, firstPort, firstPort+blockSize-1)
|
|
||||||
return firstPort, ln
|
|
||||||
}
|
|
||||||
panic("freeport: cannot allocate port block")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustTake is the same as Take except it panics on error.
|
|
||||||
func MustTake(n int) (ports []int) {
|
|
||||||
ports, err := Take(n)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return ports
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take 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 Take(n int) (ports []int, err error) {
|
|
||||||
if n <= 0 {
|
|
||||||
return nil, fmt.Errorf("freeport: cannot take %d ports", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
// Reserve a port block
|
|
||||||
once.Do(initialize)
|
|
||||||
|
|
||||||
if n > total {
|
|
||||||
return nil, fmt.Errorf("freeport: block size too small")
|
|
||||||
}
|
|
||||||
|
|
||||||
for len(ports) < n {
|
|
||||||
for freePorts.Len() == 0 {
|
|
||||||
if total == 0 {
|
|
||||||
return nil, fmt.Errorf("freeport: impossible to satisfy request; there are no actual free ports in the block anymore")
|
|
||||||
}
|
|
||||||
condNotEmpty.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
elem := freePorts.Front()
|
|
||||||
freePorts.Remove(elem)
|
|
||||||
port := elem.Value.(int)
|
|
||||||
|
|
||||||
if used := isPortInUse(port); used {
|
|
||||||
// Something outside of the test suite has stolen this port, possibly
|
|
||||||
// due to assignment to an ephemeral port, remove it completely.
|
|
||||||
logf("WARN", "leaked port %d due to theft; removing from circulation", port)
|
|
||||||
total--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ports = append(ports, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
// logf("DEBUG", "free ports: %v", ports)
|
|
||||||
return ports, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return returns a block of ports back to the general pool. These ports should
|
|
||||||
// have been returned from a call to Take().
|
|
||||||
func Return(ports []int) {
|
|
||||||
if len(ports) == 0 {
|
|
||||||
return // convenience short circuit for test ergonomics
|
|
||||||
}
|
|
||||||
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
for _, port := range ports {
|
|
||||||
if port > firstPort && port < firstPort+blockSize {
|
|
||||||
pendingPorts.PushBack(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPortInUse(port int) bool {
|
|
||||||
ln, err := net.ListenTCP("tcp", tcpAddr("127.0.0.1", port))
|
|
||||||
if err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
_ = ln.Close()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcpAddr(ip string, port int) *net.TCPAddr {
|
|
||||||
return &net.TCPAddr{IP: net.ParseIP(ip), Port: port}
|
|
||||||
}
|
|
||||||
|
|
||||||
// intervalOverlap returns true if the doubly-inclusive integer intervals
|
|
||||||
// represented by [min1, max1] and [min2, max2] overlap.
|
|
||||||
func intervalOverlap(min1, max1, min2, max2 int) bool {
|
|
||||||
if min1 > max1 {
|
|
||||||
logf("WARN", "interval1 is not ordered [%d, %d]", min1, max1)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if min2 > max2 {
|
|
||||||
logf("WARN", "interval2 is not ordered [%d, %d]", min2, max2)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return min1 <= max2 && min2 <= max1
|
|
||||||
}
|
|
||||||
|
|
||||||
func logf(severity string, format string, a ...interface{}) {
|
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "["+severity+"] freeport: "+format+"\n", a...)
|
|
||||||
}
|
|
|
@ -1,281 +0,0 @@
|
||||||
package freeport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/consul/sdk/testutil/retry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// reset will reverse the setup from initialize() and then redo it (for tests)
|
|
||||||
func reset() {
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
logf("INFO", "resetting the freeport package state")
|
|
||||||
|
|
||||||
effectiveMaxBlocks = 0
|
|
||||||
firstPort = 0
|
|
||||||
if lockLn != nil {
|
|
||||||
lockLn.Close()
|
|
||||||
lockLn = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
once = sync.Once{}
|
|
||||||
|
|
||||||
freePorts = nil
|
|
||||||
pendingPorts = nil
|
|
||||||
total = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// peekFree returns the next port that will be returned by Take to aid in testing.
|
|
||||||
func peekFree() int {
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
return freePorts.Front().Value.(int)
|
|
||||||
}
|
|
||||||
|
|
||||||
// peekAllFree returns all free ports that could be returned by Take to aid in testing.
|
|
||||||
func peekAllFree() []int {
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
var out []int
|
|
||||||
for elem := freePorts.Front(); elem != nil; elem = elem.Next() {
|
|
||||||
port := elem.Value.(int)
|
|
||||||
out = append(out, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// stats returns diagnostic data to aid in testing
|
|
||||||
func stats() (numTotal, numPending, numFree int) {
|
|
||||||
mu.Lock()
|
|
||||||
defer mu.Unlock()
|
|
||||||
return total, pendingPorts.Len(), freePorts.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTakeReturn(t *testing.T) {
|
|
||||||
// NOTE: for global var reasons this cannot execute in parallel
|
|
||||||
// ci.Parallel(t)
|
|
||||||
|
|
||||||
// Since this test is destructive (i.e. it leaks all ports) it means that
|
|
||||||
// any other test cases in this package will not function after it runs. To
|
|
||||||
// help out we reset the global state after we run this test.
|
|
||||||
defer reset()
|
|
||||||
|
|
||||||
// OK: do a simple take/return cycle to trigger the package initialization
|
|
||||||
func() {
|
|
||||||
ports, err := Take(1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
defer Return(ports)
|
|
||||||
|
|
||||||
if len(ports) != 1 {
|
|
||||||
t.Fatalf("expected %d but got %d ports", 1, len(ports))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
waitForStatsReset := func() (numTotal int) {
|
|
||||||
t.Helper()
|
|
||||||
numTotal, numPending, numFree := stats()
|
|
||||||
if numTotal != numFree+numPending {
|
|
||||||
t.Fatalf("expected total (%d) and free+pending (%d) ports to match", numTotal, numFree+numPending)
|
|
||||||
}
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
|
||||||
numTotal, numPending, numFree = stats()
|
|
||||||
if numPending != 0 {
|
|
||||||
r.Fatalf("pending is still non zero: %d", numPending)
|
|
||||||
}
|
|
||||||
if numTotal != numFree {
|
|
||||||
r.Fatalf("total (%d) does not equal free (%d)", numTotal, numFree)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return numTotal
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
numTotal := waitForStatsReset()
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// OK: take the max
|
|
||||||
func() {
|
|
||||||
ports, err := Take(numTotal)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
defer Return(ports)
|
|
||||||
|
|
||||||
if len(ports) != numTotal {
|
|
||||||
t.Fatalf("expected %d but got %d ports", numTotal, len(ports))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
numTotal = waitForStatsReset()
|
|
||||||
|
|
||||||
expectError := func(expected string, got error) {
|
|
||||||
t.Helper()
|
|
||||||
if got == nil {
|
|
||||||
t.Fatalf("expected error but was nil")
|
|
||||||
}
|
|
||||||
if got.Error() != expected {
|
|
||||||
t.Fatalf("expected error %q but got %q", expected, got.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// ERROR: take too many ports
|
|
||||||
func() {
|
|
||||||
ports, err := Take(numTotal + 1)
|
|
||||||
defer Return(ports)
|
|
||||||
expectError("freeport: block size too small", err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// ERROR: invalid ports request (negative)
|
|
||||||
func() {
|
|
||||||
_, err := Take(-1)
|
|
||||||
expectError("freeport: cannot take -1 ports", err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// ERROR: invalid ports request (zero)
|
|
||||||
func() {
|
|
||||||
_, err := Take(0)
|
|
||||||
expectError("freeport: cannot take 0 ports", err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// OK: Steal a port under the covers and let freeport detect the theft and compensate
|
|
||||||
leakedPort := peekFree()
|
|
||||||
func() {
|
|
||||||
leakyListener, err := net.ListenTCP("tcp", tcpAddr("127.0.0.1", leakedPort))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
defer leakyListener.Close()
|
|
||||||
|
|
||||||
func() {
|
|
||||||
ports, err := Take(3)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
defer Return(ports)
|
|
||||||
|
|
||||||
if len(ports) != 3 {
|
|
||||||
t.Fatalf("expected %d but got %d ports", 3, len(ports))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, port := range ports {
|
|
||||||
if port == leakedPort {
|
|
||||||
t.Fatalf("did not expect for Take to return the leaked port")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
newNumTotal := waitForStatsReset()
|
|
||||||
if newNumTotal != numTotal-1 {
|
|
||||||
t.Fatalf("expected total to drop to %d but got %d", numTotal-1, newNumTotal)
|
|
||||||
}
|
|
||||||
numTotal = newNumTotal // update outer variable for later tests
|
|
||||||
}()
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// OK: sequence it so that one Take must wait on another Take to Return.
|
|
||||||
func() {
|
|
||||||
mostPorts, err := Take(numTotal - 5)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type reply struct {
|
|
||||||
ports []int
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
ch := make(chan reply, 1)
|
|
||||||
go func() {
|
|
||||||
ports, err := Take(10)
|
|
||||||
ch <- reply{ports: ports, err: err}
|
|
||||||
}()
|
|
||||||
|
|
||||||
Return(mostPorts)
|
|
||||||
|
|
||||||
r := <-ch
|
|
||||||
if r.err != nil {
|
|
||||||
t.Fatalf("err: %v", r.err)
|
|
||||||
}
|
|
||||||
defer Return(r.ports)
|
|
||||||
|
|
||||||
if len(r.ports) != 10 {
|
|
||||||
t.Fatalf("expected %d ports but got %d", 10, len(r.ports))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
numTotal = waitForStatsReset()
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
// ERROR: Now we end on the crazy "Ocean's 11" level port theft where we
|
|
||||||
// orchestrate a situation where all ports are stolen and we don't find out
|
|
||||||
// until Take.
|
|
||||||
func() {
|
|
||||||
// 1. Grab all of the ports.
|
|
||||||
allPorts := peekAllFree()
|
|
||||||
|
|
||||||
// 2. Leak all of the ports
|
|
||||||
leaked := make([]io.Closer, 0, len(allPorts))
|
|
||||||
defer func() {
|
|
||||||
for _, c := range leaked {
|
|
||||||
c.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for _, port := range allPorts {
|
|
||||||
ln, err := net.ListenTCP("tcp", tcpAddr("127.0.0.1", port))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
leaked = append(leaked, ln)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Request 1 port which will detect the leaked ports and fail.
|
|
||||||
_, err := Take(1)
|
|
||||||
expectError("freeport: impossible to satisfy request; there are no actual free ports in the block anymore", err)
|
|
||||||
|
|
||||||
// 4. Wait for the block to zero out.
|
|
||||||
newNumTotal := waitForStatsReset()
|
|
||||||
if newNumTotal != 0 {
|
|
||||||
t.Fatalf("expected total to drop to %d but got %d", 0, newNumTotal)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntervalOverlap(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
min1, max1, min2, max2 int
|
|
||||||
overlap bool
|
|
||||||
}{
|
|
||||||
{0, 0, 0, 0, true},
|
|
||||||
{1, 1, 1, 1, true},
|
|
||||||
{1, 3, 1, 3, true}, // same
|
|
||||||
{1, 3, 4, 6, false}, // serial
|
|
||||||
{1, 4, 3, 6, true}, // inner overlap
|
|
||||||
{1, 6, 3, 4, true}, // nest
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
t.Run(fmt.Sprintf("%d:%d vs %d:%d", tc.min1, tc.max1, tc.min2, tc.max2), func(t *testing.T) {
|
|
||||||
if tc.overlap != intervalOverlap(tc.min1, tc.max1, tc.min2, tc.max2) { // 1 vs 2
|
|
||||||
t.Fatalf("expected %v but got %v", tc.overlap, !tc.overlap)
|
|
||||||
}
|
|
||||||
if tc.overlap != intervalOverlap(tc.min2, tc.max2, tc.min1, tc.max1) { // 2 vs 1
|
|
||||||
t.Fatalf("expected %v but got %v", tc.overlap, !tc.overlap)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,9 +6,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/helper/testlog"
|
"github.com/hashicorp/nomad/helper/testlog"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/shoenig/test/must"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestPool(t *testing.T) *ConnPool {
|
func newTestPool(t *testing.T) *ConnPool {
|
||||||
|
@ -18,24 +18,19 @@ func newTestPool(t *testing.T) *ConnPool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConnPool_ConnListener(t *testing.T) {
|
func TestConnPool_ConnListener(t *testing.T) {
|
||||||
require := require.New(t)
|
ports := ci.PortAllocator.Grab(1)
|
||||||
|
|
||||||
ports := freeport.MustTake(1)
|
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
addrStr := fmt.Sprintf("127.0.0.1:%d", ports[0])
|
addrStr := fmt.Sprintf("127.0.0.1:%d", ports[0])
|
||||||
addr, err := net.ResolveTCPAddr("tcp", addrStr)
|
addr, err := net.ResolveTCPAddr("tcp", addrStr)
|
||||||
require.Nil(err)
|
must.NoError(t, err)
|
||||||
|
|
||||||
exitCh := make(chan struct{})
|
exitCh := make(chan struct{})
|
||||||
defer close(exitCh)
|
defer close(exitCh)
|
||||||
go func() {
|
go func() {
|
||||||
ln, err := net.Listen("tcp", addrStr)
|
ln, listenErr := net.Listen("tcp", addrStr)
|
||||||
require.Nil(err)
|
must.NoError(t, listenErr)
|
||||||
defer ln.Close()
|
defer func() { _ = ln.Close() }()
|
||||||
conn, _ := ln.Accept()
|
conn, _ := ln.Accept()
|
||||||
defer conn.Close()
|
defer func() { _ = conn.Close() }()
|
||||||
|
|
||||||
<-exitCh
|
<-exitCh
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -50,7 +45,7 @@ func TestConnPool_ConnListener(t *testing.T) {
|
||||||
|
|
||||||
// Make an RPC
|
// Make an RPC
|
||||||
_, err = pool.acquire("test", addr)
|
_, err = pool.acquire("test", addr)
|
||||||
require.Nil(err)
|
must.NoError(t, err)
|
||||||
|
|
||||||
// Assert we get a connection.
|
// Assert we get a connection.
|
||||||
select {
|
select {
|
||||||
|
@ -60,7 +55,9 @@ func TestConnPool_ConnListener(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the channel is closed when the pool shuts down.
|
// Test that the channel is closed when the pool shuts down.
|
||||||
require.Nil(pool.Shutdown())
|
err = pool.Shutdown()
|
||||||
|
must.NoError(t, err)
|
||||||
|
|
||||||
_, ok := <-c
|
_, ok := <-c
|
||||||
require.False(ok)
|
must.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1568,8 +1568,7 @@ func TestACLEndpoint_UpsertTokens(t *testing.T) {
|
||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
// Each sub-test uses the same server to avoid creating a new one for each
|
// Each sub-test uses the same server to avoid creating a new one for each
|
||||||
// test. This means some care has to be taken with resource naming, but
|
// test. This means some care has to be taken with resource naming.
|
||||||
// does avoid lots of calls to systems such as freeport.
|
|
||||||
testServer, rootACLToken, testServerCleanup := TestACLServer(t, nil)
|
testServer, rootACLToken, testServerCleanup := TestACLServer(t, nil)
|
||||||
defer testServerCleanup()
|
defer testServerCleanup()
|
||||||
codec := rpcClient(t, testServer)
|
codec := rpcClient(t, testServer)
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"github.com/hashicorp/nomad/acl"
|
"github.com/hashicorp/nomad/acl"
|
||||||
"github.com/hashicorp/nomad/ci"
|
"github.com/hashicorp/nomad/ci"
|
||||||
cstructs "github.com/hashicorp/nomad/client/structs"
|
cstructs "github.com/hashicorp/nomad/client/structs"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/snapshot"
|
"github.com/hashicorp/nomad/helper/snapshot"
|
||||||
"github.com/hashicorp/nomad/helper/uuid"
|
"github.com/hashicorp/nomad/helper/uuid"
|
||||||
"github.com/hashicorp/nomad/nomad/mock"
|
"github.com/hashicorp/nomad/nomad/mock"
|
||||||
|
@ -148,8 +147,7 @@ func TestOperator_RaftRemovePeerByAddress(t *testing.T) {
|
||||||
codec := rpcClient(t, s1)
|
codec := rpcClient(t, s1)
|
||||||
testutil.WaitForLeader(t, s1.RPC)
|
testutil.WaitForLeader(t, s1.RPC)
|
||||||
|
|
||||||
ports := freeport.MustTake(1)
|
ports := ci.PortAllocator.Grab(1)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
// Try to remove a peer that's not there.
|
// Try to remove a peer that's not there.
|
||||||
arg := structs.RaftPeerByAddressRequest{
|
arg := structs.RaftPeerByAddressRequest{
|
||||||
|
@ -216,8 +214,7 @@ func TestOperator_RaftRemovePeerByAddress_ACL(t *testing.T) {
|
||||||
// Create ACL token
|
// Create ACL token
|
||||||
invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", mock.NodePolicy(acl.PolicyWrite))
|
invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", mock.NodePolicy(acl.PolicyWrite))
|
||||||
|
|
||||||
ports := freeport.MustTake(1)
|
ports := ci.PortAllocator.Grab(1)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
arg := structs.RaftPeerByAddressRequest{
|
arg := structs.RaftPeerByAddressRequest{
|
||||||
Address: raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", ports[0])),
|
Address: raft.ServerAddress(fmt.Sprintf("127.0.0.1:%d", ports[0])),
|
||||||
|
@ -276,8 +273,7 @@ func TestOperator_RaftRemovePeerByID(t *testing.T) {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ports := freeport.MustTake(1)
|
ports := ci.PortAllocator.Grab(1)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
// Add it manually to Raft.
|
// Add it manually to Raft.
|
||||||
{
|
{
|
||||||
|
@ -337,8 +333,7 @@ func TestOperator_RaftRemovePeerByID_ACL(t *testing.T) {
|
||||||
}
|
}
|
||||||
arg.Region = s1.config.Region
|
arg.Region = s1.config.Region
|
||||||
|
|
||||||
ports := freeport.MustTake(1)
|
ports := ci.PortAllocator.Grab(1)
|
||||||
defer freeport.Return(ports)
|
|
||||||
|
|
||||||
// Add peer manually to Raft.
|
// Add peer manually to Raft.
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/command/agent/consul"
|
"github.com/hashicorp/nomad/command/agent/consul"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
"github.com/hashicorp/nomad/helper/pluginutils/catalog"
|
"github.com/hashicorp/nomad/helper/pluginutils/catalog"
|
||||||
"github.com/hashicorp/nomad/helper/pluginutils/singleton"
|
"github.com/hashicorp/nomad/helper/pluginutils/singleton"
|
||||||
"github.com/hashicorp/nomad/helper/testlog"
|
"github.com/hashicorp/nomad/helper/testlog"
|
||||||
|
@ -111,7 +111,7 @@ func TestServerErr(t *testing.T, cb func(*Config)) (*Server, func(), error) {
|
||||||
|
|
||||||
for i := 10; i >= 0; i-- {
|
for i := 10; i >= 0; i-- {
|
||||||
// Get random ports, need to cleanup later
|
// Get random ports, need to cleanup later
|
||||||
ports := freeport.MustTake(2)
|
ports := ci.PortAllocator.Grab(2)
|
||||||
|
|
||||||
config.RPCAddr = &net.TCPAddr{
|
config.RPCAddr = &net.TCPAddr{
|
||||||
IP: []byte{127, 0, 0, 1},
|
IP: []byte{127, 0, 0, 1},
|
||||||
|
@ -132,8 +132,6 @@ func TestServerErr(t *testing.T, cb func(*Config)) (*Server, func(), error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ch <- fmt.Errorf("failed to shutdown server: %w", err)
|
ch <- fmt.Errorf("failed to shutdown server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
freeport.Return(ports)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
@ -146,12 +144,10 @@ func TestServerErr(t *testing.T, cb func(*Config)) (*Server, func(), error) {
|
||||||
}
|
}
|
||||||
}, nil
|
}, nil
|
||||||
} else if i == 0 {
|
} else if i == 0 {
|
||||||
freeport.Return(ports)
|
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
} else {
|
} else {
|
||||||
if server != nil {
|
if server != nil {
|
||||||
_ = server.Shutdown()
|
_ = server.Shutdown()
|
||||||
freeport.Return(ports)
|
|
||||||
}
|
}
|
||||||
wait := time.Duration(rand.Int31n(2000)) * time.Millisecond
|
wait := time.Duration(rand.Int31n(2000)) * time.Millisecond
|
||||||
time.Sleep(wait)
|
time.Sleep(wait)
|
||||||
|
|
|
@ -23,8 +23,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||||
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/helper/discover"
|
"github.com/hashicorp/nomad/helper/discover"
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
|
||||||
testing "github.com/mitchellh/go-testing-interface"
|
testing "github.com/mitchellh/go-testing-interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -98,8 +98,8 @@ type ServerConfigCallback func(c *TestServerConfig)
|
||||||
|
|
||||||
// defaultServerConfig returns a new TestServerConfig struct
|
// defaultServerConfig returns a new TestServerConfig struct
|
||||||
// with all of the listen ports incremented by one.
|
// with all of the listen ports incremented by one.
|
||||||
func defaultServerConfig() (*TestServerConfig, []int) {
|
func defaultServerConfig() *TestServerConfig {
|
||||||
ports := freeport.MustTake(3)
|
ports := ci.PortAllocator.Grab(3)
|
||||||
return &TestServerConfig{
|
return &TestServerConfig{
|
||||||
NodeName: fmt.Sprintf("node-%d", ports[0]),
|
NodeName: fmt.Sprintf("node-%d", ports[0]),
|
||||||
DisableCheckpoint: true,
|
DisableCheckpoint: true,
|
||||||
|
@ -123,7 +123,7 @@ func defaultServerConfig() (*TestServerConfig, []int) {
|
||||||
ACL: &ACLConfig{
|
ACL: &ACLConfig{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
},
|
},
|
||||||
}, ports
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestServer is the main server wrapper struct.
|
// TestServer is the main server wrapper struct.
|
||||||
|
@ -132,10 +132,6 @@ type TestServer struct {
|
||||||
Config *TestServerConfig
|
Config *TestServerConfig
|
||||||
t testing.T
|
t testing.T
|
||||||
|
|
||||||
// ports (if any) that are reserved through freeport that must be returned
|
|
||||||
// at the end of a test, done when Close() is called.
|
|
||||||
ports []int
|
|
||||||
|
|
||||||
HTTPAddr string
|
HTTPAddr string
|
||||||
SerfAddr string
|
SerfAddr string
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
|
@ -169,7 +165,7 @@ func NewTestServer(t testing.T, cb ServerConfigCallback) *TestServer {
|
||||||
}
|
}
|
||||||
defer configFile.Close()
|
defer configFile.Close()
|
||||||
|
|
||||||
nomadConfig, ports := defaultServerConfig()
|
nomadConfig := defaultServerConfig()
|
||||||
nomadConfig.DataDir = dataDir
|
nomadConfig.DataDir = dataDir
|
||||||
|
|
||||||
if cb != nil {
|
if cb != nil {
|
||||||
|
@ -216,8 +212,6 @@ func NewTestServer(t testing.T, cb ServerConfigCallback) *TestServer {
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
t: t,
|
t: t,
|
||||||
|
|
||||||
ports: ports,
|
|
||||||
|
|
||||||
HTTPAddr: fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.HTTP),
|
HTTPAddr: fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.HTTP),
|
||||||
SerfAddr: fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.Serf),
|
SerfAddr: fmt.Sprintf("127.0.0.1:%d", nomadConfig.Ports.Serf),
|
||||||
HTTPClient: client,
|
HTTPClient: client,
|
||||||
|
@ -240,8 +234,6 @@ func NewTestServer(t testing.T, cb ServerConfigCallback) *TestServer {
|
||||||
// Stop stops the test Nomad server, and removes the Nomad data
|
// Stop stops the test Nomad server, and removes the Nomad data
|
||||||
// directory once we are done.
|
// directory once we are done.
|
||||||
func (s *TestServer) Stop() {
|
func (s *TestServer) Stop() {
|
||||||
defer freeport.Return(s.ports)
|
|
||||||
|
|
||||||
defer os.RemoveAll(s.Config.DataDir)
|
defer os.RemoveAll(s.Config.DataDir)
|
||||||
|
|
||||||
// wait for the process to exit to be sure that the data dir can be
|
// wait for the process to exit to be sure that the data dir can be
|
||||||
|
|
|
@ -3,12 +3,11 @@ package testutil
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/nomad/helper/freeport"
|
"github.com/hashicorp/nomad/ci"
|
||||||
"github.com/hashicorp/nomad/helper/testlog"
|
"github.com/hashicorp/nomad/helper/testlog"
|
||||||
"github.com/hashicorp/nomad/helper/uuid"
|
"github.com/hashicorp/nomad/helper/uuid"
|
||||||
"github.com/hashicorp/nomad/nomad/structs/config"
|
"github.com/hashicorp/nomad/nomad/structs/config"
|
||||||
|
@ -30,10 +29,6 @@ type TestVault struct {
|
||||||
t testing.T
|
t testing.T
|
||||||
waitCh chan error
|
waitCh chan error
|
||||||
|
|
||||||
// ports (if any) that are reserved through freeport that must be returned
|
|
||||||
// at the end of a test, done when Stop() is called.
|
|
||||||
ports []int
|
|
||||||
|
|
||||||
Addr string
|
Addr string
|
||||||
HTTPAddr string
|
HTTPAddr string
|
||||||
RootToken string
|
RootToken string
|
||||||
|
@ -42,93 +37,70 @@ type TestVault struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestVaultFromPath(t testing.T, binary string) *TestVault {
|
func NewTestVaultFromPath(t testing.T, binary string) *TestVault {
|
||||||
var ports []int
|
port := ci.PortAllocator.Grab(1)[0]
|
||||||
nextPort := func() int {
|
token := uuid.Generate()
|
||||||
next := freeport.MustTake(1)
|
bind := fmt.Sprintf("-dev-listen-address=127.0.0.1:%d", port)
|
||||||
ports = append(ports, next...)
|
http := fmt.Sprintf("http://127.0.0.1:%d", port)
|
||||||
return next[0]
|
root := fmt.Sprintf("-dev-root-token-id=%s", token)
|
||||||
|
|
||||||
|
cmd := exec.Command(binary, "server", "-dev", bind, root)
|
||||||
|
cmd.Stdout = testlog.NewWriter(t)
|
||||||
|
cmd.Stderr = testlog.NewWriter(t)
|
||||||
|
|
||||||
|
// Build the config
|
||||||
|
conf := vapi.DefaultConfig()
|
||||||
|
conf.Address = http
|
||||||
|
|
||||||
|
// Make the client and set the token to the root token
|
||||||
|
client, err := vapi.NewClient(conf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to build Vault API client: %v", err)
|
||||||
|
}
|
||||||
|
client.SetToken(token)
|
||||||
|
|
||||||
|
enable := true
|
||||||
|
tv := &TestVault{
|
||||||
|
cmd: cmd,
|
||||||
|
t: t,
|
||||||
|
Addr: bind,
|
||||||
|
HTTPAddr: http,
|
||||||
|
RootToken: token,
|
||||||
|
Client: client,
|
||||||
|
Config: &config.VaultConfig{
|
||||||
|
Enabled: &enable,
|
||||||
|
Token: token,
|
||||||
|
Addr: http,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 10; i >= 0; i-- {
|
if err = tv.cmd.Start(); err != nil {
|
||||||
|
tv.t.Fatalf("failed to start vault: %v", err)
|
||||||
port := nextPort() // collect every port for cleanup after the test
|
|
||||||
|
|
||||||
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)
|
|
||||||
root := fmt.Sprintf("-dev-root-token-id=%s", token)
|
|
||||||
|
|
||||||
cmd := exec.Command(binary, "server", "-dev", bind, root)
|
|
||||||
cmd.Stdout = testlog.NewWriter(t)
|
|
||||||
cmd.Stderr = testlog.NewWriter(t)
|
|
||||||
|
|
||||||
// Build the config
|
|
||||||
conf := vapi.DefaultConfig()
|
|
||||||
conf.Address = http
|
|
||||||
|
|
||||||
// Make the client and set the token to the root token
|
|
||||||
client, err := vapi.NewClient(conf)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to build Vault API client: %v", err)
|
|
||||||
}
|
|
||||||
client.SetToken(token)
|
|
||||||
|
|
||||||
enable := true
|
|
||||||
tv := &TestVault{
|
|
||||||
cmd: cmd,
|
|
||||||
t: t,
|
|
||||||
ports: ports,
|
|
||||||
Addr: bind,
|
|
||||||
HTTPAddr: http,
|
|
||||||
RootToken: token,
|
|
||||||
Client: client,
|
|
||||||
Config: &config.VaultConfig{
|
|
||||||
Enabled: &enable,
|
|
||||||
Token: token,
|
|
||||||
Addr: http,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tv.cmd.Start(); err != nil {
|
|
||||||
tv.t.Fatalf("failed to start vault: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the waiter
|
|
||||||
tv.waitCh = make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
err := tv.cmd.Wait()
|
|
||||||
tv.waitCh <- err
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Ensure Vault started
|
|
||||||
var startErr error
|
|
||||||
select {
|
|
||||||
case startErr = <-tv.waitCh:
|
|
||||||
case <-time.After(time.Duration(500*TestMultiplier()) * time.Millisecond):
|
|
||||||
}
|
|
||||||
|
|
||||||
if startErr != nil && i == 0 {
|
|
||||||
t.Fatalf("failed to start vault: %v", startErr)
|
|
||||||
} else if startErr != nil {
|
|
||||||
wait := time.Duration(rand.Int31n(2000)) * time.Millisecond
|
|
||||||
time.Sleep(wait)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
waitErr := tv.waitForAPI()
|
|
||||||
if waitErr != nil && i == 0 {
|
|
||||||
t.Fatalf("failed to start vault: %v", waitErr)
|
|
||||||
} else if waitErr != nil {
|
|
||||||
wait := time.Duration(rand.Int31n(2000)) * time.Millisecond
|
|
||||||
time.Sleep(wait)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return tv
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
// Start the waiter
|
||||||
|
tv.waitCh = make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
err = tv.cmd.Wait()
|
||||||
|
tv.waitCh <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Ensure Vault started
|
||||||
|
var startErr error
|
||||||
|
select {
|
||||||
|
case startErr = <-tv.waitCh:
|
||||||
|
case <-time.After(time.Duration(500*TestMultiplier()) * time.Millisecond):
|
||||||
|
}
|
||||||
|
|
||||||
|
if startErr != nil {
|
||||||
|
t.Fatalf("failed to start vault: %v", startErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitErr := tv.waitForAPI()
|
||||||
|
if waitErr != nil {
|
||||||
|
t.Fatalf("failed to start vault: %v", waitErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tv
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTestVault returns a new TestVault instance that is ready for API calls
|
// NewTestVault returns a new TestVault instance that is ready for API calls
|
||||||
|
@ -141,7 +113,7 @@ func NewTestVault(t testing.T) *TestVault {
|
||||||
// Start must be called and it is the callers responsibility to deal with any
|
// Start must be called and it is the callers responsibility to deal with any
|
||||||
// port conflicts that may occur and retry accordingly.
|
// port conflicts that may occur and retry accordingly.
|
||||||
func NewTestVaultDelayed(t testing.T) *TestVault {
|
func NewTestVaultDelayed(t testing.T) *TestVault {
|
||||||
port := freeport.MustTake(1)[0]
|
port := ci.PortAllocator.Grab(1)[0]
|
||||||
token := uuid.Generate()
|
token := uuid.Generate()
|
||||||
bind := fmt.Sprintf("-dev-listen-address=127.0.0.1:%d", port)
|
bind := fmt.Sprintf("-dev-listen-address=127.0.0.1:%d", port)
|
||||||
http := fmt.Sprintf("http://127.0.0.1:%d", port)
|
http := fmt.Sprintf("http://127.0.0.1:%d", port)
|
||||||
|
@ -209,8 +181,6 @@ func (tv *TestVault) Start() error {
|
||||||
|
|
||||||
// Stop stops the test Vault server
|
// Stop stops the test Vault server
|
||||||
func (tv *TestVault) Stop() {
|
func (tv *TestVault) Stop() {
|
||||||
defer freeport.Return(tv.ports)
|
|
||||||
|
|
||||||
if tv.cmd.Process == nil {
|
if tv.cmd.Process == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue