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 (
|
import (
|
||||||
"fmt"
|
"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)
|
res, err := ctx.daemonRPC.GetHosts(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return daemon.GetHostsResult{}, fmt.Errorf("calling GetHosts: %w", err)
|
return nil, fmt.Errorf("calling GetHosts: %w", err)
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ var subCmdDaemon = subCmd{
|
|||||||
{
|
{
|
||||||
logger := logger.WithNamespace("http")
|
logger := logger.WithNamespace("http")
|
||||||
httpSrv, err := newHTTPServer(
|
httpSrv, err := newHTTPServer(
|
||||||
ctx, logger, daemon.NewRPC(daemonInst),
|
ctx, logger, daemonInst,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("starting HTTP server: %w", err)
|
return fmt.Errorf("starting HTTP server: %w", err)
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"isle/daemon"
|
"isle/daemon"
|
||||||
"isle/daemon/jsonrpc2"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -18,7 +17,7 @@ import (
|
|||||||
const daemonHTTPRPCPath = "/rpc/v0.json"
|
const daemonHTTPRPCPath = "/rpc/v0.json"
|
||||||
|
|
||||||
func newHTTPServer(
|
func newHTTPServer(
|
||||||
ctx context.Context, logger *mlog.Logger, rpc daemon.RPC,
|
ctx context.Context, logger *mlog.Logger, daemonInst *daemon.Daemon,
|
||||||
) (
|
) (
|
||||||
*http.Server, error,
|
*http.Server, error,
|
||||||
) {
|
) {
|
||||||
@ -51,16 +50,8 @@ func newHTTPServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Info(ctx, "HTTP server socket created")
|
logger.Info(ctx, "HTTP server socket created")
|
||||||
|
|
||||||
rpcHandler := jsonrpc2.Chain(
|
|
||||||
jsonrpc2.NewMLogMiddleware(logger.WithNamespace("rpc")),
|
|
||||||
jsonrpc2.ExposeServerSideErrorsMiddleware,
|
|
||||||
)(
|
|
||||||
jsonrpc2.NewDispatchHandler(&rpc),
|
|
||||||
)
|
|
||||||
|
|
||||||
httpMux := http.NewServeMux()
|
httpMux := http.NewServeMux()
|
||||||
httpMux.Handle(daemonHTTPRPCPath, jsonrpc2.NewHTTPHandler(rpcHandler))
|
httpMux.Handle(daemonHTTPRPCPath, daemonInst.HTTPRPCHandler())
|
||||||
|
|
||||||
srv := &http.Server{Handler: httpMux}
|
srv := &http.Server{Handler: httpMux}
|
||||||
|
|
||||||
|
@ -43,7 +43,11 @@ var subCmdHostCreate = subCmd{
|
|||||||
return errors.New("--hostname is required")
|
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{
|
ctx, hostName.V, daemon.CreateHostOpts{
|
||||||
IP: ip.V,
|
IP: ip.V,
|
||||||
CanCreateHosts: *canCreateHosts,
|
CanCreateHosts: *canCreateHosts,
|
||||||
@ -53,7 +57,7 @@ var subCmdHostCreate = subCmd{
|
|||||||
return fmt.Errorf("calling CreateHost: %w", err)
|
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"`
|
Storage bootstrap.GarageHost `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
hosts := make([]host, 0, len(hostsRes.Hosts))
|
hosts := make([]host, 0, len(hostsRes))
|
||||||
for _, h := range hostsRes.Hosts {
|
for _, h := range hostsRes {
|
||||||
|
|
||||||
host := host{
|
host := host{
|
||||||
Name: string(h.Name),
|
Name: string(h.Name),
|
||||||
|
@ -53,7 +53,7 @@ var subCmdNebulaCreateCert = subCmd{
|
|||||||
return fmt.Errorf("calling CreateNebulaCertificate: %w", err)
|
return fmt.Errorf("calling CreateNebulaCertificate: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nebulaHostCertPEM, err := res.HostNebulaCertificate.Unwrap().MarshalToPEM()
|
nebulaHostCertPEM, err := res.Unwrap().MarshalToPEM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("marshaling cert to PEM: %w", err)
|
return fmt.Errorf("marshaling cert to PEM: %w", err)
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ var subCmdNebulaShow = subCmd{
|
|||||||
SubnetCIDR: subnet.String(),
|
SubnetCIDR: subnet.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, h := range hosts.Hosts {
|
for _, h := range hosts {
|
||||||
if h.Nebula.PublicAddr == "" {
|
if h.Nebula.PublicAddr == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ package daemon
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"isle/bootstrap"
|
||||||
"isle/daemon/jsonrpc2"
|
"isle/daemon/jsonrpc2"
|
||||||
"isle/nebula"
|
"isle/nebula"
|
||||||
)
|
)
|
||||||
@ -22,24 +23,24 @@ func RPCFromClient(client jsonrpc2.Client) RPC {
|
|||||||
return &rpcClient{client}
|
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(
|
err = c.client.Call(
|
||||||
ctx,
|
ctx,
|
||||||
&c2,
|
&j1,
|
||||||
"CreateHost",
|
"CreateHost",
|
||||||
hostName,
|
h1,
|
||||||
opts,
|
c2,
|
||||||
)
|
)
|
||||||
return
|
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(
|
err = c.client.Call(
|
||||||
ctx,
|
ctx,
|
||||||
&c2,
|
&c2,
|
||||||
"CreateNebulaCertificate",
|
"CreateNebulaCertificate",
|
||||||
hostName,
|
h1,
|
||||||
hostEncryptingPublicKey,
|
e1,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -66,10 +67,10 @@ func (c *rpcClient) GetGarageClientParams(ctx context.Context) (g1 GarageClientP
|
|||||||
return
|
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(
|
err = c.client.Call(
|
||||||
ctx,
|
ctx,
|
||||||
&g1,
|
&ha1,
|
||||||
"GetHosts",
|
"GetHosts",
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@ -84,12 +85,12 @@ func (c *rpcClient) GetNebulaCAPublicCredentials(ctx context.Context) (c2 nebula
|
|||||||
return
|
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(
|
err = c.client.Call(
|
||||||
ctx,
|
ctx,
|
||||||
nil,
|
nil,
|
||||||
"JoinNetwork",
|
"JoinNetwork",
|
||||||
req,
|
j1,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ package daemon
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
@ -17,97 +18,14 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
"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
|
// Opts are optional parameters which can be passed in when initializing a new
|
||||||
// Daemon instance. A nil Opts is equivalent to a zero value.
|
// Daemon instance. A nil Opts is equivalent to a zero value.
|
||||||
type Opts struct {
|
type Opts struct {
|
||||||
@ -145,7 +63,25 @@ const (
|
|||||||
daemonStateShutdown
|
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
|
logger *mlog.Logger
|
||||||
daemonConfig Config
|
daemonConfig Config
|
||||||
envBinDirPath string
|
envBinDirPath string
|
||||||
@ -163,20 +99,7 @@ type daemon struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDaemon initializes and returns a Daemon instance which will manage all
|
// NewDaemon initializes and returns a Daemon.
|
||||||
// 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.
|
|
||||||
func NewDaemon(
|
func NewDaemon(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
@ -184,10 +107,10 @@ func NewDaemon(
|
|||||||
envBinDirPath string,
|
envBinDirPath string,
|
||||||
opts *Opts,
|
opts *Opts,
|
||||||
) (
|
) (
|
||||||
Daemon, error,
|
*Daemon, error,
|
||||||
) {
|
) {
|
||||||
var (
|
var (
|
||||||
d = &daemon{
|
d = &Daemon{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
daemonConfig: daemonConfig,
|
daemonConfig: daemonConfig,
|
||||||
envBinDirPath: envBinDirPath,
|
envBinDirPath: envBinDirPath,
|
||||||
@ -229,7 +152,7 @@ func NewDaemon(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialize must be called with d.l write lock held.
|
// initialize must be called with d.l write lock held.
|
||||||
func (d *daemon) initialize(
|
func (d *Daemon) initialize(
|
||||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||||
) error {
|
) error {
|
||||||
// we update this Host's data using whatever configuration has been provided
|
// we update this Host's data using whatever configuration has been provided
|
||||||
@ -295,7 +218,7 @@ func (d *daemon) initialize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func withCurrBootstrap[Res any](
|
func withCurrBootstrap[Res any](
|
||||||
d *daemon, fn func(bootstrap.Bootstrap) (Res, error),
|
d *Daemon, fn func(bootstrap.Bootstrap) (Res, error),
|
||||||
) (Res, error) {
|
) (Res, error) {
|
||||||
var zero Res
|
var zero Res
|
||||||
d.l.RLock()
|
d.l.RLock()
|
||||||
@ -320,7 +243,7 @@ func withCurrBootstrap[Res any](
|
|||||||
// reload will check the existing hosts data from currBootstrap against a
|
// reload will check the existing hosts data from currBootstrap against a
|
||||||
// potentially updated set of hosts data, and if there are any differences will
|
// potentially updated set of hosts data, and if there are any differences will
|
||||||
// perform whatever changes are necessary.
|
// perform whatever changes are necessary.
|
||||||
func (d *daemon) reload(
|
func (d *Daemon) reload(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
currBootstrap bootstrap.Bootstrap,
|
currBootstrap bootstrap.Bootstrap,
|
||||||
newHosts map[nebula.HostName]bootstrap.Host,
|
newHosts map[nebula.HostName]bootstrap.Host,
|
||||||
@ -374,7 +297,7 @@ func (d *daemon) reload(
|
|||||||
return errors.Join(errs...)
|
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 {
|
if len(d.daemonConfig.Storage.Allocations) > 0 {
|
||||||
d.logger.Info(ctx, "Applying garage layout")
|
d.logger.Info(ctx, "Applying garage layout")
|
||||||
if err := garageApplyLayout(
|
if err := garageApplyLayout(
|
||||||
@ -420,7 +343,7 @@ func (d *daemon) postInit(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *daemon) reloadLoop(ctx context.Context) {
|
func (d *Daemon) reloadLoop(ctx context.Context) {
|
||||||
ticker := time.NewTicker(3 * time.Minute)
|
ticker := time.NewTicker(3 * time.Minute)
|
||||||
defer ticker.Stop()
|
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,
|
ctx context.Context,
|
||||||
name, domain string, ipNet nebula.IPNet, hostName nebula.HostName,
|
name, domain string, ipNet nebula.IPNet, hostName nebula.HostName,
|
||||||
) error {
|
) error {
|
||||||
@ -515,7 +448,12 @@ func (d *daemon) CreateNetwork(
|
|||||||
return nil
|
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,
|
ctx context.Context, newBootstrap JoiningBootstrap,
|
||||||
) error {
|
) error {
|
||||||
d.l.Lock()
|
d.l.Lock()
|
||||||
@ -538,7 +476,7 @@ func (d *daemon) JoinNetwork(
|
|||||||
return nil
|
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(
|
return withCurrBootstrap(d, func(
|
||||||
currBootstrap bootstrap.Bootstrap,
|
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,
|
ctx context.Context,
|
||||||
) (
|
) (
|
||||||
GarageClientParams, error,
|
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
|
// TODO RemoveHost should publish a certificate revocation for the host
|
||||||
// being removed.
|
// being removed.
|
||||||
_, err := withCurrBootstrap(d, func(
|
_, 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")
|
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,
|
ctx context.Context,
|
||||||
hostName nebula.HostName,
|
hostName nebula.HostName,
|
||||||
opts CreateHostOpts,
|
opts CreateHostOpts,
|
||||||
@ -746,7 +733,16 @@ func (d *daemon) CreateHost(
|
|||||||
return joiningBootstrap, nil
|
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,
|
ctx context.Context,
|
||||||
hostName nebula.HostName,
|
hostName nebula.HostName,
|
||||||
hostPubKey nebula.EncryptingPublicKey,
|
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()
|
d.l.Lock()
|
||||||
defer d.l.Unlock()
|
defer d.l.Unlock()
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ type GarageClientParams struct {
|
|||||||
RPCSecret string
|
RPCSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *daemon) getGarageClientParams(
|
func (d *Daemon) getGarageClientParams(
|
||||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
GarageClientParams, error,
|
GarageClientParams, error,
|
||||||
|
@ -63,7 +63,7 @@ func garageInitializeGlobalBucket(
|
|||||||
// putGarageBoostrapHost places the <hostname>.json.signed file for this host
|
// putGarageBoostrapHost places the <hostname>.json.signed file for this host
|
||||||
// into garage so that other hosts are able to see relevant configuration for
|
// into garage so that other hosts are able to see relevant configuration for
|
||||||
// it.
|
// it.
|
||||||
func (d *daemon) putGarageBoostrapHost(
|
func (d *Daemon) putGarageBoostrapHost(
|
||||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||||
) error {
|
) error {
|
||||||
garageClientParams, err := d.getGarageClientParams(ctx, currBootstrap)
|
garageClientParams, err := d.getGarageClientParams(ctx, currBootstrap)
|
||||||
@ -112,7 +112,7 @@ func (d *daemon) putGarageBoostrapHost(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *daemon) getGarageBootstrapHosts(
|
func (d *Daemon) getGarageBootstrapHosts(
|
||||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
map[nebula.HostName]bootstrap.Host, error,
|
map[nebula.HostName]bootstrap.Host, error,
|
||||||
|
173
go/daemon/rpc.go
173
go/daemon/rpc.go
@ -1,44 +1,17 @@
|
|||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/jsonrpc2"
|
||||||
"isle/nebula"
|
"isle/nebula"
|
||||||
"slices"
|
"net/http"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetHostsResult wraps the results from the GetHosts RPC method.
|
// RPC defines the methods which the Daemon exposes over RPC (via the RPCHandler
|
||||||
type GetHostsResult struct {
|
// or HTTPHandler methods). Method documentation can be found on the Daemon
|
||||||
Hosts []bootstrap.Host
|
// type.
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
type RPC interface {
|
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(
|
CreateNetwork(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
name string,
|
name string,
|
||||||
@ -47,141 +20,47 @@ type RPC interface {
|
|||||||
hostName nebula.HostName,
|
hostName nebula.HostName,
|
||||||
) error
|
) error
|
||||||
|
|
||||||
// JoinNetwork passes through to the Daemon method of the same name.
|
JoinNetwork(context.Context, JoiningBootstrap) error
|
||||||
JoinNetwork(ctx context.Context, req JoiningBootstrap) error
|
|
||||||
|
|
||||||
// GetHosts returns all hosts known to the network, sorted by their name.
|
GetHosts(context.Context) ([]bootstrap.Host, error)
|
||||||
GetHosts(ctx context.Context) (GetHostsResult, error)
|
|
||||||
|
|
||||||
// GetGarageClientParams passes the call through to the Daemon method of the
|
GetGarageClientParams(context.Context) (GarageClientParams, error)
|
||||||
// same name.
|
|
||||||
GetGarageClientParams(ctx context.Context) (GarageClientParams, error)
|
|
||||||
|
|
||||||
// GetNebulaCAPublicCredentials returns the CAPublicCredentials for the
|
|
||||||
// network.
|
|
||||||
GetNebulaCAPublicCredentials(
|
GetNebulaCAPublicCredentials(
|
||||||
ctx context.Context,
|
context.Context,
|
||||||
) (
|
) (
|
||||||
nebula.CAPublicCredentials, error,
|
nebula.CAPublicCredentials, error,
|
||||||
)
|
)
|
||||||
|
|
||||||
// RemoveHost passes the call through to the Daemon method of the same name.
|
|
||||||
RemoveHost(ctx context.Context, hostName nebula.HostName) error
|
RemoveHost(ctx context.Context, hostName nebula.HostName) error
|
||||||
|
|
||||||
// CreateHost passes the call through to the Daemon method of the same name.
|
|
||||||
CreateHost(
|
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(
|
CreateNebulaCertificate(
|
||||||
ctx context.Context,
|
context.Context, nebula.HostName, nebula.EncryptingPublicKey,
|
||||||
hostName nebula.HostName,
|
|
||||||
hostEncryptingPublicKey nebula.EncryptingPublicKey,
|
|
||||||
) (
|
) (
|
||||||
CreateNebulaCertificateResult, error,
|
nebula.Certificate, error,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcImpl struct {
|
// RPCHandler returns a jsonrpc2.Handler which will use the Daemon to serve all
|
||||||
daemon Daemon
|
// methods defined on the RPC interface.
|
||||||
}
|
func (d *Daemon) RPCHandler() jsonrpc2.Handler {
|
||||||
|
rpc := RPC(d)
|
||||||
// NewRPC initializes and returns an RPC instance.
|
return jsonrpc2.Chain(
|
||||||
func NewRPC(daemon Daemon) RPC {
|
jsonrpc2.NewMLogMiddleware(d.logger.WithNamespace("rpc")),
|
||||||
return &rpcImpl{daemon}
|
jsonrpc2.ExposeServerSideErrorsMiddleware,
|
||||||
}
|
)(
|
||||||
|
jsonrpc2.NewDispatchHandler(&rpc),
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcImpl) JoinNetwork(
|
// HTTPRPCHandler returns an http.Handler which will use the Daemon to serve all
|
||||||
ctx context.Context, req JoiningBootstrap,
|
// methods defined on the RPC interface via the JSONRPC2 protocol.
|
||||||
) error {
|
func (d *Daemon) HTTPRPCHandler() http.Handler {
|
||||||
return r.daemon.JoinNetwork(ctx, req)
|
return jsonrpc2.NewHTTPHandler(d.RPCHandler())
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user