Allow creating a network without configuring it in daemon.yml
This commit is contained in:
parent
73af69fa04
commit
3111d2ca74
@ -30,7 +30,7 @@ The requirements for this host are:
|
||||
|
||||
* At least 3 directories should be chosen, each of which will be committing at
|
||||
least 1GB. Ideally these directories should be on different physical disks,
|
||||
but if that's not possible it's ok. See the Next Steps section.
|
||||
but if that's not possible it's ok.
|
||||
|
||||
* None of the resources being used for this network (the UDP port or storage
|
||||
locations) should be being used by other networks.
|
||||
|
@ -25,8 +25,12 @@ create the signature.
|
||||
|
||||
## Releasing
|
||||
|
||||
Releases are uploaded to the repository's Releases page, and release naming
|
||||
follows the conventional semantic versioning system. Each release should be
|
||||
accompanied by a set of changes which have occurred since the last release,
|
||||
described both in the `CHANGELOG.md` file and in the description on the Release
|
||||
itself.
|
||||
Release artifactes are hosted at `micropelago.net` under
|
||||
`/isle/releases/<release name>`. An `index.gmi` page should be created in that
|
||||
directory which includes links to each artifact, as well as a changelog
|
||||
detailing all new features and fixes included since the previous release.
|
||||
|
||||
A link to the new release should be included at `/isle/releases/index.gmi`.
|
||||
|
||||
The release shoulld be tagged in the git repo using its release name as well,
|
||||
with the tag notes linking to the `micropelago.net` page.
|
||||
|
@ -11,6 +11,11 @@ go test ./... # Test everything
|
||||
|
||||
## Integration Tests
|
||||
|
||||
NOTE: before running integration tests you will want to make sure you can [build
|
||||
Isle][building] in the first place.
|
||||
|
||||
[building]: ./building.md
|
||||
|
||||
Integration tests are those which require processes or state external to the
|
||||
test itself. Integration tests are marked using the
|
||||
`toolkit.MarkIntegrationTest` function, which will cause them to be skipped
|
||||
|
@ -6,7 +6,9 @@ import (
|
||||
"fmt"
|
||||
"isle/nebula"
|
||||
"isle/toolkit"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||
)
|
||||
@ -34,14 +36,41 @@ func (f *textUnmarshalerFlag[T, P]) String() string {
|
||||
|
||||
func (f *textUnmarshalerFlag[T, P]) Type() string { return "string" }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type (
|
||||
hostNameFlag = textUnmarshalerFlag[nebula.HostName, *nebula.HostName]
|
||||
ipNetFlag = textUnmarshalerFlag[nebula.IPNet, *nebula.IPNet]
|
||||
ipFlag = textUnmarshalerFlag[netip.Addr, *netip.Addr]
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type (
|
||||
addrFlagStr string
|
||||
addrFlag = textUnmarshalerFlag[addrFlagStr, *addrFlagStr]
|
||||
)
|
||||
|
||||
func (f addrFlagStr) MarshalText() ([]byte, error) {
|
||||
return []byte(f), nil
|
||||
}
|
||||
|
||||
func (f *addrFlagStr) UnmarshalText(b []byte) error {
|
||||
str := string(b)
|
||||
|
||||
_, portStr, err := net.SplitHostPort(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := strconv.ParseUint(portStr, 10, 16); err != nil {
|
||||
return fmt.Errorf("invalid port %q", portStr)
|
||||
}
|
||||
|
||||
*f = addrFlagStr(str)
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type logLevelFlag struct {
|
||||
mlog.Level
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon"
|
||||
"isle/daemon/jsonrpc2"
|
||||
"isle/toolkit"
|
||||
@ -15,6 +17,14 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func bootstrapNewCreationParams(name, domain string) bootstrap.CreationParams {
|
||||
return bootstrap.CreationParams{
|
||||
ID: fmt.Sprintf("%s-%s", name, domain),
|
||||
Name: name,
|
||||
Domain: domain,
|
||||
}
|
||||
}
|
||||
|
||||
type runHarness struct {
|
||||
ctx context.Context
|
||||
logger *mlog.Logger
|
||||
@ -59,10 +69,11 @@ func (h *runHarness) run(t *testing.T, args ...string) error {
|
||||
)
|
||||
|
||||
return doRootCmd(h.ctx, h.logger, &subCmdCtxOpts{
|
||||
args: args,
|
||||
stdout: h.stdout,
|
||||
changeStager: h.changeStager,
|
||||
daemonRPC: daemonRPCClient,
|
||||
args: args,
|
||||
stdout: h.stdout,
|
||||
changeStager: h.changeStager,
|
||||
daemonRPC: daemonRPCClient,
|
||||
bootstrapNewCreationParams: bootstrapNewCreationParams,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,15 @@ import (
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/daemon/network"
|
||||
"isle/jsonutil"
|
||||
"isle/nebula"
|
||||
"isle/toolkit"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var subCmdNetworkCreate = subCmd{
|
||||
@ -17,8 +22,9 @@ var subCmdNetworkCreate = subCmd{
|
||||
descr: "Create's a new network, with this host being the first host in that network.",
|
||||
do: func(ctx subCmdCtx) error {
|
||||
var (
|
||||
ipNet ipNetFlag
|
||||
hostName hostNameFlag
|
||||
ipNet ipNetFlag
|
||||
hostName hostNameFlag
|
||||
vpnPublicAddr addrFlag
|
||||
)
|
||||
|
||||
name := ctx.flags.StringP(
|
||||
@ -44,6 +50,19 @@ var subCmdNetworkCreate = subCmd{
|
||||
"Name of this host, which will be the first host in the network",
|
||||
)
|
||||
|
||||
vpnPublicAddrF := ctx.flags.VarPF(
|
||||
&vpnPublicAddr,
|
||||
"vpn-public-address",
|
||||
"",
|
||||
"Public address (host:port) that this host is publicly available on",
|
||||
)
|
||||
|
||||
storageAllocStrs := ctx.flags.StringArray(
|
||||
"storage-allocation",
|
||||
nil,
|
||||
"Storage allocation on this host, in the form '<capacity-in-gb>@<path>`",
|
||||
)
|
||||
|
||||
ctx, err := ctx.withParsedFlags(&withParsedFlagsOpts{
|
||||
noNetwork: true,
|
||||
})
|
||||
@ -58,14 +77,61 @@ var subCmdNetworkCreate = subCmd{
|
||||
return errors.New("--name, --domain, --ip-net, and --hostname are required")
|
||||
}
|
||||
|
||||
type storageAlloc struct {
|
||||
capacity uint64
|
||||
path string
|
||||
}
|
||||
|
||||
storageAllocs := make([]storageAlloc, len(*storageAllocStrs))
|
||||
for i, str := range *storageAllocStrs {
|
||||
capStr, path, ok := strings.Cut(str, "@")
|
||||
if !ok {
|
||||
return fmt.Errorf(
|
||||
"malformed --storage-allocation %q, no '@' found", str,
|
||||
)
|
||||
}
|
||||
|
||||
capacity, err := strconv.ParseUint(capStr, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"invalid --storage-allocation capacity %q", capStr,
|
||||
)
|
||||
}
|
||||
|
||||
storageAllocs[i] = storageAlloc{capacity, path}
|
||||
}
|
||||
|
||||
daemonRPC, err := ctx.newDaemonRPC()
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating daemon RPC client: %w", err)
|
||||
}
|
||||
defer daemonRPC.Close()
|
||||
|
||||
var networkConfig *daecommon.NetworkConfig
|
||||
if vpnPublicAddrF.Changed || len(storageAllocs) > 0 {
|
||||
networkConfig = toolkit.Ptr(
|
||||
daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
|
||||
c.VPN.PublicAddr = string(vpnPublicAddr.V)
|
||||
for _, a := range storageAllocs {
|
||||
c.Storage.Allocations = append(
|
||||
c.Storage.Allocations,
|
||||
daecommon.ConfigStorageAllocation{
|
||||
DataPath: filepath.Join(a.path, "data"),
|
||||
MetaPath: filepath.Join(a.path, "meta"),
|
||||
Capacity: int(a.capacity),
|
||||
},
|
||||
)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
err = daemonRPC.CreateNetwork(
|
||||
ctx, *name, *domain, ipNet.V, hostName.V,
|
||||
ctx,
|
||||
ctx.opts.bootstrapNewCreationParams(*name, *domain),
|
||||
ipNet.V,
|
||||
hostName.V,
|
||||
&daemon.CreateNetworkOpts{Config: networkConfig},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating network: %w", err)
|
||||
|
@ -5,14 +5,133 @@ import (
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/nebula"
|
||||
"isle/toolkit"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNetworkCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expect func(*testing.T, *daemon.MockRPC)
|
||||
flags []string
|
||||
}{
|
||||
{
|
||||
name: "no given config",
|
||||
expect: func(t *testing.T, daemonRPC *daemon.MockRPC) {
|
||||
daemonRPC.
|
||||
On(
|
||||
"CreateNetwork",
|
||||
toolkit.MockArg[context.Context](),
|
||||
bootstrapNewCreationParams("aaa", "a.com"),
|
||||
nebula.MustParseIPNet(t, "172.16.1.0/24"),
|
||||
nebula.HostName("foo"),
|
||||
&daemon.CreateNetworkOpts{},
|
||||
).
|
||||
Return(nil).
|
||||
Once()
|
||||
},
|
||||
flags: []string{
|
||||
"--name=aaa",
|
||||
"--domain=a.com",
|
||||
"--ip-net=172.16.1.0/24",
|
||||
"--hostname=foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "partially given config",
|
||||
expect: func(t *testing.T, daemonRPC *daemon.MockRPC) {
|
||||
networkConfig := daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
|
||||
c.VPN.PublicAddr = "1.2.3.4:5"
|
||||
})
|
||||
|
||||
daemonRPC.
|
||||
On(
|
||||
"CreateNetwork",
|
||||
toolkit.MockArg[context.Context](),
|
||||
bootstrapNewCreationParams("aaa", "a.com"),
|
||||
nebula.MustParseIPNet(t, "172.16.1.0/24"),
|
||||
nebula.HostName("foo"),
|
||||
&daemon.CreateNetworkOpts{
|
||||
Config: &networkConfig,
|
||||
},
|
||||
).
|
||||
Return(nil).
|
||||
Once()
|
||||
},
|
||||
flags: []string{
|
||||
"--name=aaa",
|
||||
"--domain=a.com",
|
||||
"--ip-net=172.16.1.0/24",
|
||||
"--hostname=foo",
|
||||
"--vpn-public-address=1.2.3.4:5",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fully given config",
|
||||
expect: func(t *testing.T, daemonRPC *daemon.MockRPC) {
|
||||
networkConfig := daecommon.NewNetworkConfig(func(c *daecommon.NetworkConfig) {
|
||||
c.VPN.PublicAddr = "1.2.3.4:5"
|
||||
c.Storage.Allocations = []daecommon.ConfigStorageAllocation{
|
||||
{
|
||||
DataPath: "/a/data",
|
||||
MetaPath: "/a/meta",
|
||||
Capacity: 100,
|
||||
},
|
||||
{
|
||||
DataPath: "/b/data",
|
||||
MetaPath: "/b/meta",
|
||||
Capacity: 200,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
daemonRPC.
|
||||
On(
|
||||
"CreateNetwork",
|
||||
toolkit.MockArg[context.Context](),
|
||||
bootstrapNewCreationParams("aaa", "a.com"),
|
||||
nebula.MustParseIPNet(t, "172.16.1.0/24"),
|
||||
nebula.HostName("foo"),
|
||||
&daemon.CreateNetworkOpts{
|
||||
Config: &networkConfig,
|
||||
},
|
||||
).
|
||||
Return(nil).
|
||||
Once()
|
||||
},
|
||||
flags: []string{
|
||||
"--name=aaa",
|
||||
"--domain=a.com",
|
||||
"--ip-net=172.16.1.0/24",
|
||||
"--hostname=foo",
|
||||
"--vpn-public-address=1.2.3.4:5",
|
||||
"--storage-allocation=100@/a",
|
||||
"--storage-allocation=200@/b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
h = newRunHarness(t)
|
||||
args = append([]string{"network", "create"}, test.flags...)
|
||||
)
|
||||
|
||||
test.expect(t, h.daemonRPC)
|
||||
assert.NoError(t, h.run(t, args...))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon"
|
||||
"isle/daemon/jsonrpc2"
|
||||
"isle/jsonutil"
|
||||
@ -40,6 +41,9 @@ type subCmdCtxOpts struct {
|
||||
stdout io.Writer
|
||||
changeStager *changeStager
|
||||
daemonRPC daemon.RPC
|
||||
|
||||
// defaults to bootstrap.NewCreationParams
|
||||
bootstrapNewCreationParams func(name, domain string) bootstrap.CreationParams
|
||||
}
|
||||
|
||||
func (o *subCmdCtxOpts) withDefaults() *subCmdCtxOpts {
|
||||
@ -55,6 +59,10 @@ func (o *subCmdCtxOpts) withDefaults() *subCmdCtxOpts {
|
||||
o.stdout = os.Stdout
|
||||
}
|
||||
|
||||
if o.bootstrapNewCreationParams == nil {
|
||||
o.bootstrapNewCreationParams = bootstrap.NewCreationParams
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
var subCmdVPNPublicAddressGet = subCmd{
|
||||
@ -38,8 +37,11 @@ var subCmdVPNPublicAddressSet = subCmd{
|
||||
name: "set",
|
||||
descr: "Set the public address of the host, or overwrite the already configured one",
|
||||
do: func(ctx subCmdCtx) error {
|
||||
publicAddr := ctx.flags.String(
|
||||
"public-addr",
|
||||
var publicAddr addrFlag
|
||||
|
||||
publicAddrF := ctx.flags.VarPF(
|
||||
&publicAddr,
|
||||
"to",
|
||||
"",
|
||||
"Public address (host:port) that this host is publicly available on",
|
||||
)
|
||||
@ -49,10 +51,8 @@ var subCmdVPNPublicAddressSet = subCmd{
|
||||
return fmt.Errorf("parsing flags: %w", err)
|
||||
}
|
||||
|
||||
if *publicAddr == "" {
|
||||
if !publicAddrF.Changed {
|
||||
return errors.New("--public-addr is required")
|
||||
} else if _, _, err := net.SplitHostPort(*publicAddr); err != nil {
|
||||
return fmt.Errorf("invalid --public-addr: %w", err)
|
||||
}
|
||||
|
||||
daemonRPC, err := ctx.newDaemonRPC()
|
||||
@ -66,7 +66,7 @@ var subCmdVPNPublicAddressSet = subCmd{
|
||||
return fmt.Errorf("getting network config: %w", err)
|
||||
}
|
||||
|
||||
config.VPN.PublicAddr = *publicAddr
|
||||
config.VPN.PublicAddr = string(publicAddr.V)
|
||||
|
||||
return daemonRPC.SetConfig(ctx, config)
|
||||
},
|
||||
|
@ -47,15 +47,15 @@ func (c *rpcClient) CreateNebulaCertificate(ctx context.Context, h1 nebula.HostN
|
||||
return
|
||||
}
|
||||
|
||||
func (c *rpcClient) CreateNetwork(ctx context.Context, name string, domain string, ipNet nebula.IPNet, hostName nebula.HostName) (err error) {
|
||||
func (c *rpcClient) CreateNetwork(ctx context.Context, creationParams bootstrap.CreationParams, ipNet nebula.IPNet, hostName nebula.HostName, opts *CreateNetworkOpts) (err error) {
|
||||
err = c.client.Call(
|
||||
ctx,
|
||||
nil,
|
||||
"CreateNetwork",
|
||||
name,
|
||||
domain,
|
||||
creationParams,
|
||||
ipNet,
|
||||
hostName,
|
||||
opts,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ var _ RPC = (*Daemon)(nil)
|
||||
type joinedNetwork struct {
|
||||
id string
|
||||
network.Network
|
||||
config *daecommon.NetworkConfig
|
||||
creationParams bootstrap.CreationParams
|
||||
config *daecommon.NetworkConfig
|
||||
}
|
||||
|
||||
// Daemon implements all methods of the Daemon interface, plus others used
|
||||
@ -96,7 +97,7 @@ func New(
|
||||
return nil, fmt.Errorf("loading network %q: %w", id, err)
|
||||
}
|
||||
|
||||
d.networks[id] = joinedNetwork{id, n, networkConfig}
|
||||
d.networks[id] = joinedNetwork{id, n, creationParams, networkConfig}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
@ -114,26 +115,37 @@ func New(
|
||||
// and will have full administrative privileges.
|
||||
//
|
||||
// Errors:
|
||||
// - network.ErrInvalidConfig
|
||||
// - ErrAlreadyJoined
|
||||
// - [network.ErrInvalidConfig]
|
||||
// - [ErrAlreadyJoined]
|
||||
// - [ErrManagedNetworkConfig] - if `opts.NetworkConfig` is given, but a
|
||||
// NetworkConfig is also provided in the Daemon's Config.
|
||||
func (d *Daemon) CreateNetwork(
|
||||
ctx context.Context,
|
||||
name, domain string, ipNet nebula.IPNet, hostName nebula.HostName,
|
||||
creationParams bootstrap.CreationParams,
|
||||
ipNet nebula.IPNet,
|
||||
hostName nebula.HostName,
|
||||
opts *CreateNetworkOpts,
|
||||
) error {
|
||||
opts = opts.withDefaults()
|
||||
|
||||
d.l.Lock()
|
||||
defer d.l.Unlock()
|
||||
|
||||
var (
|
||||
creationParams = bootstrap.NewCreationParams(name, domain)
|
||||
networkConfig = pickNetworkConfig(
|
||||
networkConfig = pickNetworkConfig(
|
||||
d.daemonConfig.Networks, creationParams,
|
||||
)
|
||||
networkLogger = networkLogger(d.logger, creationParams)
|
||||
)
|
||||
|
||||
if joined, err := alreadyJoined(ctx, d.networks, creationParams); err != nil {
|
||||
return fmt.Errorf("checking if already joined to network: %w", err)
|
||||
} else if joined {
|
||||
if opts.Config != nil {
|
||||
if networkConfig != nil {
|
||||
return ErrManagedNetworkConfig
|
||||
}
|
||||
networkConfig = opts.Config
|
||||
}
|
||||
|
||||
if alreadyJoined(d.networks, creationParams) {
|
||||
return ErrAlreadyJoined
|
||||
}
|
||||
|
||||
@ -154,7 +166,7 @@ func (d *Daemon) CreateNetwork(
|
||||
|
||||
networkLogger.Info(ctx, "Network created successfully")
|
||||
d.networks[creationParams.ID] = joinedNetwork{
|
||||
creationParams.ID, n, networkConfig,
|
||||
creationParams.ID, n, creationParams, networkConfig,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -181,9 +193,7 @@ func (d *Daemon) JoinNetwork(
|
||||
)
|
||||
)
|
||||
|
||||
if joined, err := alreadyJoined(ctx, d.networks, creationParams); err != nil {
|
||||
return fmt.Errorf("checking if already joined to network: %w", err)
|
||||
} else if joined {
|
||||
if alreadyJoined(d.networks, creationParams) {
|
||||
return ErrAlreadyJoined
|
||||
}
|
||||
|
||||
@ -203,7 +213,9 @@ func (d *Daemon) JoinNetwork(
|
||||
}
|
||||
|
||||
networkLogger.Info(ctx, "Network joined successfully")
|
||||
d.networks[networkID] = joinedNetwork{networkID, n, networkConfig}
|
||||
d.networks[networkID] = joinedNetwork{
|
||||
networkID, n, creationParams, networkConfig,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/daemon/network"
|
||||
"isle/nebula"
|
||||
"isle/toolkit"
|
||||
"testing"
|
||||
|
||||
@ -234,6 +235,134 @@ func TestNew(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
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_SetConfig(t *testing.T) {
|
||||
t.Run("success", func(t *testing.T) {
|
||||
var (
|
||||
|
@ -58,22 +58,13 @@ func pickNetwork(
|
||||
}
|
||||
|
||||
func alreadyJoined(
|
||||
ctx context.Context,
|
||||
networks map[string]joinedNetwork,
|
||||
creationParams bootstrap.CreationParams,
|
||||
) (
|
||||
bool, error,
|
||||
) {
|
||||
for networkID, network := range networks {
|
||||
existingCreationParams, err := network.GetNetworkCreationParams(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf(
|
||||
"getting creation params of network %q: %w", networkID, err,
|
||||
)
|
||||
} else if existingCreationParams.Conflicts(creationParams) {
|
||||
return true, nil
|
||||
networks map[string]joinedNetwork, creationParams bootstrap.CreationParams,
|
||||
) bool {
|
||||
for _, network := range networks {
|
||||
if network.creationParams.Conflicts(creationParams) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
return false
|
||||
}
|
||||
|
@ -14,16 +14,31 @@ import (
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||
)
|
||||
|
||||
// CreateNetworkOpts are optional arguments to the CreateNetwork method. A nil
|
||||
// value is equivalent to a zero value.
|
||||
type CreateNetworkOpts struct {
|
||||
// Config will be used as the NetworkConfig for the new Network, rather than
|
||||
// picking one provided in the Daemon's Config.
|
||||
Config *daecommon.NetworkConfig
|
||||
}
|
||||
|
||||
func (o *CreateNetworkOpts) withDefaults() *CreateNetworkOpts {
|
||||
if o == nil {
|
||||
o = new(CreateNetworkOpts)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// RPC defines the methods which the Daemon exposes over RPC (via the RPCHandler
|
||||
// or HTTPHandler methods). Method documentation can be found on the Daemon
|
||||
// type.
|
||||
type RPC interface {
|
||||
CreateNetwork(
|
||||
ctx context.Context,
|
||||
name string,
|
||||
domain string,
|
||||
creationParams bootstrap.CreationParams,
|
||||
ipNet nebula.IPNet,
|
||||
hostName nebula.HostName,
|
||||
opts *CreateNetworkOpts,
|
||||
) error
|
||||
|
||||
JoinNetwork(context.Context, network.JoiningBootstrap) error
|
||||
|
@ -76,17 +76,17 @@ func (_m *MockRPC) CreateNebulaCertificate(_a0 context.Context, _a1 nebula.HostN
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateNetwork provides a mock function with given fields: ctx, name, domain, ipNet, hostName
|
||||
func (_m *MockRPC) CreateNetwork(ctx context.Context, name string, domain string, ipNet nebula.IPNet, hostName nebula.HostName) error {
|
||||
ret := _m.Called(ctx, name, domain, ipNet, hostName)
|
||||
// CreateNetwork provides a mock function with given fields: ctx, creationParams, ipNet, hostName, opts
|
||||
func (_m *MockRPC) CreateNetwork(ctx context.Context, creationParams bootstrap.CreationParams, ipNet nebula.IPNet, hostName nebula.HostName, opts *CreateNetworkOpts) error {
|
||||
ret := _m.Called(ctx, creationParams, ipNet, hostName, opts)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreateNetwork")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, nebula.IPNet, nebula.HostName) error); ok {
|
||||
r0 = rf(ctx, name, domain, ipNet, hostName)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, bootstrap.CreationParams, nebula.IPNet, nebula.HostName, *CreateNetworkOpts) error); ok {
|
||||
r0 = rf(ctx, creationParams, ipNet, hostName, opts)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
@ -4,11 +4,21 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// IPNet is the CIDR of a nebula network.
|
||||
type IPNet net.IPNet
|
||||
|
||||
// MustParseIPNet is a test helper for parsing a string into an IPNet.
|
||||
func MustParseIPNet(t *testing.T, str string) IPNet {
|
||||
var ipNet IPNet
|
||||
if err := ipNet.UnmarshalText([]byte(str)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ipNet
|
||||
}
|
||||
|
||||
// UnmarshalText parses and validates an IPNet from a text string.
|
||||
func (n *IPNet) UnmarshalText(b []byte) error {
|
||||
str := string(b)
|
||||
|
@ -10,3 +10,8 @@ func IsZero[T any](v T) bool {
|
||||
var zero T
|
||||
return reflect.DeepEqual(v, zero)
|
||||
}
|
||||
|
||||
// Ptr returns a pointer to the given value.
|
||||
func Ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
12
tasks/soon/code/storage-allocation-modify.md
Normal file
12
tasks/soon/code/storage-allocation-modify.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
type: task
|
||||
---
|
||||
|
||||
# Implement `storage allocation modify` Sub-Command
|
||||
|
||||
It should be possible to modify an allocation from the command-line, including
|
||||
changing its capacity, location, and port numbers.
|
||||
|
||||
Location will be the difficult one. It will require shutting down the process,
|
||||
copying the data from the old location, starting the process back up, and either
|
||||
removing or renaming the old data to mark it as being a backup.
|
@ -1,7 +1,5 @@
|
||||
---
|
||||
type: task
|
||||
after:
|
||||
- code/**
|
||||
---
|
||||
|
||||
# Update Documentation
|
||||
@ -11,3 +9,13 @@ updated.
|
||||
|
||||
Check through all development documentation, especially that surrounding
|
||||
testing.
|
||||
|
||||
Doc changes to included are:
|
||||
|
||||
- New page on network configuration, how it can be done via the `daemon.yml`
|
||||
file or via the command-line (but not both!)
|
||||
|
||||
- Rework "Contributing a Lighthouse" so that it doesn't directly mention nebula
|
||||
or lighthouses at all.
|
||||
|
||||
- Remove nebula reference from `daemon.yml` comments.
|
||||
|
Loading…
Reference in New Issue
Block a user