Pass NetworkConfig into Network loaders as an optional argument
WIP: daemon needs tests
This commit is contained in:
parent
72bca72b29
commit
4f074391e4
@ -44,22 +44,20 @@ var HTTPSocketPath = sync.OnceValue(func() string {
|
||||
|
||||
func pickNetworkConfig(
|
||||
daemonConfig daecommon.Config, creationParams bootstrap.CreationParams,
|
||||
) (
|
||||
daecommon.NetworkConfig, bool,
|
||||
) {
|
||||
) *daecommon.NetworkConfig {
|
||||
if len(daemonConfig.Networks) == 1 { // DEPRECATED
|
||||
if c, ok := daemonConfig.Networks[daecommon.DeprecatedNetworkID]; ok {
|
||||
return c, true
|
||||
return &c
|
||||
}
|
||||
}
|
||||
|
||||
for searchStr, networkConfig := range daemonConfig.Networks {
|
||||
if creationParams.Matches(searchStr) {
|
||||
return networkConfig, true
|
||||
return &networkConfig
|
||||
}
|
||||
}
|
||||
|
||||
return daecommon.NetworkConfig{}, false
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -19,6 +19,11 @@ import (
|
||||
|
||||
var _ RPC = (*Daemon)(nil)
|
||||
|
||||
type joinedNetwork struct {
|
||||
network.Network
|
||||
config *daecommon.NetworkConfig
|
||||
}
|
||||
|
||||
// Daemon implements all methods of the Daemon interface, plus others used
|
||||
// to manage this particular implementation.
|
||||
//
|
||||
@ -41,7 +46,7 @@ type Daemon struct {
|
||||
daemonConfig daecommon.Config
|
||||
|
||||
l sync.RWMutex
|
||||
networks map[string]network.Network
|
||||
networks map[string]joinedNetwork
|
||||
}
|
||||
|
||||
// New initializes and returns a Daemon.
|
||||
@ -57,7 +62,7 @@ func New(
|
||||
logger: logger,
|
||||
networkLoader: networkLoader,
|
||||
daemonConfig: daemonConfig,
|
||||
networks: map[string]network.Network{},
|
||||
networks: map[string]joinedNetwork{},
|
||||
}
|
||||
|
||||
loadableNetworks, err := networkLoader.Loadable(ctx)
|
||||
@ -69,20 +74,23 @@ func New(
|
||||
ctx = mctx.WithAnnotator(ctx, creationParams)
|
||||
|
||||
var (
|
||||
id = creationParams.ID
|
||||
networkConfig, _ = pickNetworkConfig(daemonConfig, creationParams)
|
||||
id = creationParams.ID
|
||||
networkConfig = pickNetworkConfig(daemonConfig, creationParams)
|
||||
)
|
||||
|
||||
d.networks[id], err = networkLoader.Load(
|
||||
n, err := networkLoader.Load(
|
||||
ctx,
|
||||
logger.WithNamespace("network"),
|
||||
networkConfig,
|
||||
creationParams,
|
||||
nil,
|
||||
&network.Opts{
|
||||
Config: networkConfig,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading network %q: %w", id, err)
|
||||
}
|
||||
|
||||
d.networks[id] = joinedNetwork{n, networkConfig}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
@ -105,17 +113,16 @@ func (d *Daemon) CreateNetwork(
|
||||
ctx context.Context,
|
||||
name, domain string, ipNet nebula.IPNet, hostName nebula.HostName,
|
||||
) error {
|
||||
creationParams := bootstrap.NewCreationParams(name, domain)
|
||||
ctx = mctx.WithAnnotator(ctx, creationParams)
|
||||
|
||||
networkConfig, ok := pickNetworkConfig(d.daemonConfig, creationParams)
|
||||
if !ok {
|
||||
return errors.New("couldn't find network config for network being created")
|
||||
}
|
||||
|
||||
d.l.Lock()
|
||||
defer d.l.Unlock()
|
||||
|
||||
var (
|
||||
creationParams = bootstrap.NewCreationParams(name, domain)
|
||||
networkConfig = pickNetworkConfig(d.daemonConfig, creationParams)
|
||||
)
|
||||
|
||||
ctx = mctx.WithAnnotator(ctx, 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 {
|
||||
@ -126,18 +133,19 @@ func (d *Daemon) CreateNetwork(
|
||||
n, err := d.networkLoader.Create(
|
||||
ctx,
|
||||
d.logger.WithNamespace("network"),
|
||||
networkConfig,
|
||||
creationParams,
|
||||
ipNet,
|
||||
hostName,
|
||||
nil,
|
||||
&network.Opts{
|
||||
Config: networkConfig,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating network: %w", err)
|
||||
}
|
||||
|
||||
d.logger.Info(ctx, "Network created successfully")
|
||||
d.networks[creationParams.ID] = n
|
||||
d.networks[creationParams.ID] = joinedNetwork{n, networkConfig}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -149,17 +157,17 @@ func (d *Daemon) CreateNetwork(
|
||||
func (d *Daemon) JoinNetwork(
|
||||
ctx context.Context, newBootstrap network.JoiningBootstrap,
|
||||
) error {
|
||||
d.l.Lock()
|
||||
defer d.l.Unlock()
|
||||
|
||||
var (
|
||||
creationParams = newBootstrap.Bootstrap.NetworkCreationParams
|
||||
networkConfig, _ = pickNetworkConfig(d.daemonConfig, creationParams)
|
||||
networkID = creationParams.ID
|
||||
creationParams = newBootstrap.Bootstrap.NetworkCreationParams
|
||||
networkID = creationParams.ID
|
||||
networkConfig = pickNetworkConfig(d.daemonConfig, creationParams)
|
||||
)
|
||||
|
||||
ctx = mctx.WithAnnotator(ctx, newBootstrap.Bootstrap.NetworkCreationParams)
|
||||
|
||||
d.l.Lock()
|
||||
defer d.l.Unlock()
|
||||
|
||||
if joined, err := alreadyJoined(ctx, d.networks, creationParams); err != nil {
|
||||
return fmt.Errorf("checking if already joined to network: %w", err)
|
||||
} else if joined {
|
||||
@ -170,9 +178,10 @@ func (d *Daemon) JoinNetwork(
|
||||
n, err := d.networkLoader.Join(
|
||||
ctx,
|
||||
d.logger.WithNamespace("network"),
|
||||
networkConfig,
|
||||
newBootstrap,
|
||||
nil,
|
||||
&network.Opts{
|
||||
Config: networkConfig,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
@ -181,14 +190,14 @@ func (d *Daemon) JoinNetwork(
|
||||
}
|
||||
|
||||
d.logger.Info(ctx, "Network joined successfully")
|
||||
d.networks[networkID] = n
|
||||
d.networks[networkID] = joinedNetwork{n, networkConfig}
|
||||
return nil
|
||||
}
|
||||
|
||||
func withNetwork[Res any](
|
||||
ctx context.Context,
|
||||
d *Daemon,
|
||||
fn func(context.Context, network.Network) (Res, error),
|
||||
fn func(context.Context, joinedNetwork) (Res, error),
|
||||
) (
|
||||
Res, error,
|
||||
) {
|
||||
@ -238,7 +247,7 @@ func (d *Daemon) GetHosts(ctx context.Context) ([]bootstrap.Host, error) {
|
||||
return withNetwork(
|
||||
ctx,
|
||||
d,
|
||||
func(ctx context.Context, n network.Network) ([]bootstrap.Host, error) {
|
||||
func(ctx context.Context, n joinedNetwork) ([]bootstrap.Host, error) {
|
||||
return n.GetHosts(ctx)
|
||||
},
|
||||
)
|
||||
@ -254,7 +263,7 @@ func (d *Daemon) GetGarageClientParams(
|
||||
ctx,
|
||||
d,
|
||||
func(
|
||||
ctx context.Context, n network.Network,
|
||||
ctx context.Context, n joinedNetwork,
|
||||
) (
|
||||
network.GarageClientParams, error,
|
||||
) {
|
||||
@ -274,7 +283,7 @@ func (d *Daemon) GetNebulaCAPublicCredentials(
|
||||
ctx,
|
||||
d,
|
||||
func(
|
||||
ctx context.Context, n network.Network,
|
||||
ctx context.Context, n joinedNetwork,
|
||||
) (
|
||||
nebula.CAPublicCredentials, error,
|
||||
) {
|
||||
@ -289,7 +298,7 @@ func (d *Daemon) RemoveHost(ctx context.Context, hostName nebula.HostName) error
|
||||
ctx,
|
||||
d,
|
||||
func(
|
||||
ctx context.Context, n network.Network,
|
||||
ctx context.Context, n joinedNetwork,
|
||||
) (
|
||||
struct{}, error,
|
||||
) {
|
||||
@ -311,7 +320,7 @@ func (d *Daemon) CreateHost(
|
||||
ctx,
|
||||
d,
|
||||
func(
|
||||
ctx context.Context, n network.Network,
|
||||
ctx context.Context, n joinedNetwork,
|
||||
) (
|
||||
network.JoiningBootstrap, error,
|
||||
) {
|
||||
@ -332,7 +341,7 @@ func (d *Daemon) CreateNebulaCertificate(
|
||||
ctx,
|
||||
d,
|
||||
func(
|
||||
ctx context.Context, n network.Network,
|
||||
ctx context.Context, n joinedNetwork,
|
||||
) (
|
||||
nebula.Certificate, error,
|
||||
) {
|
||||
@ -341,6 +350,7 @@ func (d *Daemon) CreateNebulaCertificate(
|
||||
)
|
||||
}
|
||||
|
||||
// GetConfig implements the method for the network.RPC interface.
|
||||
func (d *Daemon) GetConfig(
|
||||
ctx context.Context,
|
||||
) (
|
||||
@ -350,7 +360,7 @@ func (d *Daemon) GetConfig(
|
||||
ctx,
|
||||
d,
|
||||
func(
|
||||
ctx context.Context, n network.Network,
|
||||
ctx context.Context, n joinedNetwork,
|
||||
) (
|
||||
daecommon.NetworkConfig, error,
|
||||
) {
|
||||
@ -359,13 +369,18 @@ func (d *Daemon) GetConfig(
|
||||
)
|
||||
}
|
||||
|
||||
// SetConfig implements the method for the network.RPC interface.
|
||||
func (d *Daemon) SetConfig(
|
||||
ctx context.Context, config daecommon.NetworkConfig,
|
||||
) error {
|
||||
_, err := withNetwork(
|
||||
ctx,
|
||||
d,
|
||||
func(ctx context.Context, n network.Network) (struct{}, error) {
|
||||
func(ctx context.Context, n joinedNetwork) (struct{}, error) {
|
||||
if n.config != nil {
|
||||
return struct{}{}, ErrUserManagedNetworkConfig
|
||||
}
|
||||
|
||||
// TODO needs to check that public addresses aren't being shared
|
||||
// across networks, and whatever else happens in Config.Validate.
|
||||
return struct{}{}, n.SetConfig(ctx, config)
|
||||
|
@ -10,6 +10,7 @@ const (
|
||||
errCodeAlreadyJoined
|
||||
errCodeNoMatchingNetworks
|
||||
errCodeMultipleMatchingNetworks
|
||||
errCodeUserManagedNetworkConfig
|
||||
)
|
||||
|
||||
var (
|
||||
@ -33,4 +34,11 @@ var (
|
||||
errCodeMultipleMatchingNetworks,
|
||||
"Multiple networks matched the search string",
|
||||
)
|
||||
|
||||
// ErrUserManagedNetworkConfig is returned when attempting to modify a
|
||||
// network config which is managed by the user.
|
||||
ErrUserManagedNetworkConfig = jsonrpc2.NewError(
|
||||
errCodeUserManagedNetworkConfig,
|
||||
"Network configuration is managed by the user",
|
||||
)
|
||||
)
|
||||
|
@ -10,17 +10,17 @@ import (
|
||||
func pickNetwork(
|
||||
ctx context.Context,
|
||||
networkLoader network.Loader,
|
||||
networks map[string]network.Network,
|
||||
networks map[string]joinedNetwork,
|
||||
) (
|
||||
network.Network, error,
|
||||
joinedNetwork, error,
|
||||
) {
|
||||
if len(networks) == 0 {
|
||||
return nil, ErrNoNetwork
|
||||
return joinedNetwork{}, ErrNoNetwork
|
||||
}
|
||||
|
||||
creationParams, err := networkLoader.Loadable(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting loadable networks: %w", err)
|
||||
return joinedNetwork{}, fmt.Errorf("getting loadable networks: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
@ -35,9 +35,9 @@ func pickNetwork(
|
||||
}
|
||||
|
||||
if len(matchingNetworkIDs) == 0 {
|
||||
return nil, ErrNoMatchingNetworks
|
||||
return joinedNetwork{}, ErrNoMatchingNetworks
|
||||
} else if len(matchingNetworkIDs) > 1 {
|
||||
return nil, ErrMultipleMatchingNetworks
|
||||
return joinedNetwork{}, ErrMultipleMatchingNetworks
|
||||
}
|
||||
|
||||
return networks[matchingNetworkIDs[0]], nil
|
||||
@ -45,7 +45,7 @@ func pickNetwork(
|
||||
|
||||
func alreadyJoined(
|
||||
ctx context.Context,
|
||||
networks map[string]network.Network,
|
||||
networks map[string]joinedNetwork,
|
||||
creationParams bootstrap.CreationParams,
|
||||
) (
|
||||
bool, error,
|
||||
|
42
go/daemon/network/config.go
Normal file
42
go/daemon/network/config.go
Normal file
@ -0,0 +1,42 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/jsonutil"
|
||||
"isle/toolkit"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// loadStoreConfig writes the given NetworkConfig to the networkStateDir if the
|
||||
// config is non-nil. If the config is nil then a config is read from
|
||||
// networkStateDir, returning the zero value if no config was previously
|
||||
// stored.
|
||||
func loadStoreConfig(
|
||||
networkStateDir toolkit.Dir, config *daecommon.NetworkConfig,
|
||||
) (
|
||||
daecommon.NetworkConfig, error,
|
||||
) {
|
||||
path := filepath.Join(networkStateDir.Path, "config.json")
|
||||
|
||||
if config == nil {
|
||||
config = new(daecommon.NetworkConfig)
|
||||
err := jsonutil.LoadFile(&config, path)
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return daecommon.NetworkConfig{}, fmt.Errorf(
|
||||
"loading %q: %w", path, err,
|
||||
)
|
||||
}
|
||||
return *config, nil
|
||||
}
|
||||
|
||||
if err := jsonutil.WriteFile(*config, path, 0600); err != nil {
|
||||
return daecommon.NetworkConfig{}, fmt.Errorf(
|
||||
"writing to %q: %w", path, err,
|
||||
)
|
||||
}
|
||||
|
||||
return *config, nil
|
||||
}
|
@ -59,7 +59,6 @@ type Loader interface {
|
||||
Load(
|
||||
context.Context,
|
||||
*mlog.Logger,
|
||||
daecommon.NetworkConfig,
|
||||
bootstrap.CreationParams,
|
||||
*Opts,
|
||||
) (
|
||||
@ -73,7 +72,6 @@ type Loader interface {
|
||||
Join(
|
||||
context.Context,
|
||||
*mlog.Logger,
|
||||
daecommon.NetworkConfig,
|
||||
JoiningBootstrap,
|
||||
*Opts,
|
||||
) (
|
||||
@ -91,12 +89,11 @@ type Loader interface {
|
||||
// - hostName: The name of this first host in the network.
|
||||
//
|
||||
// Errors:
|
||||
// - ErrInvalidConfig - if daemonConfig doesn't have 3 storage allocations
|
||||
// configured.
|
||||
// - ErrInvalidConfig - If the Opts.Config field is not valid. It must be
|
||||
// non-nil and have at least 3 storage allocations.
|
||||
Create(
|
||||
context.Context,
|
||||
*mlog.Logger,
|
||||
daecommon.NetworkConfig,
|
||||
bootstrap.CreationParams,
|
||||
nebula.IPNet,
|
||||
nebula.HostName,
|
||||
@ -188,7 +185,7 @@ func (l *loader) Loadable(
|
||||
creationParams := make([]bootstrap.CreationParams, 0, len(networkStateDirs))
|
||||
|
||||
for _, networkStateDir := range networkStateDirs {
|
||||
thisCreationParams, err := LoadCreationParams(networkStateDir)
|
||||
thisCreationParams, err := loadCreationParams(networkStateDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"loading creation params from %q: %w",
|
||||
@ -205,7 +202,6 @@ func (l *loader) Loadable(
|
||||
func (l *loader) Load(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
networkConfig daecommon.NetworkConfig,
|
||||
creationParams bootstrap.CreationParams,
|
||||
opts *Opts,
|
||||
) (
|
||||
@ -226,7 +222,6 @@ func (l *loader) Load(
|
||||
ctx,
|
||||
logger.WithNamespace("network"),
|
||||
l.envBinDirPath,
|
||||
networkConfig,
|
||||
networkStateDir,
|
||||
networkRuntimeDir,
|
||||
opts,
|
||||
@ -236,7 +231,6 @@ func (l *loader) Load(
|
||||
func (l *loader) Join(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
networkConfig daecommon.NetworkConfig,
|
||||
joiningBootstrap JoiningBootstrap,
|
||||
opts *Opts,
|
||||
) (
|
||||
@ -260,7 +254,6 @@ func (l *loader) Join(
|
||||
ctx,
|
||||
logger.WithNamespace("network"),
|
||||
l.envBinDirPath,
|
||||
networkConfig,
|
||||
joiningBootstrap,
|
||||
networkStateDir,
|
||||
networkRuntimeDir,
|
||||
@ -271,7 +264,6 @@ func (l *loader) Join(
|
||||
func (l *loader) Create(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
networkConfig daecommon.NetworkConfig,
|
||||
creationParams bootstrap.CreationParams,
|
||||
ipNet nebula.IPNet,
|
||||
hostName nebula.HostName,
|
||||
@ -294,7 +286,6 @@ func (l *loader) Create(
|
||||
ctx,
|
||||
logger.WithNamespace("network"),
|
||||
l.envBinDirPath,
|
||||
networkConfig,
|
||||
networkStateDir,
|
||||
networkRuntimeDir,
|
||||
creationParams,
|
||||
|
@ -154,6 +154,14 @@ type Network interface {
|
||||
// Network instance. A nil Opts is equivalent to a zero value.
|
||||
type Opts struct {
|
||||
GarageAdminToken string // Will be randomly generated if left unset.
|
||||
|
||||
// Config will be used as the configuration of the Network from its
|
||||
// initialization onwards.
|
||||
//
|
||||
// If not given then the most recent NetworkConfig for the network will be
|
||||
// used, either that which it was most recently initialized with or which
|
||||
// was passed to [SetConfig].
|
||||
Config *daecommon.NetworkConfig
|
||||
}
|
||||
|
||||
func (o *Opts) withDefaults() *Opts {
|
||||
@ -189,36 +197,53 @@ type network struct {
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// instatiateNetwork returns an instantiated *network instance which has not yet
|
||||
// been initialized.
|
||||
func instatiateNetwork(
|
||||
// newNetwork returns an instantiated *network instance. All initialization
|
||||
// steps which are common to all *network creation methods (load, join, create)
|
||||
// are included here as well.
|
||||
func newNetwork(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
networkConfig daecommon.NetworkConfig,
|
||||
envBinDirPath string,
|
||||
stateDir toolkit.Dir,
|
||||
runtimeDir toolkit.Dir,
|
||||
dirsMayExist bool,
|
||||
opts *Opts,
|
||||
) *network {
|
||||
ctx = context.WithoutCancel(ctx)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &network{
|
||||
logger: logger,
|
||||
networkConfig: networkConfig,
|
||||
envBinDirPath: envBinDirPath,
|
||||
stateDir: stateDir,
|
||||
runtimeDir: runtimeDir,
|
||||
opts: opts.withDefaults(),
|
||||
workerCtx: ctx,
|
||||
workerCancel: cancel,
|
||||
) (
|
||||
*network, error,
|
||||
) {
|
||||
ctx, cancel := context.WithCancel(context.WithoutCancel(ctx))
|
||||
|
||||
var (
|
||||
n = &network{
|
||||
logger: logger,
|
||||
envBinDirPath: envBinDirPath,
|
||||
stateDir: stateDir,
|
||||
runtimeDir: runtimeDir,
|
||||
opts: opts.withDefaults(),
|
||||
workerCtx: ctx,
|
||||
workerCancel: cancel,
|
||||
}
|
||||
err error
|
||||
)
|
||||
|
||||
n.networkConfig, err = loadStoreConfig(n.stateDir, n.opts.Config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolving network config: %w", err)
|
||||
}
|
||||
|
||||
secretsDir, err := n.stateDir.MkChildDir("secrets", dirsMayExist)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating secrets dir: %w", err)
|
||||
}
|
||||
|
||||
n.secretsStore = secrets.NewFSStore(secretsDir.Path)
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// LoadCreationParams returns the CreationParams of a Network which was
|
||||
// loadCreationParams returns the CreationParams of a Network which was
|
||||
// Created/Joined with the given state directory.
|
||||
//
|
||||
// TODO probably can be private
|
||||
func LoadCreationParams(
|
||||
func loadCreationParams(
|
||||
stateDir toolkit.Dir,
|
||||
) (
|
||||
bootstrap.CreationParams, error,
|
||||
@ -244,25 +269,23 @@ func load(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
envBinDirPath string,
|
||||
networkConfig daecommon.NetworkConfig,
|
||||
stateDir toolkit.Dir,
|
||||
runtimeDir toolkit.Dir,
|
||||
opts *Opts,
|
||||
) (
|
||||
Network, error,
|
||||
) {
|
||||
n := instatiateNetwork(
|
||||
n, err := newNetwork(
|
||||
ctx,
|
||||
logger,
|
||||
networkConfig,
|
||||
envBinDirPath,
|
||||
stateDir,
|
||||
runtimeDir,
|
||||
true,
|
||||
opts,
|
||||
)
|
||||
|
||||
if err := n.initializeDirs(true); err != nil {
|
||||
return nil, fmt.Errorf("initializing directories: %w", err)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("instantiating Network: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
@ -285,7 +308,6 @@ func join(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
envBinDirPath string,
|
||||
networkConfig daecommon.NetworkConfig,
|
||||
joiningBootstrap JoiningBootstrap,
|
||||
stateDir toolkit.Dir,
|
||||
runtimeDir toolkit.Dir,
|
||||
@ -293,18 +315,17 @@ func join(
|
||||
) (
|
||||
Network, error,
|
||||
) {
|
||||
n := instatiateNetwork(
|
||||
n, err := newNetwork(
|
||||
ctx,
|
||||
logger,
|
||||
networkConfig,
|
||||
envBinDirPath,
|
||||
stateDir,
|
||||
runtimeDir,
|
||||
false,
|
||||
opts,
|
||||
)
|
||||
|
||||
if err := n.initializeDirs(false); err != nil {
|
||||
return nil, fmt.Errorf("initializing directories: %w", err)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("instantiating Network: %w", err)
|
||||
}
|
||||
|
||||
if err := secrets.Import(
|
||||
@ -324,7 +345,6 @@ func create(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
envBinDirPath string,
|
||||
networkConfig daecommon.NetworkConfig,
|
||||
stateDir toolkit.Dir,
|
||||
runtimeDir toolkit.Dir,
|
||||
creationParams bootstrap.CreationParams,
|
||||
@ -334,12 +354,6 @@ func create(
|
||||
) (
|
||||
Network, error,
|
||||
) {
|
||||
if len(networkConfig.Storage.Allocations) < 3 {
|
||||
return nil, ErrInvalidConfig.WithData(
|
||||
"At least three storage allocations are required.",
|
||||
)
|
||||
}
|
||||
|
||||
nebulaCACreds, err := nebula.NewCACredentials(creationParams.Domain, ipNet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating nebula CA cert: %w", err)
|
||||
@ -347,18 +361,23 @@ func create(
|
||||
|
||||
garageRPCSecret := toolkit.RandStr(32)
|
||||
|
||||
n := instatiateNetwork(
|
||||
n, err := newNetwork(
|
||||
ctx,
|
||||
logger,
|
||||
networkConfig,
|
||||
envBinDirPath,
|
||||
stateDir,
|
||||
runtimeDir,
|
||||
false,
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("instantiating Network: %w", err)
|
||||
}
|
||||
|
||||
if err := n.initializeDirs(false); err != nil {
|
||||
return nil, fmt.Errorf("initializing directories: %w", err)
|
||||
if len(n.networkConfig.Storage.Allocations) < 3 {
|
||||
return nil, ErrInvalidConfig.WithData(
|
||||
"At least three storage allocations are required.",
|
||||
)
|
||||
}
|
||||
|
||||
err = daecommon.SetGarageRPCSecret(ctx, n.secretsStore, garageRPCSecret)
|
||||
@ -391,16 +410,6 @@ func create(
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (n *network) initializeDirs(mayExist bool) error {
|
||||
secretsDir, err := n.stateDir.MkChildDir("secrets", mayExist)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating secrets dir: %w", err)
|
||||
}
|
||||
|
||||
n.secretsStore = secrets.NewFSStore(secretsDir.Path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) periodically(
|
||||
label string,
|
||||
fn func(context.Context) error,
|
||||
@ -1016,6 +1025,10 @@ func (n *network) GetConfig(context.Context) (daecommon.NetworkConfig, error) {
|
||||
func (n *network) SetConfig(
|
||||
ctx context.Context, config daecommon.NetworkConfig,
|
||||
) error {
|
||||
if _, err := loadStoreConfig(n.stateDir, &config); err != nil {
|
||||
return fmt.Errorf("storing new config: %w", err)
|
||||
}
|
||||
|
||||
prevBootstrap, err := n.reload(ctx, &config, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reloading config: %w", err)
|
||||
|
@ -17,7 +17,7 @@ func TestCreate(t *testing.T) {
|
||||
network = h.createNetwork(t, "primus", nil)
|
||||
)
|
||||
|
||||
gotCreationParams, err := LoadCreationParams(network.stateDir)
|
||||
gotCreationParams, err := loadCreationParams(network.stateDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(
|
||||
t, gotCreationParams, network.getBootstrap(t).NetworkCreationParams,
|
||||
@ -25,31 +25,30 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
var (
|
||||
h = newIntegrationHarness(t)
|
||||
network = h.createNetwork(t, "primus", &createNetworkOpts{
|
||||
manualShutdown: true,
|
||||
})
|
||||
)
|
||||
t.Run("given config", func(t *testing.T) {
|
||||
var (
|
||||
h = newIntegrationHarness(t)
|
||||
network = h.createNetwork(t, "primus", nil)
|
||||
networkConfig = network.getConfig(t)
|
||||
)
|
||||
|
||||
t.Log("Shutting down network")
|
||||
assert.NoError(t, network.Shutdown())
|
||||
network.opts.Config = &networkConfig
|
||||
network.restart(t)
|
||||
|
||||
t.Log("Calling Load")
|
||||
loadedNetwork, err := load(
|
||||
h.ctx,
|
||||
h.logger.WithNamespace("loadedNetwork"),
|
||||
getEnvBinDirPath(),
|
||||
network.getConfig(t),
|
||||
network.stateDir,
|
||||
h.mkDir(t, "runtime"),
|
||||
network.opts,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, networkConfig, network.getConfig(t))
|
||||
})
|
||||
|
||||
t.Cleanup(func() {
|
||||
t.Log("Shutting down loadedNetwork")
|
||||
assert.NoError(t, loadedNetwork.Shutdown())
|
||||
t.Run("load previous config", func(t *testing.T) {
|
||||
var (
|
||||
h = newIntegrationHarness(t)
|
||||
network = h.createNetwork(t, "primus", nil)
|
||||
networkConfig = network.getConfig(t)
|
||||
)
|
||||
|
||||
network.opts.Config = nil
|
||||
network.restart(t)
|
||||
|
||||
assert.Equal(t, networkConfig, network.getConfig(t))
|
||||
})
|
||||
}
|
||||
|
||||
@ -61,13 +60,7 @@ func TestJoin(t *testing.T) {
|
||||
secondus = h.joinNetwork(t, primus, "secondus", nil)
|
||||
)
|
||||
|
||||
primusHosts, err := primus.GetHosts(h.ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
secondusHosts, err := secondus.GetHosts(h.ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, primusHosts, secondusHosts)
|
||||
assert.Equal(t, primus.getHostsByName(t), secondus.getHostsByName(t))
|
||||
})
|
||||
|
||||
t.Run("with alloc", func(t *testing.T) {
|
||||
@ -84,28 +77,10 @@ func TestJoin(t *testing.T) {
|
||||
t.Log("reloading primus' hosts")
|
||||
assert.NoError(t, primus.Network.(*network).reloadHosts(h.ctx))
|
||||
|
||||
primusHosts, err := primus.GetHosts(h.ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
secondusHosts, err := secondus.GetHosts(h.ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, primusHosts, secondusHosts)
|
||||
assert.Equal(t, primus.getHostsByName(t), secondus.getHostsByName(t))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNetwork_GetConfig(t *testing.T) {
|
||||
var (
|
||||
h = newIntegrationHarness(t)
|
||||
network = h.createNetwork(t, "primus", nil)
|
||||
)
|
||||
|
||||
config, err := network.GetConfig(h.ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, config, network.getConfig(t))
|
||||
}
|
||||
|
||||
func TestNetwork_SetConfig(t *testing.T) {
|
||||
allocsToRoles := func(
|
||||
hostName nebula.HostName, allocs []bootstrap.GarageHostInstance,
|
||||
@ -259,4 +234,22 @@ func TestNetwork_SetConfig(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, layout.Roles, removedRole)
|
||||
})
|
||||
|
||||
t.Run("changes reflected after restart", func(t *testing.T) {
|
||||
var (
|
||||
h = newIntegrationHarness(t)
|
||||
network = h.createNetwork(t, "primus", &createNetworkOpts{
|
||||
numStorageAllocs: 4,
|
||||
})
|
||||
networkConfig = network.getConfig(t)
|
||||
)
|
||||
|
||||
networkConfig.Storage.Allocations = networkConfig.Storage.Allocations[:3]
|
||||
assert.NoError(t, network.SetConfig(h.ctx, networkConfig))
|
||||
|
||||
network.opts.Config = nil
|
||||
network.restart(t)
|
||||
|
||||
assert.Equal(t, networkConfig, network.getConfig(t))
|
||||
})
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ func (h *integrationHarness) createNetwork(
|
||||
t *testing.T,
|
||||
hostNameStr string,
|
||||
opts *createNetworkOpts,
|
||||
) integrationHarnessNetwork {
|
||||
) *integrationHarnessNetwork {
|
||||
t.Logf("Creating as %q", hostNameStr)
|
||||
opts = opts.withDefaults()
|
||||
|
||||
@ -222,6 +222,7 @@ func (h *integrationHarness) createNetwork(
|
||||
|
||||
networkOpts = &Opts{
|
||||
GarageAdminToken: "admin_token",
|
||||
Config: &networkConfig,
|
||||
}
|
||||
)
|
||||
|
||||
@ -229,7 +230,6 @@ func (h *integrationHarness) createNetwork(
|
||||
h.ctx,
|
||||
logger,
|
||||
getEnvBinDirPath(),
|
||||
networkConfig,
|
||||
stateDir,
|
||||
runtimeDir,
|
||||
opts.creationParams,
|
||||
@ -241,16 +241,7 @@ func (h *integrationHarness) createNetwork(
|
||||
t.Fatalf("creating Network: %v", err)
|
||||
}
|
||||
|
||||
if !opts.manualShutdown {
|
||||
t.Cleanup(func() {
|
||||
t.Logf("Shutting down Network %q", hostNameStr)
|
||||
if err := network.Shutdown(); err != nil {
|
||||
t.Logf("Shutting down Network %q failed: %v", hostNameStr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return integrationHarnessNetwork{
|
||||
nh := &integrationHarnessNetwork{
|
||||
network,
|
||||
h.ctx,
|
||||
logger,
|
||||
@ -259,6 +250,17 @@ func (h *integrationHarness) createNetwork(
|
||||
runtimeDir,
|
||||
networkOpts,
|
||||
}
|
||||
|
||||
if !opts.manualShutdown {
|
||||
t.Cleanup(func() {
|
||||
t.Logf("Shutting down Network %q", hostNameStr)
|
||||
if err := nh.Shutdown(); err != nil {
|
||||
t.Logf("Shutting down Network %q failed: %v", hostNameStr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return nh
|
||||
}
|
||||
|
||||
type joinNetworkOpts struct {
|
||||
@ -279,10 +281,10 @@ func (o *joinNetworkOpts) withDefaults() *joinNetworkOpts {
|
||||
|
||||
func (h *integrationHarness) joinNetwork(
|
||||
t *testing.T,
|
||||
network integrationHarnessNetwork,
|
||||
network *integrationHarnessNetwork,
|
||||
hostNameStr string,
|
||||
opts *joinNetworkOpts,
|
||||
) integrationHarnessNetwork {
|
||||
) *integrationHarnessNetwork {
|
||||
opts = opts.withDefaults()
|
||||
hostName := nebula.HostName(hostNameStr)
|
||||
|
||||
@ -301,6 +303,7 @@ func (h *integrationHarness) joinNetwork(
|
||||
runtimeDir = h.mkDir(t, "runtime")
|
||||
networkOpts = &Opts{
|
||||
GarageAdminToken: "admin_token",
|
||||
Config: &networkConfig,
|
||||
}
|
||||
)
|
||||
|
||||
@ -309,7 +312,6 @@ func (h *integrationHarness) joinNetwork(
|
||||
h.ctx,
|
||||
logger,
|
||||
getEnvBinDirPath(),
|
||||
networkConfig,
|
||||
joiningBootstrap,
|
||||
stateDir,
|
||||
runtimeDir,
|
||||
@ -319,16 +321,7 @@ func (h *integrationHarness) joinNetwork(
|
||||
t.Fatalf("joining network: %v", err)
|
||||
}
|
||||
|
||||
if !opts.manualShutdown {
|
||||
t.Cleanup(func() {
|
||||
t.Logf("Shutting down Network %q", hostNameStr)
|
||||
if err := joinedNetwork.Shutdown(); err != nil {
|
||||
t.Logf("Shutting down Network %q failed: %v", hostNameStr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return integrationHarnessNetwork{
|
||||
nh := &integrationHarnessNetwork{
|
||||
joinedNetwork,
|
||||
h.ctx,
|
||||
logger,
|
||||
@ -337,6 +330,34 @@ func (h *integrationHarness) joinNetwork(
|
||||
runtimeDir,
|
||||
networkOpts,
|
||||
}
|
||||
|
||||
if !opts.manualShutdown {
|
||||
t.Cleanup(func() {
|
||||
t.Logf("Shutting down Network %q", hostNameStr)
|
||||
if err := nh.Shutdown(); err != nil {
|
||||
t.Logf("Shutting down Network %q failed: %v", hostNameStr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return nh
|
||||
}
|
||||
|
||||
func (nh *integrationHarnessNetwork) restart(t *testing.T) {
|
||||
t.Log("Shutting down network (restart)")
|
||||
require.NoError(t, nh.Network.Shutdown())
|
||||
|
||||
t.Log("Loading network (restart)")
|
||||
var err error
|
||||
nh.Network, err = load(
|
||||
nh.ctx,
|
||||
nh.logger,
|
||||
getEnvBinDirPath(),
|
||||
nh.stateDir,
|
||||
nh.runtimeDir,
|
||||
nh.opts,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func (nh *integrationHarnessNetwork) getConfig(t *testing.T) daecommon.NetworkConfig {
|
||||
|
@ -3,6 +3,7 @@ package daemon
|
||||
import (
|
||||
"context"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/daemon/jsonrpc2"
|
||||
"isle/daemon/network"
|
||||
"isle/nebula"
|
||||
@ -25,13 +26,23 @@ type RPC interface {
|
||||
|
||||
GetNetworks(context.Context) ([]bootstrap.CreationParams, error)
|
||||
|
||||
// SetConfig extends the [network.RPC] method of the same name such that
|
||||
// [ErrUserManagedNetworkConfig] is returned if the picked network is
|
||||
// configured as part of the [daecommon.Config] which the Daemon was
|
||||
// initialized with.
|
||||
//
|
||||
// See the `network.RPC` documentation in this interface for more usage
|
||||
// details.
|
||||
SetConfig(context.Context, daecommon.NetworkConfig) error
|
||||
|
||||
// All network.RPC methods are automatically implemented by Daemon using the
|
||||
// currently joined network. If no network is joined then any call to these
|
||||
// methods will return ErrNoNetwork.
|
||||
//
|
||||
// All calls to these methods must be accompanied with a context produced by
|
||||
// WithNetwork, in order to choose the network. These methods may return
|
||||
// these errors, in addition to those documented on the individual methods:
|
||||
// If more than one Network is joined then all calls to these methods must
|
||||
// be accompanied with a context produced by WithNetwork, in order to choose
|
||||
// the network. These methods may return these errors, in addition to those
|
||||
// documented on the individual methods:
|
||||
//
|
||||
// Errors:
|
||||
// - ErrNoNetwork
|
||||
|
Loading…
Reference in New Issue
Block a user