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) { func (ctx subCmdCtx) getHosts() (daemon.GetHostsResult, error) {
var res daemon.GetHostsResult res, err := ctx.daemonRPC.GetHosts(ctx.ctx, struct{}{})
err := ctx.daemonRCPClient.Call(ctx.ctx, &res, "GetHosts", nil)
if err != nil { if err != nil {
return daemon.GetHostsResult{}, fmt.Errorf("calling GetHosts: %w", err) return daemon.GetHostsResult{}, fmt.Errorf("calling GetHosts: %w", err)
} }

View File

@ -18,7 +18,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, rpc daemon.RPC,
) ( ) (
*http.Server, error, *http.Server, error,
) { ) {

View File

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

View File

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

View File

@ -47,12 +47,8 @@ var subCmdNebulaCreateCert = subCmd{
return fmt.Errorf("unmarshaling public key as PEM: %w", err) return fmt.Errorf("unmarshaling public key as PEM: %w", err)
} }
var res daemon.CreateNebulaCertificateResult res, err := subCmdCtx.daemonRPC.CreateNebulaCertificate(
err = subCmdCtx.daemonRCPClient.Call( subCmdCtx.ctx, daemon.CreateNebulaCertificateRequest{
subCmdCtx.ctx,
&res,
"CreateNebulaCertificate",
daemon.CreateNebulaCertificateRequest{
HostName: hostName.V, HostName: hostName.V,
HostEncryptingPublicKey: hostPub, HostEncryptingPublicKey: hostPub,
}, },
@ -89,9 +85,8 @@ var subCmdNebulaShow = subCmd{
return fmt.Errorf("getting hosts: %w", err) return fmt.Errorf("getting hosts: %w", err)
} }
var caPublicCreds nebula.CAPublicCredentials caPublicCreds, err := subCmdCtx.daemonRPC.GetNebulaCAPublicCredentials(
err = subCmdCtx.daemonRCPClient.Call( subCmdCtx.ctx, struct{}{},
subCmdCtx.ctx, &caPublicCreds, "GetNebulaCAPublicCredentials", nil,
) )
if err != nil { if err != nil {
return fmt.Errorf("calling GetNebulaCAPublicCredentials: %w", err) 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.", descr: "Create's a new network, with this host being the first host in that network.",
do: func(subCmdCtx subCmdCtx) error { do: func(subCmdCtx subCmdCtx) error {
var ( var (
ctx = subCmdCtx.ctx
flags = subCmdCtx.flagSet(false) flags = subCmdCtx.flagSet(false)
ipNet ipNetFlag ipNet ipNetFlag
hostName hostNameFlag hostName hostNameFlag
) )
@ -53,14 +51,14 @@ var subCmdNetworkCreate = subCmd{
return errors.New("--name, --domain, --ip-net, and --hostname are required") return errors.New("--name, --domain, --ip-net, and --hostname are required")
} }
req := daemon.CreateNetworkRequest{ _, err := subCmdCtx.daemonRPC.CreateNetwork(
subCmdCtx.ctx, daemon.CreateNetworkRequest{
Name: *name, Name: *name,
Domain: *domain, Domain: *domain,
IPNet: ipNet.V, IPNet: ipNet.V,
HostName: hostName.V, HostName: hostName.V,
} },
)
err := subCmdCtx.daemonRCPClient.Call(ctx, nil, "CreateNetwork", req)
if err != nil { if err != nil {
return fmt.Errorf("creating network: %w", err) return fmt.Errorf("creating network: %w", err)
} }
@ -74,13 +72,11 @@ var subCmdNetworkJoin = subCmd{
descr: "Joins this host to an existing network", descr: "Joins this host to an existing network",
do: func(subCmdCtx subCmdCtx) error { do: func(subCmdCtx subCmdCtx) error {
var ( var (
ctx = subCmdCtx.ctx
flags = subCmdCtx.flagSet(false) flags = subCmdCtx.flagSet(false)
) bootstrapPath = flags.StringP(
bootstrapPath := flags.StringP(
"bootstrap-path", "b", "", "Path to a bootstrap.json file.", "bootstrap-path", "b", "", "Path to a bootstrap.json file.",
) )
)
if err := flags.Parse(subCmdCtx.args); err != nil { if err := flags.Parse(subCmdCtx.args); err != nil {
return fmt.Errorf("parsing flags: %w", err) return fmt.Errorf("parsing flags: %w", err)
@ -97,9 +93,10 @@ var subCmdNetworkJoin = subCmd{
) )
} }
return subCmdCtx.daemonRCPClient.Call( _, err := subCmdCtx.daemonRPC.JoinNetwork(
ctx, nil, "JoinNetwork", newBootstrap, subCmdCtx.ctx, newBootstrap,
) )
return err
}, },
} }

View File

@ -36,7 +36,7 @@ type subCmdCtx struct {
ctx context.Context ctx context.Context
logger *mlog.Logger logger *mlog.Logger
daemonRCPClient jsonrpc2.Client daemonRPC daemon.RPC
} }
type subCmd struct { type subCmd struct {
@ -128,8 +128,8 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error {
printUsageExit(subCmdName) printUsageExit(subCmdName)
} }
daemonRCPClient := jsonrpc2.NewUnixHTTPClient( daemonRPC := daemon.RPCFromClient(
daemon.HTTPSocketPath(), daemonHTTPRPCPath, jsonrpc2.NewUnixHTTPClient(daemon.HTTPSocketPath(), daemonHTTPRPCPath),
) )
err := subCmd.do(subCmdCtx{ err := subCmd.do(subCmdCtx{
@ -138,7 +138,7 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error {
subCmdNames: append(ctx.subCmdNames, subCmdName), subCmdNames: append(ctx.subCmdNames, subCmdName),
ctx: ctx.ctx, ctx: ctx.ctx,
logger: ctx.logger, logger: ctx.logger,
daemonRCPClient: daemonRCPClient, daemonRPC: daemonRPC,
}) })
if err != nil { 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" "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. // CreateNetworkRequest contains the arguments to the CreateNetwork RPC method.
// //
// All fields are required. // All fields are required.
@ -41,76 +30,11 @@ type CreateNetworkRequest struct {
HostName nebula.HostName 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. // GetHostsResult wraps the results from the GetHosts RPC method.
type GetHostsResult struct { type GetHostsResult struct {
Hosts []bootstrap.Host 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. // RemoveHostRequest contains the arguments to the RemoveHost RPC method.
// //
// All fields are required. // All fields are required.
@ -118,11 +42,6 @@ type RemoveHostRequest struct {
HostName nebula.HostName 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 // CreateHostRequest contains the arguments to the
// CreateHost RPC method. // CreateHost RPC method.
// //
@ -137,23 +56,6 @@ type CreateHostResult struct {
JoiningBootstrap JoiningBootstrap 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 // CreateNebulaCertificateRequest contains the arguments to the
// CreateNebulaCertificate RPC method. // CreateNebulaCertificate RPC method.
// //
@ -169,9 +71,161 @@ type CreateNebulaCertificateResult struct {
HostNebulaCertifcate nebula.Certificate HostNebulaCertifcate nebula.Certificate
} }
// CreateNebulaCertificate passes the call through to the Daemon method of the // RPC exposes all RPC methods which are available to be called over the RPC
// same name. // interface.
func (r *RPC) CreateNebulaCertificate( 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, ctx context.Context, req CreateNebulaCertificateRequest,
) ( ) (
CreateNebulaCertificateResult, error, 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 = [ buildInputs = [
pkgs.go pkgs.go
pkgs.golangci-lint pkgs.golangci-lint
(pkgs.callPackage ./nix/gowrap.nix {})
]; ];
shellHook = '' shellHook = ''
true # placeholder true # placeholder