TUN-8415: Refactor capnp rpc into a single module

Combines the tunnelrpc and quic/schema capnp files into the same module.

To help reduce future issues with capnp id generation, capnpids are
provided in the capnp files from the existing capnp struct ids generated
in the go files.

Reduces the overall interface of the Capnp methods to the rest of
the code by providing an interface that will handle the quic protocol
selection.

Introduces a new `rpc-timeout` config that will allow all of the
SessionManager and ConfigurationManager RPC requests to have a timeout.
The timeout for these values is set to 5 seconds as non of these operations
for the managers should take a long time to complete.

Removed the RPC-specific logger as it never provided good debugging value
as the RPC method names were not visible in the logs.
This commit is contained in:
Devin Carr
2024-05-13 21:22:06 -07:00
parent 7d76ce2d24
commit eb2e4349e8
39 changed files with 1121 additions and 1028 deletions

31
tunnelrpc/proto/go.capnp Normal file
View File

@@ -0,0 +1,31 @@
# Generate go.capnp.out with:
# capnp compile -o- go.capnp > go.capnp.out
# Must run inside this directory to preserve paths.
@0xd12a1c51fedd6c88;
annotation package(file) :Text;
# The Go package name for the generated file.
annotation import(file) :Text;
# The Go import path that the generated file is accessible from.
# Used to generate import statements and check if two types are in the
# same package.
annotation doc(struct, field, enum) :Text;
# Adds a doc comment to the generated code.
annotation tag(enumerant) :Text;
# Changes the string representation of the enum in the generated code.
annotation notag(enumerant) :Void;
# Removes the string representation of the enum in the generated code.
annotation customtype(field) :Text;
# OBSOLETE, not used by code generator.
annotation name(struct, field, union, enum, enumerant, interface, method, param, annotation, const, group) :Text;
# Used to rename the element in the generated code.
$package("capnp");
$import("zombiezen.com/go/capnproto2");

View File

@@ -0,0 +1,28 @@
using Go = import "go.capnp";
@0xb29021ef7421cc32;
$Go.package("proto");
$Go.import("github.com/cloudflare/cloudflared/tunnelrpc");
struct ConnectRequest @0xc47116a1045e4061 {
dest @0 :Text;
type @1 :ConnectionType;
metadata @2 :List(Metadata);
}
enum ConnectionType @0xc52e1bac26d379c8 {
http @0;
websocket @1;
tcp @2;
}
struct Metadata @0xe1446b97bfd1cd37 {
key @0 :Text;
val @1 :Text;
}
struct ConnectResponse @0xb1032ec91cef8727 {
error @0 :Text;
metadata @1 :List(Metadata);
}

View File

@@ -0,0 +1,395 @@
// Code generated by capnpc-go. DO NOT EDIT.
package proto
import (
capnp "zombiezen.com/go/capnproto2"
text "zombiezen.com/go/capnproto2/encoding/text"
schemas "zombiezen.com/go/capnproto2/schemas"
)
type ConnectRequest struct{ capnp.Struct }
// ConnectRequest_TypeID is the unique identifier for the type ConnectRequest.
const ConnectRequest_TypeID = 0xc47116a1045e4061
func NewConnectRequest(s *capnp.Segment) (ConnectRequest, error) {
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2})
return ConnectRequest{st}, err
}
func NewRootConnectRequest(s *capnp.Segment) (ConnectRequest, error) {
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2})
return ConnectRequest{st}, err
}
func ReadRootConnectRequest(msg *capnp.Message) (ConnectRequest, error) {
root, err := msg.RootPtr()
return ConnectRequest{root.Struct()}, err
}
func (s ConnectRequest) String() string {
str, _ := text.Marshal(0xc47116a1045e4061, s.Struct)
return str
}
func (s ConnectRequest) Dest() (string, error) {
p, err := s.Struct.Ptr(0)
return p.Text(), err
}
func (s ConnectRequest) HasDest() bool {
p, err := s.Struct.Ptr(0)
return p.IsValid() || err != nil
}
func (s ConnectRequest) DestBytes() ([]byte, error) {
p, err := s.Struct.Ptr(0)
return p.TextBytes(), err
}
func (s ConnectRequest) SetDest(v string) error {
return s.Struct.SetText(0, v)
}
func (s ConnectRequest) Type() ConnectionType {
return ConnectionType(s.Struct.Uint16(0))
}
func (s ConnectRequest) SetType(v ConnectionType) {
s.Struct.SetUint16(0, uint16(v))
}
func (s ConnectRequest) Metadata() (Metadata_List, error) {
p, err := s.Struct.Ptr(1)
return Metadata_List{List: p.List()}, err
}
func (s ConnectRequest) HasMetadata() bool {
p, err := s.Struct.Ptr(1)
return p.IsValid() || err != nil
}
func (s ConnectRequest) SetMetadata(v Metadata_List) error {
return s.Struct.SetPtr(1, v.List.ToPtr())
}
// NewMetadata sets the metadata field to a newly
// allocated Metadata_List, preferring placement in s's segment.
func (s ConnectRequest) NewMetadata(n int32) (Metadata_List, error) {
l, err := NewMetadata_List(s.Struct.Segment(), n)
if err != nil {
return Metadata_List{}, err
}
err = s.Struct.SetPtr(1, l.List.ToPtr())
return l, err
}
// ConnectRequest_List is a list of ConnectRequest.
type ConnectRequest_List struct{ capnp.List }
// NewConnectRequest creates a new list of ConnectRequest.
func NewConnectRequest_List(s *capnp.Segment, sz int32) (ConnectRequest_List, error) {
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}, sz)
return ConnectRequest_List{l}, err
}
func (s ConnectRequest_List) At(i int) ConnectRequest { return ConnectRequest{s.List.Struct(i)} }
func (s ConnectRequest_List) Set(i int, v ConnectRequest) error { return s.List.SetStruct(i, v.Struct) }
func (s ConnectRequest_List) String() string {
str, _ := text.MarshalList(0xc47116a1045e4061, s.List)
return str
}
// ConnectRequest_Promise is a wrapper for a ConnectRequest promised by a client call.
type ConnectRequest_Promise struct{ *capnp.Pipeline }
func (p ConnectRequest_Promise) Struct() (ConnectRequest, error) {
s, err := p.Pipeline.Struct()
return ConnectRequest{s}, err
}
type ConnectionType uint16
// ConnectionType_TypeID is the unique identifier for the type ConnectionType.
const ConnectionType_TypeID = 0xc52e1bac26d379c8
// Values of ConnectionType.
const (
ConnectionType_http ConnectionType = 0
ConnectionType_websocket ConnectionType = 1
ConnectionType_tcp ConnectionType = 2
)
// String returns the enum's constant name.
func (c ConnectionType) String() string {
switch c {
case ConnectionType_http:
return "http"
case ConnectionType_websocket:
return "websocket"
case ConnectionType_tcp:
return "tcp"
default:
return ""
}
}
// ConnectionTypeFromString returns the enum value with a name,
// or the zero value if there's no such value.
func ConnectionTypeFromString(c string) ConnectionType {
switch c {
case "http":
return ConnectionType_http
case "websocket":
return ConnectionType_websocket
case "tcp":
return ConnectionType_tcp
default:
return 0
}
}
type ConnectionType_List struct{ capnp.List }
func NewConnectionType_List(s *capnp.Segment, sz int32) (ConnectionType_List, error) {
l, err := capnp.NewUInt16List(s, sz)
return ConnectionType_List{l.List}, err
}
func (l ConnectionType_List) At(i int) ConnectionType {
ul := capnp.UInt16List{List: l.List}
return ConnectionType(ul.At(i))
}
func (l ConnectionType_List) Set(i int, v ConnectionType) {
ul := capnp.UInt16List{List: l.List}
ul.Set(i, uint16(v))
}
type Metadata struct{ capnp.Struct }
// Metadata_TypeID is the unique identifier for the type Metadata.
const Metadata_TypeID = 0xe1446b97bfd1cd37
func NewMetadata(s *capnp.Segment) (Metadata, error) {
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
return Metadata{st}, err
}
func NewRootMetadata(s *capnp.Segment) (Metadata, error) {
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
return Metadata{st}, err
}
func ReadRootMetadata(msg *capnp.Message) (Metadata, error) {
root, err := msg.RootPtr()
return Metadata{root.Struct()}, err
}
func (s Metadata) String() string {
str, _ := text.Marshal(0xe1446b97bfd1cd37, s.Struct)
return str
}
func (s Metadata) Key() (string, error) {
p, err := s.Struct.Ptr(0)
return p.Text(), err
}
func (s Metadata) HasKey() bool {
p, err := s.Struct.Ptr(0)
return p.IsValid() || err != nil
}
func (s Metadata) KeyBytes() ([]byte, error) {
p, err := s.Struct.Ptr(0)
return p.TextBytes(), err
}
func (s Metadata) SetKey(v string) error {
return s.Struct.SetText(0, v)
}
func (s Metadata) Val() (string, error) {
p, err := s.Struct.Ptr(1)
return p.Text(), err
}
func (s Metadata) HasVal() bool {
p, err := s.Struct.Ptr(1)
return p.IsValid() || err != nil
}
func (s Metadata) ValBytes() ([]byte, error) {
p, err := s.Struct.Ptr(1)
return p.TextBytes(), err
}
func (s Metadata) SetVal(v string) error {
return s.Struct.SetText(1, v)
}
// Metadata_List is a list of Metadata.
type Metadata_List struct{ capnp.List }
// NewMetadata creates a new list of Metadata.
func NewMetadata_List(s *capnp.Segment, sz int32) (Metadata_List, error) {
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz)
return Metadata_List{l}, err
}
func (s Metadata_List) At(i int) Metadata { return Metadata{s.List.Struct(i)} }
func (s Metadata_List) Set(i int, v Metadata) error { return s.List.SetStruct(i, v.Struct) }
func (s Metadata_List) String() string {
str, _ := text.MarshalList(0xe1446b97bfd1cd37, s.List)
return str
}
// Metadata_Promise is a wrapper for a Metadata promised by a client call.
type Metadata_Promise struct{ *capnp.Pipeline }
func (p Metadata_Promise) Struct() (Metadata, error) {
s, err := p.Pipeline.Struct()
return Metadata{s}, err
}
type ConnectResponse struct{ capnp.Struct }
// ConnectResponse_TypeID is the unique identifier for the type ConnectResponse.
const ConnectResponse_TypeID = 0xb1032ec91cef8727
func NewConnectResponse(s *capnp.Segment) (ConnectResponse, error) {
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
return ConnectResponse{st}, err
}
func NewRootConnectResponse(s *capnp.Segment) (ConnectResponse, error) {
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
return ConnectResponse{st}, err
}
func ReadRootConnectResponse(msg *capnp.Message) (ConnectResponse, error) {
root, err := msg.RootPtr()
return ConnectResponse{root.Struct()}, err
}
func (s ConnectResponse) String() string {
str, _ := text.Marshal(0xb1032ec91cef8727, s.Struct)
return str
}
func (s ConnectResponse) Error() (string, error) {
p, err := s.Struct.Ptr(0)
return p.Text(), err
}
func (s ConnectResponse) HasError() bool {
p, err := s.Struct.Ptr(0)
return p.IsValid() || err != nil
}
func (s ConnectResponse) ErrorBytes() ([]byte, error) {
p, err := s.Struct.Ptr(0)
return p.TextBytes(), err
}
func (s ConnectResponse) SetError(v string) error {
return s.Struct.SetText(0, v)
}
func (s ConnectResponse) Metadata() (Metadata_List, error) {
p, err := s.Struct.Ptr(1)
return Metadata_List{List: p.List()}, err
}
func (s ConnectResponse) HasMetadata() bool {
p, err := s.Struct.Ptr(1)
return p.IsValid() || err != nil
}
func (s ConnectResponse) SetMetadata(v Metadata_List) error {
return s.Struct.SetPtr(1, v.List.ToPtr())
}
// NewMetadata sets the metadata field to a newly
// allocated Metadata_List, preferring placement in s's segment.
func (s ConnectResponse) NewMetadata(n int32) (Metadata_List, error) {
l, err := NewMetadata_List(s.Struct.Segment(), n)
if err != nil {
return Metadata_List{}, err
}
err = s.Struct.SetPtr(1, l.List.ToPtr())
return l, err
}
// ConnectResponse_List is a list of ConnectResponse.
type ConnectResponse_List struct{ capnp.List }
// NewConnectResponse creates a new list of ConnectResponse.
func NewConnectResponse_List(s *capnp.Segment, sz int32) (ConnectResponse_List, error) {
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz)
return ConnectResponse_List{l}, err
}
func (s ConnectResponse_List) At(i int) ConnectResponse { return ConnectResponse{s.List.Struct(i)} }
func (s ConnectResponse_List) Set(i int, v ConnectResponse) error {
return s.List.SetStruct(i, v.Struct)
}
func (s ConnectResponse_List) String() string {
str, _ := text.MarshalList(0xb1032ec91cef8727, s.List)
return str
}
// ConnectResponse_Promise is a wrapper for a ConnectResponse promised by a client call.
type ConnectResponse_Promise struct{ *capnp.Pipeline }
func (p ConnectResponse_Promise) Struct() (ConnectResponse, error) {
s, err := p.Pipeline.Struct()
return ConnectResponse{s}, err
}
const schema_b29021ef7421cc32 = "x\xda\xb4\x911k\x14A\x1c\xc5\xdf\x9b\xcde-\x0e" +
"\xf7\x86KlT\xc2\x05Q\x13\xdc\x8b\xc9\x09\xa2 \x1c" +
"\x18A%\xc1\x9b`mX7\x83\x09w\xee\xce\xed\xce" +
"\x19\xee\x13\xd8\xda\x89\xa5\xbd \x09X\xdb((h!" +
"\x16\xd6\x0a66\xf9\x04\xb22\x0b\x9b\x83\x90B\x04\xbb" +
"\xe1\xcd\x9by\xbf\xff\xff5\xbeu\xc5r\xed\x11\x01\xd5" +
"\xa8M\x17\x17\x9e\x1e\x9c\xf9\xd8\xf6\xf6 C\x16+\x9f" +
"Z\xf6\xa0\xf5l\x1f5\xe1\x03\xcb/~Q\xbe\xf1\x01" +
"\xb9\xb7\x0b\x16Q\xf7\xc1\xd4\xcbS\xc3wP!\x8fZ" +
";-\xfe`\xf3\x06}\xa0y\x8d\xaf\xc1\xe2\xc3\xf8\xeb" +
"\xf9W\xa7\xdb\xef!C11\x83\x9d\x9f\xceI\xf7\xa8" +
"\xf9\x9b\xf7\xc0\xe2\xea\xe7/o\x9f\xf7W\xbf\x1fC\xd0" +
"\x99\x15\xfbl\x86\xa5yA8\x08;J\x12=\xc8\xcc" +
"t\xbcd\xb2\xd4\xa6K\xc3\xd1N\xbc\xf9X\xdbh+" +
"\xb2\xd1f\xa9\xc5\xe9\xa0\x1dG&1\xd7o\xa6I\xa2" +
"c\xbb\xa1s\x13\xa4I\xae{\xa4:\xe1M\x01S\x04" +
"\xe4\xc2\x0a\xa0\xceyT\x97\x05%9C'\x86w\x01" +
"u\xc9\xa3\xba-8\xa7\xb3,\xcdX\x87`\x1d,\xaa" +
"\x14\x00<\x09\xf6<\xb21\xa1\x07\x9d\xf8\xaf\x80\xc3\x91" +
"\xafs\xeb\xf8\xea\x87|\xb7\x16\x01\xd5\xf5\xa8\xd6\x04+" +
"\xbc;N[\xf5\xa8z\x82Rp\x86\x02\x90\xeb\x8ey" +
"\xcd\xa3\xda\x16\x0c\xb6tn+\xe4\xc0\x8e\x8df0)" +
"\x03d\xf0_'\xd9I\x93\xfb\xfe\xd8\x94\x9b\xae\x97p" +
"g\x17\xdd\x87rv\x03\xa0\x90r\x1e\x08\xb6\xad5\xc5" +
"\xae~\x98\xa7q_\x83\xd6\xb7\xb19\x8c\xab\xfdU\xdc" +
"\xba\xb6s\xe5\xc5\x91J\xe7\x8f\xab\xd4\x89\x17=\xaa+" +
"\x82~_\x8f\xab\xed\xf8O\xa2Au\xfe\x13\x00\x00\xff" +
"\xff\x1d\xce\xd1\xb0"
func init() {
schemas.Register(schema_b29021ef7421cc32,
0xb1032ec91cef8727,
0xc47116a1045e4061,
0xc52e1bac26d379c8,
0xe1446b97bfd1cd37)
}

View File

@@ -0,0 +1,173 @@
using Go = import "go.capnp";
@0xdb8274f9144abc7e;
$Go.package("proto");
$Go.import("github.com/cloudflare/cloudflared/tunnelrpc");
struct Authentication @0xc082ef6e0d42ed1d {
key @0 :Text;
email @1 :Text;
originCAKey @2 :Text;
}
struct TunnelRegistration @0xf41a0f001ad49e46 {
err @0 :Text;
# the url to access the tunnel
url @1 :Text;
# Used to inform the client of actions taken.
logLines @2 :List(Text);
# In case of error, whether the client should attempt to reconnect.
permanentFailure @3 :Bool;
# Displayed to user
tunnelID @4 :Text;
# How long should this connection wait to retry in seconds, if the error wasn't permanent
retryAfterSeconds @5 :UInt16;
# A unique ID used to reconnect this tunnel.
eventDigest @6 :Data;
# A unique ID used to prove this tunnel was previously connected to a given metal.
connDigest @7 :Data;
}
struct RegistrationOptions @0xc793e50592935b4a {
# The tunnel client's unique identifier, used to verify a reconnection.
clientId @0 :Text;
# Information about the running binary.
version @1 :Text;
os @2 :Text;
# What to do with existing tunnels for the given hostname.
existingTunnelPolicy @3 :ExistingTunnelPolicy;
# If using the balancing policy, identifies the LB pool to use.
poolName @4 :Text;
# Client-defined tags to associate with the tunnel
tags @5 :List(Tag);
# A unique identifier for a high-availability connection made by a single client.
connectionId @6 :UInt8;
# origin LAN IP
originLocalIp @7 :Text;
# whether Argo Tunnel client has been autoupdated
isAutoupdated @8 :Bool;
# whether Argo Tunnel client is run from a terminal
runFromTerminal @9 :Bool;
# cross stream compression setting, 0 - off, 3 - high
compressionQuality @10 :UInt64;
uuid @11 :Text;
# number of previous attempts to send RegisterTunnel/ReconnectTunnel
numPreviousAttempts @12 :UInt8;
# Set of features this cloudflared knows it supports
features @13 :List(Text);
}
struct Tag @0xcbd96442ae3bb01a {
name @0 :Text;
value @1 :Text;
}
enum ExistingTunnelPolicy @0x84cb9536a2cf6d3c {
ignore @0;
disconnect @1;
balance @2;
}
struct ServerInfo @0xf2c68e2547ec3866 {
locationName @0 :Text;
}
struct AuthenticateResponse @0x82c325a07ad22a65 {
permanentErr @0 :Text;
retryableErr @1 :Text;
jwt @2 :Data;
hoursUntilRefresh @3 :UInt8;
}
struct ClientInfo @0x83ced0145b2f114b {
# The tunnel client's unique identifier, used to verify a reconnection.
clientId @0 :Data;
# Set of features this cloudflared knows it supports
features @1 :List(Text);
# Information about the running binary.
version @2 :Text;
# Client OS and CPU info
arch @3 :Text;
}
struct ConnectionOptions @0xb4bf9861fe035d04 {
# client details
client @0 :ClientInfo;
# origin LAN IP
originLocalIp @1 :Data;
# What to do if connection already exists
replaceExisting @2 :Bool;
# cross stream compression setting, 0 - off, 3 - high
compressionQuality @3 :UInt8;
# number of previous attempts to send RegisterConnection
numPreviousAttempts @4 :UInt8;
}
struct ConnectionResponse @0xdbaa9d03d52b62dc {
result :union {
error @0 :ConnectionError;
connectionDetails @1 :ConnectionDetails;
}
}
struct ConnectionError @0xf5f383d2785edb86 {
cause @0 :Text;
# How long should this connection wait to retry in ns
retryAfter @1 :Int64;
shouldRetry @2 :Bool;
}
struct ConnectionDetails @0xb5f39f082b9ac18a {
# identifier of this connection
uuid @0 :Data;
# airport code of the colo where this connection landed
locationName @1 :Text;
# tells if the tunnel is remotely managed
tunnelIsRemotelyManaged @2: Bool;
}
struct TunnelAuth @0x9496331ab9cd463f {
accountTag @0 :Text;
tunnelSecret @1 :Data;
}
interface RegistrationServer @0xf71695ec7fe85497 {
registerConnection @0 (auth :TunnelAuth, tunnelId :Data, connIndex :UInt8, options :ConnectionOptions) -> (result :ConnectionResponse);
unregisterConnection @1 () -> ();
updateLocalConfiguration @2 (config :Data) -> ();
}
interface TunnelServer @0xea58385c65416035 extends (RegistrationServer) {
registerTunnel @0 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration);
getServerInfo @1 () -> (result :ServerInfo);
unregisterTunnel @2 (gracePeriodNanoSec :Int64) -> ();
# obsoleteDeclarativeTunnelConnect RPC deprecated in TUN-3019
obsoleteDeclarativeTunnelConnect @3 () -> ();
authenticate @4 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :AuthenticateResponse);
reconnectTunnel @5 (jwt :Data, eventDigest :Data, connDigest :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration);
}
struct RegisterUdpSessionResponse @0xab6d5210c1f26687 {
err @0 :Text;
spans @1 :Data;
}
interface SessionManager @0x839445a59fb01686 {
# Let the edge decide closeAfterIdle to make sure cloudflared doesn't close session before the edge closes its side
registerUdpSession @0 (sessionId :Data, dstIp :Data, dstPort :UInt16, closeAfterIdleHint :Int64, traceContext :Text = "") -> (result :RegisterUdpSessionResponse);
unregisterUdpSession @1 (sessionId :Data, message :Text) -> ();
}
struct UpdateConfigurationResponse @0xdb58ff694ba05cf9 {
# Latest configuration that was applied successfully. The err field might be populated at the same time to indicate
# that cloudflared is using an older configuration because the latest cannot be applied
latestAppliedVersion @0 :Int32;
# Any error encountered when trying to apply the last configuration
err @1 :Text;
}
# ConfigurationManager defines RPC to manage cloudflared configuration remotely
interface ConfigurationManager @0xb48edfbdaa25db04 {
updateConfiguration @0 (version :Int32, config :Data) -> (result: UpdateConfigurationResponse);
}
interface CloudflaredServer @0xf548cef9dea2a4a1 extends(SessionManager, ConfigurationManager) {}

File diff suppressed because it is too large Load Diff