diff --git a/go/daemon/daemon.go b/go/daemon/daemon.go index 134815a..4aa2528 100644 --- a/go/daemon/daemon.go +++ b/go/daemon/daemon.go @@ -41,6 +41,12 @@ func (o *Opts) withDefaults() *Opts { var _ RPC = (*Daemon)(nil) +type joinedNetwork struct { + network.Network + + userConfig bool +} + // Daemon implements all methods of the Daemon interface, plus others used // to manage this particular implementation. // @@ -67,7 +73,7 @@ type Daemon struct { networksRuntimeDir toolkit.Dir l sync.RWMutex - networks map[string]network.Network + networks map[string]joinedNetwork } // New initializes and returns a Daemon. @@ -97,7 +103,7 @@ func New( daemonConfig: daemonConfig, envBinDirPath: envBinDirPath, opts: opts, - networks: map[string]network.Network{}, + networks: map[string]joinedNetwork{}, } { @@ -135,7 +141,7 @@ func New( networkConfig, _ := pickNetworkConfig(daemonConfig, creationParams) - d.networks[id], err = network.Load( + network, err := network.Load( ctx, logger.WithNamespace("network"), networkConfig, @@ -149,6 +155,11 @@ func New( if err != nil { return nil, fmt.Errorf("loading network %q: %w", id, err) } + + d.networks[id] = joinedNetwork{ + Network: network, + userConfig: true, + } } return d, nil @@ -169,7 +180,9 @@ func New( // - network.ErrInvalidConfig func (d *Daemon) CreateNetwork( ctx context.Context, - name, domain string, ipNet nebula.IPNet, hostName nebula.HostName, + name, domain string, + ipNet nebula.IPNet, + hostName nebula.HostName, ) error { creationParams := bootstrap.NewCreationParams(name, domain) ctx = mctx.WithAnnotator(ctx, creationParams) @@ -221,7 +234,11 @@ func (d *Daemon) CreateNetwork( } d.logger.Info(ctx, "Network created successfully") - d.networks[creationParams.ID] = n + d.networks[creationParams.ID] = joinedNetwork{ + Network: n, + userConfig: true, + } + return nil } @@ -279,7 +296,10 @@ func (d *Daemon) JoinNetwork( } d.logger.Info(ctx, "Network joined successfully") - d.networks[networkID] = n + d.networks[networkID] = joinedNetwork{ + Network: n, + userConfig: true, + } return nil } @@ -296,7 +316,7 @@ func withNetwork[Res any]( network, err := pickNetwork(ctx, d.networks, d.networksStateDir) if err != nil { var zero Res - return zero, nil + return zero, err } return fn(ctx, network) @@ -331,6 +351,24 @@ func (d *Daemon) GetNetworks( return res, nil } +func (d *Daemon) SetConfig( + ctx context.Context, config daecommon.NetworkConfig, +) error { + d.l.RLock() + defer d.l.RUnlock() + + network, err := pickNetwork(ctx, d.networks, d.networksStateDir) + if err != nil { + return err + } + + if network.userConfig { + return ErrUserManagedNetworkConfig + } + + return network.SetConfig(ctx, config) +} + // GetHost implements the method for the network.RPC interface. func (d *Daemon) GetHosts(ctx context.Context) ([]bootstrap.Host, error) { return withNetwork( diff --git a/go/daemon/errors.go b/go/daemon/errors.go index 1ddd242..1e4b2ce 100644 --- a/go/daemon/errors.go +++ b/go/daemon/errors.go @@ -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", + ) ) diff --git a/go/daemon/network.go b/go/daemon/network.go index 72f2653..8a25fb4 100644 --- a/go/daemon/network.go +++ b/go/daemon/network.go @@ -75,18 +75,18 @@ func loadableNetworks( func pickNetwork( ctx context.Context, - networks map[string]network.Network, + networks map[string]joinedNetwork, networksStateDir toolkit.Dir, ) ( - network.Network, error, + joinedNetwork, error, ) { if len(networks) == 0 { - return nil, ErrNoNetwork + return joinedNetwork{}, ErrNoNetwork } creationParams, err := loadableNetworks(networksStateDir) if err != nil { - return nil, fmt.Errorf("getting loadable networks: %w", err) + return joinedNetwork{}, fmt.Errorf("getting loadable networks: %w", err) } var ( @@ -101,9 +101,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 @@ -111,7 +111,7 @@ func pickNetwork( func alreadyJoined( ctx context.Context, - networks map[string]network.Network, + networks map[string]joinedNetwork, creationParams bootstrap.CreationParams, ) ( bool, error, diff --git a/go/daemon/rpc.go b/go/daemon/rpc.go index 3165246..53f83e9 100644 --- a/go/daemon/rpc.go +++ b/go/daemon/rpc.go @@ -3,6 +3,7 @@ package daemon import ( "context" "isle/bootstrap" + "isle/daemon/daecommon" "isle/daemon/jsonrpc2" "isle/daemon/network" "isle/nebula" @@ -25,6 +26,8 @@ type RPC interface { GetNetworks(context.Context) ([]bootstrap.CreationParams, error) + 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. @@ -34,6 +37,7 @@ type RPC interface { // these errors, in addition to those documented on the individual methods: // // Errors: + // - ErrNoNetwork // - ErrNoMatchingNetworks // - ErrMultipleMatchingNetworks network.RPC