diff --git a/go/daemon/network/constructors.go b/go/daemon/network/constructors.go new file mode 100644 index 0000000..04d5a2e --- /dev/null +++ b/go/daemon/network/constructors.go @@ -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, + ) +} diff --git a/go/daemon/network/constructors_mock.go b/go/daemon/network/constructors_mock.go new file mode 100644 index 0000000..de07964 --- /dev/null +++ b/go/daemon/network/constructors_mock.go @@ -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 +} diff --git a/go/daemon/network/loader.go b/go/daemon/network/loader.go index d41403c..7575063 100644 --- a/go/daemon/network/loader.go +++ b/go/daemon/network/loader.go @@ -139,6 +139,8 @@ type Loader interface { type LoaderOpts struct { // Defaults to that returned by daecommon.GetEnvVars. EnvVars daecommon.EnvVars + + constructors constructors // defaults to newConstructors() } func (o *LoaderOpts) withDefaults() *LoaderOpts { @@ -150,10 +152,15 @@ func (o *LoaderOpts) withDefaults() *LoaderOpts { o.EnvVars = daecommon.GetEnvVars() } + if o.constructors == nil { + o.constructors = newConstructors() + } + return o } type loader struct { + opts *LoaderOpts envBinDirPath string networksStateDir toolkit.Dir networksRuntimeDir toolkit.Dir @@ -196,6 +203,7 @@ func NewLoader( } return &loader{ + opts, envBinDirPath, networksStateDir, networksRuntimeDir, @@ -247,6 +255,21 @@ func (l *loader) StoredConfig( 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( ctx context.Context, logger *mlog.Logger, @@ -257,6 +280,12 @@ func (l *loader) Load( ) { 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( l.networksStateDir, l.networksRuntimeDir, networkID, true, ) @@ -266,7 +295,7 @@ func (l *loader) Load( ) } - return load( + return l.opts.constructors.load( ctx, logger, l.envBinDirPath, @@ -290,22 +319,6 @@ func (l *loader) Join( 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( l.networksStateDir, l.networksRuntimeDir, networkID, false, ) @@ -315,7 +328,7 @@ func (l *loader) Join( ) } - n, err := join( + n, err := l.opts.constructors.join( ctx, logger, l.envBinDirPath, @@ -356,7 +369,7 @@ func (l *loader) Create( ) } - n, err := create( + n, err := l.opts.constructors.create( ctx, logger, l.envBinDirPath, diff --git a/go/daemon/network/loader_test.go b/go/daemon/network/loader_test.go new file mode 100644 index 0000000..bb1371b --- /dev/null +++ b/go/daemon/network/loader_test.go @@ -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) + }) +} diff --git a/go/daemon/network/network.go b/go/daemon/network/network.go index 3f2a495..de3a077 100644 --- a/go/daemon/network/network.go +++ b/go/daemon/network/network.go @@ -139,6 +139,12 @@ type Network interface { Shutdown() error } +// Implements constructors interface, methods defined alongside the rest of the +// Network implementation. +type constructorsImpl struct{} + +func newConstructors() constructors { return constructorsImpl{} } + //////////////////////////////////////////////////////////////////////////////// // Network implementation @@ -257,7 +263,7 @@ func loadCreationParams( return bs.NetworkCreationParams, nil } -func load( +func (constructorsImpl) load( ctx context.Context, logger *mlog.Logger, envBinDirPath string, @@ -298,7 +304,7 @@ func load( return n, nil } -func join( +func (constructorsImpl) join( ctx context.Context, logger *mlog.Logger, envBinDirPath string, @@ -337,7 +343,7 @@ func join( return n, nil } -func create( +func (constructorsImpl) create( ctx context.Context, logger *mlog.Logger, envBinDirPath string, diff --git a/go/daemon/network/network_it_util_test.go b/go/daemon/network/network_it_util_test.go index 833bd68..9253cbc 100644 --- a/go/daemon/network/network_it_util_test.go +++ b/go/daemon/network/network_it_util_test.go @@ -63,6 +63,7 @@ func newPublicAddr() string { type integrationHarness struct { ctx context.Context logger *mlog.Logger + constructors constructors rootDir toolkit.Dir dirCounter atomic.Uint64 nebulaDeviceNamer *children.NebulaDeviceNamer @@ -90,6 +91,7 @@ func newIntegrationHarness(t *testing.T) *integrationHarness { return &integrationHarness{ ctx: context.Background(), logger: toolkit.NewTestLogger(t), + constructors: newConstructors(), rootDir: toolkit.Dir{Path: rootDir}, nebulaDeviceNamer: children.NewNebulaDeviceNamer(), } @@ -171,6 +173,7 @@ type integrationHarnessNetwork struct { ctx context.Context logger *mlog.Logger + constructors constructors hostName nebula.HostName stateDir, runtimeDir toolkit.Dir nebulaDeviceNamer *children.NebulaDeviceNamer @@ -204,7 +207,7 @@ func (h *integrationHarness) createNetwork( } ) - network, err := create( + network, err := h.constructors.create( h.ctx, logger, getEnvBinDirPath(), @@ -224,6 +227,7 @@ func (h *integrationHarness) createNetwork( network, h.ctx, logger, + h.constructors, hostName, stateDir, runtimeDir, @@ -288,7 +292,7 @@ func (h *integrationHarness) joinNetwork( ) t.Logf("Joining as %q", hostNameStr) - joinedNetwork, err := join( + joinedNetwork, err := h.constructors.join( h.ctx, logger, getEnvBinDirPath(), @@ -306,6 +310,7 @@ func (h *integrationHarness) joinNetwork( joinedNetwork, h.ctx, logger, + h.constructors, hostName, stateDir, runtimeDir, @@ -331,7 +336,7 @@ func (nh *integrationHarnessNetwork) restart(t *testing.T) { t.Log("Loading network (restart)") var err error - nh.Network, err = load( + nh.Network, err = nh.constructors.load( nh.ctx, nh.logger, getEnvBinDirPath(),