From b7cd874cbf334efd5c01a2f10b2efe95d02efd3d Mon Sep 17 00:00:00 2001 From: Gareth Date: Fri, 31 Oct 2025 18:43:29 -0700 Subject: [PATCH] restore old impl and abandon tunneling approach as liable to be fragile --- gen/go/v1sync/syncservice.pb.go | 1132 ++++++++++++++++- gen/go/v1sync/syncservice_grpc.pb.go | 440 ++----- .../v1syncconnect/syncservice.connect.go | 334 ++--- internal/api/syncapi/client.go | 18 - internal/api/syncapi/cmdstreamutil.go | 111 ++ .../api/syncapi/{handler.go => handler.nogo} | 0 internal/api/syncapi/handler_test.go | 92 -- .../api/syncapi/{old => }/peerstate_test.go | 18 +- internal/api/syncapi/{old => }/syncclient.go | 0 internal/api/syncapi/{old => }/synccommon.go | 0 internal/api/syncapi/{old => }/synchandler.go | 0 .../{syncmanager.nogo => syncmanager.go} | 0 ...cstatehandler.nogo => syncstatehandler.go} | 0 internal/api/syncapi/tunnel/aes.go | 107 -- internal/api/syncapi/tunnel/aes_test.go | 338 ----- internal/api/syncapi/tunnel/connstate.go | 211 --- internal/api/syncapi/tunnel/httpclient.go | 38 - internal/api/syncapi/tunnel/listener.go | 59 - internal/api/syncapi/tunnel/streamutil.go | 146 --- internal/api/syncapi/tunnel/tunnel_test.go | 117 -- internal/api/syncapi/tunnel/tunnelhandler.go | 41 - internal/api/syncapi/tunnel/wrappedstream.go | 326 ----- proto/v1sync/syncservice.proto | 50 +- webui/gen/ts/v1sync/syncservice_pb.ts | 500 ++++++-- 24 files changed, 1805 insertions(+), 2273 deletions(-) delete mode 100644 internal/api/syncapi/client.go create mode 100644 internal/api/syncapi/cmdstreamutil.go rename internal/api/syncapi/{handler.go => handler.nogo} (100%) delete mode 100644 internal/api/syncapi/handler_test.go rename internal/api/syncapi/{old => }/peerstate_test.go (86%) rename internal/api/syncapi/{old => }/syncclient.go (100%) rename internal/api/syncapi/{old => }/synccommon.go (100%) rename internal/api/syncapi/{old => }/synchandler.go (100%) rename internal/api/syncapi/{syncmanager.nogo => syncmanager.go} (100%) rename internal/api/syncapi/{syncstatehandler.nogo => syncstatehandler.go} (100%) delete mode 100644 internal/api/syncapi/tunnel/aes.go delete mode 100644 internal/api/syncapi/tunnel/aes_test.go delete mode 100644 internal/api/syncapi/tunnel/connstate.go delete mode 100644 internal/api/syncapi/tunnel/httpclient.go delete mode 100644 internal/api/syncapi/tunnel/listener.go delete mode 100644 internal/api/syncapi/tunnel/streamutil.go delete mode 100644 internal/api/syncapi/tunnel/tunnel_test.go delete mode 100644 internal/api/syncapi/tunnel/tunnelhandler.go delete mode 100644 internal/api/syncapi/tunnel/wrappedstream.go diff --git a/gen/go/v1sync/syncservice.pb.go b/gen/go/v1sync/syncservice.pb.go index 98c94fc7..a47ebe58 100644 --- a/gen/go/v1sync/syncservice.pb.go +++ b/gen/go/v1sync/syncservice.pb.go @@ -7,13 +7,13 @@ package v1sync import ( - types "github.com/garethgeorge/backrest/gen/go/types" + _ "github.com/garethgeorge/backrest/gen/go/types" v1 "github.com/garethgeorge/backrest/gen/go/v1" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" _ "google.golang.org/protobuf/types/known/anypb" - emptypb "google.golang.org/protobuf/types/known/emptypb" + _ "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -90,6 +90,61 @@ func (ConnectionState) EnumDescriptor() ([]byte, []int) { return file_v1sync_syncservice_proto_rawDescGZIP(), []int{0} } +type SyncStreamItem_RepoConnectionState int32 + +const ( + SyncStreamItem_CONNECTION_STATE_UNKNOWN SyncStreamItem_RepoConnectionState = 0 + SyncStreamItem_CONNECTION_STATE_PENDING SyncStreamItem_RepoConnectionState = 1 // queried, response not yet received. + SyncStreamItem_CONNECTION_STATE_CONNECTED SyncStreamItem_RepoConnectionState = 2 + SyncStreamItem_CONNECTION_STATE_UNAUTHORIZED SyncStreamItem_RepoConnectionState = 3 + SyncStreamItem_CONNECTION_STATE_NOT_FOUND SyncStreamItem_RepoConnectionState = 4 +) + +// Enum value maps for SyncStreamItem_RepoConnectionState. +var ( + SyncStreamItem_RepoConnectionState_name = map[int32]string{ + 0: "CONNECTION_STATE_UNKNOWN", + 1: "CONNECTION_STATE_PENDING", + 2: "CONNECTION_STATE_CONNECTED", + 3: "CONNECTION_STATE_UNAUTHORIZED", + 4: "CONNECTION_STATE_NOT_FOUND", + } + SyncStreamItem_RepoConnectionState_value = map[string]int32{ + "CONNECTION_STATE_UNKNOWN": 0, + "CONNECTION_STATE_PENDING": 1, + "CONNECTION_STATE_CONNECTED": 2, + "CONNECTION_STATE_UNAUTHORIZED": 3, + "CONNECTION_STATE_NOT_FOUND": 4, + } +) + +func (x SyncStreamItem_RepoConnectionState) Enum() *SyncStreamItem_RepoConnectionState { + p := new(SyncStreamItem_RepoConnectionState) + *p = x + return p +} + +func (x SyncStreamItem_RepoConnectionState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SyncStreamItem_RepoConnectionState) Descriptor() protoreflect.EnumDescriptor { + return file_v1sync_syncservice_proto_enumTypes[1].Descriptor() +} + +func (SyncStreamItem_RepoConnectionState) Type() protoreflect.EnumType { + return &file_v1sync_syncservice_proto_enumTypes[1] +} + +func (x SyncStreamItem_RepoConnectionState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SyncStreamItem_RepoConnectionState.Descriptor instead. +func (SyncStreamItem_RepoConnectionState) EnumDescriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 0} +} + type SyncStateStreamRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Subscribe bool `protobuf:"varint,1,opt,name=subscribe,proto3" json:"subscribe,omitempty"` // If true, the stream will continue to send updates until cancelled. @@ -734,6 +789,856 @@ func (x *AuthorizationToken) GetInstanceId() *v1.SignedMessage { return nil } +type SyncStreamItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Action: + // + // *SyncStreamItem_SignedMessage + // *SyncStreamItem_Handshake + // *SyncStreamItem_Heartbeat + // *SyncStreamItem_DiffOperations + // *SyncStreamItem_SendOperations + // *SyncStreamItem_SendConfig + // *SyncStreamItem_SetConfig + // *SyncStreamItem_ListResources + // *SyncStreamItem_GetLog + // *SyncStreamItem_SendLogData + // *SyncStreamItem_Throttle + Action isSyncStreamItem_Action `protobuf_oneof:"action"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem) Reset() { + *x = SyncStreamItem{} + mi := &file_v1sync_syncservice_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem) ProtoMessage() {} + +func (x *SyncStreamItem) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem.ProtoReflect.Descriptor instead. +func (*SyncStreamItem) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11} +} + +func (x *SyncStreamItem) GetAction() isSyncStreamItem_Action { + if x != nil { + return x.Action + } + return nil +} + +func (x *SyncStreamItem) GetSignedMessage() *v1.SignedMessage { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_SignedMessage); ok { + return x.SignedMessage + } + } + return nil +} + +func (x *SyncStreamItem) GetHandshake() *SyncStreamItem_SyncActionHandshake { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_Handshake); ok { + return x.Handshake + } + } + return nil +} + +func (x *SyncStreamItem) GetHeartbeat() *SyncStreamItem_SyncActionHeartbeat { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_Heartbeat); ok { + return x.Heartbeat + } + } + return nil +} + +func (x *SyncStreamItem) GetDiffOperations() *SyncStreamItem_SyncActionDiffOperations { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_DiffOperations); ok { + return x.DiffOperations + } + } + return nil +} + +func (x *SyncStreamItem) GetSendOperations() *SyncStreamItem_SyncActionSendOperations { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_SendOperations); ok { + return x.SendOperations + } + } + return nil +} + +func (x *SyncStreamItem) GetSendConfig() *SyncStreamItem_SyncActionSendConfig { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_SendConfig); ok { + return x.SendConfig + } + } + return nil +} + +func (x *SyncStreamItem) GetSetConfig() *SyncStreamItem_SyncActionSetConfig { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_SetConfig); ok { + return x.SetConfig + } + } + return nil +} + +func (x *SyncStreamItem) GetListResources() *SyncStreamItem_SyncActionListResources { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_ListResources); ok { + return x.ListResources + } + } + return nil +} + +func (x *SyncStreamItem) GetGetLog() *SyncStreamItem_SyncActionGetLog { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_GetLog); ok { + return x.GetLog + } + } + return nil +} + +func (x *SyncStreamItem) GetSendLogData() *SyncStreamItem_SyncActionSendLogData { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_SendLogData); ok { + return x.SendLogData + } + } + return nil +} + +func (x *SyncStreamItem) GetThrottle() *SyncStreamItem_SyncActionThrottle { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_Throttle); ok { + return x.Throttle + } + } + return nil +} + +type isSyncStreamItem_Action interface { + isSyncStreamItem_Action() +} + +type SyncStreamItem_SignedMessage struct { + SignedMessage *v1.SignedMessage `protobuf:"bytes,1,opt,name=signed_message,json=signedMessage,proto3,oneof"` +} + +type SyncStreamItem_Handshake struct { + Handshake *SyncStreamItem_SyncActionHandshake `protobuf:"bytes,3,opt,name=handshake,proto3,oneof"` // note: mostly deprecated, sent through headers rather than stream. +} + +type SyncStreamItem_Heartbeat struct { + Heartbeat *SyncStreamItem_SyncActionHeartbeat `protobuf:"bytes,4,opt,name=heartbeat,proto3,oneof"` +} + +type SyncStreamItem_DiffOperations struct { + DiffOperations *SyncStreamItem_SyncActionDiffOperations `protobuf:"bytes,20,opt,name=diff_operations,json=diffOperations,proto3,oneof"` +} + +type SyncStreamItem_SendOperations struct { + SendOperations *SyncStreamItem_SyncActionSendOperations `protobuf:"bytes,21,opt,name=send_operations,json=sendOperations,proto3,oneof"` +} + +type SyncStreamItem_SendConfig struct { + SendConfig *SyncStreamItem_SyncActionSendConfig `protobuf:"bytes,22,opt,name=send_config,json=sendConfig,proto3,oneof"` +} + +type SyncStreamItem_SetConfig struct { + SetConfig *SyncStreamItem_SyncActionSetConfig `protobuf:"bytes,24,opt,name=set_config,json=setConfig,proto3,oneof"` +} + +type SyncStreamItem_ListResources struct { + ListResources *SyncStreamItem_SyncActionListResources `protobuf:"bytes,25,opt,name=list_resources,json=listResources,proto3,oneof"` +} + +type SyncStreamItem_GetLog struct { + GetLog *SyncStreamItem_SyncActionGetLog `protobuf:"bytes,26,opt,name=get_log,json=getLog,proto3,oneof"` +} + +type SyncStreamItem_SendLogData struct { + SendLogData *SyncStreamItem_SyncActionSendLogData `protobuf:"bytes,27,opt,name=send_log_data,json=sendLogData,proto3,oneof"` +} + +type SyncStreamItem_Throttle struct { + Throttle *SyncStreamItem_SyncActionThrottle `protobuf:"bytes,1000,opt,name=throttle,proto3,oneof"` +} + +func (*SyncStreamItem_SignedMessage) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_Handshake) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_Heartbeat) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_DiffOperations) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_SendOperations) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_SendConfig) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_SetConfig) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_ListResources) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_GetLog) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_SendLogData) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_Throttle) isSyncStreamItem_Action() {} + +type SyncStreamItem_SyncActionHandshake struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProtocolVersion int64 `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"` + PublicKey *v1.PublicKey `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + InstanceId *v1.SignedMessage `protobuf:"bytes,3,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionHandshake) Reset() { + *x = SyncStreamItem_SyncActionHandshake{} + mi := &file_v1sync_syncservice_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionHandshake) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionHandshake) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionHandshake) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionHandshake.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionHandshake) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 0} +} + +func (x *SyncStreamItem_SyncActionHandshake) GetProtocolVersion() int64 { + if x != nil { + return x.ProtocolVersion + } + return 0 +} + +func (x *SyncStreamItem_SyncActionHandshake) GetPublicKey() *v1.PublicKey { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *SyncStreamItem_SyncActionHandshake) GetInstanceId() *v1.SignedMessage { + if x != nil { + return x.InstanceId + } + return nil +} + +// SyncActionHeartbeat is sent periodically to keep the connection alive. +type SyncStreamItem_SyncActionHeartbeat struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionHeartbeat) Reset() { + *x = SyncStreamItem_SyncActionHeartbeat{} + mi := &file_v1sync_syncservice_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionHeartbeat) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionHeartbeat) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionHeartbeat) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionHeartbeat.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionHeartbeat) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 1} +} + +type SyncStreamItem_SyncActionSendConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *RemoteConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionSendConfig) Reset() { + *x = SyncStreamItem_SyncActionSendConfig{} + mi := &file_v1sync_syncservice_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionSendConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionSendConfig) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionSendConfig) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionSendConfig.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionSendConfig) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 2} +} + +func (x *SyncStreamItem_SyncActionSendConfig) GetConfig() *RemoteConfig { + if x != nil { + return x.Config + } + return nil +} + +type SyncStreamItem_SyncActionSetConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Repos []*v1.Repo `protobuf:"bytes,1,rep,name=repos,proto3" json:"repos,omitempty"` + Plans []*v1.Plan `protobuf:"bytes,2,rep,name=plans,proto3" json:"plans,omitempty"` + ReposToDelete []string `protobuf:"bytes,3,rep,name=repos_to_delete,json=reposToDelete,proto3" json:"repos_to_delete,omitempty"` + PlansToDelete []string `protobuf:"bytes,4,rep,name=plans_to_delete,json=plansToDelete,proto3" json:"plans_to_delete,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionSetConfig) Reset() { + *x = SyncStreamItem_SyncActionSetConfig{} + mi := &file_v1sync_syncservice_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionSetConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionSetConfig) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionSetConfig) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionSetConfig.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionSetConfig) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 3} +} + +func (x *SyncStreamItem_SyncActionSetConfig) GetRepos() []*v1.Repo { + if x != nil { + return x.Repos + } + return nil +} + +func (x *SyncStreamItem_SyncActionSetConfig) GetPlans() []*v1.Plan { + if x != nil { + return x.Plans + } + return nil +} + +func (x *SyncStreamItem_SyncActionSetConfig) GetReposToDelete() []string { + if x != nil { + return x.ReposToDelete + } + return nil +} + +func (x *SyncStreamItem_SyncActionSetConfig) GetPlansToDelete() []string { + if x != nil { + return x.PlansToDelete + } + return nil +} + +type SyncStreamItem_SyncActionListResources struct { + state protoimpl.MessageState `protogen:"open.v1"` + Repos []*RepoMetadata `protobuf:"bytes,1,rep,name=repos,proto3" json:"repos,omitempty"` + Plans []*PlanMetadata `protobuf:"bytes,2,rep,name=plans,proto3" json:"plans,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionListResources) Reset() { + *x = SyncStreamItem_SyncActionListResources{} + mi := &file_v1sync_syncservice_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionListResources) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionListResources) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionListResources) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionListResources.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionListResources) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 4} +} + +func (x *SyncStreamItem_SyncActionListResources) GetRepos() []*RepoMetadata { + if x != nil { + return x.Repos + } + return nil +} + +func (x *SyncStreamItem_SyncActionListResources) GetPlans() []*PlanMetadata { + if x != nil { + return x.Plans + } + return nil +} + +type SyncStreamItem_SyncActionConnectRepo struct { + state protoimpl.MessageState `protogen:"open.v1"` + RepoId string `protobuf:"bytes,1,opt,name=repo_id,json=repoId,proto3" json:"repo_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionConnectRepo) Reset() { + *x = SyncStreamItem_SyncActionConnectRepo{} + mi := &file_v1sync_syncservice_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionConnectRepo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionConnectRepo) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionConnectRepo) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionConnectRepo.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionConnectRepo) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 5} +} + +func (x *SyncStreamItem_SyncActionConnectRepo) GetRepoId() string { + if x != nil { + return x.RepoId + } + return "" +} + +type SyncStreamItem_SyncActionDiffOperations struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Client connects and sends a list of "have_operations" that exist in its log. + // have_operation_ids and have_operation_modnos are the operation IDs and modnos that the client has when zip'd pairwise. + HaveOperationsSelector *v1.OpSelector `protobuf:"bytes,1,opt,name=have_operations_selector,json=haveOperationsSelector,proto3" json:"have_operations_selector,omitempty"` + HaveOperationIds []int64 `protobuf:"varint,2,rep,packed,name=have_operation_ids,json=haveOperationIds,proto3" json:"have_operation_ids,omitempty"` + HaveOperationModnos []int64 `protobuf:"varint,3,rep,packed,name=have_operation_modnos,json=haveOperationModnos,proto3" json:"have_operation_modnos,omitempty"` + // Server sends a list of "request_operations" for any operations that it doesn't have. + RequestOperations []int64 `protobuf:"varint,4,rep,packed,name=request_operations,json=requestOperations,proto3" json:"request_operations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionDiffOperations) Reset() { + *x = SyncStreamItem_SyncActionDiffOperations{} + mi := &file_v1sync_syncservice_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionDiffOperations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionDiffOperations) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionDiffOperations) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionDiffOperations.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionDiffOperations) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 6} +} + +func (x *SyncStreamItem_SyncActionDiffOperations) GetHaveOperationsSelector() *v1.OpSelector { + if x != nil { + return x.HaveOperationsSelector + } + return nil +} + +func (x *SyncStreamItem_SyncActionDiffOperations) GetHaveOperationIds() []int64 { + if x != nil { + return x.HaveOperationIds + } + return nil +} + +func (x *SyncStreamItem_SyncActionDiffOperations) GetHaveOperationModnos() []int64 { + if x != nil { + return x.HaveOperationModnos + } + return nil +} + +func (x *SyncStreamItem_SyncActionDiffOperations) GetRequestOperations() []int64 { + if x != nil { + return x.RequestOperations + } + return nil +} + +type SyncStreamItem_SyncActionSendOperations struct { + state protoimpl.MessageState `protogen:"open.v1"` + Event *v1.OperationEvent `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionSendOperations) Reset() { + *x = SyncStreamItem_SyncActionSendOperations{} + mi := &file_v1sync_syncservice_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionSendOperations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionSendOperations) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionSendOperations) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionSendOperations.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionSendOperations) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 7} +} + +func (x *SyncStreamItem_SyncActionSendOperations) GetEvent() *v1.OperationEvent { + if x != nil { + return x.Event + } + return nil +} + +type SyncStreamItem_SyncActionGetLog struct { + state protoimpl.MessageState `protogen:"open.v1"` + LogId string `protobuf:"bytes,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionGetLog) Reset() { + *x = SyncStreamItem_SyncActionGetLog{} + mi := &file_v1sync_syncservice_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionGetLog) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionGetLog) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionGetLog) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionGetLog.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionGetLog) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 8} +} + +func (x *SyncStreamItem_SyncActionGetLog) GetLogId() string { + if x != nil { + return x.LogId + } + return "" +} + +type SyncStreamItem_SyncActionSendLogData struct { + state protoimpl.MessageState `protogen:"open.v1"` + LogId string `protobuf:"bytes,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` + // Required only for first message in a log data stream. + OwnerOpid int64 `protobuf:"varint,2,opt,name=owner_opid,json=ownerOpid,proto3" json:"owner_opid,omitempty"` // The operation ID of the operation that owns this log data. + ExpirationTsUnix int64 `protobuf:"varint,3,opt,name=expiration_ts_unix,json=expirationTsUnix,proto3" json:"expiration_ts_unix,omitempty"` // Unix timestamp in seconds when the log data expires. + // Can be sent repeatedly, must be terminated by a packet with size = 0. + Chunk []byte `protobuf:"bytes,4,opt,name=chunk,proto3" json:"chunk,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionSendLogData) Reset() { + *x = SyncStreamItem_SyncActionSendLogData{} + mi := &file_v1sync_syncservice_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionSendLogData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionSendLogData) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionSendLogData) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionSendLogData.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionSendLogData) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 9} +} + +func (x *SyncStreamItem_SyncActionSendLogData) GetLogId() string { + if x != nil { + return x.LogId + } + return "" +} + +func (x *SyncStreamItem_SyncActionSendLogData) GetOwnerOpid() int64 { + if x != nil { + return x.OwnerOpid + } + return 0 +} + +func (x *SyncStreamItem_SyncActionSendLogData) GetExpirationTsUnix() int64 { + if x != nil { + return x.ExpirationTsUnix + } + return 0 +} + +func (x *SyncStreamItem_SyncActionSendLogData) GetChunk() []byte { + if x != nil { + return x.Chunk + } + return nil +} + +type SyncStreamItem_SyncActionThrottle struct { + state protoimpl.MessageState `protogen:"open.v1"` + DelayMs int64 `protobuf:"varint,1,opt,name=delay_ms,json=delayMs,proto3" json:"delay_ms,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionThrottle) Reset() { + *x = SyncStreamItem_SyncActionThrottle{} + mi := &file_v1sync_syncservice_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionThrottle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionThrottle) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionThrottle) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionThrottle.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionThrottle) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 10} +} + +func (x *SyncStreamItem_SyncActionThrottle) GetDelayMs() int64 { + if x != nil { + return x.DelayMs + } + return 0 +} + +type SyncStreamItem_SyncEstablishSharedSecret struct { + state protoimpl.MessageState `protogen:"open.v1"` + // a one-time-use ed25519 public key with a matching unshared private key. Used to perform a key exchange. + // See https://pkg.go.dev/crypto/ecdh#PrivateKey.ECDH . + Ed25519 string `protobuf:"bytes,2,opt,name=ed25519,json=ed25519pub,proto3" json:"ed25519,omitempty"` // base64 encoded public key + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncEstablishSharedSecret) Reset() { + *x = SyncStreamItem_SyncEstablishSharedSecret{} + mi := &file_v1sync_syncservice_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncEstablishSharedSecret) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncEstablishSharedSecret) ProtoMessage() {} + +func (x *SyncStreamItem_SyncEstablishSharedSecret) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncEstablishSharedSecret.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncEstablishSharedSecret) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{11, 11} +} + +func (x *SyncStreamItem_SyncEstablishSharedSecret) GetEd25519() string { + if x != nil { + return x.Ed25519 + } + return "" +} + var File_v1sync_syncservice_proto protoreflect.FileDescriptor const file_v1sync_syncservice_proto_rawDesc = "" + @@ -787,7 +1692,67 @@ const file_v1sync_syncservice_proto_rawDesc = "" + "\n" + "public_key\x18\x01 \x01(\v2\r.v1.PublicKeyR\tpublicKey\x122\n" + "\vinstance_id\x18\x02 \x01(\v2\x11.v1.SignedMessageR\n" + - "instanceId*\x9c\x02\n" + + "instanceId\"\xec\x11\n" + + "\x0eSyncStreamItem\x12:\n" + + "\x0esigned_message\x18\x01 \x01(\v2\x11.v1.SignedMessageH\x00R\rsignedMessage\x12J\n" + + "\thandshake\x18\x03 \x01(\v2*.v1sync.SyncStreamItem.SyncActionHandshakeH\x00R\thandshake\x12J\n" + + "\theartbeat\x18\x04 \x01(\v2*.v1sync.SyncStreamItem.SyncActionHeartbeatH\x00R\theartbeat\x12Z\n" + + "\x0fdiff_operations\x18\x14 \x01(\v2/.v1sync.SyncStreamItem.SyncActionDiffOperationsH\x00R\x0ediffOperations\x12Z\n" + + "\x0fsend_operations\x18\x15 \x01(\v2/.v1sync.SyncStreamItem.SyncActionSendOperationsH\x00R\x0esendOperations\x12N\n" + + "\vsend_config\x18\x16 \x01(\v2+.v1sync.SyncStreamItem.SyncActionSendConfigH\x00R\n" + + "sendConfig\x12K\n" + + "\n" + + "set_config\x18\x18 \x01(\v2*.v1sync.SyncStreamItem.SyncActionSetConfigH\x00R\tsetConfig\x12W\n" + + "\x0elist_resources\x18\x19 \x01(\v2..v1sync.SyncStreamItem.SyncActionListResourcesH\x00R\rlistResources\x12B\n" + + "\aget_log\x18\x1a \x01(\v2'.v1sync.SyncStreamItem.SyncActionGetLogH\x00R\x06getLog\x12R\n" + + "\rsend_log_data\x18\x1b \x01(\v2,.v1sync.SyncStreamItem.SyncActionSendLogDataH\x00R\vsendLogData\x12H\n" + + "\bthrottle\x18\xe8\a \x01(\v2).v1sync.SyncStreamItem.SyncActionThrottleH\x00R\bthrottle\x1a\xa2\x01\n" + + "\x13SyncActionHandshake\x12)\n" + + "\x10protocol_version\x18\x01 \x01(\x03R\x0fprotocolVersion\x12,\n" + + "\n" + + "public_key\x18\x02 \x01(\v2\r.v1.PublicKeyR\tpublicKey\x122\n" + + "\vinstance_id\x18\x03 \x01(\v2\x11.v1.SignedMessageR\n" + + "instanceId\x1a\x15\n" + + "\x13SyncActionHeartbeat\x1aD\n" + + "\x14SyncActionSendConfig\x12,\n" + + "\x06config\x18\x01 \x01(\v2\x14.v1sync.RemoteConfigR\x06config\x1a\xa5\x01\n" + + "\x13SyncActionSetConfig\x12\x1e\n" + + "\x05repos\x18\x01 \x03(\v2\b.v1.RepoR\x05repos\x12\x1e\n" + + "\x05plans\x18\x02 \x03(\v2\b.v1.PlanR\x05plans\x12&\n" + + "\x0frepos_to_delete\x18\x03 \x03(\tR\rreposToDelete\x12&\n" + + "\x0fplans_to_delete\x18\x04 \x03(\tR\rplansToDelete\x1aq\n" + + "\x17SyncActionListResources\x12*\n" + + "\x05repos\x18\x01 \x03(\v2\x14.v1sync.RepoMetadataR\x05repos\x12*\n" + + "\x05plans\x18\x02 \x03(\v2\x14.v1sync.PlanMetadataR\x05plans\x1a0\n" + + "\x15SyncActionConnectRepo\x12\x17\n" + + "\arepo_id\x18\x01 \x01(\tR\x06repoId\x1a\xf5\x01\n" + + "\x18SyncActionDiffOperations\x12H\n" + + "\x18have_operations_selector\x18\x01 \x01(\v2\x0e.v1.OpSelectorR\x16haveOperationsSelector\x12,\n" + + "\x12have_operation_ids\x18\x02 \x03(\x03R\x10haveOperationIds\x122\n" + + "\x15have_operation_modnos\x18\x03 \x03(\x03R\x13haveOperationModnos\x12-\n" + + "\x12request_operations\x18\x04 \x03(\x03R\x11requestOperations\x1aD\n" + + "\x18SyncActionSendOperations\x12(\n" + + "\x05event\x18\x01 \x01(\v2\x12.v1.OperationEventR\x05event\x1a)\n" + + "\x10SyncActionGetLog\x12\x15\n" + + "\x06log_id\x18\x01 \x01(\tR\x05logId\x1a\x91\x01\n" + + "\x15SyncActionSendLogData\x12\x15\n" + + "\x06log_id\x18\x01 \x01(\tR\x05logId\x12\x1d\n" + + "\n" + + "owner_opid\x18\x02 \x01(\x03R\townerOpid\x12,\n" + + "\x12expiration_ts_unix\x18\x03 \x01(\x03R\x10expirationTsUnix\x12\x14\n" + + "\x05chunk\x18\x04 \x01(\fR\x05chunk\x1a/\n" + + "\x12SyncActionThrottle\x12\x19\n" + + "\bdelay_ms\x18\x01 \x01(\x03R\adelayMs\x1a8\n" + + "\x19SyncEstablishSharedSecret\x12\x1b\n" + + "\aed25519\x18\x02 \x01(\tR\n" + + "ed25519pub\"\xb4\x01\n" + + "\x13RepoConnectionState\x12\x1c\n" + + "\x18CONNECTION_STATE_UNKNOWN\x10\x00\x12\x1c\n" + + "\x18CONNECTION_STATE_PENDING\x10\x01\x12\x1e\n" + + "\x1aCONNECTION_STATE_CONNECTED\x10\x02\x12!\n" + + "\x1dCONNECTION_STATE_UNAUTHORIZED\x10\x03\x12\x1e\n" + + "\x1aCONNECTION_STATE_NOT_FOUND\x10\x04B\b\n" + + "\x06action*\x9c\x02\n" + "\x0fConnectionState\x12\x1c\n" + "\x18CONNECTION_STATE_UNKNOWN\x10\x00\x12\x1c\n" + "\x18CONNECTION_STATE_PENDING\x10\x01\x12\x1e\n" + @@ -797,17 +1762,11 @@ const file_v1sync_syncservice_proto_rawDesc = "" + "\x1bCONNECTION_STATE_ERROR_AUTH\x10\n" + "\x12#\n" + "\x1fCONNECTION_STATE_ERROR_PROTOCOL\x10\v\x12#\n" + - "\x1fCONNECTION_STATE_ERROR_INTERNAL\x10\f2l\n" + + "\x1fCONNECTION_STATE_ERROR_INTERNAL\x10\f2S\n" + + "\x13BackrestSyncService\x12<\n" + + "\x04Sync\x12\x16.v1sync.SyncStreamItem\x1a\x16.v1sync.SyncStreamItem\"\x00(\x010\x012l\n" + "\x18BackrestSyncStateService\x12P\n" + - "\x17GetPeerSyncStatesStream\x12\x1e.v1sync.SyncStateStreamRequest\x1a\x11.v1sync.PeerState\"\x000\x012\xf4\x03\n" + - "\x0fSyncPeerService\x12E\n" + - "\fAuthenticate\x12\x1b.v1sync.AuthenticateRequest\x1a\x16.google.protobuf.Empty\"\x00\x12N\n" + - "\x14GetOperationMetadata\x12\x0e.v1.OpSelector\x1a$.v1sync.GetOperationMetadataResponse\"\x00\x12;\n" + - "\x0eSendOperations\x12\r.v1.Operation\x1a\x16.google.protobuf.Empty\"\x00(\x01\x126\n" + - "\x06GetLog\x12\x12.types.StringValue\x1a\x14.v1sync.LogDataEntry\"\x000\x01\x12W\n" + - "\x15SetAvailableResources\x12$.v1sync.SetAvailableResourcesRequest\x1a\x16.google.protobuf.Empty\"\x00\x12?\n" + - "\tSetConfig\x12\x18.v1sync.SetConfigRequest\x1a\x16.google.protobuf.Empty\"\x00\x12;\n" + - "\tGetConfig\x12\x16.google.protobuf.Empty\x1a\x14.v1sync.RemoteConfig\"\x00B0Z.github.com/garethgeorge/backrest/gen/go/v1syncb\x06proto3" + "\x17GetPeerSyncStatesStream\x12\x1e.v1sync.SyncStateStreamRequest\x1a\x11.v1sync.PeerState\"\x000\x01B0Z.github.com/garethgeorge/backrest/gen/go/v1syncb\x06proto3" var ( file_v1sync_syncservice_proto_rawDescOnce sync.Once @@ -821,65 +1780,85 @@ func file_v1sync_syncservice_proto_rawDescGZIP() []byte { return file_v1sync_syncservice_proto_rawDescData } -var file_v1sync_syncservice_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_v1sync_syncservice_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_v1sync_syncservice_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_v1sync_syncservice_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_v1sync_syncservice_proto_goTypes = []any{ - (ConnectionState)(0), // 0: v1sync.ConnectionState - (*SyncStateStreamRequest)(nil), // 1: v1sync.SyncStateStreamRequest - (*PeerState)(nil), // 2: v1sync.PeerState - (*AuthenticateRequest)(nil), // 3: v1sync.AuthenticateRequest - (*GetOperationMetadataResponse)(nil), // 4: v1sync.GetOperationMetadataResponse - (*LogDataEntry)(nil), // 5: v1sync.LogDataEntry - (*SetAvailableResourcesRequest)(nil), // 6: v1sync.SetAvailableResourcesRequest - (*RepoMetadata)(nil), // 7: v1sync.RepoMetadata - (*PlanMetadata)(nil), // 8: v1sync.PlanMetadata - (*SetConfigRequest)(nil), // 9: v1sync.SetConfigRequest - (*RemoteConfig)(nil), // 10: v1sync.RemoteConfig - (*AuthorizationToken)(nil), // 11: v1sync.AuthorizationToken - (*v1.SignedMessage)(nil), // 12: v1.SignedMessage - (*v1.Plan)(nil), // 13: v1.Plan - (*v1.Repo)(nil), // 14: v1.Repo - (*v1.PublicKey)(nil), // 15: v1.PublicKey - (*v1.OpSelector)(nil), // 16: v1.OpSelector - (*v1.Operation)(nil), // 17: v1.Operation - (*types.StringValue)(nil), // 18: types.StringValue - (*emptypb.Empty)(nil), // 19: google.protobuf.Empty + (ConnectionState)(0), // 0: v1sync.ConnectionState + (SyncStreamItem_RepoConnectionState)(0), // 1: v1sync.SyncStreamItem.RepoConnectionState + (*SyncStateStreamRequest)(nil), // 2: v1sync.SyncStateStreamRequest + (*PeerState)(nil), // 3: v1sync.PeerState + (*AuthenticateRequest)(nil), // 4: v1sync.AuthenticateRequest + (*GetOperationMetadataResponse)(nil), // 5: v1sync.GetOperationMetadataResponse + (*LogDataEntry)(nil), // 6: v1sync.LogDataEntry + (*SetAvailableResourcesRequest)(nil), // 7: v1sync.SetAvailableResourcesRequest + (*RepoMetadata)(nil), // 8: v1sync.RepoMetadata + (*PlanMetadata)(nil), // 9: v1sync.PlanMetadata + (*SetConfigRequest)(nil), // 10: v1sync.SetConfigRequest + (*RemoteConfig)(nil), // 11: v1sync.RemoteConfig + (*AuthorizationToken)(nil), // 12: v1sync.AuthorizationToken + (*SyncStreamItem)(nil), // 13: v1sync.SyncStreamItem + (*SyncStreamItem_SyncActionHandshake)(nil), // 14: v1sync.SyncStreamItem.SyncActionHandshake + (*SyncStreamItem_SyncActionHeartbeat)(nil), // 15: v1sync.SyncStreamItem.SyncActionHeartbeat + (*SyncStreamItem_SyncActionSendConfig)(nil), // 16: v1sync.SyncStreamItem.SyncActionSendConfig + (*SyncStreamItem_SyncActionSetConfig)(nil), // 17: v1sync.SyncStreamItem.SyncActionSetConfig + (*SyncStreamItem_SyncActionListResources)(nil), // 18: v1sync.SyncStreamItem.SyncActionListResources + (*SyncStreamItem_SyncActionConnectRepo)(nil), // 19: v1sync.SyncStreamItem.SyncActionConnectRepo + (*SyncStreamItem_SyncActionDiffOperations)(nil), // 20: v1sync.SyncStreamItem.SyncActionDiffOperations + (*SyncStreamItem_SyncActionSendOperations)(nil), // 21: v1sync.SyncStreamItem.SyncActionSendOperations + (*SyncStreamItem_SyncActionGetLog)(nil), // 22: v1sync.SyncStreamItem.SyncActionGetLog + (*SyncStreamItem_SyncActionSendLogData)(nil), // 23: v1sync.SyncStreamItem.SyncActionSendLogData + (*SyncStreamItem_SyncActionThrottle)(nil), // 24: v1sync.SyncStreamItem.SyncActionThrottle + (*SyncStreamItem_SyncEstablishSharedSecret)(nil), // 25: v1sync.SyncStreamItem.SyncEstablishSharedSecret + (*v1.SignedMessage)(nil), // 26: v1.SignedMessage + (*v1.Plan)(nil), // 27: v1.Plan + (*v1.Repo)(nil), // 28: v1.Repo + (*v1.PublicKey)(nil), // 29: v1.PublicKey + (*v1.OpSelector)(nil), // 30: v1.OpSelector + (*v1.OperationEvent)(nil), // 31: v1.OperationEvent } var file_v1sync_syncservice_proto_depIdxs = []int32{ 0, // 0: v1sync.PeerState.state:type_name -> v1sync.ConnectionState - 8, // 1: v1sync.PeerState.known_plans:type_name -> v1sync.PlanMetadata - 7, // 2: v1sync.PeerState.known_repos:type_name -> v1sync.RepoMetadata - 10, // 3: v1sync.PeerState.remote_config:type_name -> v1sync.RemoteConfig - 12, // 4: v1sync.AuthenticateRequest.instance_id:type_name -> v1.SignedMessage - 8, // 5: v1sync.SetAvailableResourcesRequest.repos:type_name -> v1sync.PlanMetadata - 7, // 6: v1sync.SetAvailableResourcesRequest.plans:type_name -> v1sync.RepoMetadata - 13, // 7: v1sync.SetConfigRequest.plans:type_name -> v1.Plan - 14, // 8: v1sync.SetConfigRequest.repos:type_name -> v1.Repo - 14, // 9: v1sync.RemoteConfig.repos:type_name -> v1.Repo - 13, // 10: v1sync.RemoteConfig.plans:type_name -> v1.Plan - 15, // 11: v1sync.AuthorizationToken.public_key:type_name -> v1.PublicKey - 12, // 12: v1sync.AuthorizationToken.instance_id:type_name -> v1.SignedMessage - 1, // 13: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:input_type -> v1sync.SyncStateStreamRequest - 3, // 14: v1sync.SyncPeerService.Authenticate:input_type -> v1sync.AuthenticateRequest - 16, // 15: v1sync.SyncPeerService.GetOperationMetadata:input_type -> v1.OpSelector - 17, // 16: v1sync.SyncPeerService.SendOperations:input_type -> v1.Operation - 18, // 17: v1sync.SyncPeerService.GetLog:input_type -> types.StringValue - 6, // 18: v1sync.SyncPeerService.SetAvailableResources:input_type -> v1sync.SetAvailableResourcesRequest - 9, // 19: v1sync.SyncPeerService.SetConfig:input_type -> v1sync.SetConfigRequest - 19, // 20: v1sync.SyncPeerService.GetConfig:input_type -> google.protobuf.Empty - 2, // 21: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:output_type -> v1sync.PeerState - 19, // 22: v1sync.SyncPeerService.Authenticate:output_type -> google.protobuf.Empty - 4, // 23: v1sync.SyncPeerService.GetOperationMetadata:output_type -> v1sync.GetOperationMetadataResponse - 19, // 24: v1sync.SyncPeerService.SendOperations:output_type -> google.protobuf.Empty - 5, // 25: v1sync.SyncPeerService.GetLog:output_type -> v1sync.LogDataEntry - 19, // 26: v1sync.SyncPeerService.SetAvailableResources:output_type -> google.protobuf.Empty - 19, // 27: v1sync.SyncPeerService.SetConfig:output_type -> google.protobuf.Empty - 10, // 28: v1sync.SyncPeerService.GetConfig:output_type -> v1sync.RemoteConfig - 21, // [21:29] is the sub-list for method output_type - 13, // [13:21] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name + 9, // 1: v1sync.PeerState.known_plans:type_name -> v1sync.PlanMetadata + 8, // 2: v1sync.PeerState.known_repos:type_name -> v1sync.RepoMetadata + 11, // 3: v1sync.PeerState.remote_config:type_name -> v1sync.RemoteConfig + 26, // 4: v1sync.AuthenticateRequest.instance_id:type_name -> v1.SignedMessage + 9, // 5: v1sync.SetAvailableResourcesRequest.repos:type_name -> v1sync.PlanMetadata + 8, // 6: v1sync.SetAvailableResourcesRequest.plans:type_name -> v1sync.RepoMetadata + 27, // 7: v1sync.SetConfigRequest.plans:type_name -> v1.Plan + 28, // 8: v1sync.SetConfigRequest.repos:type_name -> v1.Repo + 28, // 9: v1sync.RemoteConfig.repos:type_name -> v1.Repo + 27, // 10: v1sync.RemoteConfig.plans:type_name -> v1.Plan + 29, // 11: v1sync.AuthorizationToken.public_key:type_name -> v1.PublicKey + 26, // 12: v1sync.AuthorizationToken.instance_id:type_name -> v1.SignedMessage + 26, // 13: v1sync.SyncStreamItem.signed_message:type_name -> v1.SignedMessage + 14, // 14: v1sync.SyncStreamItem.handshake:type_name -> v1sync.SyncStreamItem.SyncActionHandshake + 15, // 15: v1sync.SyncStreamItem.heartbeat:type_name -> v1sync.SyncStreamItem.SyncActionHeartbeat + 20, // 16: v1sync.SyncStreamItem.diff_operations:type_name -> v1sync.SyncStreamItem.SyncActionDiffOperations + 21, // 17: v1sync.SyncStreamItem.send_operations:type_name -> v1sync.SyncStreamItem.SyncActionSendOperations + 16, // 18: v1sync.SyncStreamItem.send_config:type_name -> v1sync.SyncStreamItem.SyncActionSendConfig + 17, // 19: v1sync.SyncStreamItem.set_config:type_name -> v1sync.SyncStreamItem.SyncActionSetConfig + 18, // 20: v1sync.SyncStreamItem.list_resources:type_name -> v1sync.SyncStreamItem.SyncActionListResources + 22, // 21: v1sync.SyncStreamItem.get_log:type_name -> v1sync.SyncStreamItem.SyncActionGetLog + 23, // 22: v1sync.SyncStreamItem.send_log_data:type_name -> v1sync.SyncStreamItem.SyncActionSendLogData + 24, // 23: v1sync.SyncStreamItem.throttle:type_name -> v1sync.SyncStreamItem.SyncActionThrottle + 29, // 24: v1sync.SyncStreamItem.SyncActionHandshake.public_key:type_name -> v1.PublicKey + 26, // 25: v1sync.SyncStreamItem.SyncActionHandshake.instance_id:type_name -> v1.SignedMessage + 11, // 26: v1sync.SyncStreamItem.SyncActionSendConfig.config:type_name -> v1sync.RemoteConfig + 28, // 27: v1sync.SyncStreamItem.SyncActionSetConfig.repos:type_name -> v1.Repo + 27, // 28: v1sync.SyncStreamItem.SyncActionSetConfig.plans:type_name -> v1.Plan + 8, // 29: v1sync.SyncStreamItem.SyncActionListResources.repos:type_name -> v1sync.RepoMetadata + 9, // 30: v1sync.SyncStreamItem.SyncActionListResources.plans:type_name -> v1sync.PlanMetadata + 30, // 31: v1sync.SyncStreamItem.SyncActionDiffOperations.have_operations_selector:type_name -> v1.OpSelector + 31, // 32: v1sync.SyncStreamItem.SyncActionSendOperations.event:type_name -> v1.OperationEvent + 13, // 33: v1sync.BackrestSyncService.Sync:input_type -> v1sync.SyncStreamItem + 2, // 34: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:input_type -> v1sync.SyncStateStreamRequest + 13, // 35: v1sync.BackrestSyncService.Sync:output_type -> v1sync.SyncStreamItem + 3, // 36: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:output_type -> v1sync.PeerState + 35, // [35:37] is the sub-list for method output_type + 33, // [33:35] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_v1sync_syncservice_proto_init() } @@ -887,13 +1866,26 @@ func file_v1sync_syncservice_proto_init() { if File_v1sync_syncservice_proto != nil { return } + file_v1sync_syncservice_proto_msgTypes[11].OneofWrappers = []any{ + (*SyncStreamItem_SignedMessage)(nil), + (*SyncStreamItem_Handshake)(nil), + (*SyncStreamItem_Heartbeat)(nil), + (*SyncStreamItem_DiffOperations)(nil), + (*SyncStreamItem_SendOperations)(nil), + (*SyncStreamItem_SendConfig)(nil), + (*SyncStreamItem_SetConfig)(nil), + (*SyncStreamItem_ListResources)(nil), + (*SyncStreamItem_GetLog)(nil), + (*SyncStreamItem_SendLogData)(nil), + (*SyncStreamItem_Throttle)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1sync_syncservice_proto_rawDesc), len(file_v1sync_syncservice_proto_rawDesc)), - NumEnums: 1, - NumMessages: 11, + NumEnums: 2, + NumMessages: 24, NumExtensions: 0, NumServices: 2, }, diff --git a/gen/go/v1sync/syncservice_grpc.pb.go b/gen/go/v1sync/syncservice_grpc.pb.go index 3692e859..b445c78f 100644 --- a/gen/go/v1sync/syncservice_grpc.pb.go +++ b/gen/go/v1sync/syncservice_grpc.pb.go @@ -8,12 +8,9 @@ package v1sync import ( context "context" - types "github.com/garethgeorge/backrest/gen/go/types" - v1 "github.com/garethgeorge/backrest/gen/go/v1" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -21,6 +18,108 @@ import ( // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 +const ( + BackrestSyncService_Sync_FullMethodName = "/v1sync.BackrestSyncService/Sync" +) + +// BackrestSyncServiceClient is the client API for BackrestSyncService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// BackrestSyncService provides methods to sync data between backrest instances. +// This service provides its own authentication and authorization. +type BackrestSyncServiceClient interface { + Sync(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SyncStreamItem, SyncStreamItem], error) +} + +type backrestSyncServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewBackrestSyncServiceClient(cc grpc.ClientConnInterface) BackrestSyncServiceClient { + return &backrestSyncServiceClient{cc} +} + +func (c *backrestSyncServiceClient) Sync(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SyncStreamItem, SyncStreamItem], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &BackrestSyncService_ServiceDesc.Streams[0], BackrestSyncService_Sync_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[SyncStreamItem, SyncStreamItem]{ClientStream: stream} + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type BackrestSyncService_SyncClient = grpc.BidiStreamingClient[SyncStreamItem, SyncStreamItem] + +// BackrestSyncServiceServer is the server API for BackrestSyncService service. +// All implementations must embed UnimplementedBackrestSyncServiceServer +// for forward compatibility. +// +// BackrestSyncService provides methods to sync data between backrest instances. +// This service provides its own authentication and authorization. +type BackrestSyncServiceServer interface { + Sync(grpc.BidiStreamingServer[SyncStreamItem, SyncStreamItem]) error + mustEmbedUnimplementedBackrestSyncServiceServer() +} + +// UnimplementedBackrestSyncServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedBackrestSyncServiceServer struct{} + +func (UnimplementedBackrestSyncServiceServer) Sync(grpc.BidiStreamingServer[SyncStreamItem, SyncStreamItem]) error { + return status.Errorf(codes.Unimplemented, "method Sync not implemented") +} +func (UnimplementedBackrestSyncServiceServer) mustEmbedUnimplementedBackrestSyncServiceServer() {} +func (UnimplementedBackrestSyncServiceServer) testEmbeddedByValue() {} + +// UnsafeBackrestSyncServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BackrestSyncServiceServer will +// result in compilation errors. +type UnsafeBackrestSyncServiceServer interface { + mustEmbedUnimplementedBackrestSyncServiceServer() +} + +func RegisterBackrestSyncServiceServer(s grpc.ServiceRegistrar, srv BackrestSyncServiceServer) { + // If the following call pancis, it indicates UnimplementedBackrestSyncServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&BackrestSyncService_ServiceDesc, srv) +} + +func _BackrestSyncService_Sync_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(BackrestSyncServiceServer).Sync(&grpc.GenericServerStream[SyncStreamItem, SyncStreamItem]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type BackrestSyncService_SyncServer = grpc.BidiStreamingServer[SyncStreamItem, SyncStreamItem] + +// BackrestSyncService_ServiceDesc is the grpc.ServiceDesc for BackrestSyncService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BackrestSyncService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "v1sync.BackrestSyncService", + HandlerType: (*BackrestSyncServiceServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "Sync", + Handler: _BackrestSyncService_Sync_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "v1sync/syncservice.proto", +} + const ( BackrestSyncStateService_GetPeerSyncStatesStream_FullMethodName = "/v1sync.BackrestSyncStateService/GetPeerSyncStatesStream" ) @@ -132,338 +231,3 @@ var BackrestSyncStateService_ServiceDesc = grpc.ServiceDesc{ }, Metadata: "v1sync/syncservice.proto", } - -const ( - SyncPeerService_Authenticate_FullMethodName = "/v1sync.SyncPeerService/Authenticate" - SyncPeerService_GetOperationMetadata_FullMethodName = "/v1sync.SyncPeerService/GetOperationMetadata" - SyncPeerService_SendOperations_FullMethodName = "/v1sync.SyncPeerService/SendOperations" - SyncPeerService_GetLog_FullMethodName = "/v1sync.SyncPeerService/GetLog" - SyncPeerService_SetAvailableResources_FullMethodName = "/v1sync.SyncPeerService/SetAvailableResources" - SyncPeerService_SetConfig_FullMethodName = "/v1sync.SyncPeerService/SetConfig" - SyncPeerService_GetConfig_FullMethodName = "/v1sync.SyncPeerService/GetConfig" -) - -// SyncPeerServiceClient is the client API for SyncPeerService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type SyncPeerServiceClient interface { - // Authenticate authenticates the peer with the sync service, must be called before any other methods. - Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - // GetOperationMetadata returns a stream of sync items from the peer. - GetOperationMetadata(ctx context.Context, in *v1.OpSelector, opts ...grpc.CallOption) (*GetOperationMetadataResponse, error) - SendOperations(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[v1.Operation, emptypb.Empty], error) - GetLog(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LogDataEntry], error) - // Called everytime the set of resources available to the peer changes. - SetAvailableResources(ctx context.Context, in *SetAvailableResourcesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - // Implements semantics for updating the remote config of the peer. - SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - GetConfig(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*RemoteConfig, error) -} - -type syncPeerServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewSyncPeerServiceClient(cc grpc.ClientConnInterface) SyncPeerServiceClient { - return &syncPeerServiceClient{cc} -} - -func (c *syncPeerServiceClient) Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SyncPeerService_Authenticate_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *syncPeerServiceClient) GetOperationMetadata(ctx context.Context, in *v1.OpSelector, opts ...grpc.CallOption) (*GetOperationMetadataResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetOperationMetadataResponse) - err := c.cc.Invoke(ctx, SyncPeerService_GetOperationMetadata_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *syncPeerServiceClient) SendOperations(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[v1.Operation, emptypb.Empty], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &SyncPeerService_ServiceDesc.Streams[0], SyncPeerService_SendOperations_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[v1.Operation, emptypb.Empty]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type SyncPeerService_SendOperationsClient = grpc.ClientStreamingClient[v1.Operation, emptypb.Empty] - -func (c *syncPeerServiceClient) GetLog(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LogDataEntry], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &SyncPeerService_ServiceDesc.Streams[1], SyncPeerService_GetLog_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[types.StringValue, LogDataEntry]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type SyncPeerService_GetLogClient = grpc.ServerStreamingClient[LogDataEntry] - -func (c *syncPeerServiceClient) SetAvailableResources(ctx context.Context, in *SetAvailableResourcesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SyncPeerService_SetAvailableResources_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *syncPeerServiceClient) SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, SyncPeerService_SetConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *syncPeerServiceClient) GetConfig(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*RemoteConfig, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RemoteConfig) - err := c.cc.Invoke(ctx, SyncPeerService_GetConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// SyncPeerServiceServer is the server API for SyncPeerService service. -// All implementations must embed UnimplementedSyncPeerServiceServer -// for forward compatibility. -type SyncPeerServiceServer interface { - // Authenticate authenticates the peer with the sync service, must be called before any other methods. - Authenticate(context.Context, *AuthenticateRequest) (*emptypb.Empty, error) - // GetOperationMetadata returns a stream of sync items from the peer. - GetOperationMetadata(context.Context, *v1.OpSelector) (*GetOperationMetadataResponse, error) - SendOperations(grpc.ClientStreamingServer[v1.Operation, emptypb.Empty]) error - GetLog(*types.StringValue, grpc.ServerStreamingServer[LogDataEntry]) error - // Called everytime the set of resources available to the peer changes. - SetAvailableResources(context.Context, *SetAvailableResourcesRequest) (*emptypb.Empty, error) - // Implements semantics for updating the remote config of the peer. - SetConfig(context.Context, *SetConfigRequest) (*emptypb.Empty, error) - GetConfig(context.Context, *emptypb.Empty) (*RemoteConfig, error) - mustEmbedUnimplementedSyncPeerServiceServer() -} - -// UnimplementedSyncPeerServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedSyncPeerServiceServer struct{} - -func (UnimplementedSyncPeerServiceServer) Authenticate(context.Context, *AuthenticateRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented") -} -func (UnimplementedSyncPeerServiceServer) GetOperationMetadata(context.Context, *v1.OpSelector) (*GetOperationMetadataResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetOperationMetadata not implemented") -} -func (UnimplementedSyncPeerServiceServer) SendOperations(grpc.ClientStreamingServer[v1.Operation, emptypb.Empty]) error { - return status.Errorf(codes.Unimplemented, "method SendOperations not implemented") -} -func (UnimplementedSyncPeerServiceServer) GetLog(*types.StringValue, grpc.ServerStreamingServer[LogDataEntry]) error { - return status.Errorf(codes.Unimplemented, "method GetLog not implemented") -} -func (UnimplementedSyncPeerServiceServer) SetAvailableResources(context.Context, *SetAvailableResourcesRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetAvailableResources not implemented") -} -func (UnimplementedSyncPeerServiceServer) SetConfig(context.Context, *SetConfigRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetConfig not implemented") -} -func (UnimplementedSyncPeerServiceServer) GetConfig(context.Context, *emptypb.Empty) (*RemoteConfig, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented") -} -func (UnimplementedSyncPeerServiceServer) mustEmbedUnimplementedSyncPeerServiceServer() {} -func (UnimplementedSyncPeerServiceServer) testEmbeddedByValue() {} - -// UnsafeSyncPeerServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to SyncPeerServiceServer will -// result in compilation errors. -type UnsafeSyncPeerServiceServer interface { - mustEmbedUnimplementedSyncPeerServiceServer() -} - -func RegisterSyncPeerServiceServer(s grpc.ServiceRegistrar, srv SyncPeerServiceServer) { - // If the following call pancis, it indicates UnimplementedSyncPeerServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&SyncPeerService_ServiceDesc, srv) -} - -func _SyncPeerService_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AuthenticateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SyncPeerServiceServer).Authenticate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: SyncPeerService_Authenticate_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SyncPeerServiceServer).Authenticate(ctx, req.(*AuthenticateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _SyncPeerService_GetOperationMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(v1.OpSelector) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SyncPeerServiceServer).GetOperationMetadata(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: SyncPeerService_GetOperationMetadata_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SyncPeerServiceServer).GetOperationMetadata(ctx, req.(*v1.OpSelector)) - } - return interceptor(ctx, in, info, handler) -} - -func _SyncPeerService_SendOperations_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(SyncPeerServiceServer).SendOperations(&grpc.GenericServerStream[v1.Operation, emptypb.Empty]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type SyncPeerService_SendOperationsServer = grpc.ClientStreamingServer[v1.Operation, emptypb.Empty] - -func _SyncPeerService_GetLog_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(types.StringValue) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(SyncPeerServiceServer).GetLog(m, &grpc.GenericServerStream[types.StringValue, LogDataEntry]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type SyncPeerService_GetLogServer = grpc.ServerStreamingServer[LogDataEntry] - -func _SyncPeerService_SetAvailableResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetAvailableResourcesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SyncPeerServiceServer).SetAvailableResources(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: SyncPeerService_SetAvailableResources_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SyncPeerServiceServer).SetAvailableResources(ctx, req.(*SetAvailableResourcesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _SyncPeerService_SetConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SyncPeerServiceServer).SetConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: SyncPeerService_SetConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SyncPeerServiceServer).SetConfig(ctx, req.(*SetConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _SyncPeerService_GetConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(emptypb.Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SyncPeerServiceServer).GetConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: SyncPeerService_GetConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SyncPeerServiceServer).GetConfig(ctx, req.(*emptypb.Empty)) - } - return interceptor(ctx, in, info, handler) -} - -// SyncPeerService_ServiceDesc is the grpc.ServiceDesc for SyncPeerService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var SyncPeerService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "v1sync.SyncPeerService", - HandlerType: (*SyncPeerServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Authenticate", - Handler: _SyncPeerService_Authenticate_Handler, - }, - { - MethodName: "GetOperationMetadata", - Handler: _SyncPeerService_GetOperationMetadata_Handler, - }, - { - MethodName: "SetAvailableResources", - Handler: _SyncPeerService_SetAvailableResources_Handler, - }, - { - MethodName: "SetConfig", - Handler: _SyncPeerService_SetConfig_Handler, - }, - { - MethodName: "GetConfig", - Handler: _SyncPeerService_GetConfig_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "SendOperations", - Handler: _SyncPeerService_SendOperations_Handler, - ClientStreams: true, - }, - { - StreamName: "GetLog", - Handler: _SyncPeerService_GetLog_Handler, - ServerStreams: true, - }, - }, - Metadata: "v1sync/syncservice.proto", -} diff --git a/gen/go/v1sync/v1syncconnect/syncservice.connect.go b/gen/go/v1sync/v1syncconnect/syncservice.connect.go index 7698cf54..e8af57be 100644 --- a/gen/go/v1sync/v1syncconnect/syncservice.connect.go +++ b/gen/go/v1sync/v1syncconnect/syncservice.connect.go @@ -8,10 +8,7 @@ import ( connect "connectrpc.com/connect" context "context" errors "errors" - types "github.com/garethgeorge/backrest/gen/go/types" - v1 "github.com/garethgeorge/backrest/gen/go/v1" v1sync "github.com/garethgeorge/backrest/gen/go/v1sync" - emptypb "google.golang.org/protobuf/types/known/emptypb" http "net/http" strings "strings" ) @@ -24,10 +21,10 @@ import ( const _ = connect.IsAtLeastVersion1_13_0 const ( + // BackrestSyncServiceName is the fully-qualified name of the BackrestSyncService service. + BackrestSyncServiceName = "v1sync.BackrestSyncService" // BackrestSyncStateServiceName is the fully-qualified name of the BackrestSyncStateService service. BackrestSyncStateServiceName = "v1sync.BackrestSyncStateService" - // SyncPeerServiceName is the fully-qualified name of the SyncPeerService service. - SyncPeerServiceName = "v1sync.SyncPeerService" ) // These constants are the fully-qualified names of the RPCs defined in this package. They're @@ -38,31 +35,84 @@ const ( // reflection-formatted method names, remove the leading slash and convert the remaining slash to a // period. const ( + // BackrestSyncServiceSyncProcedure is the fully-qualified name of the BackrestSyncService's Sync + // RPC. + BackrestSyncServiceSyncProcedure = "/v1sync.BackrestSyncService/Sync" // BackrestSyncStateServiceGetPeerSyncStatesStreamProcedure is the fully-qualified name of the // BackrestSyncStateService's GetPeerSyncStatesStream RPC. BackrestSyncStateServiceGetPeerSyncStatesStreamProcedure = "/v1sync.BackrestSyncStateService/GetPeerSyncStatesStream" - // SyncPeerServiceAuthenticateProcedure is the fully-qualified name of the SyncPeerService's - // Authenticate RPC. - SyncPeerServiceAuthenticateProcedure = "/v1sync.SyncPeerService/Authenticate" - // SyncPeerServiceGetOperationMetadataProcedure is the fully-qualified name of the SyncPeerService's - // GetOperationMetadata RPC. - SyncPeerServiceGetOperationMetadataProcedure = "/v1sync.SyncPeerService/GetOperationMetadata" - // SyncPeerServiceSendOperationsProcedure is the fully-qualified name of the SyncPeerService's - // SendOperations RPC. - SyncPeerServiceSendOperationsProcedure = "/v1sync.SyncPeerService/SendOperations" - // SyncPeerServiceGetLogProcedure is the fully-qualified name of the SyncPeerService's GetLog RPC. - SyncPeerServiceGetLogProcedure = "/v1sync.SyncPeerService/GetLog" - // SyncPeerServiceSetAvailableResourcesProcedure is the fully-qualified name of the - // SyncPeerService's SetAvailableResources RPC. - SyncPeerServiceSetAvailableResourcesProcedure = "/v1sync.SyncPeerService/SetAvailableResources" - // SyncPeerServiceSetConfigProcedure is the fully-qualified name of the SyncPeerService's SetConfig - // RPC. - SyncPeerServiceSetConfigProcedure = "/v1sync.SyncPeerService/SetConfig" - // SyncPeerServiceGetConfigProcedure is the fully-qualified name of the SyncPeerService's GetConfig - // RPC. - SyncPeerServiceGetConfigProcedure = "/v1sync.SyncPeerService/GetConfig" ) +// BackrestSyncServiceClient is a client for the v1sync.BackrestSyncService service. +type BackrestSyncServiceClient interface { + Sync(context.Context) *connect.BidiStreamForClient[v1sync.SyncStreamItem, v1sync.SyncStreamItem] +} + +// NewBackrestSyncServiceClient constructs a client for the v1sync.BackrestSyncService service. By +// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, +// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the +// connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewBackrestSyncServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) BackrestSyncServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + backrestSyncServiceMethods := v1sync.File_v1sync_syncservice_proto.Services().ByName("BackrestSyncService").Methods() + return &backrestSyncServiceClient{ + sync: connect.NewClient[v1sync.SyncStreamItem, v1sync.SyncStreamItem]( + httpClient, + baseURL+BackrestSyncServiceSyncProcedure, + connect.WithSchema(backrestSyncServiceMethods.ByName("Sync")), + connect.WithClientOptions(opts...), + ), + } +} + +// backrestSyncServiceClient implements BackrestSyncServiceClient. +type backrestSyncServiceClient struct { + sync *connect.Client[v1sync.SyncStreamItem, v1sync.SyncStreamItem] +} + +// Sync calls v1sync.BackrestSyncService.Sync. +func (c *backrestSyncServiceClient) Sync(ctx context.Context) *connect.BidiStreamForClient[v1sync.SyncStreamItem, v1sync.SyncStreamItem] { + return c.sync.CallBidiStream(ctx) +} + +// BackrestSyncServiceHandler is an implementation of the v1sync.BackrestSyncService service. +type BackrestSyncServiceHandler interface { + Sync(context.Context, *connect.BidiStream[v1sync.SyncStreamItem, v1sync.SyncStreamItem]) error +} + +// NewBackrestSyncServiceHandler builds an HTTP handler from the service implementation. It returns +// the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewBackrestSyncServiceHandler(svc BackrestSyncServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + backrestSyncServiceMethods := v1sync.File_v1sync_syncservice_proto.Services().ByName("BackrestSyncService").Methods() + backrestSyncServiceSyncHandler := connect.NewBidiStreamHandler( + BackrestSyncServiceSyncProcedure, + svc.Sync, + connect.WithSchema(backrestSyncServiceMethods.ByName("Sync")), + connect.WithHandlerOptions(opts...), + ) + return "/v1sync.BackrestSyncService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case BackrestSyncServiceSyncProcedure: + backrestSyncServiceSyncHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedBackrestSyncServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedBackrestSyncServiceHandler struct{} + +func (UnimplementedBackrestSyncServiceHandler) Sync(context.Context, *connect.BidiStream[v1sync.SyncStreamItem, v1sync.SyncStreamItem]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.BackrestSyncService.Sync is not implemented")) +} + // BackrestSyncStateServiceClient is a client for the v1sync.BackrestSyncStateService service. type BackrestSyncStateServiceClient interface { GetPeerSyncStatesStream(context.Context, *connect.Request[v1sync.SyncStateStreamRequest]) (*connect.ServerStreamForClient[v1sync.PeerState], error) @@ -133,237 +183,3 @@ type UnimplementedBackrestSyncStateServiceHandler struct{} func (UnimplementedBackrestSyncStateServiceHandler) GetPeerSyncStatesStream(context.Context, *connect.Request[v1sync.SyncStateStreamRequest], *connect.ServerStream[v1sync.PeerState]) error { return connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.BackrestSyncStateService.GetPeerSyncStatesStream is not implemented")) } - -// SyncPeerServiceClient is a client for the v1sync.SyncPeerService service. -type SyncPeerServiceClient interface { - // Authenticate authenticates the peer with the sync service, must be called before any other methods. - Authenticate(context.Context, *connect.Request[v1sync.AuthenticateRequest]) (*connect.Response[emptypb.Empty], error) - // GetOperationMetadata returns a stream of sync items from the peer. - GetOperationMetadata(context.Context, *connect.Request[v1.OpSelector]) (*connect.Response[v1sync.GetOperationMetadataResponse], error) - SendOperations(context.Context) *connect.ClientStreamForClient[v1.Operation, emptypb.Empty] - GetLog(context.Context, *connect.Request[types.StringValue]) (*connect.ServerStreamForClient[v1sync.LogDataEntry], error) - // Called everytime the set of resources available to the peer changes. - SetAvailableResources(context.Context, *connect.Request[v1sync.SetAvailableResourcesRequest]) (*connect.Response[emptypb.Empty], error) - // Implements semantics for updating the remote config of the peer. - SetConfig(context.Context, *connect.Request[v1sync.SetConfigRequest]) (*connect.Response[emptypb.Empty], error) - GetConfig(context.Context, *connect.Request[emptypb.Empty]) (*connect.Response[v1sync.RemoteConfig], error) -} - -// NewSyncPeerServiceClient constructs a client for the v1sync.SyncPeerService service. By default, -// it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and -// sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() -// or connect.WithGRPCWeb() options. -// -// The URL supplied here should be the base URL for the Connect or gRPC server (for example, -// http://api.acme.com or https://acme.com/grpc). -func NewSyncPeerServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) SyncPeerServiceClient { - baseURL = strings.TrimRight(baseURL, "/") - syncPeerServiceMethods := v1sync.File_v1sync_syncservice_proto.Services().ByName("SyncPeerService").Methods() - return &syncPeerServiceClient{ - authenticate: connect.NewClient[v1sync.AuthenticateRequest, emptypb.Empty]( - httpClient, - baseURL+SyncPeerServiceAuthenticateProcedure, - connect.WithSchema(syncPeerServiceMethods.ByName("Authenticate")), - connect.WithClientOptions(opts...), - ), - getOperationMetadata: connect.NewClient[v1.OpSelector, v1sync.GetOperationMetadataResponse]( - httpClient, - baseURL+SyncPeerServiceGetOperationMetadataProcedure, - connect.WithSchema(syncPeerServiceMethods.ByName("GetOperationMetadata")), - connect.WithClientOptions(opts...), - ), - sendOperations: connect.NewClient[v1.Operation, emptypb.Empty]( - httpClient, - baseURL+SyncPeerServiceSendOperationsProcedure, - connect.WithSchema(syncPeerServiceMethods.ByName("SendOperations")), - connect.WithClientOptions(opts...), - ), - getLog: connect.NewClient[types.StringValue, v1sync.LogDataEntry]( - httpClient, - baseURL+SyncPeerServiceGetLogProcedure, - connect.WithSchema(syncPeerServiceMethods.ByName("GetLog")), - connect.WithClientOptions(opts...), - ), - setAvailableResources: connect.NewClient[v1sync.SetAvailableResourcesRequest, emptypb.Empty]( - httpClient, - baseURL+SyncPeerServiceSetAvailableResourcesProcedure, - connect.WithSchema(syncPeerServiceMethods.ByName("SetAvailableResources")), - connect.WithClientOptions(opts...), - ), - setConfig: connect.NewClient[v1sync.SetConfigRequest, emptypb.Empty]( - httpClient, - baseURL+SyncPeerServiceSetConfigProcedure, - connect.WithSchema(syncPeerServiceMethods.ByName("SetConfig")), - connect.WithClientOptions(opts...), - ), - getConfig: connect.NewClient[emptypb.Empty, v1sync.RemoteConfig]( - httpClient, - baseURL+SyncPeerServiceGetConfigProcedure, - connect.WithSchema(syncPeerServiceMethods.ByName("GetConfig")), - connect.WithClientOptions(opts...), - ), - } -} - -// syncPeerServiceClient implements SyncPeerServiceClient. -type syncPeerServiceClient struct { - authenticate *connect.Client[v1sync.AuthenticateRequest, emptypb.Empty] - getOperationMetadata *connect.Client[v1.OpSelector, v1sync.GetOperationMetadataResponse] - sendOperations *connect.Client[v1.Operation, emptypb.Empty] - getLog *connect.Client[types.StringValue, v1sync.LogDataEntry] - setAvailableResources *connect.Client[v1sync.SetAvailableResourcesRequest, emptypb.Empty] - setConfig *connect.Client[v1sync.SetConfigRequest, emptypb.Empty] - getConfig *connect.Client[emptypb.Empty, v1sync.RemoteConfig] -} - -// Authenticate calls v1sync.SyncPeerService.Authenticate. -func (c *syncPeerServiceClient) Authenticate(ctx context.Context, req *connect.Request[v1sync.AuthenticateRequest]) (*connect.Response[emptypb.Empty], error) { - return c.authenticate.CallUnary(ctx, req) -} - -// GetOperationMetadata calls v1sync.SyncPeerService.GetOperationMetadata. -func (c *syncPeerServiceClient) GetOperationMetadata(ctx context.Context, req *connect.Request[v1.OpSelector]) (*connect.Response[v1sync.GetOperationMetadataResponse], error) { - return c.getOperationMetadata.CallUnary(ctx, req) -} - -// SendOperations calls v1sync.SyncPeerService.SendOperations. -func (c *syncPeerServiceClient) SendOperations(ctx context.Context) *connect.ClientStreamForClient[v1.Operation, emptypb.Empty] { - return c.sendOperations.CallClientStream(ctx) -} - -// GetLog calls v1sync.SyncPeerService.GetLog. -func (c *syncPeerServiceClient) GetLog(ctx context.Context, req *connect.Request[types.StringValue]) (*connect.ServerStreamForClient[v1sync.LogDataEntry], error) { - return c.getLog.CallServerStream(ctx, req) -} - -// SetAvailableResources calls v1sync.SyncPeerService.SetAvailableResources. -func (c *syncPeerServiceClient) SetAvailableResources(ctx context.Context, req *connect.Request[v1sync.SetAvailableResourcesRequest]) (*connect.Response[emptypb.Empty], error) { - return c.setAvailableResources.CallUnary(ctx, req) -} - -// SetConfig calls v1sync.SyncPeerService.SetConfig. -func (c *syncPeerServiceClient) SetConfig(ctx context.Context, req *connect.Request[v1sync.SetConfigRequest]) (*connect.Response[emptypb.Empty], error) { - return c.setConfig.CallUnary(ctx, req) -} - -// GetConfig calls v1sync.SyncPeerService.GetConfig. -func (c *syncPeerServiceClient) GetConfig(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[v1sync.RemoteConfig], error) { - return c.getConfig.CallUnary(ctx, req) -} - -// SyncPeerServiceHandler is an implementation of the v1sync.SyncPeerService service. -type SyncPeerServiceHandler interface { - // Authenticate authenticates the peer with the sync service, must be called before any other methods. - Authenticate(context.Context, *connect.Request[v1sync.AuthenticateRequest]) (*connect.Response[emptypb.Empty], error) - // GetOperationMetadata returns a stream of sync items from the peer. - GetOperationMetadata(context.Context, *connect.Request[v1.OpSelector]) (*connect.Response[v1sync.GetOperationMetadataResponse], error) - SendOperations(context.Context, *connect.ClientStream[v1.Operation]) (*connect.Response[emptypb.Empty], error) - GetLog(context.Context, *connect.Request[types.StringValue], *connect.ServerStream[v1sync.LogDataEntry]) error - // Called everytime the set of resources available to the peer changes. - SetAvailableResources(context.Context, *connect.Request[v1sync.SetAvailableResourcesRequest]) (*connect.Response[emptypb.Empty], error) - // Implements semantics for updating the remote config of the peer. - SetConfig(context.Context, *connect.Request[v1sync.SetConfigRequest]) (*connect.Response[emptypb.Empty], error) - GetConfig(context.Context, *connect.Request[emptypb.Empty]) (*connect.Response[v1sync.RemoteConfig], error) -} - -// NewSyncPeerServiceHandler builds an HTTP handler from the service implementation. It returns the -// path on which to mount the handler and the handler itself. -// -// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf -// and JSON codecs. They also support gzip compression. -func NewSyncPeerServiceHandler(svc SyncPeerServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { - syncPeerServiceMethods := v1sync.File_v1sync_syncservice_proto.Services().ByName("SyncPeerService").Methods() - syncPeerServiceAuthenticateHandler := connect.NewUnaryHandler( - SyncPeerServiceAuthenticateProcedure, - svc.Authenticate, - connect.WithSchema(syncPeerServiceMethods.ByName("Authenticate")), - connect.WithHandlerOptions(opts...), - ) - syncPeerServiceGetOperationMetadataHandler := connect.NewUnaryHandler( - SyncPeerServiceGetOperationMetadataProcedure, - svc.GetOperationMetadata, - connect.WithSchema(syncPeerServiceMethods.ByName("GetOperationMetadata")), - connect.WithHandlerOptions(opts...), - ) - syncPeerServiceSendOperationsHandler := connect.NewClientStreamHandler( - SyncPeerServiceSendOperationsProcedure, - svc.SendOperations, - connect.WithSchema(syncPeerServiceMethods.ByName("SendOperations")), - connect.WithHandlerOptions(opts...), - ) - syncPeerServiceGetLogHandler := connect.NewServerStreamHandler( - SyncPeerServiceGetLogProcedure, - svc.GetLog, - connect.WithSchema(syncPeerServiceMethods.ByName("GetLog")), - connect.WithHandlerOptions(opts...), - ) - syncPeerServiceSetAvailableResourcesHandler := connect.NewUnaryHandler( - SyncPeerServiceSetAvailableResourcesProcedure, - svc.SetAvailableResources, - connect.WithSchema(syncPeerServiceMethods.ByName("SetAvailableResources")), - connect.WithHandlerOptions(opts...), - ) - syncPeerServiceSetConfigHandler := connect.NewUnaryHandler( - SyncPeerServiceSetConfigProcedure, - svc.SetConfig, - connect.WithSchema(syncPeerServiceMethods.ByName("SetConfig")), - connect.WithHandlerOptions(opts...), - ) - syncPeerServiceGetConfigHandler := connect.NewUnaryHandler( - SyncPeerServiceGetConfigProcedure, - svc.GetConfig, - connect.WithSchema(syncPeerServiceMethods.ByName("GetConfig")), - connect.WithHandlerOptions(opts...), - ) - return "/v1sync.SyncPeerService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case SyncPeerServiceAuthenticateProcedure: - syncPeerServiceAuthenticateHandler.ServeHTTP(w, r) - case SyncPeerServiceGetOperationMetadataProcedure: - syncPeerServiceGetOperationMetadataHandler.ServeHTTP(w, r) - case SyncPeerServiceSendOperationsProcedure: - syncPeerServiceSendOperationsHandler.ServeHTTP(w, r) - case SyncPeerServiceGetLogProcedure: - syncPeerServiceGetLogHandler.ServeHTTP(w, r) - case SyncPeerServiceSetAvailableResourcesProcedure: - syncPeerServiceSetAvailableResourcesHandler.ServeHTTP(w, r) - case SyncPeerServiceSetConfigProcedure: - syncPeerServiceSetConfigHandler.ServeHTTP(w, r) - case SyncPeerServiceGetConfigProcedure: - syncPeerServiceGetConfigHandler.ServeHTTP(w, r) - default: - http.NotFound(w, r) - } - }) -} - -// UnimplementedSyncPeerServiceHandler returns CodeUnimplemented from all methods. -type UnimplementedSyncPeerServiceHandler struct{} - -func (UnimplementedSyncPeerServiceHandler) Authenticate(context.Context, *connect.Request[v1sync.AuthenticateRequest]) (*connect.Response[emptypb.Empty], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.SyncPeerService.Authenticate is not implemented")) -} - -func (UnimplementedSyncPeerServiceHandler) GetOperationMetadata(context.Context, *connect.Request[v1.OpSelector]) (*connect.Response[v1sync.GetOperationMetadataResponse], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.SyncPeerService.GetOperationMetadata is not implemented")) -} - -func (UnimplementedSyncPeerServiceHandler) SendOperations(context.Context, *connect.ClientStream[v1.Operation]) (*connect.Response[emptypb.Empty], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.SyncPeerService.SendOperations is not implemented")) -} - -func (UnimplementedSyncPeerServiceHandler) GetLog(context.Context, *connect.Request[types.StringValue], *connect.ServerStream[v1sync.LogDataEntry]) error { - return connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.SyncPeerService.GetLog is not implemented")) -} - -func (UnimplementedSyncPeerServiceHandler) SetAvailableResources(context.Context, *connect.Request[v1sync.SetAvailableResourcesRequest]) (*connect.Response[emptypb.Empty], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.SyncPeerService.SetAvailableResources is not implemented")) -} - -func (UnimplementedSyncPeerServiceHandler) SetConfig(context.Context, *connect.Request[v1sync.SetConfigRequest]) (*connect.Response[emptypb.Empty], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.SyncPeerService.SetConfig is not implemented")) -} - -func (UnimplementedSyncPeerServiceHandler) GetConfig(context.Context, *connect.Request[emptypb.Empty]) (*connect.Response[v1sync.RemoteConfig], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1sync.SyncPeerService.GetConfig is not implemented")) -} diff --git a/internal/api/syncapi/client.go b/internal/api/syncapi/client.go deleted file mode 100644 index 6e444134..00000000 --- a/internal/api/syncapi/client.go +++ /dev/null @@ -1,18 +0,0 @@ -package syncapi - -import ( - v1 "github.com/garethgeorge/backrest/gen/go/v1" - "github.com/garethgeorge/backrest/gen/go/v1sync/v1syncconnect" -) - -type syncClient struct { - client *v1syncconnect.SyncPeerServiceClient - peer *v1.Multihost_Peer -} - -func newSyncClient(client *v1syncconnect.SyncPeerServiceClient, peer *v1.Multihost_Peer) *syncClient { - return &syncClient{ - client: client, - peer: peer, - } -} diff --git a/internal/api/syncapi/cmdstreamutil.go b/internal/api/syncapi/cmdstreamutil.go new file mode 100644 index 00000000..bbf4ab15 --- /dev/null +++ b/internal/api/syncapi/cmdstreamutil.go @@ -0,0 +1,111 @@ +package syncapi + +import ( + "context" + "errors" + "fmt" + "io" + "time" + + "connectrpc.com/connect" + "github.com/garethgeorge/backrest/gen/go/v1sync" +) + +type syncCommandStreamTrait interface { + Send(item *v1sync.SyncStreamItem) error + Receive() (*v1sync.SyncStreamItem, error) +} + +var _ syncCommandStreamTrait = (*connect.BidiStream[v1sync.SyncStreamItem, v1sync.SyncStreamItem])(nil) // Ensure that connect.BidiStream implements syncCommandStreamTrait +var _ syncCommandStreamTrait = (*connect.BidiStreamForClient[v1sync.SyncStreamItem, v1sync.SyncStreamItem])(nil) // Ensure that connect.BidiStreamForClient implements syncCommandStreamTrait + +type bidiSyncCommandStream struct { + sendChan chan *v1sync.SyncStreamItem + recvChan chan *v1sync.SyncStreamItem + terminateWithErrChan chan error +} + +func newBidiSyncCommandStream() *bidiSyncCommandStream { + return &bidiSyncCommandStream{ + sendChan: make(chan *v1sync.SyncStreamItem, 64), // Buffered channel to allow sending items without blocking + recvChan: make(chan *v1sync.SyncStreamItem, 1), + terminateWithErrChan: make(chan error, 1), + } +} + +func (s *bidiSyncCommandStream) Send(item *v1sync.SyncStreamItem) { + select { + case s.sendChan <- item: + default: + // Try again with a timeout, if it fails, send an error to terminate the stream + select { + case s.sendChan <- item: + case <-time.After(100 * time.Millisecond): + s.SendErrorAndTerminate(NewSyncErrorDisconnected(errors.New("send channel is full, cannot send item"))) + } + } +} + +// SendErrorAndTerminate sends an error to the termination channel. +// If the error is nil, it terminates only. +func (s *bidiSyncCommandStream) SendErrorAndTerminate(err error) { + if err == nil { + return + } + select { + case s.terminateWithErrChan <- err: + default: + // If the channel is full, we can't send the error, so we just ignore it. + // This is a best-effort termination. + } +} + +func (s *bidiSyncCommandStream) ReadChannel() chan *v1sync.SyncStreamItem { + return s.recvChan +} + +func (s *bidiSyncCommandStream) ReceiveWithinDuration(d time.Duration) *v1sync.SyncStreamItem { + select { + case item := <-s.recvChan: + return item + case <-time.After(d): + return nil // Return nil if no item is received within the duration + } +} + +func (s *bidiSyncCommandStream) ConnectStream(ctx context.Context, stream syncCommandStreamTrait) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + go func() { + for ctx.Err() == nil { + if val, err := stream.Receive(); err != nil { + s.SendErrorAndTerminate(NewSyncErrorDisconnected(fmt.Errorf("receiving item: %w", err))) + break + } else { + s.recvChan <- val + } + } + close(s.recvChan) + }() + + for { + select { + case item := <-s.sendChan: + if item == nil { + continue + } + if err := stream.Send(item); err != nil { + if errors.Is(err, io.EOF) { + err = fmt.Errorf("connection failed or dropped: %w", err) + } + s.SendErrorAndTerminate(err) + return err + } + case err := <-s.terminateWithErrChan: + return err // Terminate the stream with the error or nil if no error was sent + case <-ctx.Done(): + // Context is done, we should stop processing. + return ctx.Err() + } + } +} diff --git a/internal/api/syncapi/handler.go b/internal/api/syncapi/handler.nogo similarity index 100% rename from internal/api/syncapi/handler.go rename to internal/api/syncapi/handler.nogo diff --git a/internal/api/syncapi/handler_test.go b/internal/api/syncapi/handler_test.go deleted file mode 100644 index 3234a33a..00000000 --- a/internal/api/syncapi/handler_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package syncapi - -import ( - "context" - "net/http" - "testing" - "time" - - "connectrpc.com/connect" - v1 "github.com/garethgeorge/backrest/gen/go/v1" - "github.com/garethgeorge/backrest/gen/go/v1sync/v1syncconnect" - "github.com/garethgeorge/backrest/internal/api/syncapi/tunnel" - "github.com/garethgeorge/backrest/internal/logstore" - "github.com/garethgeorge/backrest/internal/oplog" - "github.com/garethgeorge/backrest/internal/oplog/memstore" - "github.com/garethgeorge/backrest/internal/testutil" - "github.com/stretchr/testify/assert" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" - "google.golang.org/protobuf/proto" -) - -func TestHandler(t *testing.T) { - ctx, cancel := testutil.WithDeadlineFromTest(t, context.Background()) - defer cancel() - - handler := newHandlerForTest(t) - handler.SetLogger(testutil.NewTestLogger(t)) - - mux := http.NewServeMux() - mux.Handle(v1syncconnect.NewSyncPeerServiceHandler(handler)) - - // Construct the service we want to provide - provider := tunnel.NewConnectionProvider(10 /* bufferSize */) - server := &http.Server{ - Handler: h2c.NewHandler(mux, &http2.Server{}), // h2c is HTTP/2 without TLS for grpc-connect support. - } - testutil.ServeForTest("HTTPServer", t, server, provider) - - // Serve the tunnel service which is proxying to the provided service. - tunnelServiceMux := http.NewServeMux() - tunnelHandler := tunnel.NewTunnelHandler(provider).SetLogger(testutil.NewTestLogger(t)) - tunnelServiceMux.Handle(v1syncconnect.NewTunnelServiceHandler(tunnelHandler)) - grpcServer := &http.Server{ - Addr: testutil.AllocOpenBindAddr(t), - Handler: h2c.NewHandler(tunnelServiceMux, &http2.Server{}), // h2c is HTTP/2 without TLS for grpc-connect support. - } - testutil.ListenAndServeForTest("gRPCTunnelServer", t, grpcServer) - - // With that "simple" setup out of the way, we can try establishing a connection. - // TODO: this really could be made more ergonomic - tunnelClient := v1syncconnect.NewTunnelServiceClient(tunnel.NewInsecureHttpClient(), "http://"+grpcServer.Addr) - stream := tunnelClient.Tunnel(ctx) - wrapped := tunnel.NewWrappedStreamFromClient(stream, tunnel.WithLogger(testutil.NewTestLogger(t).Named("tunnel-client"))) - go func() { - t.Log("Client stream started") - if err := wrapped.HandlePackets(ctx); err != nil { - t.Errorf("Failed to handle packets: %v", err) - } - t.Log("Client stream ended") - }() - - // Spin until the connection is ready. - for !wrapped.IsReady() { - time.Sleep(10 * time.Millisecond) - } - - t.Log("Connection is ready") - client := v1syncconnect.NewSyncPeerServiceClient(tunnel.NewWrappedStreamClient(wrapped), "https://localhost:80") - resp, err := client.GetOperationMetadata(ctx, connect.NewRequest(&v1.OpSelector{ - OriginalInstanceKeyid: proto.String("test-instance-key-id"), - })) - if err != nil { - t.Fatalf("Failed to get operation metadata: %v", err) - } - t.Logf("Received response: %v", resp.Msg) -} - -func newHandlerForTest(t *testing.T) *syncHandler { - t.Helper() - // Create a new oplog - opstore := memstore.NewMemStore() - oplog, err := oplog.NewOpLog(opstore) - assert.NoError(t, err, "Failed to create oplog") - - // Create a new log store - logStore, err := logstore.NewLogStore(t.TempDir()) - assert.NoError(t, err, "Failed to create log store") - - // Create the sync handler - return NewSyncHandler(oplog, logStore) -} diff --git a/internal/api/syncapi/old/peerstate_test.go b/internal/api/syncapi/peerstate_test.go similarity index 86% rename from internal/api/syncapi/old/peerstate_test.go rename to internal/api/syncapi/peerstate_test.go index a83cf8fa..3f8bc18b 100644 --- a/internal/api/syncapi/old/peerstate_test.go +++ b/internal/api/syncapi/peerstate_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "github.com/garethgeorge/backrest/gen/go/v1sync" "github.com/garethgeorge/backrest/internal/kvstore" "github.com/google/go-cmp/cmp" ) @@ -33,8 +34,21 @@ func TestPeerStateManager_GetSet(t *testing.T) { InstanceID: "testInstance", KeyID: keyID, LastHeartbeat: time.Now().Round(time.Millisecond), - KnownRepos: map[string]struct{}{"repo1": {}, "repo2": {}}, - KnownPlans: map[string]struct{}{"plan1": {}, "plan2": {}}, + KnownRepos: map[string]*v1sync.RepoMetadata{ + "repo1": { + Id: "repo1", + Guid: "guid1", + }, + "repo2": { + Id: "repo2", + Guid: "guid2", + }, + }, + KnownPlans: map[string]*v1sync.PlanMetadata{ + "plan1": { + Id: "plan1", + }, + }, } psm.SetPeerState(keyID, state) gotState := psm.GetPeerState(keyID) diff --git a/internal/api/syncapi/old/syncclient.go b/internal/api/syncapi/syncclient.go similarity index 100% rename from internal/api/syncapi/old/syncclient.go rename to internal/api/syncapi/syncclient.go diff --git a/internal/api/syncapi/old/synccommon.go b/internal/api/syncapi/synccommon.go similarity index 100% rename from internal/api/syncapi/old/synccommon.go rename to internal/api/syncapi/synccommon.go diff --git a/internal/api/syncapi/old/synchandler.go b/internal/api/syncapi/synchandler.go similarity index 100% rename from internal/api/syncapi/old/synchandler.go rename to internal/api/syncapi/synchandler.go diff --git a/internal/api/syncapi/syncmanager.nogo b/internal/api/syncapi/syncmanager.go similarity index 100% rename from internal/api/syncapi/syncmanager.nogo rename to internal/api/syncapi/syncmanager.go diff --git a/internal/api/syncapi/syncstatehandler.nogo b/internal/api/syncapi/syncstatehandler.go similarity index 100% rename from internal/api/syncapi/syncstatehandler.nogo rename to internal/api/syncapi/syncstatehandler.go diff --git a/internal/api/syncapi/tunnel/aes.go b/internal/api/syncapi/tunnel/aes.go deleted file mode 100644 index 7c944483..00000000 --- a/internal/api/syncapi/tunnel/aes.go +++ /dev/null @@ -1,107 +0,0 @@ -package tunnel - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "errors" - "io" - "sync" -) - -type crypt struct { - secret []byte - block cipher.Block - once sync.Once - err error -} - -func newCrypt(secret []byte) *crypt { - return &crypt{secret: secret} -} - -func (c *crypt) init() { - c.block, c.err = aes.NewCipher(c.secret) -} - -// pkcs5Padding adds PKCS#5 padding to the data -func pkcs5Padding(data []byte, blockSize int) []byte { - padding := blockSize - len(data)%blockSize - padtext := make([]byte, padding) - for i := range padtext { - padtext[i] = byte(padding) - } - return append(data, padtext...) -} - -// pkcs5Unpadding removes PKCS#5 padding from the data -func pkcs5Unpadding(data []byte) ([]byte, error) { - if len(data) == 0 { - return nil, errors.New("data is empty") - } - - padding := int(data[len(data)-1]) - if padding > len(data) || padding == 0 { - return nil, errors.New("invalid padding") - } - - // Check that all padding bytes are correct - for i := len(data) - padding; i < len(data); i++ { - if data[i] != byte(padding) { - return nil, errors.New("invalid padding") - } - } - - return data[:len(data)-padding], nil -} - -func (c *crypt) Encrypt(data []byte) ([]byte, error) { - c.once.Do(c.init) - if c.err != nil { - return nil, c.err - } - - // Add PKCS#5 padding - paddedData := pkcs5Padding(data, aes.BlockSize) - - // Create a random IV - ciphertext := make([]byte, aes.BlockSize+len(paddedData)) - iv := ciphertext[:aes.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - - // Encrypt the data - mode := cipher.NewCBCEncrypter(c.block, iv) - mode.CryptBlocks(ciphertext[aes.BlockSize:], paddedData) - - return ciphertext, nil -} - -func (c *crypt) Decrypt(data []byte) ([]byte, error) { - if len(data) < aes.BlockSize { - return nil, errors.New("ciphertext too short") - } - - c.once.Do(c.init) - if c.err != nil { - return nil, c.err - } - - // Extract IV and ciphertext - iv := data[:aes.BlockSize] - ciphertext := data[aes.BlockSize:] - - // Check that ciphertext is a multiple of block size - if len(ciphertext)%aes.BlockSize != 0 { - return nil, errors.New("ciphertext is not a multiple of the block size") - } - - // Decrypt the data - mode := cipher.NewCBCDecrypter(c.block, iv) - plaintext := make([]byte, len(ciphertext)) - mode.CryptBlocks(plaintext, ciphertext) - - // Remove PKCS#5 padding - return pkcs5Unpadding(plaintext) -} diff --git a/internal/api/syncapi/tunnel/aes_test.go b/internal/api/syncapi/tunnel/aes_test.go deleted file mode 100644 index 96a97e9d..00000000 --- a/internal/api/syncapi/tunnel/aes_test.go +++ /dev/null @@ -1,338 +0,0 @@ -package tunnel - -import ( - "bytes" - "crypto/rand" - "testing" -) - -func TestNewCrypt(t *testing.T) { - secret := []byte("test-secret-key-") - c := newCrypt(secret) - - if c == nil { - t.Fatal("newCrypt returned nil") - } - - if !bytes.Equal(c.secret, secret) { - t.Errorf("expected secret %v, got %v", secret, c.secret) - } -} - -func TestPKCS5Padding(t *testing.T) { - tests := []struct { - name string - data []byte - blockSize int - expected int // expected length after padding - }{ - { - name: "empty data", - data: []byte{}, - blockSize: 16, - expected: 16, - }, - { - name: "data length equals block size", - data: make([]byte, 16), - blockSize: 16, - expected: 32, - }, - { - name: "data length less than block size", - data: []byte("hello"), - blockSize: 16, - expected: 16, - }, - { - name: "data length greater than block size", - data: make([]byte, 20), - blockSize: 16, - expected: 32, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - padded := pkcs5Padding(tt.data, tt.blockSize) - - if len(padded) != tt.expected { - t.Errorf("expected length %d, got %d", tt.expected, len(padded)) - } - - // Check that padding is correct - paddingLen := tt.blockSize - (len(tt.data) % tt.blockSize) - for i := len(tt.data); i < len(padded); i++ { - if padded[i] != byte(paddingLen) { - t.Errorf("invalid padding byte at position %d: expected %d, got %d", i, paddingLen, padded[i]) - } - } - }) - } -} - -func TestPKCS5Unpadding(t *testing.T) { - tests := []struct { - name string - data []byte - expectErr bool - expected []byte - }{ - { - name: "valid padding", - data: []byte{1, 2, 3, 4, 5, 5, 5, 5, 5}, - expectErr: false, - expected: []byte{1, 2, 3, 4}, - }, - { - name: "full block padding", - data: []byte{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, - expectErr: false, - expected: []byte{}, - }, - { - name: "empty data", - data: []byte{}, - expectErr: true, - expected: nil, - }, - { - name: "zero padding", - data: []byte{1, 2, 3, 0}, - expectErr: true, - expected: nil, - }, - { - name: "invalid padding length", - data: []byte{1, 2, 3, 10}, - expectErr: true, - expected: nil, - }, - { - name: "inconsistent padding", - data: []byte{1, 2, 3, 3, 2}, - expectErr: true, - expected: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := pkcs5Unpadding(tt.data) - - if tt.expectErr { - if err == nil { - t.Error("expected error but got none") - } - } else { - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !bytes.Equal(result, tt.expected) { - t.Errorf("expected %v, got %v", tt.expected, result) - } - } - }) - } -} - -func TestEncryptDecrypt(t *testing.T) { - secret := []byte("my-secret-key...") - c := newCrypt(secret) - - tests := []struct { - name string - data []byte - }{ - { - name: "empty data", - data: []byte{}, - }, - { - name: "short data", - data: []byte("hello"), - }, - { - name: "data equal to block size", - data: make([]byte, 16), - }, - { - name: "data larger than block size", - data: []byte("this is a longer message that spans multiple blocks"), - }, - { - name: "binary data", - data: []byte{0, 1, 2, 3, 255, 254, 253, 252}, - }, - { - name: "large data", - data: make([]byte, 1024), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Fill large data with random bytes - if len(tt.data) == 1024 { - rand.Read(tt.data) - } - - // Encrypt - encrypted, err := c.Encrypt(tt.data) - if err != nil { - t.Fatalf("encryption failed: %v", err) - } - - // Verify encrypted data is different from original (unless original is empty) - if len(tt.data) > 0 && bytes.Equal(encrypted, tt.data) { - t.Error("encrypted data is same as original") - } - - // Verify encrypted data includes IV (at least block size) - if len(encrypted) < 16 { - t.Errorf("encrypted data too short: %d bytes", len(encrypted)) - } - - // Decrypt - decrypted, err := c.Decrypt(encrypted) - if err != nil { - t.Fatalf("decryption failed: %v", err) - } - - // Verify decrypted matches original - if !bytes.Equal(decrypted, tt.data) { - t.Errorf("decrypted data doesn't match original.\nOriginal: %v\nDecrypted: %v", tt.data, decrypted) - } - }) - } -} - -func TestEncryptionIsRandomized(t *testing.T) { - secret := []byte("test-secret.....") - c := newCrypt(secret) - data := []byte("same data every time") - - // Encrypt the same data multiple times - encrypted1, err := c.Encrypt(data) - if err != nil { - t.Fatalf("encryption 1 failed: %v", err) - } - - encrypted2, err := c.Encrypt(data) - if err != nil { - t.Fatalf("encryption 2 failed: %v", err) - } - - // Encrypted results should be different due to random IV - if bytes.Equal(encrypted1, encrypted2) { - t.Error("encryption is not randomized - same input produced same output") - } - - // But both should decrypt to the same original data - decrypted1, err := c.Decrypt(encrypted1) - if err != nil { - t.Fatalf("decryption 1 failed: %v", err) - } - - decrypted2, err := c.Decrypt(encrypted2) - if err != nil { - t.Fatalf("decryption 2 failed: %v", err) - } - - if !bytes.Equal(decrypted1, data) || !bytes.Equal(decrypted2, data) { - t.Error("decryption failed to recover original data") - } -} - -func TestDecryptInvalidData(t *testing.T) { - secret := []byte("test-secret.....") - c := newCrypt(secret) - - tests := []struct { - name string - data []byte - }{ - { - name: "too short", - data: []byte("short"), - }, - { - name: "not multiple of block size", - data: make([]byte, 17), // 16 + 1 - }, - { - name: "corrupted padding", - data: func() []byte { - // Create valid encrypted data then corrupt it - original := []byte("test data") - encrypted, _ := c.Encrypt(original) - // Corrupt the last byte (padding) - encrypted[len(encrypted)-1] = 255 - return encrypted - }(), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := c.Decrypt(tt.data) - if err == nil { - t.Error("expected decryption to fail but it succeeded") - } - }) - } -} - -func TestDifferentSecretsCannotDecrypt(t *testing.T) { - data := []byte("secret message") - - c1 := newCrypt([]byte("secret1.........")) - c2 := newCrypt([]byte("secret2.........")) - - // Encrypt with first secret - encrypted, err := c1.Encrypt(data) - if err != nil { - t.Fatalf("encryption failed: %v", err) - } - - // Try to decrypt with second secret - _, err = c2.Decrypt(encrypted) - if err == nil { - t.Error("decryption with wrong secret should fail") - } -} - -func BenchmarkEncrypt(b *testing.B) { - secret := []byte("benchmark-secret") - c := newCrypt(secret) - data := make([]byte, 1024) - rand.Read(data) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := c.Encrypt(data) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkDecrypt(b *testing.B) { - secret := []byte("benchmark-secret") - c := newCrypt(secret) - data := make([]byte, 1024) - rand.Read(data) - - encrypted, err := c.Encrypt(data) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := c.Decrypt(encrypted) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/internal/api/syncapi/tunnel/connstate.go b/internal/api/syncapi/tunnel/connstate.go deleted file mode 100644 index dac40aee..00000000 --- a/internal/api/syncapi/tunnel/connstate.go +++ /dev/null @@ -1,211 +0,0 @@ -package tunnel - -import ( - "bytes" - "fmt" - "net" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/garethgeorge/backrest/gen/go/v1sync" - "go.uber.org/zap" -) - -type connState struct { - connId int64 - nextWriteSeqno int64 - stream stream // stream it's associated with - logger *zap.Logger // can be nil - - closed atomic.Bool - closedCh chan struct{} - - sendMu sync.Mutex - seqno int64 - - readsMu sync.Mutex - reads chan []byte - readsBuf []byte - - readDeadlineMu sync.Mutex - readDeadlineTimer *time.Timer - readDeadlineChan chan struct{} -} - -var _ net.Conn = (*connState)(nil) - -func newConnState(streamm stream, connId int64, secret []byte, logger *zap.Logger) *connState { - if logger != nil { - logger = logger.Named("connState").With( - zap.Int64("connId", connId)) - } - - var s stream = streamm - // if len(secret) > 0 { - // s = newCryptedStream(streamm, secret) - // } - - return &connState{ - connId: connId, - seqno: 0, - stream: s, - logger: logger, - - closedCh: make(chan struct{}), - - reads: make(chan []byte, 10), // Buffered channel to hold a few writes worth of messages before blocking - readsBuf: nil, - - readDeadlineChan: make(chan struct{}), - } -} - -// sendOpenPacket is only sent by the end starting the connection, not by the end that's opening it in response to a receive. -func (c *connState) sendOpenPacket() error { - if c.closed.Load() { - return net.ErrClosed - } - - if c.logger != nil { - c.logger.Info("sending open packet") - } - - return c.stream.Send(&v1sync.TunnelMessage{ - ConnId: c.connId, - Seqno: 0, // Open packet has Seqno 0 - }) -} - -func (c *connState) Write(data []byte) (int, error) { - c.sendMu.Lock() - defer c.sendMu.Unlock() - if c.closed.Load() { - return 0, net.ErrClosed - } - - c.seqno++ - if c.logger != nil { - c.logger.Debug("writing data", zap.Int("dataLength", len(data)), zap.Int64("seqno", c.seqno)) - } - err := c.stream.Send(&v1sync.TunnelMessage{ - ConnId: c.connId, - Data: bytes.Clone(data), - Seqno: c.nextWriteSeqno, - }) - if err != nil { - if c.logger != nil { - c.logger.Error("failed to send data", zap.Int64("connId", c.connId), zap.Error(err)) - } - return 0, err - } - return len(data), nil -} - -func (c *connState) Read(b []byte) (int, error) { - c.readsMu.Lock() - defer c.readsMu.Unlock() - - if len(c.readsBuf) > 0 { - n := copy(b, c.readsBuf) - c.readsBuf = c.readsBuf[n:] - return n, nil - } - - c.readDeadlineMu.Lock() - readDeadlineChan := c.readDeadlineChan - c.readDeadlineMu.Unlock() - - select { - case data := <-c.reads: - if len(data) == 0 { - return 0, net.ErrClosed - } - if c.logger != nil { - c.logger.Debug("conn state c.reads received packet", zap.Int("dataLength", len(data))) - } - n := copy(b, data) - if n < len(data) { - c.readsBuf = data[n:] - } - return n, nil - case <-readDeadlineChan: - if c.logger != nil { - c.logger.Info("read deadline reached") - } - return 0, os.ErrDeadlineExceeded - case <-c.closedCh: - if c.logger != nil { - c.logger.Info("connection closed while waiting for read") - } - return 0, net.ErrClosed - } -} - -func (c *connState) Close() error { - if !c.closed.Swap(true) { - if c.logger != nil { - c.logger.Info("closing connection") - } - close(c.closedCh) - if err := c.stream.Send(&v1sync.TunnelMessage{ - ConnId: c.connId, - Close: true, - }); err != nil { - if c.logger != nil { - c.logger.Error("failed to send close message", zap.Error(err)) - } - return fmt.Errorf("send close message: %w", err) - } - if c.logger != nil { - c.logger.Info("connection closed successfully") - } - } else if c.logger != nil { - c.logger.Warn("close called on already closed connection") - } - return nil -} - -func (c *connState) LocalAddr() net.Addr { - // Return the local address of the tunnel connection - return &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } -} - -func (c *connState) RemoteAddr() net.Addr { - // Return the remote address of the tunnel connection - return &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } -} - -func (c *connState) SetDeadline(t time.Time) error { - c.SetReadDeadline(t) - c.SetWriteDeadline(t) - return nil -} - -func (c *connState) SetReadDeadline(t time.Time) error { - c.readDeadlineMu.Lock() - defer c.readDeadlineMu.Unlock() - if c.readDeadlineTimer != nil { - c.readDeadlineTimer.Stop() - } - if t.Before(time.Now()) { - return nil - } - c.readDeadlineTimer = time.AfterFunc(time.Until(t), func() { - close(c.readDeadlineChan) - }) - c.readDeadlineChan = make(chan struct{}) // Reset the channel to ensure it is ready for the next deadline - return nil -} - -func (c *connState) SetWriteDeadline(t time.Time) error { - // TODO: simply does not work right now - return nil -} diff --git a/internal/api/syncapi/tunnel/httpclient.go b/internal/api/syncapi/tunnel/httpclient.go deleted file mode 100644 index f9019472..00000000 --- a/internal/api/syncapi/tunnel/httpclient.go +++ /dev/null @@ -1,38 +0,0 @@ -package tunnel - -import ( - "crypto/tls" - "net" - "net/http" - "time" - - "connectrpc.com/connect" - "golang.org/x/net/http2" -) - -// NewInsecureHttpClient creates a new HTTP client that allows insecure connections (HTTP/2 over plain TCP). -// This is useful for testing or when you don't need TLS. -func NewInsecureHttpClient() connect.HTTPClient { - return &http.Client{ - Transport: &http2.Transport{ - AllowHTTP: true, - DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { - return net.Dial(network, addr) - }, - IdleConnTimeout: 300 * time.Second, - ReadIdleTimeout: 60 * time.Second, - }, - } -} - -func NewWrappedStreamClient(stream *WrappedStream) connect.HTTPClient { - return &http.Client{ - Transport: &http2.Transport{ - AllowHTTP: true, - DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { - // Note that the addr doesn't matter at all when using a wrapped stream. - return stream.Dial() - }, - }, - } -} diff --git a/internal/api/syncapi/tunnel/listener.go b/internal/api/syncapi/tunnel/listener.go deleted file mode 100644 index 822ec54c..00000000 --- a/internal/api/syncapi/tunnel/listener.go +++ /dev/null @@ -1,59 +0,0 @@ -package tunnel - -import ( - "net" - "sync" -) - -// tunnelListener is a net.Listener that makes it possible to feed net.Conn instances into it. -type ConnectionProvider struct { - doClose sync.Once - isOpen chan struct{} - nextConn chan net.Conn -} - -var _ net.Listener = (*ConnectionProvider)(nil) - -func NewConnectionProvider(bufferSize int) *ConnectionProvider { - return &ConnectionProvider{ - isOpen: make(chan struct{}), - nextConn: make(chan net.Conn, bufferSize), // Buffered channel to hold incoming connections - } -} - -var _ net.Listener = (*ConnectionProvider)(nil) - -func (l *ConnectionProvider) Accept() (net.Conn, error) { - select { - case <-l.isOpen: - // If the listener is closed, we don't accept new connections. - return nil, &net.OpError{Op: "accept", Net: "tunnel", Err: net.ErrClosed} - case conn := <-l.nextConn: - return conn, nil - } -} - -func (l *ConnectionProvider) Close() error { - l.doClose.Do(func() { - close(l.isOpen) // Close the channel to signal that the listener is closed - }) - return nil -} - -func (l *ConnectionProvider) Addr() net.Addr { - return &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, // Port is not used in this implementation - } -} - -func (l *ConnectionProvider) ProvideConn(conn net.Conn) { - select { - case <-l.isOpen: - // If the listener is closed, we don't accept new connections. - conn.Close() - return - case l.nextConn <- conn: - // Successfully provided the connection - } -} diff --git a/internal/api/syncapi/tunnel/streamutil.go b/internal/api/syncapi/tunnel/streamutil.go deleted file mode 100644 index e7d4394f..00000000 --- a/internal/api/syncapi/tunnel/streamutil.go +++ /dev/null @@ -1,146 +0,0 @@ -package tunnel - -import ( - "errors" - "fmt" - "sync" - "sync/atomic" - - "connectrpc.com/connect" - "github.com/garethgeorge/backrest/gen/go/v1sync" - "github.com/hashicorp/go-multierror" - "google.golang.org/protobuf/proto" -) - -var ErrStreamClosed = errors.New("stream closed") - -type stream interface { - Send(item *v1sync.TunnelMessage) error - Receive() (*v1sync.TunnelMessage, error) - Close() error -} - -type cryptedStream struct { - stream - crypt -} - -func newCryptedStream(s stream, secret []byte) *cryptedStream { - return &cryptedStream{ - stream: s, - crypt: crypt{ - secret: secret, - }, - } -} - -func (cs *cryptedStream) Send(item *v1sync.TunnelMessage) error { - if item.Data == nil { - return cs.stream.Send(item) - } - bytes, err := proto.Marshal(item) - if err != nil { - return err - } - enc, err := cs.Encrypt(bytes) - if err != nil { - return fmt.Errorf("encrypt: %w", err) - } - return cs.stream.Send(&v1sync.TunnelMessage{ - Encrypted: enc, - }) -} - -func (cs *cryptedStream) Receive() (*v1sync.TunnelMessage, error) { - msg, err := cs.stream.Receive() - if err != nil { - return nil, err - } - dec, err := cs.Decrypt(msg.Encrypted) - if err != nil { - return nil, fmt.Errorf("decrypt: %w", err) - } - var tm v1sync.TunnelMessage - if err := proto.Unmarshal(dec, &tm); err != nil { - return nil, fmt.Errorf("unmarshal: %w", err) - } - if len(tm.Encrypted) != 0 { - return nil, fmt.Errorf("unexpected encrypted field in decrypted message") - } - return &tm, nil -} - -type clientStream struct { - sendMu sync.Mutex - receiveMu sync.Mutex - stream *connect.BidiStreamForClient[v1sync.TunnelMessage, v1sync.TunnelMessage] - closed atomic.Bool -} - -func (s *clientStream) Send(item *v1sync.TunnelMessage) error { - s.sendMu.Lock() - defer s.sendMu.Unlock() - if s.closed.Load() { - return connect.NewError(connect.CodeFailedPrecondition, ErrStreamClosed) - } - return s.stream.Send(item) -} - -func (s *clientStream) Receive() (*v1sync.TunnelMessage, error) { - s.receiveMu.Lock() - defer s.receiveMu.Unlock() - if s.closed.Load() { - return nil, connect.NewError(connect.CodeFailedPrecondition, ErrStreamClosed) - } - return s.stream.Receive() -} - -// Close closes the request side of the stream, allowing the server to finish processing. -// It will block if Receive or Send are in progress. -func (s *clientStream) Close() error { - s.closed.Store(true) - s.receiveMu.Lock() - var err error - if e := s.stream.CloseResponse(); e != nil { - err = multierror.Append(err, e) - } - s.receiveMu.Unlock() - s.sendMu.Lock() - if e := s.stream.CloseRequest(); e != nil { - err = multierror.Append(err, e) - } - s.sendMu.Unlock() - return err -} - -type serverStream struct { - sendMu sync.Mutex - receiveMu sync.Mutex - stream *connect.BidiStream[v1sync.TunnelMessage, v1sync.TunnelMessage] - closed atomic.Bool -} - -func (s *serverStream) Send(item *v1sync.TunnelMessage) error { - s.sendMu.Lock() - defer s.sendMu.Unlock() - if s.closed.Load() { - return connect.NewError(connect.CodeFailedPrecondition, ErrStreamClosed) - } - return s.stream.Send(item) -} - -func (s *serverStream) Receive() (*v1sync.TunnelMessage, error) { - s.receiveMu.Lock() - defer s.receiveMu.Unlock() - if s.closed.Load() { - return nil, connect.NewError(connect.CodeFailedPrecondition, ErrStreamClosed) - } - return s.stream.Receive() -} - -func (s *serverStream) Close() error { - s.receiveMu.Lock() - s.sendMu.Lock() - s.closed.Store(true) - return nil -} diff --git a/internal/api/syncapi/tunnel/tunnel_test.go b/internal/api/syncapi/tunnel/tunnel_test.go deleted file mode 100644 index c5e8e0e5..00000000 --- a/internal/api/syncapi/tunnel/tunnel_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package tunnel - -import ( - "bufio" - "context" - "io" - "net/http" - "testing" - "time" - - "github.com/garethgeorge/backrest/gen/go/v1sync/v1syncconnect" - "github.com/garethgeorge/backrest/internal/testutil" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" -) - -func newHelloHandler() http.Handler { - mux := http.NewServeMux() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("Hello, World!")) - }) - return mux -} - -func waitForConnectionReady(ctx context.Context, t *testing.T, wrapped *WrappedStream) { - for { - select { - case <-ctx.Done(): - t.Fatalf("timed out waiting for connection to be ready: %v", ctx.Err()) - default: - if wrapped.IsReady() { - return - } - time.Sleep(10 * time.Millisecond) - } - } -} - -func TestConnect(t *testing.T) { - t.Skip("skipping syncapi tests") - ctx, cancel := testutil.WithDeadlineFromTest(t, context.Background()) - defer cancel() - - // Construct the service we want to provide - provider := NewConnectionProvider(10 /* bufferSize */) - sampleHandler := NewTunnelHandler(provider).SetLogger(testutil.NewTestLogger(t)) - - server := &http.Server{ - Handler: newHelloHandler(), - } - testutil.ServeForTest("HTTPServer", t, server, provider) - - // Serve the sample handler - mux := http.NewServeMux() - mux.Handle(v1syncconnect.NewTunnelServiceHandler(sampleHandler)) - grpcServer := &http.Server{ - Addr: testutil.AllocOpenBindAddr(t), - Handler: h2c.NewHandler(mux, &http2.Server{}), // h2c is HTTP/2 without TLS for grpc-connect support. - } - testutil.ListenAndServeForTest("gRPCServer", t, grpcServer) - - // Create a client and connect to the server - client := v1syncconnect.NewTunnelServiceClient(NewInsecureHttpClient(), "http://"+grpcServer.Addr) - stream := client.Tunnel(ctx) - wrapped := NewWrappedStreamFromClient(stream, WithLogger(testutil.NewTestLogger(t).Named("client"))) - go func() { - t.Log("Client stream started") - if err := wrapped.HandlePackets(ctx); err != nil { - t.Errorf("Failed to handle packets: %v", err) - } - t.Log("Client stream ended") - }() - - waitForConnectionReady(ctx, t, wrapped) - - // Attempt to connect to the server - conn, err := wrapped.Dial() - if err != nil { - t.Fatalf("Failed to dial server: %v", err) - } - - // Write an HTTP request to the connection - req, err := http.NewRequest("GET", "http://example.com", nil) - if err != nil { - t.Fatalf("Failed to create request: %v", err) - } - if err := req.Write(conn); err != nil { - t.Fatalf("Failed to write request: %v", err) - } - - // Read the response - resp, err := http.ReadResponse(bufio.NewReader(conn), req) - if err != nil { - t.Fatalf("Failed to read response: %v", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - t.Fatalf("Expected status OK, got %s", resp.Status) - } - t.Logf("Received response: %s", resp.Status) - body, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Failed to read response body: %v", err) - } - t.Logf("Response body: %s", body) - - t.Logf("Closing connection with ID %d", conn.(*connState).connId) - if err := conn.Close(); err != nil { - t.Fatalf("Failed to close connection: %v", err) - } - t.Logf("Calling wrapped.Shutdown()") - if err := wrapped.Shutdown(); err != nil { - t.Fatalf("Failed to shutdown wrapped stream: %v", err) - } - t.Log("Test completed successfully") -} diff --git a/internal/api/syncapi/tunnel/tunnelhandler.go b/internal/api/syncapi/tunnel/tunnelhandler.go deleted file mode 100644 index b3f50d11..00000000 --- a/internal/api/syncapi/tunnel/tunnelhandler.go +++ /dev/null @@ -1,41 +0,0 @@ -package tunnel - -import ( - "context" - - "connectrpc.com/connect" - "github.com/garethgeorge/backrest/gen/go/v1sync" - "github.com/garethgeorge/backrest/gen/go/v1sync/v1syncconnect" - "go.uber.org/zap" -) - -type TunnelHandler struct { - logger *zap.Logger - streams []*WrappedStream - provider *ConnectionProvider -} - -var _ v1syncconnect.TunnelServiceHandler = (*TunnelHandler)(nil) - -func NewTunnelHandler(provider *ConnectionProvider) *TunnelHandler { - return &TunnelHandler{ - logger: zap.NewNop(), - streams: make([]*WrappedStream, 0), - provider: provider, - } -} - -func (th *TunnelHandler) SetLogger(logger *zap.Logger) *TunnelHandler { - if logger == nil { - logger = zap.NewNop() - } - th.logger = logger.Named("tunnel-handler") - return th -} - -func (th *TunnelHandler) Tunnel(ctx context.Context, stream *connect.BidiStream[v1sync.TunnelMessage, v1sync.TunnelMessage]) error { - wrapped := NewWrappedStream(stream, WithLogger(th.logger)) - wrapped.ProvideConnectionsTo(th.provider) - th.streams = append(th.streams, wrapped) - return wrapped.HandlePackets(ctx) -} diff --git a/internal/api/syncapi/tunnel/wrappedstream.go b/internal/api/syncapi/tunnel/wrappedstream.go deleted file mode 100644 index b8886275..00000000 --- a/internal/api/syncapi/tunnel/wrappedstream.go +++ /dev/null @@ -1,326 +0,0 @@ -package tunnel - -import ( - "context" - "crypto/ecdh" - "crypto/rand" - "fmt" - "net" - "sync" - "sync/atomic" - "time" - - "connectrpc.com/connect" - "github.com/garethgeorge/backrest/gen/go/v1sync" - "go.uber.org/zap" -) - -type WrappedStreamOptions func(*WrappedStream) - -func WithLogger(logger *zap.Logger) WrappedStreamOptions { - return func(ws *WrappedStream) { - ws.logger = logger - } -} - -func WithHeartbeatInterval(interval time.Duration) WrappedStreamOptions { - return func(ws *WrappedStream) { - ws.heartbeatInterval = interval - } -} - -type WrappedStream struct { - isClient bool - - stream stream - logger *zap.Logger - - provider *ConnectionProvider // is nil until ProvideConnectionsTo is called - heartbeatInterval time.Duration // interval for heartbeat messages, if set - - // pool of connections, every connection is bidirectional so can be initiated from either side. - connsMu sync.Mutex - conns map[int64]*connState - - lastConnID atomic.Int64 - - handlingPackets atomic.Bool - streamStopped atomic.Bool - - sharedSecretCh chan struct{} - sharedSecret []byte -} - -func newWrappedStreamInternal(stream stream, isClient bool, opts ...WrappedStreamOptions) *WrappedStream { - ws := &WrappedStream{ - isClient: isClient, - stream: stream, - heartbeatInterval: 30 * time.Second, - conns: make(map[int64]*connState), - sharedSecretCh: make(chan struct{}), - } - for _, opt := range opts { - opt(ws) - } - if isClient { - ws.lastConnID.Store(1) // Use odd numbered connection IDs for client-initiated connections. - } else { - ws.lastConnID.Store(2) // Use even numbered connection IDs for server-initiated connections. - } - return ws -} - -func NewWrappedStream(stream *connect.BidiStream[v1sync.TunnelMessage, v1sync.TunnelMessage], opts ...WrappedStreamOptions) *WrappedStream { - return newWrappedStreamInternal(&serverStream{ - stream: stream, - }, false, opts...) -} - -func NewWrappedStreamFromClient(stream *connect.BidiStreamForClient[v1sync.TunnelMessage, v1sync.TunnelMessage], opts ...WrappedStreamOptions) *WrappedStream { - return newWrappedStreamInternal(&clientStream{ - stream: stream, - }, true, opts...) -} - -func (ws *WrappedStream) allocConnID() int64 { - return ws.lastConnID.Add(2) -} - -func (ws *WrappedStream) getSharedSecret() ([]byte, error) { - <-ws.sharedSecretCh - if ws.sharedSecret == nil { - return nil, fmt.Errorf("shared secret not available") - } - return ws.sharedSecret, nil -} - -func (ws *WrappedStream) IsReady() bool { - return ws.handlingPackets.Load() && !ws.streamStopped.Load() -} - -func (ws *WrappedStream) Dial() (net.Conn, error) { - if !ws.handlingPackets.Load() { - return nil, fmt.Errorf("cannot dial before handling packets") - } - - secret, err := ws.getSharedSecret() - if err != nil { - return nil, fmt.Errorf("get shared secret: %w", err) - } - - connID := ws.allocConnID() - new := newConnState(ws.stream, connID, secret, ws.logger) - if err := new.sendOpenPacket(); err != nil { - return nil, fmt.Errorf("send open packet: %w", err) - } - ws.connsMu.Lock() - defer ws.connsMu.Unlock() - ws.conns[connID] = new - return new, nil -} - -func (ws *WrappedStream) ProvideConnectionsTo(provider *ConnectionProvider) { - ws.provider = provider -} - -func (ws *WrappedStream) sendHeartbeats(ctx context.Context, stream stream) { - if ws.heartbeatInterval <= 0 || !ws.isClient { - return - } - - ticker := time.NewTicker(ws.heartbeatInterval) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - if ws.logger != nil { - ws.logger.Debug("sending heartbeat") - } - if err := stream.Send(&v1sync.TunnelMessage{ - ConnId: -1, // handshake packet - }); err != nil && ws.logger != nil { - ws.logger.Error("failed to send heartbeat", zap.Error(err)) - } - } - } -} - -func (ws *WrappedStream) HandlePackets(ctx context.Context) error { - if ws.handlingPackets.Swap(true) { - return fmt.Errorf("already handling packets") - } - defer ws.handlingPackets.Store(false) - if ws.streamStopped.Load() { - return fmt.Errorf("stream already stopped") - } - - // TODO: optimization, it is generally secure and performant have a singleton key that is generated once and reused for all connections. - // the only risk w/this approach is if the key were somehow leaked all related connections would be compromised. The risk is low in this case since - // the key is only kept in memory for the lifetime of the process, and ideally is a second line of defense after TLS. - key, err := ecdh.X25519().GenerateKey(rand.Reader) - if err != nil { - return fmt.Errorf("generate key for handshake packet: %w", err) - } - - if err := ws.stream.Send(&v1sync.TunnelMessage{ - ConnId: -100, // handshake packet - PubkeyEcdhX25519: key.PublicKey().Bytes(), - }); err != nil { - return fmt.Errorf("send handshake packet: %w", err) - } - - go func() { - timeoutTimer := time.NewTimer(5 * time.Second) - defer timeoutTimer.Stop() - select { - case <-timeoutTimer.C: - if ws.logger != nil { - ws.logger.Warn("timeout waiting for handshake response") - } - ws.Shutdown() - case <-ws.sharedSecretCh: - if ws.logger != nil { - ws.logger.Info("handshake response received, shared secret established") - } - } - }() - - // receive handshake packet - handshake, err := ws.stream.Receive() - if err != nil { - return fmt.Errorf("receive handshake packet: %w", err) - } - if handshake.GetConnId() != -100 { - return fmt.Errorf("expected handshake packet with connId -100, got %d", handshake.GetConnId()) - } - if len(handshake.PubkeyEcdhX25519) == 0 { - return fmt.Errorf("handshake packet does not contain public key") - } - peerKey, err := ecdh.X25519().NewPublicKey(handshake.PubkeyEcdhX25519) - if err != nil { - return fmt.Errorf("parse peer public key: %w", err) - } - sharedSecret, err := key.ECDH(peerKey) - if err != nil { - return fmt.Errorf("compute shared key: %w", err) - } - ws.sharedSecret = sharedSecret - close(ws.sharedSecretCh) - - // cryptedStream := newCryptedStream(ws.stream, ws.sharedSecret) - cryptedStream := ws.stream - - newConn := func(connId int64) *connState { - if ws.logger != nil { - ws.logger.Info("new tunnel connection", zap.Int64("connId", connId)) - } - new := newConnState(ws.stream, connId, ws.sharedSecret, ws.logger) - ws.conns[connId] = new - ws.provider.ProvideConn(new) - return new - } - - headOfLineBlockingTimer := time.NewTimer(0) - defer headOfLineBlockingTimer.Stop() - - // send heartbeats in a separate goroutine if heartbeat interval is set - go ws.sendHeartbeats(ctx, cryptedStream) - - for { - msg, err := cryptedStream.Receive() - if err != nil { - if ws.handlingPackets.Load() { - return nil - } - return fmt.Errorf("receive message: %w", err) - } - - connId := msg.GetConnId() - if connId <= 0 { - // negative IDs are reserved for healthchecks and control messages, ignored. - continue - } - - if msg.Close { - if ws.logger != nil { - ws.logger.Info("closing connection", zap.Int64("connId", connId)) - } - ws.connsMu.Lock() - if conn, exists := ws.conns[connId]; exists { - if err := conn.Close(); err != nil && ws.logger != nil { - ws.logger.Error("failed to close connection", zap.Int64("connId", connId), zap.Error(err)) - } - delete(ws.conns, connId) - } - ws.connsMu.Unlock() - continue - } - - ws.connsMu.Lock() - conn, exists := ws.conns[connId] - if !exists { - if msg.Seqno != 0 { - if ws.logger != nil { - ws.logger.Warn("received message for unknown connection", zap.Int64("connId", connId), zap.Int64("seqno", msg.Seqno)) - } - continue - } - if ws.provider == nil { - if ws.logger != nil { - ws.logger.Warn("received open packet for unknown connection, but no provider is set, ignoring the connection", zap.Int64("connId", connId)) - } - continue - } - - conn = newConn(connId) - } - ws.connsMu.Unlock() - - if len(msg.Data) == 0 { - continue - } - - headOfLineBlockingTimer.Reset(100 * time.Millisecond) - select { - case <-conn.closedCh: - if ws.logger != nil { - ws.logger.Warn("received message for closed connection", zap.Int64("connId", connId), zap.Int64("seqno", msg.Seqno)) - } - case conn.reads <- msg.Data: - if ws.logger != nil { - ws.logger.Debug("received data on tunnel connection", zap.Int64("connId", connId), zap.Int64("seqno", msg.Seqno), zap.Int("dataLength", len(msg.Data))) - } - case <-headOfLineBlockingTimer.C: - if ws.logger != nil { - ws.logger.Warn("head-of-line blocking detected, no reads available for connection", zap.Int64("connId", connId), zap.Int64("seqno", msg.Seqno)) - } - conn.Close() - case <-ctx.Done(): - // Close all open connections when the context is done. - ws.connsMu.Lock() - if ws.logger != nil { - ws.logger.Info("context done, closing all connections") - } - for _, c := range ws.conns { - if err := c.Close(); err != nil && ws.logger != nil { - ws.logger.Error("failed to close connection on context done", zap.Int64("connId", c.connId), zap.Error(err)) - } - } - ws.connsMu.Unlock() - return nil - } - } -} - -func (ws *WrappedStream) Shutdown() error { - if ws.streamStopped.Swap(true) { - if ws.logger != nil { - ws.logger.Warn("wrapped stream already shutdown") - } - return nil - } - return ws.stream.Close() -} diff --git a/proto/v1sync/syncservice.proto b/proto/v1sync/syncservice.proto index 164554df..d412a047 100644 --- a/proto/v1sync/syncservice.proto +++ b/proto/v1sync/syncservice.proto @@ -15,6 +15,13 @@ import "google/api/annotations.proto"; import "google/protobuf/any.proto"; + +// BackrestSyncService provides methods to sync data between backrest instances. +// This service provides its own authentication and authorization. +service BackrestSyncService { + rpc Sync(stream SyncStreamItem) returns (stream SyncStreamItem) {} +} + // BackrestSyncStateService provides methods to query the sync state of known hosts and clients. // This service should be served behind authentication and authorization. service BackrestSyncStateService { @@ -39,23 +46,6 @@ message PeerState { int64 last_heartbeat_millis = 8; // The last time the peer sent a heartbeat, in milliseconds since epoch. } -service SyncPeerService { - // Authenticate authenticates the peer with the sync service, must be called before any other methods. - rpc Authenticate(AuthenticateRequest) returns (google.protobuf.Empty) {} - - // GetOperationMetadata returns a stream of sync items from the peer. - rpc GetOperationMetadata(v1.OpSelector) returns (GetOperationMetadataResponse) {} - rpc SendOperations(stream v1.Operation) returns (google.protobuf.Empty) {} - rpc GetLog(types.StringValue) returns (stream LogDataEntry) {} - - // Called everytime the set of resources available to the peer changes. - rpc SetAvailableResources(SetAvailableResourcesRequest) returns (google.protobuf.Empty) {} - - // Implements semantics for updating the remote config of the peer. - rpc SetConfig(SetConfigRequest) returns (google.protobuf.Empty) {} - rpc GetConfig(google.protobuf.Empty) returns (RemoteConfig) {} -} - message AuthenticateRequest { v1.SignedMessage instance_id = 1; // The ID of the peer instance. } @@ -116,7 +106,6 @@ message AuthorizationToken { v1.SignedMessage instance_id = 2; // The ID of the peer instance. } -/*SyncStreamItem message SyncStreamItem { oneof action { v1.SignedMessage signed_message = 1; @@ -136,8 +125,8 @@ message SyncStreamItem { message SyncActionHandshake { int64 protocol_version = 1; - PublicKey public_key = 2; - SignedMessage instance_id = 3; + v1.PublicKey public_key = 2; + v1.SignedMessage instance_id = 3; } // SyncActionHeartbeat is sent periodically to keep the connection alive. @@ -148,15 +137,15 @@ message SyncStreamItem { } message SyncActionSetConfig { - repeated Repo repos = 1; - repeated Plan plans = 2; + repeated v1.Repo repos = 1; + repeated v1.Plan plans = 2; repeated string repos_to_delete = 3; repeated string plans_to_delete = 4; } message SyncActionListResources { - repeated SyncRepoMetadata repos = 1; - repeated SyncPlanMetadata plans = 2; + repeated RepoMetadata repos = 1; + repeated PlanMetadata plans = 2; } message SyncActionConnectRepo { @@ -174,7 +163,7 @@ message SyncStreamItem { message SyncActionDiffOperations { // Client connects and sends a list of "have_operations" that exist in its log. // have_operation_ids and have_operation_modnos are the operation IDs and modnos that the client has when zip'd pairwise. - OpSelector have_operations_selector = 1; + v1.OpSelector have_operations_selector = 1; repeated int64 have_operation_ids = 2; repeated int64 have_operation_modnos = 3; // Server sends a list of "request_operations" for any operations that it doesn't have. @@ -182,7 +171,7 @@ message SyncStreamItem { } message SyncActionSendOperations { - OperationEvent event = 1; + v1.OperationEvent event = 1; } message SyncActionGetLog { @@ -210,12 +199,3 @@ message SyncStreamItem { string ed25519 = 2 [json_name="ed25519pub"]; // base64 encoded public key } } - -// RemoteConfig contains shareable properties from a remote backrest instance. -message RemoteConfig { - int32 modno = 1; // The modno of the config. - int32 version = 2; // The storage version of the config. - repeated Repo repos = 3; - repeated Plan plans = 4; -} -*/ diff --git a/webui/gen/ts/v1sync/syncservice_pb.ts b/webui/gen/ts/v1sync/syncservice_pb.ts index 76e59eb2..ad3d3420 100644 --- a/webui/gen/ts/v1sync/syncservice_pb.ts +++ b/webui/gen/ts/v1sync/syncservice_pb.ts @@ -9,13 +9,11 @@ import { file_v1_config } from "../v1/config_pb"; import type { PublicKey, SignedMessage } from "../v1/crypto_pb"; import { file_v1_crypto } from "../v1/crypto_pb"; import { file_v1_restic } from "../v1/restic_pb"; -import type { OpSelectorSchema } from "../v1/service_pb"; +import type { OpSelector } from "../v1/service_pb"; import { file_v1_service } from "../v1/service_pb"; -import type { OperationSchema } from "../v1/operations_pb"; +import type { OperationEvent } from "../v1/operations_pb"; import { file_v1_operations } from "../v1/operations_pb"; -import type { StringValueSchema } from "../types/value_pb"; import { file_types_value } from "../types/value_pb"; -import type { EmptySchema } from "@bufbuild/protobuf/wkt"; import { file_google_protobuf_any, file_google_protobuf_empty } from "@bufbuild/protobuf/wkt"; import { file_google_api_annotations } from "../google/api/annotations_pb"; import type { Message } from "@bufbuild/protobuf"; @@ -24,7 +22,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file v1sync/syncservice.proto. */ export const file_v1sync_syncservice: GenFile = /*@__PURE__*/ - fileDesc("Chh2MXN5bmMvc3luY3NlcnZpY2UucHJvdG8SBnYxc3luYyIrChZTeW5jU3RhdGVTdHJlYW1SZXF1ZXN0EhEKCXN1YnNjcmliZRgBIAEoCCKbAgoJUGVlclN0YXRlEhgKEHBlZXJfaW5zdGFuY2VfaWQYASABKAkSEgoKcGVlcl9rZXlpZBgCIAEoCRImCgVzdGF0ZRgDIAEoDjIXLnYxc3luYy5Db25uZWN0aW9uU3RhdGUSFgoOc3RhdHVzX21lc3NhZ2UYBCABKAkSKQoLa25vd25fcGxhbnMYBSADKAsyFC52MXN5bmMuUGxhbk1ldGFkYXRhEikKC2tub3duX3JlcG9zGAYgAygLMhQudjFzeW5jLlJlcG9NZXRhZGF0YRIrCg1yZW1vdGVfY29uZmlnGAcgASgLMhQudjFzeW5jLlJlbW90ZUNvbmZpZxIdChVsYXN0X2hlYXJ0YmVhdF9taWxsaXMYCCABKAMiPQoTQXV0aGVudGljYXRlUmVxdWVzdBImCgtpbnN0YW5jZV9pZBgBIAEoCzIRLnYxLlNpZ25lZE1lc3NhZ2UiPgocR2V0T3BlcmF0aW9uTWV0YWRhdGFSZXNwb25zZRIOCgZvcF9pZHMYASADKAMSDgoGbW9kbm9zGAIgAygDIl0KDExvZ0RhdGFFbnRyeRIOCgZsb2dfaWQYASABKAkSEgoKb3duZXJfb3BpZBgCIAEoAxIaChJleHBpcmF0aW9uX3RzX3VuaXgYAyABKAMSDQoFY2h1bmsYBCABKAwiaAocU2V0QXZhaWxhYmxlUmVzb3VyY2VzUmVxdWVzdBIjCgVyZXBvcxgBIAMoCzIULnYxc3luYy5QbGFuTWV0YWRhdGESIwoFcGxhbnMYAiADKAsyFC52MXN5bmMuUmVwb01ldGFkYXRhIigKDFJlcG9NZXRhZGF0YRIKCgJpZBgBIAEoCRIMCgRndWlkGAIgASgJIhoKDFBsYW5NZXRhZGF0YRIKCgJpZBgBIAEoCSJ2ChBTZXRDb25maWdSZXF1ZXN0EhcKBXBsYW5zGAEgAygLMggudjEuUGxhbhIXCgVyZXBvcxgCIAMoCzIILnYxLlJlcG8SFwoPcmVwb3NfdG9fZGVsZXRlGAMgAygJEhcKD3BsYW5zX3RvX2RlbGV0ZRgEIAMoCSJgCgxSZW1vdGVDb25maWcSDQoFbW9kbm8YASABKAUSDwoHdmVyc2lvbhgCIAEoBRIXCgVyZXBvcxgDIAMoCzIILnYxLlJlcG8SFwoFcGxhbnMYBCADKAsyCC52MS5QbGFuIl8KEkF1dGhvcml6YXRpb25Ub2tlbhIhCgpwdWJsaWNfa2V5GAEgASgLMg0udjEuUHVibGljS2V5EiYKC2luc3RhbmNlX2lkGAIgASgLMhEudjEuU2lnbmVkTWVzc2FnZSqcAgoPQ29ubmVjdGlvblN0YXRlEhwKGENPTk5FQ1RJT05fU1RBVEVfVU5LTk9XThAAEhwKGENPTk5FQ1RJT05fU1RBVEVfUEVORElORxABEh4KGkNPTk5FQ1RJT05fU1RBVEVfQ09OTkVDVEVEEAISIQodQ09OTkVDVElPTl9TVEFURV9ESVNDT05ORUNURUQQAxIfChtDT05ORUNUSU9OX1NUQVRFX1JFVFJZX1dBSVQQBBIfChtDT05ORUNUSU9OX1NUQVRFX0VSUk9SX0FVVEgQChIjCh9DT05ORUNUSU9OX1NUQVRFX0VSUk9SX1BST1RPQ09MEAsSIwofQ09OTkVDVElPTl9TVEFURV9FUlJPUl9JTlRFUk5BTBAMMmwKGEJhY2tyZXN0U3luY1N0YXRlU2VydmljZRJQChdHZXRQZWVyU3luY1N0YXRlc1N0cmVhbRIeLnYxc3luYy5TeW5jU3RhdGVTdHJlYW1SZXF1ZXN0GhEudjFzeW5jLlBlZXJTdGF0ZSIAMAEy9AMKD1N5bmNQZWVyU2VydmljZRJFCgxBdXRoZW50aWNhdGUSGy52MXN5bmMuQXV0aGVudGljYXRlUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIAEk4KFEdldE9wZXJhdGlvbk1ldGFkYXRhEg4udjEuT3BTZWxlY3RvchokLnYxc3luYy5HZXRPcGVyYXRpb25NZXRhZGF0YVJlc3BvbnNlIgASOwoOU2VuZE9wZXJhdGlvbnMSDS52MS5PcGVyYXRpb24aFi5nb29nbGUucHJvdG9idWYuRW1wdHkiACgBEjYKBkdldExvZxISLnR5cGVzLlN0cmluZ1ZhbHVlGhQudjFzeW5jLkxvZ0RhdGFFbnRyeSIAMAESVwoVU2V0QXZhaWxhYmxlUmVzb3VyY2VzEiQudjFzeW5jLlNldEF2YWlsYWJsZVJlc291cmNlc1JlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiABI/CglTZXRDb25maWcSGC52MXN5bmMuU2V0Q29uZmlnUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIAEjsKCUdldENvbmZpZxIWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRoULnYxc3luYy5SZW1vdGVDb25maWciAEIwWi5naXRodWIuY29tL2dhcmV0aGdlb3JnZS9iYWNrcmVzdC9nZW4vZ28vdjFzeW5jYgZwcm90bzM", [file_v1_config, file_v1_crypto, file_v1_restic, file_v1_service, file_v1_operations, file_types_value, file_google_protobuf_empty, file_google_api_annotations, file_google_protobuf_any]); + fileDesc("Chh2MXN5bmMvc3luY3NlcnZpY2UucHJvdG8SBnYxc3luYyIrChZTeW5jU3RhdGVTdHJlYW1SZXF1ZXN0EhEKCXN1YnNjcmliZRgBIAEoCCKbAgoJUGVlclN0YXRlEhgKEHBlZXJfaW5zdGFuY2VfaWQYASABKAkSEgoKcGVlcl9rZXlpZBgCIAEoCRImCgVzdGF0ZRgDIAEoDjIXLnYxc3luYy5Db25uZWN0aW9uU3RhdGUSFgoOc3RhdHVzX21lc3NhZ2UYBCABKAkSKQoLa25vd25fcGxhbnMYBSADKAsyFC52MXN5bmMuUGxhbk1ldGFkYXRhEikKC2tub3duX3JlcG9zGAYgAygLMhQudjFzeW5jLlJlcG9NZXRhZGF0YRIrCg1yZW1vdGVfY29uZmlnGAcgASgLMhQudjFzeW5jLlJlbW90ZUNvbmZpZxIdChVsYXN0X2hlYXJ0YmVhdF9taWxsaXMYCCABKAMiPQoTQXV0aGVudGljYXRlUmVxdWVzdBImCgtpbnN0YW5jZV9pZBgBIAEoCzIRLnYxLlNpZ25lZE1lc3NhZ2UiPgocR2V0T3BlcmF0aW9uTWV0YWRhdGFSZXNwb25zZRIOCgZvcF9pZHMYASADKAMSDgoGbW9kbm9zGAIgAygDIl0KDExvZ0RhdGFFbnRyeRIOCgZsb2dfaWQYASABKAkSEgoKb3duZXJfb3BpZBgCIAEoAxIaChJleHBpcmF0aW9uX3RzX3VuaXgYAyABKAMSDQoFY2h1bmsYBCABKAwiaAocU2V0QXZhaWxhYmxlUmVzb3VyY2VzUmVxdWVzdBIjCgVyZXBvcxgBIAMoCzIULnYxc3luYy5QbGFuTWV0YWRhdGESIwoFcGxhbnMYAiADKAsyFC52MXN5bmMuUmVwb01ldGFkYXRhIigKDFJlcG9NZXRhZGF0YRIKCgJpZBgBIAEoCRIMCgRndWlkGAIgASgJIhoKDFBsYW5NZXRhZGF0YRIKCgJpZBgBIAEoCSJ2ChBTZXRDb25maWdSZXF1ZXN0EhcKBXBsYW5zGAEgAygLMggudjEuUGxhbhIXCgVyZXBvcxgCIAMoCzIILnYxLlJlcG8SFwoPcmVwb3NfdG9fZGVsZXRlGAMgAygJEhcKD3BsYW5zX3RvX2RlbGV0ZRgEIAMoCSJgCgxSZW1vdGVDb25maWcSDQoFbW9kbm8YASABKAUSDwoHdmVyc2lvbhgCIAEoBRIXCgVyZXBvcxgDIAMoCzIILnYxLlJlcG8SFwoFcGxhbnMYBCADKAsyCC52MS5QbGFuIl8KEkF1dGhvcml6YXRpb25Ub2tlbhIhCgpwdWJsaWNfa2V5GAEgASgLMg0udjEuUHVibGljS2V5EiYKC2luc3RhbmNlX2lkGAIgASgLMhEudjEuU2lnbmVkTWVzc2FnZSLZDgoOU3luY1N0cmVhbUl0ZW0SKwoOc2lnbmVkX21lc3NhZ2UYASABKAsyES52MS5TaWduZWRNZXNzYWdlSAASPwoJaGFuZHNoYWtlGAMgASgLMioudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25IYW5kc2hha2VIABI/CgloZWFydGJlYXQYBCABKAsyKi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvbkhlYXJ0YmVhdEgAEkoKD2RpZmZfb3BlcmF0aW9ucxgUIAEoCzIvLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uRGlmZk9wZXJhdGlvbnNIABJKCg9zZW5kX29wZXJhdGlvbnMYFSABKAsyLy52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblNlbmRPcGVyYXRpb25zSAASQgoLc2VuZF9jb25maWcYFiABKAsyKy52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblNlbmRDb25maWdIABJACgpzZXRfY29uZmlnGBggASgLMioudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25TZXRDb25maWdIABJICg5saXN0X3Jlc291cmNlcxgZIAEoCzIuLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uTGlzdFJlc291cmNlc0gAEjoKB2dldF9sb2cYGiABKAsyJy52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvbkdldExvZ0gAEkUKDXNlbmRfbG9nX2RhdGEYGyABKAsyLC52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblNlbmRMb2dEYXRhSAASPgoIdGhyb3R0bGUY6AcgASgLMikudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25UaHJvdHRsZUgAGnoKE1N5bmNBY3Rpb25IYW5kc2hha2USGAoQcHJvdG9jb2xfdmVyc2lvbhgBIAEoAxIhCgpwdWJsaWNfa2V5GAIgASgLMg0udjEuUHVibGljS2V5EiYKC2luc3RhbmNlX2lkGAMgASgLMhEudjEuU2lnbmVkTWVzc2FnZRoVChNTeW5jQWN0aW9uSGVhcnRiZWF0GjwKFFN5bmNBY3Rpb25TZW5kQ29uZmlnEiQKBmNvbmZpZxgBIAEoCzIULnYxc3luYy5SZW1vdGVDb25maWcaeQoTU3luY0FjdGlvblNldENvbmZpZxIXCgVyZXBvcxgBIAMoCzIILnYxLlJlcG8SFwoFcGxhbnMYAiADKAsyCC52MS5QbGFuEhcKD3JlcG9zX3RvX2RlbGV0ZRgDIAMoCRIXCg9wbGFuc190b19kZWxldGUYBCADKAkaYwoXU3luY0FjdGlvbkxpc3RSZXNvdXJjZXMSIwoFcmVwb3MYASADKAsyFC52MXN5bmMuUmVwb01ldGFkYXRhEiMKBXBsYW5zGAIgAygLMhQudjFzeW5jLlBsYW5NZXRhZGF0YRooChVTeW5jQWN0aW9uQ29ubmVjdFJlcG8SDwoHcmVwb19pZBgBIAEoCRqjAQoYU3luY0FjdGlvbkRpZmZPcGVyYXRpb25zEjAKGGhhdmVfb3BlcmF0aW9uc19zZWxlY3RvchgBIAEoCzIOLnYxLk9wU2VsZWN0b3ISGgoSaGF2ZV9vcGVyYXRpb25faWRzGAIgAygDEh0KFWhhdmVfb3BlcmF0aW9uX21vZG5vcxgDIAMoAxIaChJyZXF1ZXN0X29wZXJhdGlvbnMYBCADKAMaPQoYU3luY0FjdGlvblNlbmRPcGVyYXRpb25zEiEKBWV2ZW50GAEgASgLMhIudjEuT3BlcmF0aW9uRXZlbnQaIgoQU3luY0FjdGlvbkdldExvZxIOCgZsb2dfaWQYASABKAkaZgoVU3luY0FjdGlvblNlbmRMb2dEYXRhEg4KBmxvZ19pZBgBIAEoCRISCgpvd25lcl9vcGlkGAIgASgDEhoKEmV4cGlyYXRpb25fdHNfdW5peBgDIAEoAxINCgVjaHVuaxgEIAEoDBomChJTeW5jQWN0aW9uVGhyb3R0bGUSEAoIZGVsYXlfbXMYASABKAMaOAoZU3luY0VzdGFibGlzaFNoYXJlZFNlY3JldBIbCgdlZDI1NTE5GAIgASgJUgplZDI1NTE5cHViIrQBChNSZXBvQ29ubmVjdGlvblN0YXRlEhwKGENPTk5FQ1RJT05fU1RBVEVfVU5LTk9XThAAEhwKGENPTk5FQ1RJT05fU1RBVEVfUEVORElORxABEh4KGkNPTk5FQ1RJT05fU1RBVEVfQ09OTkVDVEVEEAISIQodQ09OTkVDVElPTl9TVEFURV9VTkFVVEhPUklaRUQQAxIeChpDT05ORUNUSU9OX1NUQVRFX05PVF9GT1VORBAEQggKBmFjdGlvbiqcAgoPQ29ubmVjdGlvblN0YXRlEhwKGENPTk5FQ1RJT05fU1RBVEVfVU5LTk9XThAAEhwKGENPTk5FQ1RJT05fU1RBVEVfUEVORElORxABEh4KGkNPTk5FQ1RJT05fU1RBVEVfQ09OTkVDVEVEEAISIQodQ09OTkVDVElPTl9TVEFURV9ESVNDT05ORUNURUQQAxIfChtDT05ORUNUSU9OX1NUQVRFX1JFVFJZX1dBSVQQBBIfChtDT05ORUNUSU9OX1NUQVRFX0VSUk9SX0FVVEgQChIjCh9DT05ORUNUSU9OX1NUQVRFX0VSUk9SX1BST1RPQ09MEAsSIwofQ09OTkVDVElPTl9TVEFURV9FUlJPUl9JTlRFUk5BTBAMMlMKE0JhY2tyZXN0U3luY1NlcnZpY2USPAoEU3luYxIWLnYxc3luYy5TeW5jU3RyZWFtSXRlbRoWLnYxc3luYy5TeW5jU3RyZWFtSXRlbSIAKAEwATJsChhCYWNrcmVzdFN5bmNTdGF0ZVNlcnZpY2USUAoXR2V0UGVlclN5bmNTdGF0ZXNTdHJlYW0SHi52MXN5bmMuU3luY1N0YXRlU3RyZWFtUmVxdWVzdBoRLnYxc3luYy5QZWVyU3RhdGUiADABQjBaLmdpdGh1Yi5jb20vZ2FyZXRoZ2VvcmdlL2JhY2tyZXN0L2dlbi9nby92MXN5bmNiBnByb3RvMw", [file_v1_config, file_v1_crypto, file_v1_restic, file_v1_service, file_v1_operations, file_types_value, file_google_protobuf_empty, file_google_api_annotations, file_google_protobuf_any]); /** * @generated from message v1sync.SyncStateStreamRequest @@ -355,6 +353,409 @@ export type AuthorizationToken = Message<"v1sync.AuthorizationToken"> & { export const AuthorizationTokenSchema: GenMessage = /*@__PURE__*/ messageDesc(file_v1sync_syncservice, 10); +/** + * @generated from message v1sync.SyncStreamItem + */ +export type SyncStreamItem = Message<"v1sync.SyncStreamItem"> & { + /** + * @generated from oneof v1sync.SyncStreamItem.action + */ + action: { + /** + * @generated from field: v1.SignedMessage signed_message = 1; + */ + value: SignedMessage; + case: "signedMessage"; + } | { + /** + * note: mostly deprecated, sent through headers rather than stream. + * + * @generated from field: v1sync.SyncStreamItem.SyncActionHandshake handshake = 3; + */ + value: SyncStreamItem_SyncActionHandshake; + case: "handshake"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionHeartbeat heartbeat = 4; + */ + value: SyncStreamItem_SyncActionHeartbeat; + case: "heartbeat"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionDiffOperations diff_operations = 20; + */ + value: SyncStreamItem_SyncActionDiffOperations; + case: "diffOperations"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionSendOperations send_operations = 21; + */ + value: SyncStreamItem_SyncActionSendOperations; + case: "sendOperations"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionSendConfig send_config = 22; + */ + value: SyncStreamItem_SyncActionSendConfig; + case: "sendConfig"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionSetConfig set_config = 24; + */ + value: SyncStreamItem_SyncActionSetConfig; + case: "setConfig"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionListResources list_resources = 25; + */ + value: SyncStreamItem_SyncActionListResources; + case: "listResources"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionGetLog get_log = 26; + */ + value: SyncStreamItem_SyncActionGetLog; + case: "getLog"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionSendLogData send_log_data = 27; + */ + value: SyncStreamItem_SyncActionSendLogData; + case: "sendLogData"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionThrottle throttle = 1000; + */ + value: SyncStreamItem_SyncActionThrottle; + case: "throttle"; + } | { case: undefined; value?: undefined }; +}; + +/** + * Describes the message v1sync.SyncStreamItem. + * Use `create(SyncStreamItemSchema)` to create a new message. + */ +export const SyncStreamItemSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionHandshake + */ +export type SyncStreamItem_SyncActionHandshake = Message<"v1sync.SyncStreamItem.SyncActionHandshake"> & { + /** + * @generated from field: int64 protocol_version = 1; + */ + protocolVersion: bigint; + + /** + * @generated from field: v1.PublicKey public_key = 2; + */ + publicKey?: PublicKey; + + /** + * @generated from field: v1.SignedMessage instance_id = 3; + */ + instanceId?: SignedMessage; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionHandshake. + * Use `create(SyncStreamItem_SyncActionHandshakeSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionHandshakeSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 0); + +/** + * SyncActionHeartbeat is sent periodically to keep the connection alive. + * + * @generated from message v1sync.SyncStreamItem.SyncActionHeartbeat + */ +export type SyncStreamItem_SyncActionHeartbeat = Message<"v1sync.SyncStreamItem.SyncActionHeartbeat"> & { +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionHeartbeat. + * Use `create(SyncStreamItem_SyncActionHeartbeatSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionHeartbeatSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 1); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionSendConfig + */ +export type SyncStreamItem_SyncActionSendConfig = Message<"v1sync.SyncStreamItem.SyncActionSendConfig"> & { + /** + * @generated from field: v1sync.RemoteConfig config = 1; + */ + config?: RemoteConfig; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionSendConfig. + * Use `create(SyncStreamItem_SyncActionSendConfigSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionSendConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 2); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionSetConfig + */ +export type SyncStreamItem_SyncActionSetConfig = Message<"v1sync.SyncStreamItem.SyncActionSetConfig"> & { + /** + * @generated from field: repeated v1.Repo repos = 1; + */ + repos: Repo[]; + + /** + * @generated from field: repeated v1.Plan plans = 2; + */ + plans: Plan[]; + + /** + * @generated from field: repeated string repos_to_delete = 3; + */ + reposToDelete: string[]; + + /** + * @generated from field: repeated string plans_to_delete = 4; + */ + plansToDelete: string[]; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionSetConfig. + * Use `create(SyncStreamItem_SyncActionSetConfigSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionSetConfigSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 3); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionListResources + */ +export type SyncStreamItem_SyncActionListResources = Message<"v1sync.SyncStreamItem.SyncActionListResources"> & { + /** + * @generated from field: repeated v1sync.RepoMetadata repos = 1; + */ + repos: RepoMetadata[]; + + /** + * @generated from field: repeated v1sync.PlanMetadata plans = 2; + */ + plans: PlanMetadata[]; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionListResources. + * Use `create(SyncStreamItem_SyncActionListResourcesSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionListResourcesSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 4); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionConnectRepo + */ +export type SyncStreamItem_SyncActionConnectRepo = Message<"v1sync.SyncStreamItem.SyncActionConnectRepo"> & { + /** + * @generated from field: string repo_id = 1; + */ + repoId: string; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionConnectRepo. + * Use `create(SyncStreamItem_SyncActionConnectRepoSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionConnectRepoSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 5); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionDiffOperations + */ +export type SyncStreamItem_SyncActionDiffOperations = Message<"v1sync.SyncStreamItem.SyncActionDiffOperations"> & { + /** + * Client connects and sends a list of "have_operations" that exist in its log. + * have_operation_ids and have_operation_modnos are the operation IDs and modnos that the client has when zip'd pairwise. + * + * @generated from field: v1.OpSelector have_operations_selector = 1; + */ + haveOperationsSelector?: OpSelector; + + /** + * @generated from field: repeated int64 have_operation_ids = 2; + */ + haveOperationIds: bigint[]; + + /** + * @generated from field: repeated int64 have_operation_modnos = 3; + */ + haveOperationModnos: bigint[]; + + /** + * Server sends a list of "request_operations" for any operations that it doesn't have. + * + * @generated from field: repeated int64 request_operations = 4; + */ + requestOperations: bigint[]; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionDiffOperations. + * Use `create(SyncStreamItem_SyncActionDiffOperationsSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionDiffOperationsSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 6); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionSendOperations + */ +export type SyncStreamItem_SyncActionSendOperations = Message<"v1sync.SyncStreamItem.SyncActionSendOperations"> & { + /** + * @generated from field: v1.OperationEvent event = 1; + */ + event?: OperationEvent; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionSendOperations. + * Use `create(SyncStreamItem_SyncActionSendOperationsSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionSendOperationsSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 7); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionGetLog + */ +export type SyncStreamItem_SyncActionGetLog = Message<"v1sync.SyncStreamItem.SyncActionGetLog"> & { + /** + * @generated from field: string log_id = 1; + */ + logId: string; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionGetLog. + * Use `create(SyncStreamItem_SyncActionGetLogSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionGetLogSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 8); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionSendLogData + */ +export type SyncStreamItem_SyncActionSendLogData = Message<"v1sync.SyncStreamItem.SyncActionSendLogData"> & { + /** + * @generated from field: string log_id = 1; + */ + logId: string; + + /** + * Required only for first message in a log data stream. + * + * The operation ID of the operation that owns this log data. + * + * @generated from field: int64 owner_opid = 2; + */ + ownerOpid: bigint; + + /** + * Unix timestamp in seconds when the log data expires. + * + * @generated from field: int64 expiration_ts_unix = 3; + */ + expirationTsUnix: bigint; + + /** + * Can be sent repeatedly, must be terminated by a packet with size = 0. + * + * @generated from field: bytes chunk = 4; + */ + chunk: Uint8Array; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionSendLogData. + * Use `create(SyncStreamItem_SyncActionSendLogDataSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionSendLogDataSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 9); + +/** + * @generated from message v1sync.SyncStreamItem.SyncActionThrottle + */ +export type SyncStreamItem_SyncActionThrottle = Message<"v1sync.SyncStreamItem.SyncActionThrottle"> & { + /** + * @generated from field: int64 delay_ms = 1; + */ + delayMs: bigint; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionThrottle. + * Use `create(SyncStreamItem_SyncActionThrottleSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionThrottleSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 10); + +/** + * @generated from message v1sync.SyncStreamItem.SyncEstablishSharedSecret + */ +export type SyncStreamItem_SyncEstablishSharedSecret = Message<"v1sync.SyncStreamItem.SyncEstablishSharedSecret"> & { + /** + * a one-time-use ed25519 public key with a matching unshared private key. Used to perform a key exchange. + * See https://pkg.go.dev/crypto/ecdh#PrivateKey.ECDH . + * + * base64 encoded public key + * + * @generated from field: string ed25519 = 2 [json_name = "ed25519pub"]; + */ + ed25519: string; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncEstablishSharedSecret. + * Use `create(SyncStreamItem_SyncEstablishSharedSecretSchema)` to create a new message. + */ +export const SyncStreamItem_SyncEstablishSharedSecretSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 11, 11); + +/** + * @generated from enum v1sync.SyncStreamItem.RepoConnectionState + */ +export enum SyncStreamItem_RepoConnectionState { + /** + * @generated from enum value: CONNECTION_STATE_UNKNOWN = 0; + */ + CONNECTION_STATE_UNKNOWN = 0, + + /** + * queried, response not yet received. + * + * @generated from enum value: CONNECTION_STATE_PENDING = 1; + */ + CONNECTION_STATE_PENDING = 1, + + /** + * @generated from enum value: CONNECTION_STATE_CONNECTED = 2; + */ + CONNECTION_STATE_CONNECTED = 2, + + /** + * @generated from enum value: CONNECTION_STATE_UNAUTHORIZED = 3; + */ + CONNECTION_STATE_UNAUTHORIZED = 3, + + /** + * @generated from enum value: CONNECTION_STATE_NOT_FOUND = 4; + */ + CONNECTION_STATE_NOT_FOUND = 4, +} + +/** + * Describes the enum v1sync.SyncStreamItem.RepoConnectionState. + */ +export const SyncStreamItem_RepoConnectionStateSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_v1sync_syncservice, 11, 0); + /** * @generated from enum v1sync.ConnectionState */ @@ -406,6 +807,24 @@ export enum ConnectionState { export const ConnectionStateSchema: GenEnum = /*@__PURE__*/ enumDesc(file_v1sync_syncservice, 0); +/** + * BackrestSyncService provides methods to sync data between backrest instances. + * This service provides its own authentication and authorization. + * + * @generated from service v1sync.BackrestSyncService + */ +export const BackrestSyncService: GenService<{ + /** + * @generated from rpc v1sync.BackrestSyncService.Sync + */ + sync: { + methodKind: "bidi_streaming"; + input: typeof SyncStreamItemSchema; + output: typeof SyncStreamItemSchema; + }, +}> = /*@__PURE__*/ + serviceDesc(file_v1sync_syncservice, 0); + /** * BackrestSyncStateService provides methods to query the sync state of known hosts and clients. * This service should be served behind authentication and authorization. @@ -421,77 +840,6 @@ export const BackrestSyncStateService: GenService<{ input: typeof SyncStateStreamRequestSchema; output: typeof PeerStateSchema; }, -}> = /*@__PURE__*/ - serviceDesc(file_v1sync_syncservice, 0); - -/** - * @generated from service v1sync.SyncPeerService - */ -export const SyncPeerService: GenService<{ - /** - * Authenticate authenticates the peer with the sync service, must be called before any other methods. - * - * @generated from rpc v1sync.SyncPeerService.Authenticate - */ - authenticate: { - methodKind: "unary"; - input: typeof AuthenticateRequestSchema; - output: typeof EmptySchema; - }, - /** - * GetOperationMetadata returns a stream of sync items from the peer. - * - * @generated from rpc v1sync.SyncPeerService.GetOperationMetadata - */ - getOperationMetadata: { - methodKind: "unary"; - input: typeof OpSelectorSchema; - output: typeof GetOperationMetadataResponseSchema; - }, - /** - * @generated from rpc v1sync.SyncPeerService.SendOperations - */ - sendOperations: { - methodKind: "client_streaming"; - input: typeof OperationSchema; - output: typeof EmptySchema; - }, - /** - * @generated from rpc v1sync.SyncPeerService.GetLog - */ - getLog: { - methodKind: "server_streaming"; - input: typeof StringValueSchema; - output: typeof LogDataEntrySchema; - }, - /** - * Called everytime the set of resources available to the peer changes. - * - * @generated from rpc v1sync.SyncPeerService.SetAvailableResources - */ - setAvailableResources: { - methodKind: "unary"; - input: typeof SetAvailableResourcesRequestSchema; - output: typeof EmptySchema; - }, - /** - * Implements semantics for updating the remote config of the peer. - * - * @generated from rpc v1sync.SyncPeerService.SetConfig - */ - setConfig: { - methodKind: "unary"; - input: typeof SetConfigRequestSchema; - output: typeof EmptySchema; - }, - /** - * @generated from rpc v1sync.SyncPeerService.GetConfig - */ - getConfig: { - methodKind: "unary"; - input: typeof EmptySchema; - output: typeof RemoteConfigSchema; - }, }> = /*@__PURE__*/ serviceDesc(file_v1sync_syncservice, 1);