Make Daemon into a concrete type which implements RPC directly
This commit is contained in:
parent
fed79c6ec7
commit
ef86c1bbd1
@ -2,13 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"isle/daemon"
|
||||
"isle/bootstrap"
|
||||
)
|
||||
|
||||
func (ctx subCmdCtx) getHosts() (daemon.GetHostsResult, error) {
|
||||
func (ctx subCmdCtx) getHosts() ([]bootstrap.Host, error) {
|
||||
res, err := ctx.daemonRPC.GetHosts(ctx)
|
||||
if err != nil {
|
||||
return daemon.GetHostsResult{}, fmt.Errorf("calling GetHosts: %w", err)
|
||||
return nil, fmt.Errorf("calling GetHosts: %w", err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ var subCmdDaemon = subCmd{
|
||||
{
|
||||
logger := logger.WithNamespace("http")
|
||||
httpSrv, err := newHTTPServer(
|
||||
ctx, logger, daemon.NewRPC(daemonInst),
|
||||
ctx, logger, daemonInst,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("starting HTTP server: %w", err)
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"isle/daemon"
|
||||
"isle/daemon/jsonrpc2"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -18,7 +17,7 @@ import (
|
||||
const daemonHTTPRPCPath = "/rpc/v0.json"
|
||||
|
||||
func newHTTPServer(
|
||||
ctx context.Context, logger *mlog.Logger, rpc daemon.RPC,
|
||||
ctx context.Context, logger *mlog.Logger, daemonInst *daemon.Daemon,
|
||||
) (
|
||||
*http.Server, error,
|
||||
) {
|
||||
@ -51,16 +50,8 @@ func newHTTPServer(
|
||||
}
|
||||
|
||||
logger.Info(ctx, "HTTP server socket created")
|
||||
|
||||
rpcHandler := jsonrpc2.Chain(
|
||||
jsonrpc2.NewMLogMiddleware(logger.WithNamespace("rpc")),
|
||||
jsonrpc2.ExposeServerSideErrorsMiddleware,
|
||||
)(
|
||||
jsonrpc2.NewDispatchHandler(&rpc),
|
||||
)
|
||||
|
||||
httpMux := http.NewServeMux()
|
||||
httpMux.Handle(daemonHTTPRPCPath, jsonrpc2.NewHTTPHandler(rpcHandler))
|
||||
httpMux.Handle(daemonHTTPRPCPath, daemonInst.HTTPRPCHandler())
|
||||
|
||||
srv := &http.Server{Handler: httpMux}
|
||||
|
||||
|
@ -43,7 +43,11 @@ var subCmdHostCreate = subCmd{
|
||||
return errors.New("--hostname is required")
|
||||
}
|
||||
|
||||
res, err := ctx.daemonRPC.CreateHost(
|
||||
var (
|
||||
res daemon.JoiningBootstrap
|
||||
err error
|
||||
)
|
||||
res, err = ctx.daemonRPC.CreateHost(
|
||||
ctx, hostName.V, daemon.CreateHostOpts{
|
||||
IP: ip.V,
|
||||
CanCreateHosts: *canCreateHosts,
|
||||
@ -53,7 +57,7 @@ var subCmdHostCreate = subCmd{
|
||||
return fmt.Errorf("calling CreateHost: %w", err)
|
||||
}
|
||||
|
||||
return json.NewEncoder(os.Stdout).Encode(res.JoiningBootstrap)
|
||||
return json.NewEncoder(os.Stdout).Encode(res)
|
||||
},
|
||||
}
|
||||
|
||||
@ -74,8 +78,8 @@ var subCmdHostList = subCmd{
|
||||
Storage bootstrap.GarageHost `json:",omitempty"`
|
||||
}
|
||||
|
||||
hosts := make([]host, 0, len(hostsRes.Hosts))
|
||||
for _, h := range hostsRes.Hosts {
|
||||
hosts := make([]host, 0, len(hostsRes))
|
||||
for _, h := range hostsRes {
|
||||
|
||||
host := host{
|
||||
Name: string(h.Name),
|
||||
|
@ -53,7 +53,7 @@ var subCmdNebulaCreateCert = subCmd{
|
||||
return fmt.Errorf("calling CreateNebulaCertificate: %w", err)
|
||||
}
|
||||
|
||||
nebulaHostCertPEM, err := res.HostNebulaCertificate.Unwrap().MarshalToPEM()
|
||||
nebulaHostCertPEM, err := res.Unwrap().MarshalToPEM()
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling cert to PEM: %w", err)
|
||||
}
|
||||
@ -112,7 +112,7 @@ var subCmdNebulaShow = subCmd{
|
||||
SubnetCIDR: subnet.String(),
|
||||
}
|
||||
|
||||
for _, h := range hosts.Hosts {
|
||||
for _, h := range hosts {
|
||||
if h.Nebula.PublicAddr == "" {
|
||||
continue
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/jsonrpc2"
|
||||
"isle/nebula"
|
||||
)
|
||||
@ -22,24 +23,24 @@ func RPCFromClient(client jsonrpc2.Client) RPC {
|
||||
return &rpcClient{client}
|
||||
}
|
||||
|
||||
func (c *rpcClient) CreateHost(ctx context.Context, hostName nebula.HostName, opts CreateHostOpts) (c2 CreateHostResult, err error) {
|
||||
func (c *rpcClient) CreateHost(ctx context.Context, h1 nebula.HostName, c2 CreateHostOpts) (j1 JoiningBootstrap, err error) {
|
||||
err = c.client.Call(
|
||||
ctx,
|
||||
&c2,
|
||||
&j1,
|
||||
"CreateHost",
|
||||
hostName,
|
||||
opts,
|
||||
h1,
|
||||
c2,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *rpcClient) CreateNebulaCertificate(ctx context.Context, hostName nebula.HostName, hostEncryptingPublicKey nebula.EncryptingPublicKey) (c2 CreateNebulaCertificateResult, err error) {
|
||||
func (c *rpcClient) CreateNebulaCertificate(ctx context.Context, h1 nebula.HostName, e1 nebula.EncryptingPublicKey) (c2 nebula.Certificate, err error) {
|
||||
err = c.client.Call(
|
||||
ctx,
|
||||
&c2,
|
||||
"CreateNebulaCertificate",
|
||||
hostName,
|
||||
hostEncryptingPublicKey,
|
||||
h1,
|
||||
e1,
|
||||
)
|
||||
return
|
||||
}
|
||||
@ -66,10 +67,10 @@ func (c *rpcClient) GetGarageClientParams(ctx context.Context) (g1 GarageClientP
|
||||
return
|
||||
}
|
||||
|
||||
func (c *rpcClient) GetHosts(ctx context.Context) (g1 GetHostsResult, err error) {
|
||||
func (c *rpcClient) GetHosts(ctx context.Context) (ha1 []bootstrap.Host, err error) {
|
||||
err = c.client.Call(
|
||||
ctx,
|
||||
&g1,
|
||||
&ha1,
|
||||
"GetHosts",
|
||||
)
|
||||
return
|
||||
@ -84,12 +85,12 @@ func (c *rpcClient) GetNebulaCAPublicCredentials(ctx context.Context) (c2 nebula
|
||||
return
|
||||
}
|
||||
|
||||
func (c *rpcClient) JoinNetwork(ctx context.Context, req JoiningBootstrap) (err error) {
|
||||
func (c *rpcClient) JoinNetwork(ctx context.Context, j1 JoiningBootstrap) (err error) {
|
||||
err = c.client.Call(
|
||||
ctx,
|
||||
nil,
|
||||
"JoinNetwork",
|
||||
req,
|
||||
j1,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ package daemon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
@ -17,97 +18,14 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// CreateHostOpts are optional parameters to the CreateHost method.
|
||||
type CreateHostOpts struct {
|
||||
// IP address of the new host. An IP address will be randomly chosen if one
|
||||
// is not given here.
|
||||
IP netip.Addr
|
||||
|
||||
// CanCreateHosts indicates that the bootstrap produced by CreateHost should
|
||||
// give the new host the ability to create new hosts as well.
|
||||
CanCreateHosts bool
|
||||
|
||||
// TODO add nebula cert tags
|
||||
}
|
||||
|
||||
// Daemon presents all functionality required for client frontends to interact
|
||||
// with isle, typically via the unix socket.
|
||||
type Daemon interface {
|
||||
|
||||
// CreateNetwork will initialize a new network using the given parameters.
|
||||
// - name: Human-readable name of the network.
|
||||
// - domain: Primary domain name that network services are served under.
|
||||
// - ipNet: An IP subnet, in CIDR form, which will be the overall range of
|
||||
// possible IPs in the network. The first IP in this network range will
|
||||
// become this first host's IP.
|
||||
// - hostName: The name of this first host in the network.
|
||||
//
|
||||
// The daemon on which this is called will become the first host in the
|
||||
// network, and will have full administrative privileges.
|
||||
CreateNetwork(
|
||||
ctx context.Context, name, domain string,
|
||||
ipNet nebula.IPNet,
|
||||
hostName nebula.HostName,
|
||||
) error
|
||||
|
||||
// JoinNetwork joins the Daemon to an existing network using the given
|
||||
// Bootstrap.
|
||||
//
|
||||
// Errors:
|
||||
// - ErrAlreadyJoined
|
||||
JoinNetwork(context.Context, JoiningBootstrap) error
|
||||
|
||||
// GetBootstraps returns the currently active Bootstrap.
|
||||
GetBootstrap(context.Context) (bootstrap.Bootstrap, error)
|
||||
|
||||
// GetGarageClientParams returns a GarageClientParams for the current
|
||||
// network state.
|
||||
GetGarageClientParams(context.Context) (GarageClientParams, error)
|
||||
|
||||
// RemoveHost removes the host of the given name from the network.
|
||||
RemoveHost(context.Context, nebula.HostName) error
|
||||
|
||||
// CreateHost creates a bootstrap for a new host with the given name and IP
|
||||
// address.
|
||||
CreateHost(
|
||||
ctx context.Context,
|
||||
hostName nebula.HostName,
|
||||
opts CreateHostOpts,
|
||||
) (
|
||||
JoiningBootstrap, error,
|
||||
)
|
||||
|
||||
// CreateNebulaCertificate creates and signs a new nebula certficate for an
|
||||
// existing host, given the public key for that host. This is currently
|
||||
// mostly useful for creating certs for mobile devices.
|
||||
//
|
||||
// TODO replace this with CreateHostBootstrap, and the
|
||||
// CreateNebulaCertificate RPC method can just pull cert out of that.
|
||||
//
|
||||
// Errors:
|
||||
// - ErrHostNotFound
|
||||
CreateNebulaCertificate(
|
||||
ctx context.Context,
|
||||
hostName nebula.HostName,
|
||||
hostPubKey nebula.EncryptingPublicKey,
|
||||
) (
|
||||
nebula.Certificate, error,
|
||||
)
|
||||
|
||||
// Shutdown blocks until all resources held or created by the daemon,
|
||||
// including child processes it has started, have been cleaned up.
|
||||
//
|
||||
// If this returns an error then it's possible that child processes are
|
||||
// still running and are no longer managed.
|
||||
Shutdown() error
|
||||
}
|
||||
|
||||
// Opts are optional parameters which can be passed in when initializing a new
|
||||
// Daemon instance. A nil Opts is equivalent to a zero value.
|
||||
type Opts struct {
|
||||
@ -145,7 +63,25 @@ const (
|
||||
daemonStateShutdown
|
||||
)
|
||||
|
||||
type daemon struct {
|
||||
var _ RPC = (*Daemon)(nil)
|
||||
|
||||
// Daemon implements all methods of the Daemon interface, plus others used
|
||||
// to manage this particular implementation.
|
||||
//
|
||||
// Daemon manages all child processes and state required by the isle
|
||||
// service, as well as an HTTP socket over which RPC calls will be served.
|
||||
//
|
||||
// Inner Children instance(s) will be wrapped such that they will be
|
||||
// automatically shutdown and re-created whenever there's changes in the network
|
||||
// which require the configuration to be changed (e.g. a new nebula lighthouse).
|
||||
// During such an inner restart all methods will return ErrRestarting, except
|
||||
// Shutdown which will block until the currently executing restart is finished
|
||||
// and then shutdown cleanly from there.
|
||||
//
|
||||
// While still starting up the Daemon for the first time all methods will return
|
||||
// ErrInitializing, except Shutdown which will block until initialization is
|
||||
// canceled.
|
||||
type Daemon struct {
|
||||
logger *mlog.Logger
|
||||
daemonConfig Config
|
||||
envBinDirPath string
|
||||
@ -163,20 +99,7 @@ type daemon struct {
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewDaemon initializes and returns a Daemon instance which will manage all
|
||||
// child processes and state required by the isle service, as well as an HTTP
|
||||
// socket over which RPC calls will be served.
|
||||
//
|
||||
// Inner Children instance(s) will be wrapped such that they will be
|
||||
// automatically shutdown and re-created whenever there's changes in the network
|
||||
// which require the configuration to be changed (e.g. a new nebula lighthouse).
|
||||
// During such an inner restart all methods will return ErrRestarting, except
|
||||
// Shutdown which will block until the currently executing restart is finished
|
||||
// and then shutdown cleanly from there.
|
||||
//
|
||||
// While still starting up the Daemon for the first time all methods will return
|
||||
// ErrInitializing, except Shutdown which will block until initialization is
|
||||
// canceled.
|
||||
// NewDaemon initializes and returns a Daemon.
|
||||
func NewDaemon(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
@ -184,10 +107,10 @@ func NewDaemon(
|
||||
envBinDirPath string,
|
||||
opts *Opts,
|
||||
) (
|
||||
Daemon, error,
|
||||
*Daemon, error,
|
||||
) {
|
||||
var (
|
||||
d = &daemon{
|
||||
d = &Daemon{
|
||||
logger: logger,
|
||||
daemonConfig: daemonConfig,
|
||||
envBinDirPath: envBinDirPath,
|
||||
@ -229,7 +152,7 @@ func NewDaemon(
|
||||
}
|
||||
|
||||
// initialize must be called with d.l write lock held.
|
||||
func (d *daemon) initialize(
|
||||
func (d *Daemon) initialize(
|
||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||
) error {
|
||||
// we update this Host's data using whatever configuration has been provided
|
||||
@ -295,7 +218,7 @@ func (d *daemon) initialize(
|
||||
}
|
||||
|
||||
func withCurrBootstrap[Res any](
|
||||
d *daemon, fn func(bootstrap.Bootstrap) (Res, error),
|
||||
d *Daemon, fn func(bootstrap.Bootstrap) (Res, error),
|
||||
) (Res, error) {
|
||||
var zero Res
|
||||
d.l.RLock()
|
||||
@ -320,7 +243,7 @@ func withCurrBootstrap[Res any](
|
||||
// reload will check the existing hosts data from currBootstrap against a
|
||||
// potentially updated set of hosts data, and if there are any differences will
|
||||
// perform whatever changes are necessary.
|
||||
func (d *daemon) reload(
|
||||
func (d *Daemon) reload(
|
||||
ctx context.Context,
|
||||
currBootstrap bootstrap.Bootstrap,
|
||||
newHosts map[nebula.HostName]bootstrap.Host,
|
||||
@ -374,7 +297,7 @@ func (d *daemon) reload(
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (d *daemon) postInit(ctx context.Context) error {
|
||||
func (d *Daemon) postInit(ctx context.Context) error {
|
||||
if len(d.daemonConfig.Storage.Allocations) > 0 {
|
||||
d.logger.Info(ctx, "Applying garage layout")
|
||||
if err := garageApplyLayout(
|
||||
@ -420,7 +343,7 @@ func (d *daemon) postInit(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) reloadLoop(ctx context.Context) {
|
||||
func (d *Daemon) reloadLoop(ctx context.Context) {
|
||||
ticker := time.NewTicker(3 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
@ -454,7 +377,17 @@ func (d *daemon) reloadLoop(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *daemon) CreateNetwork(
|
||||
// CreateNetwork will initialize a new network using the given parameters.
|
||||
// - name: Human-readable name of the network.
|
||||
// - domain: Primary domain name that network services are served under.
|
||||
// - ipNet: An IP subnet, in CIDR form, which will be the overall range of
|
||||
// possible IPs in the network. The first IP in this network range will become
|
||||
// this first host's IP.
|
||||
// - hostName: The name of this first host in the network.
|
||||
//
|
||||
// The daemon on which this is called will become the first host in the network,
|
||||
// and will have full administrative privileges.
|
||||
func (d *Daemon) CreateNetwork(
|
||||
ctx context.Context,
|
||||
name, domain string, ipNet nebula.IPNet, hostName nebula.HostName,
|
||||
) error {
|
||||
@ -515,7 +448,12 @@ func (d *daemon) CreateNetwork(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) JoinNetwork(
|
||||
// JoinNetwork joins the Daemon to an existing network using the given
|
||||
// Bootstrap.
|
||||
//
|
||||
// Errors:
|
||||
// - ErrAlreadyJoined
|
||||
func (d *Daemon) JoinNetwork(
|
||||
ctx context.Context, newBootstrap JoiningBootstrap,
|
||||
) error {
|
||||
d.l.Lock()
|
||||
@ -538,7 +476,7 @@ func (d *daemon) JoinNetwork(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) GetBootstrap(ctx context.Context) (bootstrap.Bootstrap, error) {
|
||||
func (d *Daemon) getBootstrap(ctx context.Context) (bootstrap.Bootstrap, error) {
|
||||
return withCurrBootstrap(d, func(
|
||||
currBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
@ -548,7 +486,24 @@ func (d *daemon) GetBootstrap(ctx context.Context) (bootstrap.Bootstrap, error)
|
||||
})
|
||||
}
|
||||
|
||||
func (d *daemon) GetGarageClientParams(
|
||||
// GetHosts returns all hosts known to the network, sorted by their name.
|
||||
func (d *Daemon) GetHosts(ctx context.Context) ([]bootstrap.Host, error) {
|
||||
b, err := d.getBootstrap(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("retrieving bootstrap: %w", err)
|
||||
}
|
||||
|
||||
hosts := maps.Values(b.Hosts)
|
||||
slices.SortFunc(hosts, func(a, b bootstrap.Host) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
// GetGarageClientParams returns a GarageClientParams for the current network
|
||||
// state.
|
||||
func (d *Daemon) GetGarageClientParams(
|
||||
ctx context.Context,
|
||||
) (
|
||||
GarageClientParams, error,
|
||||
@ -562,7 +517,24 @@ func (d *daemon) GetGarageClientParams(
|
||||
})
|
||||
}
|
||||
|
||||
func (d *daemon) RemoveHost(ctx context.Context, hostName nebula.HostName) error {
|
||||
// GetNebulaCAPublicCredentials returns the CAPublicCredentials for the network.
|
||||
func (d *Daemon) GetNebulaCAPublicCredentials(
|
||||
ctx context.Context,
|
||||
) (
|
||||
nebula.CAPublicCredentials, error,
|
||||
) {
|
||||
b, err := d.getBootstrap(ctx)
|
||||
if err != nil {
|
||||
return nebula.CAPublicCredentials{}, fmt.Errorf(
|
||||
"retrieving bootstrap: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
return b.CAPublicCredentials, nil
|
||||
}
|
||||
|
||||
// RemoveHost removes the host of the given name from the network.
|
||||
func (d *Daemon) RemoveHost(ctx context.Context, hostName nebula.HostName) error {
|
||||
// TODO RemoveHost should publish a certificate revocation for the host
|
||||
// being removed.
|
||||
_, err := withCurrBootstrap(d, func(
|
||||
@ -670,7 +642,22 @@ func chooseAvailableIP(b bootstrap.Bootstrap) (netip.Addr, error) {
|
||||
panic("All ips are in-use, but somehow that wasn't determined earlier")
|
||||
}
|
||||
|
||||
func (d *daemon) CreateHost(
|
||||
// CreateHostOpts are optional parameters to the CreateHost method.
|
||||
type CreateHostOpts struct {
|
||||
// IP address of the new host. An IP address will be randomly chosen if one
|
||||
// is not given here.
|
||||
IP netip.Addr
|
||||
|
||||
// CanCreateHosts indicates that the bootstrap produced by CreateHost should
|
||||
// give the new host the ability to create new hosts as well.
|
||||
CanCreateHosts bool
|
||||
|
||||
// TODO add nebula cert tags
|
||||
}
|
||||
|
||||
// CreateHost creates a bootstrap for a new host with the given name and IP
|
||||
// address.
|
||||
func (d *Daemon) CreateHost(
|
||||
ctx context.Context,
|
||||
hostName nebula.HostName,
|
||||
opts CreateHostOpts,
|
||||
@ -746,7 +733,16 @@ func (d *daemon) CreateHost(
|
||||
return joiningBootstrap, nil
|
||||
}
|
||||
|
||||
func (d *daemon) CreateNebulaCertificate(
|
||||
// CreateNebulaCertificate creates and signs a new nebula certficate for an
|
||||
// existing host, given the public key for that host. This is currently mostly
|
||||
// useful for creating certs for mobile devices.
|
||||
//
|
||||
// TODO replace this with CreateHostBootstrap, and the CreateNebulaCertificate
|
||||
// RPC method can just pull cert out of that.
|
||||
//
|
||||
// Errors:
|
||||
// - ErrHostNotFound
|
||||
func (d *Daemon) CreateNebulaCertificate(
|
||||
ctx context.Context,
|
||||
hostName nebula.HostName,
|
||||
hostPubKey nebula.EncryptingPublicKey,
|
||||
@ -777,7 +773,12 @@ func (d *daemon) CreateNebulaCertificate(
|
||||
})
|
||||
}
|
||||
|
||||
func (d *daemon) Shutdown() error {
|
||||
// Shutdown blocks until all resources held or created by the daemon,
|
||||
// including child processes it has started, have been cleaned up.
|
||||
//
|
||||
// If this returns an error then it's possible that child processes are
|
||||
// still running and are no longer managed.
|
||||
func (d *Daemon) Shutdown() error {
|
||||
d.l.Lock()
|
||||
defer d.l.Unlock()
|
||||
|
||||
|
@ -19,7 +19,7 @@ type GarageClientParams struct {
|
||||
RPCSecret string
|
||||
}
|
||||
|
||||
func (d *daemon) getGarageClientParams(
|
||||
func (d *Daemon) getGarageClientParams(
|
||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
GarageClientParams, error,
|
||||
|
@ -63,7 +63,7 @@ func garageInitializeGlobalBucket(
|
||||
// putGarageBoostrapHost places the <hostname>.json.signed file for this host
|
||||
// into garage so that other hosts are able to see relevant configuration for
|
||||
// it.
|
||||
func (d *daemon) putGarageBoostrapHost(
|
||||
func (d *Daemon) putGarageBoostrapHost(
|
||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||
) error {
|
||||
garageClientParams, err := d.getGarageClientParams(ctx, currBootstrap)
|
||||
@ -112,7 +112,7 @@ func (d *daemon) putGarageBoostrapHost(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) getGarageBootstrapHosts(
|
||||
func (d *Daemon) getGarageBootstrapHosts(
|
||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
map[nebula.HostName]bootstrap.Host, error,
|
||||
|
173
go/daemon/rpc.go
173
go/daemon/rpc.go
@ -1,44 +1,17 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/jsonrpc2"
|
||||
"isle/nebula"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetHostsResult wraps the results from the GetHosts RPC method.
|
||||
type GetHostsResult struct {
|
||||
Hosts []bootstrap.Host
|
||||
}
|
||||
|
||||
// CreateHostResult wraps the results from the CreateHost RPC method.
|
||||
type CreateHostResult struct {
|
||||
JoiningBootstrap JoiningBootstrap
|
||||
}
|
||||
|
||||
// CreateNebulaCertificateResult wraps the results from the
|
||||
// CreateNebulaCertificate RPC method.
|
||||
type CreateNebulaCertificateResult struct {
|
||||
HostNebulaCertificate nebula.Certificate
|
||||
}
|
||||
|
||||
// RPC exposes all RPC methods which are available to be called over the RPC
|
||||
// interface.
|
||||
// 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 passes through to the Daemon method of the same name.
|
||||
//
|
||||
// name: Human-readable name of the network.
|
||||
// domain: Primary domain name that network services are served under.
|
||||
// ipNet:
|
||||
// An IP subnet, in CIDR form, which will be the overall range of
|
||||
// possible IPs in the network. The first IP in this network range will
|
||||
// become this first host's IP.
|
||||
// hostName: The name of this first host in the network.
|
||||
CreateNetwork(
|
||||
ctx context.Context,
|
||||
name string,
|
||||
@ -47,141 +20,47 @@ type RPC interface {
|
||||
hostName nebula.HostName,
|
||||
) error
|
||||
|
||||
// JoinNetwork passes through to the Daemon method of the same name.
|
||||
JoinNetwork(ctx context.Context, req JoiningBootstrap) error
|
||||
JoinNetwork(context.Context, JoiningBootstrap) error
|
||||
|
||||
// GetHosts returns all hosts known to the network, sorted by their name.
|
||||
GetHosts(ctx context.Context) (GetHostsResult, error)
|
||||
GetHosts(context.Context) ([]bootstrap.Host, error)
|
||||
|
||||
// GetGarageClientParams passes the call through to the Daemon method of the
|
||||
// same name.
|
||||
GetGarageClientParams(ctx context.Context) (GarageClientParams, error)
|
||||
GetGarageClientParams(context.Context) (GarageClientParams, error)
|
||||
|
||||
// GetNebulaCAPublicCredentials returns the CAPublicCredentials for the
|
||||
// network.
|
||||
GetNebulaCAPublicCredentials(
|
||||
ctx context.Context,
|
||||
context.Context,
|
||||
) (
|
||||
nebula.CAPublicCredentials, error,
|
||||
)
|
||||
|
||||
// RemoveHost passes the call through to the Daemon method of the same name.
|
||||
RemoveHost(ctx context.Context, hostName nebula.HostName) error
|
||||
|
||||
// CreateHost passes the call through to the Daemon method of the same name.
|
||||
CreateHost(
|
||||
ctx context.Context, hostName nebula.HostName, opts CreateHostOpts,
|
||||
context.Context, nebula.HostName, CreateHostOpts,
|
||||
) (
|
||||
CreateHostResult, error,
|
||||
JoiningBootstrap, error,
|
||||
)
|
||||
|
||||
// CreateNebulaCertificate passes the call through to the Daemon method of
|
||||
// the same name.
|
||||
CreateNebulaCertificate(
|
||||
ctx context.Context,
|
||||
hostName nebula.HostName,
|
||||
hostEncryptingPublicKey nebula.EncryptingPublicKey,
|
||||
context.Context, nebula.HostName, nebula.EncryptingPublicKey,
|
||||
) (
|
||||
CreateNebulaCertificateResult, error,
|
||||
nebula.Certificate, error,
|
||||
)
|
||||
}
|
||||
|
||||
type rpcImpl struct {
|
||||
daemon Daemon
|
||||
}
|
||||
|
||||
// NewRPC initializes and returns an RPC instance.
|
||||
func NewRPC(daemon Daemon) RPC {
|
||||
return &rpcImpl{daemon}
|
||||
}
|
||||
|
||||
func (r *rpcImpl) CreateNetwork(
|
||||
ctx context.Context,
|
||||
name string,
|
||||
domain string,
|
||||
ipNet nebula.IPNet,
|
||||
hostName nebula.HostName,
|
||||
) error {
|
||||
return r.daemon.CreateNetwork(
|
||||
ctx, name, domain, ipNet, hostName,
|
||||
// RPCHandler returns a jsonrpc2.Handler which will use the Daemon to serve all
|
||||
// methods defined on the RPC interface.
|
||||
func (d *Daemon) RPCHandler() jsonrpc2.Handler {
|
||||
rpc := RPC(d)
|
||||
return jsonrpc2.Chain(
|
||||
jsonrpc2.NewMLogMiddleware(d.logger.WithNamespace("rpc")),
|
||||
jsonrpc2.ExposeServerSideErrorsMiddleware,
|
||||
)(
|
||||
jsonrpc2.NewDispatchHandler(&rpc),
|
||||
)
|
||||
}
|
||||
|
||||
func (r *rpcImpl) JoinNetwork(
|
||||
ctx context.Context, req JoiningBootstrap,
|
||||
) error {
|
||||
return r.daemon.JoinNetwork(ctx, req)
|
||||
}
|
||||
|
||||
func (r *rpcImpl) GetHosts(ctx context.Context) (GetHostsResult, error) {
|
||||
b, err := r.daemon.GetBootstrap(ctx)
|
||||
if err != nil {
|
||||
return GetHostsResult{}, fmt.Errorf("retrieving bootstrap: %w", err)
|
||||
}
|
||||
|
||||
hosts := maps.Values(b.Hosts)
|
||||
slices.SortFunc(hosts, func(a, b bootstrap.Host) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
return GetHostsResult{hosts}, nil
|
||||
}
|
||||
|
||||
func (r *rpcImpl) GetGarageClientParams(
|
||||
ctx context.Context,
|
||||
) (
|
||||
GarageClientParams, error,
|
||||
) {
|
||||
return r.daemon.GetGarageClientParams(ctx)
|
||||
}
|
||||
|
||||
func (r *rpcImpl) GetNebulaCAPublicCredentials(
|
||||
ctx context.Context,
|
||||
) (
|
||||
nebula.CAPublicCredentials, error,
|
||||
) {
|
||||
b, err := r.daemon.GetBootstrap(ctx)
|
||||
if err != nil {
|
||||
return nebula.CAPublicCredentials{}, fmt.Errorf(
|
||||
"retrieving bootstrap: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
return b.CAPublicCredentials, nil
|
||||
}
|
||||
|
||||
func (r *rpcImpl) RemoveHost(
|
||||
ctx context.Context, hostName nebula.HostName,
|
||||
) error {
|
||||
return r.daemon.RemoveHost(ctx, hostName)
|
||||
}
|
||||
|
||||
func (r *rpcImpl) CreateHost(
|
||||
ctx context.Context, hostName nebula.HostName, opts CreateHostOpts,
|
||||
) (
|
||||
CreateHostResult, error,
|
||||
) {
|
||||
joiningBootstrap, err := r.daemon.CreateHost(ctx, hostName, opts)
|
||||
if err != nil {
|
||||
return CreateHostResult{}, err
|
||||
}
|
||||
|
||||
return CreateHostResult{JoiningBootstrap: joiningBootstrap}, nil
|
||||
}
|
||||
|
||||
func (r *rpcImpl) CreateNebulaCertificate(
|
||||
ctx context.Context,
|
||||
hostName nebula.HostName,
|
||||
hostEncryptingPublicKey nebula.EncryptingPublicKey,
|
||||
) (
|
||||
CreateNebulaCertificateResult, error,
|
||||
) {
|
||||
cert, err := r.daemon.CreateNebulaCertificate(
|
||||
ctx, hostName, hostEncryptingPublicKey,
|
||||
)
|
||||
if err != nil {
|
||||
return CreateNebulaCertificateResult{}, err
|
||||
}
|
||||
|
||||
return CreateNebulaCertificateResult{HostNebulaCertificate: cert}, nil
|
||||
// HTTPRPCHandler returns an http.Handler which will use the Daemon to serve all
|
||||
// methods defined on the RPC interface via the JSONRPC2 protocol.
|
||||
func (d *Daemon) HTTPRPCHandler() http.Handler {
|
||||
return jsonrpc2.NewHTTPHandler(d.RPCHandler())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user