Compare commits

..

No commits in common. "4f074391e44fccd537d7818f35ba0cbbe011a574" and "f0cb29b553e0c11a82aab8ff7181ff980ccdba16" have entirely different histories.

8 changed files with 229 additions and 276 deletions

View File

@ -44,20 +44,22 @@ var HTTPSocketPath = sync.OnceValue(func() string {
func pickNetworkConfig( func pickNetworkConfig(
daemonConfig daecommon.Config, creationParams bootstrap.CreationParams, daemonConfig daecommon.Config, creationParams bootstrap.CreationParams,
) *daecommon.NetworkConfig { ) (
daecommon.NetworkConfig, bool,
) {
if len(daemonConfig.Networks) == 1 { // DEPRECATED if len(daemonConfig.Networks) == 1 { // DEPRECATED
if c, ok := daemonConfig.Networks[daecommon.DeprecatedNetworkID]; ok { if c, ok := daemonConfig.Networks[daecommon.DeprecatedNetworkID]; ok {
return &c return c, true
} }
} }
for searchStr, networkConfig := range daemonConfig.Networks { for searchStr, networkConfig := range daemonConfig.Networks {
if creationParams.Matches(searchStr) { if creationParams.Matches(searchStr) {
return &networkConfig return networkConfig, true
} }
} }
return nil return daecommon.NetworkConfig{}, false
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -21,7 +21,8 @@ var _ RPC = (*Daemon)(nil)
type joinedNetwork struct { type joinedNetwork struct {
network.Network network.Network
config *daecommon.NetworkConfig
userConfig bool
} }
// Daemon implements all methods of the Daemon interface, plus others used // Daemon implements all methods of the Daemon interface, plus others used
@ -74,23 +75,25 @@ func New(
ctx = mctx.WithAnnotator(ctx, creationParams) ctx = mctx.WithAnnotator(ctx, creationParams)
var ( var (
id = creationParams.ID id = creationParams.ID
networkConfig = pickNetworkConfig(daemonConfig, creationParams) networkConfig, _ = pickNetworkConfig(daemonConfig, creationParams)
) )
n, err := networkLoader.Load( network, err := networkLoader.Load(
ctx, ctx,
logger.WithNamespace("network"), logger.WithNamespace("network"),
networkConfig,
creationParams, creationParams,
&network.Opts{ nil,
Config: networkConfig,
},
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("loading network %q: %w", id, err) return nil, fmt.Errorf("loading network %q: %w", id, err)
} }
d.networks[id] = joinedNetwork{n, networkConfig} d.networks[id] = joinedNetwork{
Network: network,
userConfig: true,
}
} }
return d, nil return d, nil
@ -111,18 +114,21 @@ func New(
// - network.ErrInvalidConfig // - network.ErrInvalidConfig
func (d *Daemon) CreateNetwork( func (d *Daemon) CreateNetwork(
ctx context.Context, ctx context.Context,
name, domain string, ipNet nebula.IPNet, hostName nebula.HostName, name, domain string,
ipNet nebula.IPNet,
hostName nebula.HostName,
) error { ) 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() d.l.Lock()
defer d.l.Unlock() 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 { if joined, err := alreadyJoined(ctx, d.networks, creationParams); err != nil {
return fmt.Errorf("checking if already joined to network: %w", err) return fmt.Errorf("checking if already joined to network: %w", err)
} else if joined { } else if joined {
@ -133,19 +139,22 @@ func (d *Daemon) CreateNetwork(
n, err := d.networkLoader.Create( n, err := d.networkLoader.Create(
ctx, ctx,
d.logger.WithNamespace("network"), d.logger.WithNamespace("network"),
networkConfig,
creationParams, creationParams,
ipNet, ipNet,
hostName, hostName,
&network.Opts{ nil,
Config: networkConfig,
},
) )
if err != nil { if err != nil {
return fmt.Errorf("creating network: %w", err) return fmt.Errorf("creating network: %w", err)
} }
d.logger.Info(ctx, "Network created successfully") d.logger.Info(ctx, "Network created successfully")
d.networks[creationParams.ID] = joinedNetwork{n, networkConfig} d.networks[creationParams.ID] = joinedNetwork{
Network: n,
userConfig: true,
}
return nil return nil
} }
@ -157,17 +166,17 @@ func (d *Daemon) CreateNetwork(
func (d *Daemon) JoinNetwork( func (d *Daemon) JoinNetwork(
ctx context.Context, newBootstrap network.JoiningBootstrap, ctx context.Context, newBootstrap network.JoiningBootstrap,
) error { ) error {
d.l.Lock()
defer d.l.Unlock()
var ( var (
creationParams = newBootstrap.Bootstrap.NetworkCreationParams creationParams = newBootstrap.Bootstrap.NetworkCreationParams
networkID = creationParams.ID networkConfig, _ = pickNetworkConfig(d.daemonConfig, creationParams)
networkConfig = pickNetworkConfig(d.daemonConfig, creationParams) networkID = creationParams.ID
) )
ctx = mctx.WithAnnotator(ctx, newBootstrap.Bootstrap.NetworkCreationParams) ctx = mctx.WithAnnotator(ctx, newBootstrap.Bootstrap.NetworkCreationParams)
d.l.Lock()
defer d.l.Unlock()
if joined, err := alreadyJoined(ctx, d.networks, creationParams); err != nil { if joined, err := alreadyJoined(ctx, d.networks, creationParams); err != nil {
return fmt.Errorf("checking if already joined to network: %w", err) return fmt.Errorf("checking if already joined to network: %w", err)
} else if joined { } else if joined {
@ -178,10 +187,9 @@ func (d *Daemon) JoinNetwork(
n, err := d.networkLoader.Join( n, err := d.networkLoader.Join(
ctx, ctx,
d.logger.WithNamespace("network"), d.logger.WithNamespace("network"),
networkConfig,
newBootstrap, newBootstrap,
&network.Opts{ nil,
Config: networkConfig,
},
) )
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
@ -190,14 +198,17 @@ func (d *Daemon) JoinNetwork(
} }
d.logger.Info(ctx, "Network joined successfully") d.logger.Info(ctx, "Network joined successfully")
d.networks[networkID] = joinedNetwork{n, networkConfig} d.networks[networkID] = joinedNetwork{
Network: n,
userConfig: true,
}
return nil return nil
} }
func withNetwork[Res any]( func withNetwork[Res any](
ctx context.Context, ctx context.Context,
d *Daemon, d *Daemon,
fn func(context.Context, joinedNetwork) (Res, error), fn func(context.Context, network.Network) (Res, error),
) ( ) (
Res, error, Res, error,
) { ) {
@ -242,12 +253,41 @@ func (d *Daemon) GetNetworks(
return res, nil return res, nil
} }
// SetConfig applies a new NetworkConfig to a joined network. It uses the same
// mechanism as the rest of the [network.RPC] methods on the Daemon to choose a
// network.
//
// Besides the errors related to network choosing which are returned by all
// other [network.RPC] methods, this may also return ErrUserManagedNetworkConfig
// if the network's configuration is not managed by the Daemon, but is instead
// provided via the config by the user.
func (d *Daemon) SetConfig(
ctx context.Context, config daecommon.NetworkConfig,
) error {
d.l.RLock()
defer d.l.RUnlock()
// TODO needs to check that public addresses aren't being shared
// across networks, and whatever else happens in Config.Validate.
network, err := pickNetwork(ctx, d.networkLoader, d.networks)
if err != nil {
return err
}
if network.userConfig {
return ErrUserManagedNetworkConfig
}
return network.SetConfig(ctx, config)
}
// GetHost implements the method for the network.RPC interface. // GetHost implements the method for the network.RPC interface.
func (d *Daemon) GetHosts(ctx context.Context) ([]bootstrap.Host, error) { func (d *Daemon) GetHosts(ctx context.Context) ([]bootstrap.Host, error) {
return withNetwork( return withNetwork(
ctx, ctx,
d, d,
func(ctx context.Context, n joinedNetwork) ([]bootstrap.Host, error) { func(ctx context.Context, n network.Network) ([]bootstrap.Host, error) {
return n.GetHosts(ctx) return n.GetHosts(ctx)
}, },
) )
@ -263,7 +303,7 @@ func (d *Daemon) GetGarageClientParams(
ctx, ctx,
d, d,
func( func(
ctx context.Context, n joinedNetwork, ctx context.Context, n network.Network,
) ( ) (
network.GarageClientParams, error, network.GarageClientParams, error,
) { ) {
@ -283,7 +323,7 @@ func (d *Daemon) GetNebulaCAPublicCredentials(
ctx, ctx,
d, d,
func( func(
ctx context.Context, n joinedNetwork, ctx context.Context, n network.Network,
) ( ) (
nebula.CAPublicCredentials, error, nebula.CAPublicCredentials, error,
) { ) {
@ -298,7 +338,7 @@ func (d *Daemon) RemoveHost(ctx context.Context, hostName nebula.HostName) error
ctx, ctx,
d, d,
func( func(
ctx context.Context, n joinedNetwork, ctx context.Context, n network.Network,
) ( ) (
struct{}, error, struct{}, error,
) { ) {
@ -320,7 +360,7 @@ func (d *Daemon) CreateHost(
ctx, ctx,
d, d,
func( func(
ctx context.Context, n joinedNetwork, ctx context.Context, n network.Network,
) ( ) (
network.JoiningBootstrap, error, network.JoiningBootstrap, error,
) { ) {
@ -341,7 +381,7 @@ func (d *Daemon) CreateNebulaCertificate(
ctx, ctx,
d, d,
func( func(
ctx context.Context, n joinedNetwork, ctx context.Context, n network.Network,
) ( ) (
nebula.Certificate, error, nebula.Certificate, error,
) { ) {
@ -350,7 +390,6 @@ func (d *Daemon) CreateNebulaCertificate(
) )
} }
// GetConfig implements the method for the network.RPC interface.
func (d *Daemon) GetConfig( func (d *Daemon) GetConfig(
ctx context.Context, ctx context.Context,
) ( ) (
@ -360,7 +399,7 @@ func (d *Daemon) GetConfig(
ctx, ctx,
d, d,
func( func(
ctx context.Context, n joinedNetwork, ctx context.Context, n network.Network,
) ( ) (
daecommon.NetworkConfig, error, daecommon.NetworkConfig, error,
) { ) {
@ -369,26 +408,6 @@ 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 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)
},
)
return err
}
// Shutdown blocks until all resources held or created by the daemon, // Shutdown blocks until all resources held or created by the daemon,
// including child processes it has started, have been cleaned up. // including child processes it has started, have been cleaned up.
// //

View File

@ -1,42 +0,0 @@
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
}

View File

@ -59,6 +59,7 @@ type Loader interface {
Load( Load(
context.Context, context.Context,
*mlog.Logger, *mlog.Logger,
daecommon.NetworkConfig,
bootstrap.CreationParams, bootstrap.CreationParams,
*Opts, *Opts,
) ( ) (
@ -72,6 +73,7 @@ type Loader interface {
Join( Join(
context.Context, context.Context,
*mlog.Logger, *mlog.Logger,
daecommon.NetworkConfig,
JoiningBootstrap, JoiningBootstrap,
*Opts, *Opts,
) ( ) (
@ -89,11 +91,12 @@ type Loader interface {
// - hostName: The name of this first host in the network. // - hostName: The name of this first host in the network.
// //
// Errors: // Errors:
// - ErrInvalidConfig - If the Opts.Config field is not valid. It must be // - ErrInvalidConfig - if daemonConfig doesn't have 3 storage allocations
// non-nil and have at least 3 storage allocations. // configured.
Create( Create(
context.Context, context.Context,
*mlog.Logger, *mlog.Logger,
daecommon.NetworkConfig,
bootstrap.CreationParams, bootstrap.CreationParams,
nebula.IPNet, nebula.IPNet,
nebula.HostName, nebula.HostName,
@ -185,7 +188,7 @@ func (l *loader) Loadable(
creationParams := make([]bootstrap.CreationParams, 0, len(networkStateDirs)) creationParams := make([]bootstrap.CreationParams, 0, len(networkStateDirs))
for _, networkStateDir := range networkStateDirs { for _, networkStateDir := range networkStateDirs {
thisCreationParams, err := loadCreationParams(networkStateDir) thisCreationParams, err := LoadCreationParams(networkStateDir)
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"loading creation params from %q: %w", "loading creation params from %q: %w",
@ -202,6 +205,7 @@ func (l *loader) Loadable(
func (l *loader) Load( func (l *loader) Load(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
networkConfig daecommon.NetworkConfig,
creationParams bootstrap.CreationParams, creationParams bootstrap.CreationParams,
opts *Opts, opts *Opts,
) ( ) (
@ -222,6 +226,7 @@ func (l *loader) Load(
ctx, ctx,
logger.WithNamespace("network"), logger.WithNamespace("network"),
l.envBinDirPath, l.envBinDirPath,
networkConfig,
networkStateDir, networkStateDir,
networkRuntimeDir, networkRuntimeDir,
opts, opts,
@ -231,6 +236,7 @@ func (l *loader) Load(
func (l *loader) Join( func (l *loader) Join(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
networkConfig daecommon.NetworkConfig,
joiningBootstrap JoiningBootstrap, joiningBootstrap JoiningBootstrap,
opts *Opts, opts *Opts,
) ( ) (
@ -254,6 +260,7 @@ func (l *loader) Join(
ctx, ctx,
logger.WithNamespace("network"), logger.WithNamespace("network"),
l.envBinDirPath, l.envBinDirPath,
networkConfig,
joiningBootstrap, joiningBootstrap,
networkStateDir, networkStateDir,
networkRuntimeDir, networkRuntimeDir,
@ -264,6 +271,7 @@ func (l *loader) Join(
func (l *loader) Create( func (l *loader) Create(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
networkConfig daecommon.NetworkConfig,
creationParams bootstrap.CreationParams, creationParams bootstrap.CreationParams,
ipNet nebula.IPNet, ipNet nebula.IPNet,
hostName nebula.HostName, hostName nebula.HostName,
@ -286,6 +294,7 @@ func (l *loader) Create(
ctx, ctx,
logger.WithNamespace("network"), logger.WithNamespace("network"),
l.envBinDirPath, l.envBinDirPath,
networkConfig,
networkStateDir, networkStateDir,
networkRuntimeDir, networkRuntimeDir,
creationParams, creationParams,

View File

@ -154,14 +154,6 @@ type Network interface {
// Network instance. A nil Opts is equivalent to a zero value. // Network instance. A nil Opts is equivalent to a zero value.
type Opts struct { type Opts struct {
GarageAdminToken string // Will be randomly generated if left unset. 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 { func (o *Opts) withDefaults() *Opts {
@ -197,61 +189,44 @@ type network struct {
wg sync.WaitGroup wg sync.WaitGroup
} }
// newNetwork returns an instantiated *network instance. All initialization // instatiateNetwork returns an instantiated *network instance which has not yet
// steps which are common to all *network creation methods (load, join, create) // been initialized.
// are included here as well. func instatiateNetwork(
func newNetwork(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
networkConfig daecommon.NetworkConfig,
envBinDirPath string, envBinDirPath string,
stateDir toolkit.Dir, stateDir toolkit.Dir,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
dirsMayExist bool,
opts *Opts, opts *Opts,
) ( ) *network {
*network, error, ctx = context.WithoutCancel(ctx)
) { ctx, cancel := context.WithCancel(ctx)
ctx, cancel := context.WithCancel(context.WithoutCancel(ctx)) return &network{
logger: logger,
var ( networkConfig: networkConfig,
n = &network{ envBinDirPath: envBinDirPath,
logger: logger, stateDir: stateDir,
envBinDirPath: envBinDirPath, runtimeDir: runtimeDir,
stateDir: stateDir, opts: opts.withDefaults(),
runtimeDir: runtimeDir, workerCtx: ctx,
opts: opts.withDefaults(), workerCancel: cancel,
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. // Created/Joined with the given state directory.
func loadCreationParams( //
// TODO probably can be private
func LoadCreationParams(
stateDir toolkit.Dir, stateDir toolkit.Dir,
) ( ) (
bootstrap.CreationParams, error, bootstrap.CreationParams, error,
) { ) {
var ( var (
// TODO store/load the creation params separately from the rest of the // TODO store/load the creation params separately from the rest of
// bootstrap, since the bootstrap contains potentially the entire host // the bootstrap, since the bootstrap contains potentially the
// list of a network, which could be pretty bulky. // entire host list of a network, which could be pretty bulky.
bootstrapFilePath = bootstrap.StateDirPath(stateDir.Path) bootstrapFilePath = bootstrap.StateDirPath(stateDir.Path)
bs bootstrap.Bootstrap bs bootstrap.Bootstrap
) )
@ -269,23 +244,25 @@ func load(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
envBinDirPath string, envBinDirPath string,
networkConfig daecommon.NetworkConfig,
stateDir toolkit.Dir, stateDir toolkit.Dir,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
opts *Opts, opts *Opts,
) ( ) (
Network, error, Network, error,
) { ) {
n, err := newNetwork( n := instatiateNetwork(
ctx, ctx,
logger, logger,
networkConfig,
envBinDirPath, envBinDirPath,
stateDir, stateDir,
runtimeDir, runtimeDir,
true,
opts, opts,
) )
if err != nil {
return nil, fmt.Errorf("instantiating Network: %w", err) if err := n.initializeDirs(true); err != nil {
return nil, fmt.Errorf("initializing directories: %w", err)
} }
var ( var (
@ -308,6 +285,7 @@ func join(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
envBinDirPath string, envBinDirPath string,
networkConfig daecommon.NetworkConfig,
joiningBootstrap JoiningBootstrap, joiningBootstrap JoiningBootstrap,
stateDir toolkit.Dir, stateDir toolkit.Dir,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
@ -315,17 +293,18 @@ func join(
) ( ) (
Network, error, Network, error,
) { ) {
n, err := newNetwork( n := instatiateNetwork(
ctx, ctx,
logger, logger,
networkConfig,
envBinDirPath, envBinDirPath,
stateDir, stateDir,
runtimeDir, runtimeDir,
false,
opts, 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 err := secrets.Import( if err := secrets.Import(
@ -345,6 +324,7 @@ func create(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
envBinDirPath string, envBinDirPath string,
networkConfig daecommon.NetworkConfig,
stateDir toolkit.Dir, stateDir toolkit.Dir,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
creationParams bootstrap.CreationParams, creationParams bootstrap.CreationParams,
@ -354,6 +334,12 @@ func create(
) ( ) (
Network, error, 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) nebulaCACreds, err := nebula.NewCACredentials(creationParams.Domain, ipNet)
if err != nil { if err != nil {
return nil, fmt.Errorf("creating nebula CA cert: %w", err) return nil, fmt.Errorf("creating nebula CA cert: %w", err)
@ -361,23 +347,18 @@ func create(
garageRPCSecret := toolkit.RandStr(32) garageRPCSecret := toolkit.RandStr(32)
n, err := newNetwork( n := instatiateNetwork(
ctx, ctx,
logger, logger,
networkConfig,
envBinDirPath, envBinDirPath,
stateDir, stateDir,
runtimeDir, runtimeDir,
false,
opts, opts,
) )
if err != nil {
return nil, fmt.Errorf("instantiating Network: %w", err)
}
if len(n.networkConfig.Storage.Allocations) < 3 { if err := n.initializeDirs(false); err != nil {
return nil, ErrInvalidConfig.WithData( return nil, fmt.Errorf("initializing directories: %w", err)
"At least three storage allocations are required.",
)
} }
err = daecommon.SetGarageRPCSecret(ctx, n.secretsStore, garageRPCSecret) err = daecommon.SetGarageRPCSecret(ctx, n.secretsStore, garageRPCSecret)
@ -410,6 +391,16 @@ func create(
return n, nil 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( func (n *network) periodically(
label string, label string,
fn func(context.Context) error, fn func(context.Context) error,
@ -1025,10 +1016,6 @@ func (n *network) GetConfig(context.Context) (daecommon.NetworkConfig, error) {
func (n *network) SetConfig( func (n *network) SetConfig(
ctx context.Context, config daecommon.NetworkConfig, ctx context.Context, config daecommon.NetworkConfig,
) error { ) error {
if _, err := loadStoreConfig(n.stateDir, &config); err != nil {
return fmt.Errorf("storing new config: %w", err)
}
prevBootstrap, err := n.reload(ctx, &config, nil) prevBootstrap, err := n.reload(ctx, &config, nil)
if err != nil { if err != nil {
return fmt.Errorf("reloading config: %w", err) return fmt.Errorf("reloading config: %w", err)

View File

@ -17,7 +17,7 @@ func TestCreate(t *testing.T) {
network = h.createNetwork(t, "primus", nil) network = h.createNetwork(t, "primus", nil)
) )
gotCreationParams, err := loadCreationParams(network.stateDir) gotCreationParams, err := LoadCreationParams(network.stateDir)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal( assert.Equal(
t, gotCreationParams, network.getBootstrap(t).NetworkCreationParams, t, gotCreationParams, network.getBootstrap(t).NetworkCreationParams,
@ -25,30 +25,31 @@ func TestCreate(t *testing.T) {
} }
func TestLoad(t *testing.T) { func TestLoad(t *testing.T) {
t.Run("given config", func(t *testing.T) { var (
var ( h = newIntegrationHarness(t)
h = newIntegrationHarness(t) network = h.createNetwork(t, "primus", &createNetworkOpts{
network = h.createNetwork(t, "primus", nil) manualShutdown: true,
networkConfig = network.getConfig(t) })
) )
network.opts.Config = &networkConfig t.Log("Shutting down network")
network.restart(t) assert.NoError(t, network.Shutdown())
assert.Equal(t, networkConfig, network.getConfig(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)
t.Run("load previous config", func(t *testing.T) { t.Cleanup(func() {
var ( t.Log("Shutting down loadedNetwork")
h = newIntegrationHarness(t) assert.NoError(t, loadedNetwork.Shutdown())
network = h.createNetwork(t, "primus", nil)
networkConfig = network.getConfig(t)
)
network.opts.Config = nil
network.restart(t)
assert.Equal(t, networkConfig, network.getConfig(t))
}) })
} }
@ -60,7 +61,13 @@ func TestJoin(t *testing.T) {
secondus = h.joinNetwork(t, primus, "secondus", nil) secondus = h.joinNetwork(t, primus, "secondus", nil)
) )
assert.Equal(t, primus.getHostsByName(t), secondus.getHostsByName(t)) 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)
}) })
t.Run("with alloc", func(t *testing.T) { t.Run("with alloc", func(t *testing.T) {
@ -77,10 +84,28 @@ func TestJoin(t *testing.T) {
t.Log("reloading primus' hosts") t.Log("reloading primus' hosts")
assert.NoError(t, primus.Network.(*network).reloadHosts(h.ctx)) assert.NoError(t, primus.Network.(*network).reloadHosts(h.ctx))
assert.Equal(t, primus.getHostsByName(t), secondus.getHostsByName(t)) 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)
}) })
} }
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) { func TestNetwork_SetConfig(t *testing.T) {
allocsToRoles := func( allocsToRoles := func(
hostName nebula.HostName, allocs []bootstrap.GarageHostInstance, hostName nebula.HostName, allocs []bootstrap.GarageHostInstance,
@ -234,22 +259,4 @@ func TestNetwork_SetConfig(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NotContains(t, layout.Roles, removedRole) 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))
})
} }

View File

@ -203,7 +203,7 @@ func (h *integrationHarness) createNetwork(
t *testing.T, t *testing.T,
hostNameStr string, hostNameStr string,
opts *createNetworkOpts, opts *createNetworkOpts,
) *integrationHarnessNetwork { ) integrationHarnessNetwork {
t.Logf("Creating as %q", hostNameStr) t.Logf("Creating as %q", hostNameStr)
opts = opts.withDefaults() opts = opts.withDefaults()
@ -222,7 +222,6 @@ func (h *integrationHarness) createNetwork(
networkOpts = &Opts{ networkOpts = &Opts{
GarageAdminToken: "admin_token", GarageAdminToken: "admin_token",
Config: &networkConfig,
} }
) )
@ -230,6 +229,7 @@ func (h *integrationHarness) createNetwork(
h.ctx, h.ctx,
logger, logger,
getEnvBinDirPath(), getEnvBinDirPath(),
networkConfig,
stateDir, stateDir,
runtimeDir, runtimeDir,
opts.creationParams, opts.creationParams,
@ -241,7 +241,16 @@ func (h *integrationHarness) createNetwork(
t.Fatalf("creating Network: %v", err) t.Fatalf("creating Network: %v", err)
} }
nh := &integrationHarnessNetwork{ 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{
network, network,
h.ctx, h.ctx,
logger, logger,
@ -250,17 +259,6 @@ func (h *integrationHarness) createNetwork(
runtimeDir, runtimeDir,
networkOpts, 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 { type joinNetworkOpts struct {
@ -281,10 +279,10 @@ func (o *joinNetworkOpts) withDefaults() *joinNetworkOpts {
func (h *integrationHarness) joinNetwork( func (h *integrationHarness) joinNetwork(
t *testing.T, t *testing.T,
network *integrationHarnessNetwork, network integrationHarnessNetwork,
hostNameStr string, hostNameStr string,
opts *joinNetworkOpts, opts *joinNetworkOpts,
) *integrationHarnessNetwork { ) integrationHarnessNetwork {
opts = opts.withDefaults() opts = opts.withDefaults()
hostName := nebula.HostName(hostNameStr) hostName := nebula.HostName(hostNameStr)
@ -303,7 +301,6 @@ func (h *integrationHarness) joinNetwork(
runtimeDir = h.mkDir(t, "runtime") runtimeDir = h.mkDir(t, "runtime")
networkOpts = &Opts{ networkOpts = &Opts{
GarageAdminToken: "admin_token", GarageAdminToken: "admin_token",
Config: &networkConfig,
} }
) )
@ -312,6 +309,7 @@ func (h *integrationHarness) joinNetwork(
h.ctx, h.ctx,
logger, logger,
getEnvBinDirPath(), getEnvBinDirPath(),
networkConfig,
joiningBootstrap, joiningBootstrap,
stateDir, stateDir,
runtimeDir, runtimeDir,
@ -321,7 +319,16 @@ func (h *integrationHarness) joinNetwork(
t.Fatalf("joining network: %v", err) t.Fatalf("joining network: %v", err)
} }
nh := &integrationHarnessNetwork{ 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{
joinedNetwork, joinedNetwork,
h.ctx, h.ctx,
logger, logger,
@ -330,34 +337,6 @@ func (h *integrationHarness) joinNetwork(
runtimeDir, runtimeDir,
networkOpts, 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 { func (nh *integrationHarnessNetwork) getConfig(t *testing.T) daecommon.NetworkConfig {

View File

@ -26,23 +26,15 @@ type RPC interface {
GetNetworks(context.Context) ([]bootstrap.CreationParams, error) 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 SetConfig(context.Context, daecommon.NetworkConfig) error
// All network.RPC methods are automatically implemented by Daemon using the // All network.RPC methods are automatically implemented by Daemon using the
// currently joined network. If no network is joined then any call to these // currently joined network. If no network is joined then any call to these
// methods will return ErrNoNetwork. // methods will return ErrNoNetwork.
// //
// If more than one Network is joined then all calls to these methods must // All calls to these methods must be accompanied with a context produced by
// be accompanied with a context produced by WithNetwork, in order to choose // WithNetwork, in order to choose the network. These methods may return
// the network. These methods may return these errors, in addition to those // these errors, in addition to those documented on the individual methods:
// documented on the individual methods:
// //
// Errors: // Errors:
// - ErrNoNetwork // - ErrNoNetwork