Make Daemon into a concrete type which implements RPC directly

This commit is contained in:
Brian Picciano 2024-09-07 13:52:32 +02:00
parent fed79c6ec7
commit ef86c1bbd1
10 changed files with 173 additions and 297 deletions

View File

@ -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
}

View File

@ -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)

View File

@ -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}

View File

@ -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),

View File

@ -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
}

View File

@ -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
}

View File

@ -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()

View File

@ -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,

View File

@ -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,

View File

@ -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())
}