Generate RPC client wrapper

This commit is contained in:
Brian Picciano 2024-09-04 21:24:45 +02:00
parent 5138ed7c6a
commit 53ad8a91b4
12 changed files with 309 additions and 159 deletions

View File

@ -6,8 +6,7 @@ import (
)
func (ctx subCmdCtx) getHosts() (daemon.GetHostsResult, error) {
var res daemon.GetHostsResult
err := ctx.daemonRCPClient.Call(ctx.ctx, &res, "GetHosts", nil)
res, err := ctx.daemonRPC.GetHosts(ctx.ctx, struct{}{})
if err != nil {
return daemon.GetHostsResult{}, fmt.Errorf("calling GetHosts: %w", err)
}

View File

@ -18,7 +18,7 @@ import (
const daemonHTTPRPCPath = "/rpc/v0.json"
func newHTTPServer(
ctx context.Context, logger *mlog.Logger, rpc *daemon.RPC,
ctx context.Context, logger *mlog.Logger, rpc daemon.RPC,
) (
*http.Server, error,
) {

View File

@ -6,8 +6,6 @@ import (
"os"
"path/filepath"
"syscall"
"isle/daemon"
)
// minio-client keeps a configuration directory which contains various pieces of
@ -53,9 +51,8 @@ var subCmdGarageMC = subCmd{
return fmt.Errorf("parsing flags: %w", err)
}
var clientParams daemon.GarageClientParams
err := subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx, &clientParams, "GetGarageClientParams", nil,
clientParams, err := subCmdCtx.daemonRPC.GetGarageClientParams(
subCmdCtx.ctx, struct{}{},
)
if err != nil {
return fmt.Errorf("calling GetGarageClientParams: %w", err)
@ -120,9 +117,8 @@ var subCmdGarageCLI = subCmd{
descr: "Runs the garage binary, automatically configured to point to the garage sub-process of a running isle daemon",
do: func(subCmdCtx subCmdCtx) error {
var clientParams daemon.GarageClientParams
err := subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx, &clientParams, "GetGarageClientParams", nil,
clientParams, err := subCmdCtx.daemonRPC.GetGarageClientParams(
subCmdCtx.ctx, struct{}{},
)
if err != nil {
return fmt.Errorf("calling GetGarageClientParams: %w", err)

View File

@ -43,12 +43,8 @@ var subCmdHostCreate = subCmd{
return errors.New("--hostname is required")
}
var res daemon.CreateHostResult
err := subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx,
&res,
"CreateHost",
daemon.CreateHostRequest{
res, err := subCmdCtx.daemonRPC.CreateHost(
subCmdCtx.ctx, daemon.CreateHostRequest{
HostName: hostName.V,
Opts: daemon.CreateHostOpts{
IP: ip.V,
@ -123,8 +119,8 @@ var subCmdHostRemove = subCmd{
return errors.New("--hostname is required")
}
err := subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx, nil, "RemoveHost", daemon.RemoveHostRequest{
_, err := subCmdCtx.daemonRPC.RemoveHost(
subCmdCtx.ctx, daemon.RemoveHostRequest{
HostName: hostName.V,
},
)

View File

@ -47,12 +47,8 @@ var subCmdNebulaCreateCert = subCmd{
return fmt.Errorf("unmarshaling public key as PEM: %w", err)
}
var res daemon.CreateNebulaCertificateResult
err = subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx,
&res,
"CreateNebulaCertificate",
daemon.CreateNebulaCertificateRequest{
res, err := subCmdCtx.daemonRPC.CreateNebulaCertificate(
subCmdCtx.ctx, daemon.CreateNebulaCertificateRequest{
HostName: hostName.V,
HostEncryptingPublicKey: hostPub,
},
@ -89,9 +85,8 @@ var subCmdNebulaShow = subCmd{
return fmt.Errorf("getting hosts: %w", err)
}
var caPublicCreds nebula.CAPublicCredentials
err = subCmdCtx.daemonRCPClient.Call(
subCmdCtx.ctx, &caPublicCreds, "GetNebulaCAPublicCredentials", nil,
caPublicCreds, err := subCmdCtx.daemonRPC.GetNebulaCAPublicCredentials(
subCmdCtx.ctx, struct{}{},
)
if err != nil {
return fmt.Errorf("calling GetNebulaCAPublicCredentials: %w", err)

View File

@ -12,9 +12,7 @@ var subCmdNetworkCreate = subCmd{
descr: "Create's a new network, with this host being the first host in that network.",
do: func(subCmdCtx subCmdCtx) error {
var (
ctx = subCmdCtx.ctx
flags = subCmdCtx.flagSet(false)
flags = subCmdCtx.flagSet(false)
ipNet ipNetFlag
hostName hostNameFlag
)
@ -53,14 +51,14 @@ var subCmdNetworkCreate = subCmd{
return errors.New("--name, --domain, --ip-net, and --hostname are required")
}
req := daemon.CreateNetworkRequest{
Name: *name,
Domain: *domain,
IPNet: ipNet.V,
HostName: hostName.V,
}
err := subCmdCtx.daemonRCPClient.Call(ctx, nil, "CreateNetwork", req)
_, err := subCmdCtx.daemonRPC.CreateNetwork(
subCmdCtx.ctx, daemon.CreateNetworkRequest{
Name: *name,
Domain: *domain,
IPNet: ipNet.V,
HostName: hostName.V,
},
)
if err != nil {
return fmt.Errorf("creating network: %w", err)
}
@ -74,12 +72,10 @@ var subCmdNetworkJoin = subCmd{
descr: "Joins this host to an existing network",
do: func(subCmdCtx subCmdCtx) error {
var (
ctx = subCmdCtx.ctx
flags = subCmdCtx.flagSet(false)
)
bootstrapPath := flags.StringP(
"bootstrap-path", "b", "", "Path to a bootstrap.json file.",
flags = subCmdCtx.flagSet(false)
bootstrapPath = flags.StringP(
"bootstrap-path", "b", "", "Path to a bootstrap.json file.",
)
)
if err := flags.Parse(subCmdCtx.args); err != nil {
@ -97,9 +93,10 @@ var subCmdNetworkJoin = subCmd{
)
}
return subCmdCtx.daemonRCPClient.Call(
ctx, nil, "JoinNetwork", newBootstrap,
_, err := subCmdCtx.daemonRPC.JoinNetwork(
subCmdCtx.ctx, newBootstrap,
)
return err
},
}

View File

@ -34,9 +34,9 @@ type subCmdCtx struct {
args []string // command-line arguments, excluding the subCmd itself.
subCmdNames []string // names of subCmds so far, including this one
ctx context.Context
logger *mlog.Logger
daemonRCPClient jsonrpc2.Client
ctx context.Context
logger *mlog.Logger
daemonRPC daemon.RPC
}
type subCmd struct {
@ -128,17 +128,17 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error {
printUsageExit(subCmdName)
}
daemonRCPClient := jsonrpc2.NewUnixHTTPClient(
daemon.HTTPSocketPath(), daemonHTTPRPCPath,
daemonRPC := daemon.RPCFromClient(
jsonrpc2.NewUnixHTTPClient(daemon.HTTPSocketPath(), daemonHTTPRPCPath),
)
err := subCmd.do(subCmdCtx{
subCmd: subCmd,
args: args,
subCmdNames: append(ctx.subCmdNames, subCmdName),
ctx: ctx.ctx,
logger: ctx.logger,
daemonRCPClient: daemonRCPClient,
subCmd: subCmd,
args: args,
subCmdNames: append(ctx.subCmdNames, subCmdName),
ctx: ctx.ctx,
logger: ctx.logger,
daemonRPC: daemonRPC,
})
if err != nil {

69
go/daemon/client.go Normal file
View File

@ -0,0 +1,69 @@
// Code generated by gowrap. DO NOT EDIT.
// template: jsonrpc2/client_gen.tpl
// gowrap: http://github.com/hexdigest/gowrap
package daemon
//go:generate gowrap gen -p isle/daemon -i RPC -t jsonrpc2/client_gen.tpl -o client.go -l ""
import (
"context"
"isle/daemon/jsonrpc2"
"isle/nebula"
)
type rpcClient struct {
client jsonrpc2.Client
}
// RPCFromClient wraps a Client so that it implements the
// RPC interface.
func RPCFromClient(client jsonrpc2.Client) RPC {
return &rpcClient{client}
}
func (c *rpcClient) CreateHost(ctx context.Context, req CreateHostRequest) (c2 CreateHostResult, err error) {
err = c.client.Call(ctx, &c2, "CreateHost", req)
return
}
func (c *rpcClient) CreateNebulaCertificate(ctx context.Context, req CreateNebulaCertificateRequest) (c2 CreateNebulaCertificateResult, err error) {
err = c.client.Call(ctx, &c2, "CreateNebulaCertificate", req)
return
}
func (c *rpcClient) CreateNetwork(ctx context.Context, req CreateNetworkRequest) (st1 struct {
}, err error) {
err = c.client.Call(ctx, &st1, "CreateNetwork", req)
return
}
func (c *rpcClient) GetGarageClientParams(ctx context.Context, req struct {
}) (g1 GarageClientParams, err error) {
err = c.client.Call(ctx, &g1, "GetGarageClientParams", req)
return
}
func (c *rpcClient) GetHosts(ctx context.Context, req struct {
}) (g1 GetHostsResult, err error) {
err = c.client.Call(ctx, &g1, "GetHosts", req)
return
}
func (c *rpcClient) GetNebulaCAPublicCredentials(ctx context.Context, req struct {
}) (c2 nebula.CAPublicCredentials, err error) {
err = c.client.Call(ctx, &c2, "GetNebulaCAPublicCredentials", req)
return
}
func (c *rpcClient) JoinNetwork(ctx context.Context, req JoiningBootstrap) (st1 struct {
}, err error) {
err = c.client.Call(ctx, &st1, "JoinNetwork", req)
return
}
func (c *rpcClient) RemoveHost(ctx context.Context, req RemoveHostRequest) (st1 struct {
}, err error) {
err = c.client.Call(ctx, &st1, "RemoveHost", req)
return
}

View File

@ -0,0 +1,26 @@
import (
"isle/daemon/jsonrpc2"
)
{{ $t := printf "%sClient" (down .Interface.Name) }}
type {{$t}} struct {
client jsonrpc2.Client
}
// {{.Interface.Name}}FromClient wraps a Client so that it implements the
// {{.Interface.Name}} interface.
func {{.Interface.Name}}FromClient(client jsonrpc2.Client) {{.Interface.Name}} {
return &{{$t}}{client}
}
{{range $method := .Interface.Methods}}
func (c *{{$t}}) {{$method.Declaration}} {
{{- $ctx := (index $method.Params 0).Name}}
{{- $arg := (index $method.Params 1).Name}}
{{- $rcv := (index $method.Results 0).Name}}
{{- $err := (index $method.Results 1).Name}}
{{- $err}} = c.client.Call({{$ctx}}, &{{$rcv}}, "{{$method.Name}}", {{$arg}})
return
}
{{end}}

View File

@ -11,17 +11,6 @@ import (
"golang.org/x/exp/maps"
)
// RPC exposes all RPC methods which are available to be called over the RPC
// interface.
type RPC struct {
daemon Daemon
}
// NewRPC initializes and returns an RPC instance.
func NewRPC(daemon Daemon) *RPC {
return &RPC{daemon}
}
// CreateNetworkRequest contains the arguments to the CreateNetwork RPC method.
//
// All fields are required.
@ -41,76 +30,11 @@ type CreateNetworkRequest struct {
HostName nebula.HostName
}
// CreateNetwork passes through to the Daemon method of the same name.
func (r *RPC) CreateNetwork(
ctx context.Context, req CreateNetworkRequest,
) (
struct{}, error,
) {
return struct{}{}, r.daemon.CreateNetwork(
ctx, req.Name, req.Domain, req.IPNet, req.HostName,
)
}
// JoinNetwork passes through to the Daemon method of the same name.
func (r *RPC) JoinNetwork(
ctx context.Context, req JoiningBootstrap,
) (
struct{}, error,
) {
return struct{}{}, r.daemon.JoinNetwork(ctx, req)
}
// GetHostsResult wraps the results from the GetHosts RPC method.
type GetHostsResult struct {
Hosts []bootstrap.Host
}
// GetHosts returns all hosts known to the network, sorted by their name.
func (r *RPC) GetHosts(
ctx context.Context, req struct{},
) (
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
}
// GetGarageClientParams passes the call through to the Daemon method of the
// same name.
func (r *RPC) GetGarageClientParams(
ctx context.Context, req struct{},
) (
GarageClientParams, error,
) {
return r.daemon.GetGarageClientParams(ctx)
}
// GetNebulaCAPublicCredentials returns the CAPublicCredentials for the network.
func (r *RPC) GetNebulaCAPublicCredentials(
ctx context.Context, req struct{},
) (
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
}
// RemoveHostRequest contains the arguments to the RemoveHost RPC method.
//
// All fields are required.
@ -118,11 +42,6 @@ type RemoveHostRequest struct {
HostName nebula.HostName
}
// RemoveHost passes the call through to the Daemon method of the same name.
func (r *RPC) RemoveHost(ctx context.Context, req RemoveHostRequest) (struct{}, error) {
return struct{}{}, r.daemon.RemoveHost(ctx, req.HostName)
}
// CreateHostRequest contains the arguments to the
// CreateHost RPC method.
//
@ -137,23 +56,6 @@ type CreateHostResult struct {
JoiningBootstrap JoiningBootstrap
}
// CreateHost passes the call through to the Daemon method of the
// same name.
func (r *RPC) CreateHost(
ctx context.Context, req CreateHostRequest,
) (
CreateHostResult, error,
) {
joiningBootstrap, err := r.daemon.CreateHost(
ctx, req.HostName, req.Opts,
)
if err != nil {
return CreateHostResult{}, err
}
return CreateHostResult{JoiningBootstrap: joiningBootstrap}, nil
}
// CreateNebulaCertificateRequest contains the arguments to the
// CreateNebulaCertificate RPC method.
//
@ -169,9 +71,161 @@ type CreateNebulaCertificateResult struct {
HostNebulaCertifcate nebula.Certificate
}
// CreateNebulaCertificate passes the call through to the Daemon method of the
// same name.
func (r *RPC) CreateNebulaCertificate(
// RPC exposes all RPC methods which are available to be called over the RPC
// interface.
type RPC interface {
// CreateNetwork passes through to the Daemon method of the same name.
CreateNetwork(
ctx context.Context, req CreateNetworkRequest,
) (
struct{}, error,
)
// JoinNetwork passes through to the Daemon method of the same name.
JoinNetwork(
ctx context.Context, req JoiningBootstrap,
) (
struct{}, error,
)
// GetHosts returns all hosts known to the network, sorted by their name.
GetHosts(
ctx context.Context, req struct{},
) (
GetHostsResult, error,
)
// GetGarageClientParams passes the call through to the Daemon method of the
// same name.
GetGarageClientParams(
ctx context.Context, req struct{},
) (
GarageClientParams, error,
)
// GetNebulaCAPublicCredentials returns the CAPublicCredentials for the
// network.
GetNebulaCAPublicCredentials(
ctx context.Context, req struct{},
) (
nebula.CAPublicCredentials, error,
)
// RemoveHost passes the call through to the Daemon method of the same name.
RemoveHost(
ctx context.Context, req RemoveHostRequest,
) (
struct{}, error,
)
// CreateHost passes the call through to the Daemon method of the same name.
CreateHost(
ctx context.Context, req CreateHostRequest,
) (
CreateHostResult, error,
)
// CreateNebulaCertificate passes the call through to the Daemon method of
// the same name.
CreateNebulaCertificate(
ctx context.Context, req CreateNebulaCertificateRequest,
) (
CreateNebulaCertificateResult, 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, req CreateNetworkRequest,
) (
struct{}, error,
) {
return struct{}{}, r.daemon.CreateNetwork(
ctx, req.Name, req.Domain, req.IPNet, req.HostName,
)
}
func (r *rpcImpl) JoinNetwork(
ctx context.Context, req JoiningBootstrap,
) (
struct{}, error,
) {
return struct{}{}, r.daemon.JoinNetwork(ctx, req)
}
func (r *rpcImpl) GetHosts(
ctx context.Context, req struct{},
) (
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, req struct{},
) (
GarageClientParams, error,
) {
return r.daemon.GetGarageClientParams(ctx)
}
func (r *rpcImpl) GetNebulaCAPublicCredentials(
ctx context.Context, req struct{},
) (
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, req RemoveHostRequest,
) (
struct{}, error,
) {
return struct{}{}, r.daemon.RemoveHost(ctx, req.HostName)
}
func (r *rpcImpl) CreateHost(
ctx context.Context, req CreateHostRequest,
) (
CreateHostResult, error,
) {
joiningBootstrap, err := r.daemon.CreateHost(
ctx, req.HostName, req.Opts,
)
if err != nil {
return CreateHostResult{}, err
}
return CreateHostResult{JoiningBootstrap: joiningBootstrap}, nil
}
func (r *rpcImpl) CreateNebulaCertificate(
ctx context.Context, req CreateNebulaCertificateRequest,
) (
CreateNebulaCertificateResult, error,

17
nix/gowrap.nix Normal file
View File

@ -0,0 +1,17 @@
{
buildGoModule,
fetchFromGitHub,
}: let
version = "1.4.0";
in buildGoModule {
pname = "gowrap";
inherit version;
src = fetchFromGitHub {
owner = "hexdigest";
repo = "gowrap";
rev = "v${version}";
hash = "sha256-eEaUANLnxRGfVbhOTwJV+R9iEWMObg0lHqmwO3AYuIk=";
};
vendorHash = "sha256-xIOyXXt8WSGQYIvIam+0e25VNI7awYEEYZBe7trC6zQ=";
subPackages = [ "cmd/gowrap" ];
}

View File

@ -14,6 +14,7 @@ in pkgs.mkShell {
buildInputs = [
pkgs.go
pkgs.golangci-lint
(pkgs.callPackage ./nix/gowrap.nix {})
];
shellHook = ''
true # placeholder