isle/go/daemon/daemon_test.go

480 lines
12 KiB
Go

package daemon
import (
"context"
"isle/bootstrap"
"isle/daemon/daecommon"
"isle/daemon/network"
"isle/nebula"
"isle/toolkit"
"testing"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type expectNetworkLoad struct {
creationParams bootstrap.CreationParams
networkConfig *daecommon.NetworkConfig
network *network.MockNetwork
}
type harnessOpts struct {
config daecommon.Config
expectNetworksLoaded []expectNetworkLoad
expectStoredConfigs map[string]daecommon.NetworkConfig
}
func (o *harnessOpts) withDefaults() *harnessOpts {
if o == nil {
o = new(harnessOpts)
}
return o
}
type harness struct {
ctx context.Context
networkLoader *network.MockLoader
daemon *Daemon
}
func newHarness(t *testing.T, opts *harnessOpts) *harness {
t.Parallel()
opts = opts.withDefaults()
var (
ctx = context.Background()
logger = toolkit.NewTestLogger(t)
networkLoader = network.NewMockLoader(t)
)
expectLoadable := make(
[]bootstrap.CreationParams, len(opts.expectNetworksLoaded),
)
for i, expectNetworkLoaded := range opts.expectNetworksLoaded {
expectLoadable[i] = expectNetworkLoaded.creationParams
networkLoader.
On(
"Load",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
expectNetworkLoaded.creationParams,
&network.Opts{
Config: expectNetworkLoaded.networkConfig,
},
).
Return(expectNetworkLoaded.network, nil).
Once()
expectNetworkLoaded.network.On("Shutdown").Return(nil).Once()
}
for id, networkConfig := range opts.expectStoredConfigs {
networkLoader.
On("StoredConfig", toolkit.MockArg[context.Context](), id).
Return(networkConfig, nil).
Once()
}
networkLoader.
On("Loadable", toolkit.MockArg[context.Context]()).
Return(expectLoadable, nil)
daemon, err := New(ctx, logger, networkLoader, opts.config)
require.NoError(t, err)
t.Cleanup(func() {
t.Log("Shutting down Daemon")
assert.NoError(t, daemon.Shutdown())
})
return &harness{ctx, networkLoader, daemon}
}
func TestNew(t *testing.T) {
t.Run("no networks loaded", func(t *testing.T) {
_ = newHarness(t, nil)
})
t.Run("DEPRECATED network config matching", func(t *testing.T) {
var (
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
networkConfig = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.DNS.Resolvers = []string{"foo"}
})
config = daecommon.Config{
Networks: map[string]daecommon.NetworkConfig{
daecommon.DeprecatedNetworkID: networkConfig,
},
}
)
_ = newHarness(t, &harnessOpts{
config: config,
expectNetworksLoaded: []expectNetworkLoad{
{
creationParams,
&networkConfig,
network.NewMockNetwork(t),
},
},
})
})
t.Run("network config matching", func(t *testing.T) {
var (
creationParamsA = bootstrap.NewCreationParams("AAA", "a.com")
creationParamsB = bootstrap.NewCreationParams("BBB", "b.com")
creationParamsC = bootstrap.NewCreationParams("CCC", "c.com")
creationParamsD = bootstrap.NewCreationParams("DDD", "d.com")
networkConfigA = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.DNS.Resolvers = []string{"foo"}
})
networkConfigB = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.VPN.PublicAddr = "1.2.3.4:5"
})
networkConfigC = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.Storage.Allocations = []daecommon.ConfigStorageAllocation{
{
DataPath: "/path/data",
MetaPath: "/path/meta",
Capacity: 1,
},
}
})
config = daecommon.Config{
Networks: map[string]daecommon.NetworkConfig{
creationParamsA.ID: networkConfigA,
creationParamsB.Name: networkConfigB,
creationParamsC.Domain: networkConfigC,
"unknown": {},
},
}
)
_ = newHarness(t, &harnessOpts{
config: config,
expectNetworksLoaded: []expectNetworkLoad{
{
creationParamsA,
&networkConfigA,
network.NewMockNetwork(t),
},
{
creationParamsB,
&networkConfigB,
network.NewMockNetwork(t),
},
{
creationParamsC,
&networkConfigC,
network.NewMockNetwork(t),
},
{
creationParamsD,
nil,
network.NewMockNetwork(t),
},
},
expectStoredConfigs: map[string]daecommon.NetworkConfig{
creationParamsD.ID: daecommon.NewNetworkConfig(nil),
},
})
})
t.Run("invalid config", func(t *testing.T) {
var (
ctx = context.Background()
logger = toolkit.NewTestLogger(t)
networkLoader = network.NewMockLoader(t)
creationParamsA = bootstrap.NewCreationParams("AAA", "a.com")
creationParamsB = bootstrap.NewCreationParams("BBB", "b.com")
networkConfigA = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.VPN.PublicAddr = "1.1.1.1:5"
})
networkConfigB = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.VPN.PublicAddr = "2.2.2.2:5"
})
config = daecommon.Config{
Networks: map[string]daecommon.NetworkConfig{
creationParamsA.ID: networkConfigA,
},
}
)
networkLoader.
On("Loadable", toolkit.MockArg[context.Context]()).
Return(
[]bootstrap.CreationParams{creationParamsA, creationParamsB},
nil,
).
Once()
networkLoader.
On(
"StoredConfig",
toolkit.MockArg[context.Context](),
creationParamsB.ID,
).
Return(networkConfigB, nil).
Once()
_, err := New(ctx, logger, networkLoader, config)
assert.ErrorContains(t, err, "two networks with the same vpn.public_addr port")
})
}
func TestDaemon_CreateNetwork(t *testing.T) {
t.Run("ErrManagedNetworkConfig", func(t *testing.T) {
var (
networkConfig = daecommon.NewNetworkConfig(nil)
h = newHarness(t, &harnessOpts{
config: daecommon.Config{
Networks: map[string]daecommon.NetworkConfig{
"AAA": networkConfig,
},
},
})
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
ipNet = nebula.MustParseIPNet(t, "172.16.0.0/24")
)
err := h.daemon.CreateNetwork(
h.ctx,
creationParams,
ipNet,
nebula.HostName("foo"),
&CreateNetworkOpts{Config: &networkConfig},
)
assert.ErrorIs(t, err, ErrManagedNetworkConfig)
})
t.Run("ErrAlreadyJoined", func(t *testing.T) {
var (
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
networkConfig = daecommon.NewNetworkConfig(nil)
h = newHarness(t, &harnessOpts{
expectNetworksLoaded: []expectNetworkLoad{{
creationParams, nil, network.NewMockNetwork(t),
}},
expectStoredConfigs: map[string]daecommon.NetworkConfig{
creationParams.ID: networkConfig,
},
})
ipNet = nebula.MustParseIPNet(t, "172.16.0.0/24")
)
err := h.daemon.CreateNetwork(
h.ctx,
bootstrap.NewCreationParams("AAA", "aaa.com"),
ipNet,
nebula.HostName("foo"),
nil,
)
assert.ErrorIs(t, err, ErrAlreadyJoined)
})
t.Run("success/config given", func(t *testing.T) {
networkA := network.NewMockNetwork(t)
networkA.On("Shutdown").Return(nil).Once()
var (
h = newHarness(t, nil)
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
networkConfig = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.VPN.PublicAddr = "1.2.3.4:50"
})
ipNet = nebula.MustParseIPNet(t, "172.16.0.0/24")
hostName = nebula.HostName("foo")
)
h.networkLoader.
On(
"Create",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
creationParams,
ipNet,
hostName,
&network.Opts{
Config: &networkConfig,
},
).
Return(networkA, nil).
Once()
err := h.daemon.CreateNetwork(
h.ctx,
creationParams,
ipNet,
hostName,
&CreateNetworkOpts{
Config: &networkConfig,
},
)
assert.NoError(t, err)
assert.Contains(t, h.daemon.networks, creationParams.ID)
})
t.Run("success/no config given", func(t *testing.T) {
networkA := network.NewMockNetwork(t)
networkA.On("Shutdown").Return(nil).Once()
var (
h = newHarness(t, nil)
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
ipNet = nebula.MustParseIPNet(t, "172.16.0.0/24")
hostName = nebula.HostName("foo")
)
h.networkLoader.
On(
"Create",
toolkit.MockArg[context.Context](),
toolkit.MockArg[*mlog.Logger](),
creationParams,
ipNet,
hostName,
&network.Opts{},
).
Return(networkA, nil).
Once()
err := h.daemon.CreateNetwork(
h.ctx,
creationParams,
ipNet,
hostName,
nil,
)
assert.NoError(t, err)
assert.Contains(t, h.daemon.networks, creationParams.ID)
})
}
func TestDaemon_LeaveNetwork(t *testing.T) {
t.Run("success", func(t *testing.T) {
var (
networkA = network.NewMockNetwork(t)
creationParamsA = bootstrap.NewCreationParams("AAA", "a.com")
h = newHarness(t, &harnessOpts{
expectNetworksLoaded: []expectNetworkLoad{{
creationParamsA, nil, networkA,
}},
expectStoredConfigs: map[string]daecommon.NetworkConfig{
creationParamsA.ID: daecommon.NewNetworkConfig(nil),
},
})
)
h.networkLoader.
On("Leave", toolkit.MockArg[context.Context](), creationParamsA).
Return(nil).
Once()
ctx := WithNetwork(h.ctx, "AAA")
assert.NoError(t, h.daemon.LeaveNetwork(ctx))
joinedCreationParams, err := h.daemon.GetNetworks(h.ctx)
assert.NoError(t, err)
assert.Empty(t, joinedCreationParams)
})
}
func TestDaemon_SetConfig(t *testing.T) {
t.Run("success", func(t *testing.T) {
var (
networkA = network.NewMockNetwork(t)
creationParamsA = bootstrap.NewCreationParams("AAA", "a.com")
h = newHarness(t, &harnessOpts{
expectNetworksLoaded: []expectNetworkLoad{{
creationParamsA, nil, networkA,
}},
expectStoredConfigs: map[string]daecommon.NetworkConfig{
creationParamsA.ID: daecommon.NewNetworkConfig(nil),
},
})
networkConfig = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.VPN.PublicAddr = "1.2.3.4:5"
})
)
networkA.
On("SetConfig", toolkit.MockArg[context.Context](), networkConfig).
Return(nil).
Once()
err := h.daemon.SetConfig(h.ctx, networkConfig)
assert.NoError(t, err)
})
t.Run("ErrManagedNetworkConfig", func(t *testing.T) {
var (
creationParams = bootstrap.NewCreationParams("AAA", "a.com")
networkA = network.NewMockNetwork(t)
networkConfig = daecommon.NewNetworkConfig(nil)
h = newHarness(t, &harnessOpts{
config: daecommon.Config{
Networks: map[string]daecommon.NetworkConfig{
creationParams.Name: networkConfig,
},
},
expectNetworksLoaded: []expectNetworkLoad{
{creationParams, &networkConfig, networkA},
},
})
)
networkConfig.VPN.PublicAddr = "1.2.3.4:5"
err := h.daemon.SetConfig(h.ctx, networkConfig)
assert.ErrorIs(t, err, ErrManagedNetworkConfig)
})
t.Run("ErrInvalidConfig", func(t *testing.T) {
var (
creationParamsA = bootstrap.NewCreationParams("AAA", "a.com")
networkA = network.NewMockNetwork(t)
networkConfigA = daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
c.VPN.PublicAddr = "1.2.3.4:5"
})
creationParamsB = bootstrap.NewCreationParams("BBB", "b.com")
networkB = network.NewMockNetwork(t)
networkConfigB = daecommon.NewNetworkConfig(nil)
h = newHarness(t, &harnessOpts{
expectNetworksLoaded: []expectNetworkLoad{
{creationParamsA, nil, networkA},
{creationParamsB, nil, networkB},
},
expectStoredConfigs: map[string]daecommon.NetworkConfig{
creationParamsA.ID: networkConfigA,
creationParamsB.ID: networkConfigB,
},
})
)
networkA.
On("GetConfig", toolkit.MockArg[context.Context]()).
Return(networkConfigA, nil).
Once()
networkConfigB.VPN.PublicAddr = "1.1.1.1:5"
err := h.daemon.SetConfig(WithNetwork(h.ctx, "BBB"), networkConfigB)
assert.ErrorIs(t, err, network.ErrInvalidConfig)
})
}