Add tests for network.Loader

This commit is contained in:
Brian Picciano 2024-12-17 16:05:41 +01:00
parent ab8dac0789
commit 53a1dc0cc2
6 changed files with 628 additions and 25 deletions

View File

@ -0,0 +1,63 @@
//go:generate mockery --name constructors --inpackage --filename constructors_mock.go
package network
import (
"context"
"isle/bootstrap"
"isle/daemon/children"
"isle/nebula"
"isle/toolkit"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
)
// constructors wraps the various Network constructor functions in an interface,
// so that they can be mocked.
//
// This is needed primarily for testing the Loader. We can't test the Loader
// during Network integration tests because the Loader doesn't allow multiple
// Networks with the same ID to be loaded, but we need to do so in integration
// tests. Working around that requirement in the integration tests would add
// extra complexity there, and it's better to do unit testing anyway.
type constructors interface {
load(
ctx context.Context,
logger *mlog.Logger,
envBinDirPath string,
nebulaDeviceNamer *children.NebulaDeviceNamer,
stateDir toolkit.Dir,
runtimeDir toolkit.Dir,
opts *Opts,
) (
Network, error,
)
join(
ctx context.Context,
logger *mlog.Logger,
envBinDirPath string,
nebulaDeviceNamer *children.NebulaDeviceNamer,
joiningBootstrap JoiningBootstrap,
stateDir toolkit.Dir,
runtimeDir toolkit.Dir,
opts *Opts,
) (
Network, error,
)
create(
ctx context.Context,
logger *mlog.Logger,
envBinDirPath string,
nebulaDeviceNamer *children.NebulaDeviceNamer,
stateDir toolkit.Dir,
runtimeDir toolkit.Dir,
creationParams bootstrap.CreationParams,
ipNet nebula.IPNet,
hostName nebula.HostName,
opts *Opts,
) (
Network, error,
)
}

View File

@ -0,0 +1,127 @@
// Code generated by mockery v2.43.1. DO NOT EDIT.
package network
import (
bootstrap "isle/bootstrap"
children "isle/daemon/children"
context "context"
mlog "dev.mediocregopher.com/mediocre-go-lib.git/mlog"
mock "github.com/stretchr/testify/mock"
nebula "isle/nebula"
toolkit "isle/toolkit"
)
// mockConstructors is an autogenerated mock type for the constructors type
type mockConstructors struct {
mock.Mock
}
// create provides a mock function with given fields: ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, creationParams, ipNet, hostName, opts
func (_m *mockConstructors) create(ctx context.Context, logger *mlog.Logger, envBinDirPath string, nebulaDeviceNamer *children.NebulaDeviceNamer, stateDir toolkit.Dir, runtimeDir toolkit.Dir, creationParams bootstrap.CreationParams, ipNet nebula.IPNet, hostName nebula.HostName, opts *Opts) (Network, error) {
ret := _m.Called(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, creationParams, ipNet, hostName, opts)
if len(ret) == 0 {
panic("no return value specified for create")
}
var r0 Network
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, toolkit.Dir, toolkit.Dir, bootstrap.CreationParams, nebula.IPNet, nebula.HostName, *Opts) (Network, error)); ok {
return rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, creationParams, ipNet, hostName, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, toolkit.Dir, toolkit.Dir, bootstrap.CreationParams, nebula.IPNet, nebula.HostName, *Opts) Network); ok {
r0 = rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, creationParams, ipNet, hostName, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(Network)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, toolkit.Dir, toolkit.Dir, bootstrap.CreationParams, nebula.IPNet, nebula.HostName, *Opts) error); ok {
r1 = rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, creationParams, ipNet, hostName, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// join provides a mock function with given fields: ctx, logger, envBinDirPath, nebulaDeviceNamer, joiningBootstrap, stateDir, runtimeDir, opts
func (_m *mockConstructors) join(ctx context.Context, logger *mlog.Logger, envBinDirPath string, nebulaDeviceNamer *children.NebulaDeviceNamer, joiningBootstrap JoiningBootstrap, stateDir toolkit.Dir, runtimeDir toolkit.Dir, opts *Opts) (Network, error) {
ret := _m.Called(ctx, logger, envBinDirPath, nebulaDeviceNamer, joiningBootstrap, stateDir, runtimeDir, opts)
if len(ret) == 0 {
panic("no return value specified for join")
}
var r0 Network
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, JoiningBootstrap, toolkit.Dir, toolkit.Dir, *Opts) (Network, error)); ok {
return rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, joiningBootstrap, stateDir, runtimeDir, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, JoiningBootstrap, toolkit.Dir, toolkit.Dir, *Opts) Network); ok {
r0 = rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, joiningBootstrap, stateDir, runtimeDir, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(Network)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, JoiningBootstrap, toolkit.Dir, toolkit.Dir, *Opts) error); ok {
r1 = rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, joiningBootstrap, stateDir, runtimeDir, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// load provides a mock function with given fields: ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, opts
func (_m *mockConstructors) load(ctx context.Context, logger *mlog.Logger, envBinDirPath string, nebulaDeviceNamer *children.NebulaDeviceNamer, stateDir toolkit.Dir, runtimeDir toolkit.Dir, opts *Opts) (Network, error) {
ret := _m.Called(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, opts)
if len(ret) == 0 {
panic("no return value specified for load")
}
var r0 Network
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, toolkit.Dir, toolkit.Dir, *Opts) (Network, error)); ok {
return rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, toolkit.Dir, toolkit.Dir, *Opts) Network); ok {
r0 = rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(Network)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *mlog.Logger, string, *children.NebulaDeviceNamer, toolkit.Dir, toolkit.Dir, *Opts) error); ok {
r1 = rf(ctx, logger, envBinDirPath, nebulaDeviceNamer, stateDir, runtimeDir, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// newMockConstructors creates a new instance of mockConstructors. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func newMockConstructors(t interface {
mock.TestingT
Cleanup(func())
}) *mockConstructors {
mock := &mockConstructors{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -139,6 +139,8 @@ type Loader interface {
type LoaderOpts struct { type LoaderOpts struct {
// Defaults to that returned by daecommon.GetEnvVars. // Defaults to that returned by daecommon.GetEnvVars.
EnvVars daecommon.EnvVars EnvVars daecommon.EnvVars
constructors constructors // defaults to newConstructors()
} }
func (o *LoaderOpts) withDefaults() *LoaderOpts { func (o *LoaderOpts) withDefaults() *LoaderOpts {
@ -150,10 +152,15 @@ func (o *LoaderOpts) withDefaults() *LoaderOpts {
o.EnvVars = daecommon.GetEnvVars() o.EnvVars = daecommon.GetEnvVars()
} }
if o.constructors == nil {
o.constructors = newConstructors()
}
return o return o
} }
type loader struct { type loader struct {
opts *LoaderOpts
envBinDirPath string envBinDirPath string
networksStateDir toolkit.Dir networksStateDir toolkit.Dir
networksRuntimeDir toolkit.Dir networksRuntimeDir toolkit.Dir
@ -196,6 +203,7 @@ func NewLoader(
} }
return &loader{ return &loader{
opts,
envBinDirPath, envBinDirPath,
networksStateDir, networksStateDir,
networksRuntimeDir, networksRuntimeDir,
@ -247,6 +255,21 @@ func (l *loader) StoredConfig(
return loadConfig(networkStateDir) return loadConfig(networkStateDir)
} }
func (l *loader) isJoined(ctx context.Context, networkID string) (bool, error) {
allJoinedCreationParams, err := l.Loadable(ctx)
if err != nil {
return false, fmt.Errorf("getting already joined networks: %w", err)
}
for _, joinedCreationParams := range allJoinedCreationParams {
if joinedCreationParams.ID == networkID {
return true, nil
}
}
return false, nil
}
func (l *loader) Load( func (l *loader) Load(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
@ -257,6 +280,12 @@ func (l *loader) Load(
) { ) {
networkID := creationParams.ID networkID := creationParams.ID
if isJoined, err := l.isJoined(ctx, networkID); err != nil {
return nil, fmt.Errorf("checking if network is already joined: %w", err)
} else if !isJoined {
return nil, errors.New("network is not yet joined")
}
networkStateDir, networkRuntimeDir, err := networkDirs( networkStateDir, networkRuntimeDir, err := networkDirs(
l.networksStateDir, l.networksRuntimeDir, networkID, true, l.networksStateDir, l.networksRuntimeDir, networkID, true,
) )
@ -266,7 +295,7 @@ func (l *loader) Load(
) )
} }
return load( return l.opts.constructors.load(
ctx, ctx,
logger, logger,
l.envBinDirPath, l.envBinDirPath,
@ -290,22 +319,6 @@ func (l *loader) Join(
networkID = creationParams.ID networkID = creationParams.ID
) )
// Start by checking that the new network isn't already joined. This
// shouldn't _technically_ be necessary, as the caller of Join should
// already be handling this, but it's worth doing to make sure we don't
// accidentally delete the state directory of an already joined network.
allJoinedCreationParams, err := l.Loadable(ctx)
if err != nil {
return nil, fmt.Errorf("getting already joined networks: %w", err)
}
for _, joinedCreationParams := range allJoinedCreationParams {
if joinedCreationParams.ID == networkID {
return nil, fmt.Errorf("network %q already joined", networkID)
}
}
networkStateDir, networkRuntimeDir, err := networkDirs( networkStateDir, networkRuntimeDir, err := networkDirs(
l.networksStateDir, l.networksRuntimeDir, networkID, false, l.networksStateDir, l.networksRuntimeDir, networkID, false,
) )
@ -315,7 +328,7 @@ func (l *loader) Join(
) )
} }
n, err := join( n, err := l.opts.constructors.join(
ctx, ctx,
logger, logger,
l.envBinDirPath, l.envBinDirPath,
@ -356,7 +369,7 @@ func (l *loader) Create(
) )
} }
n, err := create( n, err := l.opts.constructors.create(
ctx, ctx,
logger, logger,
l.envBinDirPath, l.envBinDirPath,

View File

@ -0,0 +1,389 @@
package network
import (
"context"
"errors"
"io/fs"
"isle/bootstrap"
"isle/daemon/children"
"isle/daemon/daecommon"
"isle/nebula"
"isle/toolkit"
"os"
"path/filepath"
"testing"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type loaderHarness struct {
ctx context.Context
logger *mlog.Logger
envBinDirPath string
stateDirPath string
runtimeDirPath string
constructors *mockConstructors
loader Loader
}
func newLoaderHarness(t *testing.T) *loaderHarness {
t.Parallel()
var (
ctx = context.Background()
logger = toolkit.NewTestLogger(t)
rootDir = toolkit.TempDir(t)
envBinDir, _ = rootDir.MkChildDir("bin", false)
stateDir, _ = rootDir.MkChildDir("state", false)
runtimeDir, _ = rootDir.MkChildDir("runtime", false)
constructors = newMockConstructors(t)
)
loader, err := NewLoader(
ctx, logger, envBinDir.Path, &LoaderOpts{
EnvVars: daecommon.EnvVars{
StateDir: stateDir,
RuntimeDir: runtimeDir,
},
constructors: constructors,
},
)
require.NoError(t, err)
return &loaderHarness{
ctx,
logger,
envBinDir.Path,
stateDir.Path,
runtimeDir.Path,
constructors,
loader,
}
}
func (h *loaderHarness) networkStateDirPath(networkID string) string {
return filepath.Join(h.stateDirPath, "networks", networkID)
}
func (h *loaderHarness) networkRuntimeDirPath(networkID string) string {
return filepath.Join(h.runtimeDirPath, "networks", networkID)
}
func (h *loaderHarness) join(
t *testing.T, creationParams bootstrap.CreationParams,
) *MockNetwork {
var (
joiningBootstrap = JoiningBootstrap{
Bootstrap: bootstrap.Bootstrap{
NetworkCreationParams: creationParams,
},
}
networkStateDirPath = h.networkStateDirPath(creationParams.ID)
networkRuntimeDirPath = h.networkRuntimeDirPath(creationParams.ID)
network = NewMockNetwork(t)
)
h.constructors.
On(
"join",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
h.envBinDirPath,
toolkit.MockArg[*children.NebulaDeviceNamer](),
joiningBootstrap,
toolkit.Dir{Path: networkStateDirPath},
toolkit.Dir{Path: networkRuntimeDirPath},
(*Opts)(nil),
).
Return(network, nil).
Once()
got, err := h.loader.Join(
h.ctx,
h.logger,
joiningBootstrap,
nil,
)
require.NoError(t, err)
require.Equal(t, got, network)
require.NoError(t, writeBootstrapToStateDir(
networkStateDirPath, joiningBootstrap.Bootstrap,
))
return network
}
func (h *loaderHarness) assertDirExists(
t *testing.T, exists bool, path string,
) {
stat, err := os.Stat(path)
if exists {
assert.NoError(t, err)
assert.Equal(t, os.ModeDir, stat.Mode().Type())
} else {
assert.ErrorIs(t, err, fs.ErrNotExist)
}
}
func TestLoader_Loadable(t *testing.T) {
allCreationParams := []bootstrap.CreationParams{
bootstrap.NewCreationParams("AAA", "a.com"),
bootstrap.NewCreationParams("BBB", "b.com"),
}
t.Run("empty", func(t *testing.T) {
h := newLoaderHarness(t)
got, err := h.loader.Loadable(h.ctx)
assert.NoError(t, err)
assert.Empty(t, got)
})
t.Run("single", func(t *testing.T) {
h := newLoaderHarness(t)
h.join(t, allCreationParams[0])
got, err := h.loader.Loadable(h.ctx)
assert.NoError(t, err)
assert.Equal(t, allCreationParams[:1], got)
})
t.Run("multiple", func(t *testing.T) {
h := newLoaderHarness(t)
h.join(t, allCreationParams[0])
h.join(t, allCreationParams[1])
got, err := h.loader.Loadable(h.ctx)
assert.NoError(t, err)
assert.ElementsMatch(t, allCreationParams, got)
})
}
func TestLoader_Load(t *testing.T) {
var (
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
)
t.Run("success", func(t *testing.T) {
var (
h = newLoaderHarness(t)
networkStateDirPath = h.networkStateDirPath(creationParams.ID)
networkRuntimeDirPath = h.networkRuntimeDirPath(creationParams.ID)
network = h.join(t, creationParams)
)
h.constructors.
On(
"load",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
h.envBinDirPath,
toolkit.MockArg[*children.NebulaDeviceNamer](),
toolkit.Dir{Path: networkStateDirPath},
toolkit.Dir{Path: networkRuntimeDirPath},
(*Opts)(nil),
).
Return(network, nil).
Once()
got, err := h.loader.Load(
h.ctx,
h.logger,
creationParams,
nil,
)
assert.NoError(t, err)
assert.Equal(t, network, got)
})
t.Run("error/not yet joined", func(t *testing.T) {
var (
h = newLoaderHarness(t)
networkStateDirPath = h.networkStateDirPath(creationParams.ID)
networkRuntimeDirPath = h.networkRuntimeDirPath(creationParams.ID)
)
_, err := h.loader.Load(
h.ctx,
h.logger,
creationParams,
nil,
)
assert.ErrorContains(t, err, "not yet joined")
h.assertDirExists(t, false, networkStateDirPath)
h.assertDirExists(t, false, networkRuntimeDirPath)
})
}
func TestLoader_Join(t *testing.T) {
var (
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
joiningBootstrap = JoiningBootstrap{
Bootstrap: bootstrap.Bootstrap{
NetworkCreationParams: creationParams,
},
}
network = NewMockNetwork(t)
)
t.Run("success", func(t *testing.T) {
var (
h = newLoaderHarness(t)
networkStateDirPath = h.networkStateDirPath(creationParams.ID)
networkRuntimeDirPath = h.networkRuntimeDirPath(creationParams.ID)
)
h.constructors.
On(
"join",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
h.envBinDirPath,
toolkit.MockArg[*children.NebulaDeviceNamer](),
joiningBootstrap,
toolkit.Dir{Path: networkStateDirPath},
toolkit.Dir{Path: networkRuntimeDirPath},
(*Opts)(nil),
).
Return(network, nil).
Once()
got, err := h.loader.Join(
h.ctx,
h.logger,
joiningBootstrap,
nil,
)
assert.NoError(t, err)
assert.Equal(t, network, got)
h.assertDirExists(t, true, networkStateDirPath)
h.assertDirExists(t, true, networkRuntimeDirPath)
})
t.Run("error/constructor", func(t *testing.T) {
var (
h = newLoaderHarness(t)
networkStateDirPath = h.networkStateDirPath(creationParams.ID)
networkRuntimeDirPath = h.networkRuntimeDirPath(creationParams.ID)
wantErr = errors.New("some error")
)
h.constructors.
On(
"join",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
h.envBinDirPath,
toolkit.MockArg[*children.NebulaDeviceNamer](),
joiningBootstrap,
toolkit.Dir{Path: networkStateDirPath},
toolkit.Dir{Path: networkRuntimeDirPath},
(*Opts)(nil),
).
Return(nil, wantErr).
Once()
_, err := h.loader.Join(
h.ctx,
h.logger,
joiningBootstrap,
nil,
)
assert.ErrorIs(t, err, wantErr)
h.assertDirExists(t, false, networkStateDirPath)
h.assertDirExists(t, false, networkRuntimeDirPath)
})
}
func TestLoader_Create(t *testing.T) {
var (
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
ipNet = nebula.MustParseIPNet(t, "172.16.0.0/24")
hostName = nebula.HostName("foo")
network = NewMockNetwork(t)
)
t.Run("success", func(t *testing.T) {
var (
h = newLoaderHarness(t)
networkStateDirPath = h.networkStateDirPath(creationParams.ID)
networkRuntimeDirPath = h.networkRuntimeDirPath(creationParams.ID)
)
h.constructors.
On(
"create",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
h.envBinDirPath,
toolkit.MockArg[*children.NebulaDeviceNamer](),
toolkit.Dir{Path: networkStateDirPath},
toolkit.Dir{Path: networkRuntimeDirPath},
creationParams,
ipNet,
hostName,
(*Opts)(nil),
).
Return(network, nil).
Once()
got, err := h.loader.Create(
h.ctx,
h.logger,
creationParams,
ipNet,
hostName,
nil,
)
assert.NoError(t, err)
assert.Equal(t, network, got)
h.assertDirExists(t, true, networkStateDirPath)
h.assertDirExists(t, true, networkRuntimeDirPath)
})
t.Run("error", func(t *testing.T) {
var (
h = newLoaderHarness(t)
networkStateDirPath = h.networkStateDirPath(creationParams.ID)
networkRuntimeDirPath = h.networkRuntimeDirPath(creationParams.ID)
wantErr = errors.New("some error")
)
h.constructors.
On(
"create",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
h.envBinDirPath,
toolkit.MockArg[*children.NebulaDeviceNamer](),
toolkit.Dir{Path: networkStateDirPath},
toolkit.Dir{Path: networkRuntimeDirPath},
creationParams,
ipNet,
hostName,
(*Opts)(nil),
).
Return(nil, wantErr).
Once()
_, err := h.loader.Create(
h.ctx,
h.logger,
creationParams,
ipNet,
hostName,
nil,
)
assert.ErrorIs(t, err, wantErr)
h.assertDirExists(t, false, networkStateDirPath)
h.assertDirExists(t, false, networkRuntimeDirPath)
})
}

View File

@ -139,6 +139,12 @@ type Network interface {
Shutdown() error Shutdown() error
} }
// Implements constructors interface, methods defined alongside the rest of the
// Network implementation.
type constructorsImpl struct{}
func newConstructors() constructors { return constructorsImpl{} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Network implementation // Network implementation
@ -257,7 +263,7 @@ func loadCreationParams(
return bs.NetworkCreationParams, nil return bs.NetworkCreationParams, nil
} }
func load( func (constructorsImpl) load(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
envBinDirPath string, envBinDirPath string,
@ -298,7 +304,7 @@ func load(
return n, nil return n, nil
} }
func join( func (constructorsImpl) join(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
envBinDirPath string, envBinDirPath string,
@ -337,7 +343,7 @@ func join(
return n, nil return n, nil
} }
func create( func (constructorsImpl) create(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
envBinDirPath string, envBinDirPath string,

View File

@ -63,6 +63,7 @@ func newPublicAddr() string {
type integrationHarness struct { type integrationHarness struct {
ctx context.Context ctx context.Context
logger *mlog.Logger logger *mlog.Logger
constructors constructors
rootDir toolkit.Dir rootDir toolkit.Dir
dirCounter atomic.Uint64 dirCounter atomic.Uint64
nebulaDeviceNamer *children.NebulaDeviceNamer nebulaDeviceNamer *children.NebulaDeviceNamer
@ -90,6 +91,7 @@ func newIntegrationHarness(t *testing.T) *integrationHarness {
return &integrationHarness{ return &integrationHarness{
ctx: context.Background(), ctx: context.Background(),
logger: toolkit.NewTestLogger(t), logger: toolkit.NewTestLogger(t),
constructors: newConstructors(),
rootDir: toolkit.Dir{Path: rootDir}, rootDir: toolkit.Dir{Path: rootDir},
nebulaDeviceNamer: children.NewNebulaDeviceNamer(), nebulaDeviceNamer: children.NewNebulaDeviceNamer(),
} }
@ -171,6 +173,7 @@ type integrationHarnessNetwork struct {
ctx context.Context ctx context.Context
logger *mlog.Logger logger *mlog.Logger
constructors constructors
hostName nebula.HostName hostName nebula.HostName
stateDir, runtimeDir toolkit.Dir stateDir, runtimeDir toolkit.Dir
nebulaDeviceNamer *children.NebulaDeviceNamer nebulaDeviceNamer *children.NebulaDeviceNamer
@ -204,7 +207,7 @@ func (h *integrationHarness) createNetwork(
} }
) )
network, err := create( network, err := h.constructors.create(
h.ctx, h.ctx,
logger, logger,
getEnvBinDirPath(), getEnvBinDirPath(),
@ -224,6 +227,7 @@ func (h *integrationHarness) createNetwork(
network, network,
h.ctx, h.ctx,
logger, logger,
h.constructors,
hostName, hostName,
stateDir, stateDir,
runtimeDir, runtimeDir,
@ -288,7 +292,7 @@ func (h *integrationHarness) joinNetwork(
) )
t.Logf("Joining as %q", hostNameStr) t.Logf("Joining as %q", hostNameStr)
joinedNetwork, err := join( joinedNetwork, err := h.constructors.join(
h.ctx, h.ctx,
logger, logger,
getEnvBinDirPath(), getEnvBinDirPath(),
@ -306,6 +310,7 @@ func (h *integrationHarness) joinNetwork(
joinedNetwork, joinedNetwork,
h.ctx, h.ctx,
logger, logger,
h.constructors,
hostName, hostName,
stateDir, stateDir,
runtimeDir, runtimeDir,
@ -331,7 +336,7 @@ func (nh *integrationHarnessNetwork) restart(t *testing.T) {
t.Log("Loading network (restart)") t.Log("Loading network (restart)")
var err error var err error
nh.Network, err = load( nh.Network, err = nh.constructors.load(
nh.ctx, nh.ctx,
nh.logger, nh.logger,
getEnvBinDirPath(), getEnvBinDirPath(),