2024-12-17 15:05:41 +00:00
|
|
|
package network
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2024-12-17 15:47:33 +00:00
|
|
|
"fmt"
|
2024-12-17 15:05:41 +00:00
|
|
|
"io/fs"
|
|
|
|
"isle/bootstrap"
|
|
|
|
"isle/daemon/children"
|
|
|
|
"isle/daemon/daecommon"
|
|
|
|
"isle/nebula"
|
|
|
|
"isle/toolkit"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"testing"
|
2024-12-17 15:47:33 +00:00
|
|
|
"time"
|
2024-12-17 15:05:41 +00:00
|
|
|
|
|
|
|
"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)
|
2024-12-17 15:47:33 +00:00
|
|
|
now = time.Date(2024, 12, 17, 16, 15, 14, 0, time.UTC)
|
2024-12-17 15:05:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
loader, err := NewLoader(
|
|
|
|
ctx, logger, envBinDir.Path, &LoaderOpts{
|
|
|
|
EnvVars: daecommon.EnvVars{
|
|
|
|
StateDir: stateDir,
|
|
|
|
RuntimeDir: runtimeDir,
|
|
|
|
},
|
|
|
|
constructors: constructors,
|
2024-12-17 15:47:33 +00:00
|
|
|
nowFunc: func() time.Time { return now },
|
2024-12-17 15:05:41 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
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)
|
|
|
|
})
|
2024-12-17 15:47:33 +00:00
|
|
|
|
|
|
|
t.Run("after Leave", func(t *testing.T) {
|
|
|
|
h := newLoaderHarness(t)
|
|
|
|
h.join(t, allCreationParams[0])
|
|
|
|
h.join(t, allCreationParams[1])
|
|
|
|
assert.NoError(t, h.loader.Leave(h.ctx, allCreationParams[1]))
|
|
|
|
got, err := h.loader.Loadable(h.ctx)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, allCreationParams[:1], got)
|
|
|
|
})
|
2024-12-17 15:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
})
|
|
|
|
}
|
2024-12-17 15:47:33 +00:00
|
|
|
|
|
|
|
func TestLoader_Leave(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)
|
|
|
|
)
|
|
|
|
|
|
|
|
h.join(t, creationParams)
|
|
|
|
|
|
|
|
assert.NoError(t, h.loader.Leave(h.ctx, creationParams))
|
|
|
|
h.assertDirExists(t, false, networkStateDirPath)
|
|
|
|
h.assertDirExists(t, false, networkRuntimeDirPath)
|
|
|
|
|
|
|
|
wantStateDirRenamedTo := h.networkStateDirPath(fmt.Sprintf(
|
|
|
|
".%s.20241217-161514.bak",
|
|
|
|
creationParams.ID,
|
|
|
|
))
|
|
|
|
t.Logf("wantStateDirRenamedTo:%q", wantStateDirRenamedTo)
|
|
|
|
h.assertDirExists(t, true, wantStateDirRenamedTo)
|
|
|
|
|
|
|
|
// Make sure the data inside the state directory was actually preserved.
|
|
|
|
bootstrap, err := loadBootstrapFromStateDir(wantStateDirRenamedTo)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, creationParams, bootstrap.NetworkCreationParams)
|
|
|
|
})
|
|
|
|
}
|