From ea4296a3d3eb73d71382e741e608be182404962c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Thu, 21 Sep 2023 13:43:10 +0200 Subject: [PATCH] Implement waiting for Connect My Computer node to join cluster * Add daemon.Service.ResolveClusterURI * Accept agents dir through command line flag tshd needs to know this out of band, so that when the Electron app tells it to watch for host UUID file for a specific cluster, the Electron app can send just the profile name of the cluster instead of an arbitrary path on the computer. * Implement WaitForConnectMyComputerNodeJoin in tsh daemon * wait: Use addEventListener instead of onabort * Make TshAbortController emit abort event only once This aligns it with a regular AbortController, which also emits the event only once. * Refactor how types are imported in tshd fixtures * Implement WaitForConnectMyComputerNodeJoin in Electron app * createAbortController: Add signal.aborted, use emitter.once * Improve wait function based on Deno implementation https://github.com/denoland/deno_std/blob/72d6e6641e3cd39ae69fba89a78feab354018ef0/async/delay.ts#L39 * Add a comment about the events package --- .../go/teleport/lib/teleterm/v1/service.pb.go | 886 ++++++++++-------- .../lib/teleterm/v1/service_grpc.pb.go | 49 + .../lib/teleterm/v1/service_grpc_pb.d.ts | 17 + .../lib/teleterm/v1/service_grpc_pb.js | 39 + .../teleport/lib/teleterm/v1/service_pb.d.ts | 45 + .../js/teleport/lib/teleterm/v1/service_pb.js | 325 +++++++ integration/proxy/teleterm_test.go | 1 + integration/teleterm_test.go | 72 ++ lib/config/fileconf.go | 2 +- lib/defaults/defaults.go | 4 + .../handler/handler_connectmycomputer.go | 24 + lib/teleterm/config.go | 6 + lib/teleterm/daemon/config.go | 19 + lib/teleterm/daemon/daemon.go | 47 +- lib/teleterm/daemon/daemon_test.go | 5 + .../connectmycomputer/connectmycomputer.go | 245 ++++- .../connectmycomputer_test.go | 185 ++++ lib/teleterm/teleterm.go | 1 + lib/teleterm/teleterm_test.go | 1 + lib/utils/utils.go | 9 +- proto/teleport/lib/teleterm/v1/service.proto | 15 +- tool/tsh/common/daemon.go | 1 + tool/tsh/common/tsh.go | 7 +- web/packages/build/package.json | 2 +- web/packages/shared/utils/wait.ts | 22 +- .../mainProcess/agentRunner/agentRunner.ts | 14 +- .../src/mainProcess/createAgentConfigFile.ts | 17 +- .../src/mainProcess/fixtures/mocks.ts | 4 + .../teleterm/src/mainProcess/mainProcess.ts | 12 + .../src/mainProcess/mainProcessClient.ts | 6 + .../src/mainProcess/runtimeSettings.ts | 4 + .../teleterm/src/mainProcess/types.ts | 1 + .../tshd/createAbortController.test.ts | 44 + .../services/tshd/createAbortController.ts | 18 +- .../src/services/tshd/createClient.ts | 37 +- .../services/tshd/createFileTransferStream.ts | 2 +- .../src/services/tshd/fixtures/mocks.ts | 84 +- .../teleterm/src/services/tshd/types.ts | 10 +- .../DocumentConnectMyComputerSetup.tsx | 59 +- .../DocumentConnectMyComputerStatus.tsx | 11 + .../connectMyComputerContext.test.tsx | 20 +- .../connectMyComputerContext.tsx | 100 +- .../connectMyComputerService.ts | 30 +- .../fileTransferClient/fileTransferService.ts | 8 + yarn.lock | 7 +- 45 files changed, 1945 insertions(+), 572 deletions(-) create mode 100644 web/packages/teleterm/src/services/tshd/createAbortController.test.ts diff --git a/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go index 8cb10057064d4..760ef118b326e 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/service.pb.go @@ -3063,6 +3063,100 @@ func (*DeleteConnectMyComputerTokenResponse) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_service_proto_rawDescGZIP(), []int{50} } +type WaitForConnectMyComputerNodeJoinRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RootClusterUri string `protobuf:"bytes,1,opt,name=root_cluster_uri,json=rootClusterUri,proto3" json:"root_cluster_uri,omitempty"` +} + +func (x *WaitForConnectMyComputerNodeJoinRequest) Reset() { + *x = WaitForConnectMyComputerNodeJoinRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WaitForConnectMyComputerNodeJoinRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitForConnectMyComputerNodeJoinRequest) ProtoMessage() {} + +func (x *WaitForConnectMyComputerNodeJoinRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[51] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WaitForConnectMyComputerNodeJoinRequest.ProtoReflect.Descriptor instead. +func (*WaitForConnectMyComputerNodeJoinRequest) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_service_proto_rawDescGZIP(), []int{51} +} + +func (x *WaitForConnectMyComputerNodeJoinRequest) GetRootClusterUri() string { + if x != nil { + return x.RootClusterUri + } + return "" +} + +type WaitForConnectMyComputerNodeJoinResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Server *Server `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` +} + +func (x *WaitForConnectMyComputerNodeJoinResponse) Reset() { + *x = WaitForConnectMyComputerNodeJoinResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WaitForConnectMyComputerNodeJoinResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WaitForConnectMyComputerNodeJoinResponse) ProtoMessage() {} + +func (x *WaitForConnectMyComputerNodeJoinResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WaitForConnectMyComputerNodeJoinResponse.ProtoReflect.Descriptor instead. +func (*WaitForConnectMyComputerNodeJoinResponse) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_v1_service_proto_rawDescGZIP(), []int{52} +} + +func (x *WaitForConnectMyComputerNodeJoinResponse) GetServer() *Server { + if x != nil { + return x.Server + } + return nil +} + // LoginPasswordlessRequestInit contains fields needed to init the stream request. type LoginPasswordlessRequest_LoginPasswordlessRequestInit struct { state protoimpl.MessageState @@ -3076,7 +3170,7 @@ type LoginPasswordlessRequest_LoginPasswordlessRequestInit struct { func (x *LoginPasswordlessRequest_LoginPasswordlessRequestInit) Reset() { *x = LoginPasswordlessRequest_LoginPasswordlessRequestInit{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[51] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3089,7 +3183,7 @@ func (x *LoginPasswordlessRequest_LoginPasswordlessRequestInit) String() string func (*LoginPasswordlessRequest_LoginPasswordlessRequestInit) ProtoMessage() {} func (x *LoginPasswordlessRequest_LoginPasswordlessRequestInit) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[51] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3125,7 +3219,7 @@ type LoginPasswordlessRequest_LoginPasswordlessPINResponse struct { func (x *LoginPasswordlessRequest_LoginPasswordlessPINResponse) Reset() { *x = LoginPasswordlessRequest_LoginPasswordlessPINResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[52] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3138,7 +3232,7 @@ func (x *LoginPasswordlessRequest_LoginPasswordlessPINResponse) String() string func (*LoginPasswordlessRequest_LoginPasswordlessPINResponse) ProtoMessage() {} func (x *LoginPasswordlessRequest_LoginPasswordlessPINResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[52] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3176,7 +3270,7 @@ type LoginPasswordlessRequest_LoginPasswordlessCredentialResponse struct { func (x *LoginPasswordlessRequest_LoginPasswordlessCredentialResponse) Reset() { *x = LoginPasswordlessRequest_LoginPasswordlessCredentialResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[53] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3189,7 +3283,7 @@ func (x *LoginPasswordlessRequest_LoginPasswordlessCredentialResponse) String() func (*LoginPasswordlessRequest_LoginPasswordlessCredentialResponse) ProtoMessage() {} func (x *LoginPasswordlessRequest_LoginPasswordlessCredentialResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[53] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3229,7 +3323,7 @@ type LoginRequest_LocalParams struct { func (x *LoginRequest_LocalParams) Reset() { *x = LoginRequest_LocalParams{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[54] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3242,7 +3336,7 @@ func (x *LoginRequest_LocalParams) String() string { func (*LoginRequest_LocalParams) ProtoMessage() {} func (x *LoginRequest_LocalParams) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[54] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3294,7 +3388,7 @@ type LoginRequest_SsoParams struct { func (x *LoginRequest_SsoParams) Reset() { *x = LoginRequest_SsoParams{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[55] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3307,7 +3401,7 @@ func (x *LoginRequest_SsoParams) String() string { func (*LoginRequest_SsoParams) ProtoMessage() {} func (x *LoginRequest_SsoParams) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[55] + mi := &file_teleport_lib_teleterm_v1_service_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3741,288 +3835,311 @@ var file_teleport_lib_teleterm_v1_service_proto_rawDesc = []byte{ 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x26, 0x0a, 0x24, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, - 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x97, - 0x01, 0x0a, 0x12, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x50, - 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, - 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x4d, 0x50, 0x54, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x41, - 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x4d, 0x50, - 0x54, 0x5f, 0x50, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x41, 0x53, 0x53, 0x57, - 0x4f, 0x52, 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x4d, 0x50, 0x54, 0x5f, 0x54, - 0x41, 0x50, 0x10, 0x02, 0x12, 0x22, 0x0a, 0x1e, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, - 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x4d, 0x50, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, - 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0x8a, 0x01, 0x0a, 0x15, 0x46, 0x69, 0x6c, - 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x23, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, - 0x46, 0x45, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, 0x20, 0x46, - 0x49, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x44, 0x49, 0x52, - 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x10, - 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, - 0x45, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, 0x4c, - 0x4f, 0x41, 0x44, 0x10, 0x02, 0x2a, 0xcd, 0x01, 0x0a, 0x1b, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, - 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x29, 0x48, 0x45, 0x41, 0x44, 0x4c, 0x45, 0x53, - 0x53, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x29, 0x0a, 0x25, 0x48, 0x45, 0x41, 0x44, 0x4c, 0x45, 0x53, 0x53, - 0x5f, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, - 0x28, 0x0a, 0x24, 0x48, 0x45, 0x41, 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x48, + 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, + 0x0a, 0x27, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x4a, 0x6f, + 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x6f, + 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x55, 0x72, 0x69, 0x22, 0x64, 0x0a, 0x28, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x4e, + 0x6f, 0x64, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x38, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2a, 0x97, 0x01, 0x0a, 0x12, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, + 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x4c, 0x45, 0x53, 0x53, + 0x5f, 0x50, 0x52, 0x4f, 0x4d, 0x50, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, + 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x4d, 0x50, 0x54, 0x5f, 0x50, 0x49, 0x4e, + 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x4c, 0x45, + 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x4d, 0x50, 0x54, 0x5f, 0x54, 0x41, 0x50, 0x10, 0x02, 0x12, + 0x22, 0x0a, 0x1e, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, + 0x50, 0x52, 0x4f, 0x4d, 0x50, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, + 0x4c, 0x10, 0x03, 0x2a, 0x8a, 0x01, 0x0a, 0x15, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x66, 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, + 0x23, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x44, + 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, 0x20, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x54, + 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, + 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x44, 0x49, + 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x02, + 0x2a, 0xcd, 0x01, 0x0a, 0x1b, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x2d, 0x0a, 0x29, 0x48, 0x45, 0x41, 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, + 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x29, 0x0a, 0x25, 0x48, 0x45, 0x41, 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, - 0x5f, 0x44, 0x45, 0x4e, 0x49, 0x45, 0x44, 0x10, 0x02, 0x12, 0x2a, 0x0a, 0x26, 0x48, 0x45, 0x41, - 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x52, 0x4f, - 0x56, 0x45, 0x44, 0x10, 0x03, 0x32, 0xb4, 0x1e, 0x0a, 0x0f, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xa0, 0x01, 0x0a, 0x1d, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3e, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x73, 0x68, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x73, 0x68, - 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x10, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, - 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x75, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x66, 0x43, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, - 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x66, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x28, 0x0a, 0x24, 0x48, 0x45, + 0x41, 0x44, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x45, 0x4e, 0x49, + 0x45, 0x44, 0x10, 0x02, 0x12, 0x2a, 0x0a, 0x26, 0x48, 0x45, 0x41, 0x44, 0x4c, 0x45, 0x53, 0x53, + 0x5f, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x03, + 0x32, 0xe0, 0x1f, 0x0a, 0x0f, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0xa0, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, + 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, + 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x6f, 0x6f, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, - 0x73, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, + 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x75, 0x0a, 0x10, 0x4c, 0x69, + 0x73, 0x74, 0x4c, 0x65, 0x61, 0x66, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x11, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x73, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, + 0x61, 0x66, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x73, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x7c, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x47, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x2e, 0x74, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, + 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x13, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x32, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x74, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x13, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, - 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x82, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x12, 0x82, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, - 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x6c, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0a, 0x41, 0x73, - 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, - 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4b, 0x75, 0x62, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x75, 0x62, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x75, 0x62, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x5c, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, - 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, - 0x68, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0a, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, + 0x6f, 0x6c, 0x65, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, + 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x73, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x0c, 0x4c, 0x69, 0x73, - 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x68, 0x0a, 0x0d, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x2e, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x47, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x86, 0x01, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x75, 0x62, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x75, 0x62, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, - 0x6e, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4c, 0x6f, 0x63, - 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4c, 0x6f, 0x63, 0x61, - 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, - 0x6b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5c, 0x0a, 0x0a, - 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x08, 0x47, 0x65, 0x74, + 0x4b, 0x75, 0x62, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x75, 0x62, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4b, + 0x75, 0x62, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0a, + 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x05, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x12, 0x26, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, - 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, + 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x68, 0x0a, 0x0d, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, - 0x74, 0x12, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, - 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, - 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, - 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x30, 0x01, 0x12, 0x6e, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x73, - 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, + 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xac, 0x01, 0x0a, 0x21, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, - 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x42, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, - 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x73, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, + 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x68, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x86, 0x01, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x75, 0x62, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x6e, 0x0a, 0x13, 0x53, 0x65, + 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, + 0x74, 0x12, 0x34, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x30, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x74, 0x68, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5c, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, + 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x58, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x26, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x80, 0x01, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x6c, 0x65, 0x73, 0x73, 0x12, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, + 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x27, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, + 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x9a, 0x01, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x66, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x30, 0x01, 0x12, + 0x6e, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, + 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0xac, 0x01, 0x0a, 0x21, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, + 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, + 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x65, 0x61, 0x64, 0x6c, + 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x9a, + 0x01, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x3c, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, + 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x52, - 0x6f, 0x6c, 0x65, 0x12, 0x3c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, + 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xa9, 0x01, 0x0a, 0x20, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x41, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, - 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, - 0x75, 0x74, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0xa9, 0x01, 0x0a, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, - 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x9d, 0x01, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x9d, 0x01, 0x0a, - 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, - 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3d, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xa9, 0x01, 0x0a, 0x20, 0x57, 0x61, 0x69, 0x74, + 0x46, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, + 0x75, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x41, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x54, 0x5a, 0x52, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, + 0x4e, 0x6f, 0x64, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, + 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4d, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, + 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -4038,7 +4155,7 @@ func file_teleport_lib_teleterm_v1_service_proto_rawDescGZIP() []byte { } var file_teleport_lib_teleterm_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_teleport_lib_teleterm_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 56) +var file_teleport_lib_teleterm_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 58) var file_teleport_lib_teleterm_v1_service_proto_goTypes = []interface{}{ (PasswordlessPrompt)(0), // 0: teleport.lib.teleterm.v1.PasswordlessPrompt (FileTransferDirection)(0), // 1: teleport.lib.teleterm.v1.FileTransferDirection @@ -4094,113 +4211,118 @@ var file_teleport_lib_teleterm_v1_service_proto_goTypes = []interface{}{ (*CreateConnectMyComputerNodeTokenResponse)(nil), // 51: teleport.lib.teleterm.v1.CreateConnectMyComputerNodeTokenResponse (*DeleteConnectMyComputerTokenRequest)(nil), // 52: teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenRequest (*DeleteConnectMyComputerTokenResponse)(nil), // 53: teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenResponse - (*LoginPasswordlessRequest_LoginPasswordlessRequestInit)(nil), // 54: teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessRequestInit - (*LoginPasswordlessRequest_LoginPasswordlessPINResponse)(nil), // 55: teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessPINResponse - (*LoginPasswordlessRequest_LoginPasswordlessCredentialResponse)(nil), // 56: teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessCredentialResponse - (*LoginRequest_LocalParams)(nil), // 57: teleport.lib.teleterm.v1.LoginRequest.LocalParams - (*LoginRequest_SsoParams)(nil), // 58: teleport.lib.teleterm.v1.LoginRequest.SsoParams - (*AccessRequest)(nil), // 59: teleport.lib.teleterm.v1.AccessRequest - (*ResourceID)(nil), // 60: teleport.lib.teleterm.v1.ResourceID - (*Cluster)(nil), // 61: teleport.lib.teleterm.v1.Cluster - (*Gateway)(nil), // 62: teleport.lib.teleterm.v1.Gateway - (*Server)(nil), // 63: teleport.lib.teleterm.v1.Server - (*Database)(nil), // 64: teleport.lib.teleterm.v1.Database - (*Kube)(nil), // 65: teleport.lib.teleterm.v1.Kube - (*Label)(nil), // 66: teleport.lib.teleterm.v1.Label - (*ReportUsageEventRequest)(nil), // 67: teleport.lib.teleterm.v1.ReportUsageEventRequest - (*AuthSettings)(nil), // 68: teleport.lib.teleterm.v1.AuthSettings + (*WaitForConnectMyComputerNodeJoinRequest)(nil), // 54: teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest + (*WaitForConnectMyComputerNodeJoinResponse)(nil), // 55: teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse + (*LoginPasswordlessRequest_LoginPasswordlessRequestInit)(nil), // 56: teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessRequestInit + (*LoginPasswordlessRequest_LoginPasswordlessPINResponse)(nil), // 57: teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessPINResponse + (*LoginPasswordlessRequest_LoginPasswordlessCredentialResponse)(nil), // 58: teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessCredentialResponse + (*LoginRequest_LocalParams)(nil), // 59: teleport.lib.teleterm.v1.LoginRequest.LocalParams + (*LoginRequest_SsoParams)(nil), // 60: teleport.lib.teleterm.v1.LoginRequest.SsoParams + (*AccessRequest)(nil), // 61: teleport.lib.teleterm.v1.AccessRequest + (*ResourceID)(nil), // 62: teleport.lib.teleterm.v1.ResourceID + (*Cluster)(nil), // 63: teleport.lib.teleterm.v1.Cluster + (*Gateway)(nil), // 64: teleport.lib.teleterm.v1.Gateway + (*Server)(nil), // 65: teleport.lib.teleterm.v1.Server + (*Database)(nil), // 66: teleport.lib.teleterm.v1.Database + (*Kube)(nil), // 67: teleport.lib.teleterm.v1.Kube + (*Label)(nil), // 68: teleport.lib.teleterm.v1.Label + (*ReportUsageEventRequest)(nil), // 69: teleport.lib.teleterm.v1.ReportUsageEventRequest + (*AuthSettings)(nil), // 70: teleport.lib.teleterm.v1.AuthSettings } var file_teleport_lib_teleterm_v1_service_proto_depIdxs = []int32{ - 59, // 0: teleport.lib.teleterm.v1.GetAccessRequestResponse.request:type_name -> teleport.lib.teleterm.v1.AccessRequest - 59, // 1: teleport.lib.teleterm.v1.GetAccessRequestsResponse.requests:type_name -> teleport.lib.teleterm.v1.AccessRequest - 60, // 2: teleport.lib.teleterm.v1.CreateAccessRequestRequest.resource_ids:type_name -> teleport.lib.teleterm.v1.ResourceID - 59, // 3: teleport.lib.teleterm.v1.CreateAccessRequestResponse.request:type_name -> teleport.lib.teleterm.v1.AccessRequest - 60, // 4: teleport.lib.teleterm.v1.GetRequestableRolesRequest.resource_ids:type_name -> teleport.lib.teleterm.v1.ResourceID - 59, // 5: teleport.lib.teleterm.v1.ReviewAccessRequestResponse.request:type_name -> teleport.lib.teleterm.v1.AccessRequest + 61, // 0: teleport.lib.teleterm.v1.GetAccessRequestResponse.request:type_name -> teleport.lib.teleterm.v1.AccessRequest + 61, // 1: teleport.lib.teleterm.v1.GetAccessRequestsResponse.requests:type_name -> teleport.lib.teleterm.v1.AccessRequest + 62, // 2: teleport.lib.teleterm.v1.CreateAccessRequestRequest.resource_ids:type_name -> teleport.lib.teleterm.v1.ResourceID + 61, // 3: teleport.lib.teleterm.v1.CreateAccessRequestResponse.request:type_name -> teleport.lib.teleterm.v1.AccessRequest + 62, // 4: teleport.lib.teleterm.v1.GetRequestableRolesRequest.resource_ids:type_name -> teleport.lib.teleterm.v1.ResourceID + 61, // 5: teleport.lib.teleterm.v1.ReviewAccessRequestResponse.request:type_name -> teleport.lib.teleterm.v1.AccessRequest 0, // 6: teleport.lib.teleterm.v1.LoginPasswordlessResponse.prompt:type_name -> teleport.lib.teleterm.v1.PasswordlessPrompt 19, // 7: teleport.lib.teleterm.v1.LoginPasswordlessResponse.credentials:type_name -> teleport.lib.teleterm.v1.CredentialInfo - 54, // 8: teleport.lib.teleterm.v1.LoginPasswordlessRequest.init:type_name -> teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessRequestInit - 55, // 9: teleport.lib.teleterm.v1.LoginPasswordlessRequest.pin:type_name -> teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessPINResponse - 56, // 10: teleport.lib.teleterm.v1.LoginPasswordlessRequest.credential:type_name -> teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessCredentialResponse + 56, // 8: teleport.lib.teleterm.v1.LoginPasswordlessRequest.init:type_name -> teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessRequestInit + 57, // 9: teleport.lib.teleterm.v1.LoginPasswordlessRequest.pin:type_name -> teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessPINResponse + 58, // 10: teleport.lib.teleterm.v1.LoginPasswordlessRequest.credential:type_name -> teleport.lib.teleterm.v1.LoginPasswordlessRequest.LoginPasswordlessCredentialResponse 1, // 11: teleport.lib.teleterm.v1.FileTransferRequest.direction:type_name -> teleport.lib.teleterm.v1.FileTransferDirection - 57, // 12: teleport.lib.teleterm.v1.LoginRequest.local:type_name -> teleport.lib.teleterm.v1.LoginRequest.LocalParams - 58, // 13: teleport.lib.teleterm.v1.LoginRequest.sso:type_name -> teleport.lib.teleterm.v1.LoginRequest.SsoParams - 61, // 14: teleport.lib.teleterm.v1.ListClustersResponse.clusters:type_name -> teleport.lib.teleterm.v1.Cluster - 62, // 15: teleport.lib.teleterm.v1.ListGatewaysResponse.gateways:type_name -> teleport.lib.teleterm.v1.Gateway - 63, // 16: teleport.lib.teleterm.v1.GetServersResponse.agents:type_name -> teleport.lib.teleterm.v1.Server - 64, // 17: teleport.lib.teleterm.v1.GetDatabasesResponse.agents:type_name -> teleport.lib.teleterm.v1.Database - 65, // 18: teleport.lib.teleterm.v1.GetKubesResponse.agents:type_name -> teleport.lib.teleterm.v1.Kube + 59, // 12: teleport.lib.teleterm.v1.LoginRequest.local:type_name -> teleport.lib.teleterm.v1.LoginRequest.LocalParams + 60, // 13: teleport.lib.teleterm.v1.LoginRequest.sso:type_name -> teleport.lib.teleterm.v1.LoginRequest.SsoParams + 63, // 14: teleport.lib.teleterm.v1.ListClustersResponse.clusters:type_name -> teleport.lib.teleterm.v1.Cluster + 64, // 15: teleport.lib.teleterm.v1.ListGatewaysResponse.gateways:type_name -> teleport.lib.teleterm.v1.Gateway + 65, // 16: teleport.lib.teleterm.v1.GetServersResponse.agents:type_name -> teleport.lib.teleterm.v1.Server + 66, // 17: teleport.lib.teleterm.v1.GetDatabasesResponse.agents:type_name -> teleport.lib.teleterm.v1.Database + 67, // 18: teleport.lib.teleterm.v1.GetKubesResponse.agents:type_name -> teleport.lib.teleterm.v1.Kube 2, // 19: teleport.lib.teleterm.v1.UpdateHeadlessAuthenticationStateRequest.state:type_name -> teleport.lib.teleterm.v1.HeadlessAuthenticationState - 66, // 20: teleport.lib.teleterm.v1.CreateConnectMyComputerNodeTokenResponse.labels:type_name -> teleport.lib.teleterm.v1.Label - 44, // 21: teleport.lib.teleterm.v1.TerminalService.UpdateTshdEventsServerAddress:input_type -> teleport.lib.teleterm.v1.UpdateTshdEventsServerAddressRequest - 26, // 22: teleport.lib.teleterm.v1.TerminalService.ListRootClusters:input_type -> teleport.lib.teleterm.v1.ListClustersRequest - 29, // 23: teleport.lib.teleterm.v1.TerminalService.ListLeafClusters:input_type -> teleport.lib.teleterm.v1.ListLeafClustersRequest - 28, // 24: teleport.lib.teleterm.v1.TerminalService.GetDatabases:input_type -> teleport.lib.teleterm.v1.GetDatabasesRequest - 30, // 25: teleport.lib.teleterm.v1.TerminalService.ListDatabaseUsers:input_type -> teleport.lib.teleterm.v1.ListDatabaseUsersRequest - 38, // 26: teleport.lib.teleterm.v1.TerminalService.GetServers:input_type -> teleport.lib.teleterm.v1.GetServersRequest - 8, // 27: teleport.lib.teleterm.v1.TerminalService.GetAccessRequests:input_type -> teleport.lib.teleterm.v1.GetAccessRequestsRequest - 7, // 28: teleport.lib.teleterm.v1.TerminalService.GetAccessRequest:input_type -> teleport.lib.teleterm.v1.GetAccessRequestRequest - 11, // 29: teleport.lib.teleterm.v1.TerminalService.DeleteAccessRequest:input_type -> teleport.lib.teleterm.v1.DeleteAccessRequestRequest - 12, // 30: teleport.lib.teleterm.v1.TerminalService.CreateAccessRequest:input_type -> teleport.lib.teleterm.v1.CreateAccessRequestRequest - 17, // 31: teleport.lib.teleterm.v1.TerminalService.ReviewAccessRequest:input_type -> teleport.lib.teleterm.v1.ReviewAccessRequestRequest - 15, // 32: teleport.lib.teleterm.v1.TerminalService.GetRequestableRoles:input_type -> teleport.lib.teleterm.v1.GetRequestableRolesRequest - 14, // 33: teleport.lib.teleterm.v1.TerminalService.AssumeRole:input_type -> teleport.lib.teleterm.v1.AssumeRoleRequest - 41, // 34: teleport.lib.teleterm.v1.TerminalService.GetKubes:input_type -> teleport.lib.teleterm.v1.GetKubesRequest - 25, // 35: teleport.lib.teleterm.v1.TerminalService.AddCluster:input_type -> teleport.lib.teleterm.v1.AddClusterRequest - 4, // 36: teleport.lib.teleterm.v1.TerminalService.RemoveCluster:input_type -> teleport.lib.teleterm.v1.RemoveClusterRequest - 33, // 37: teleport.lib.teleterm.v1.TerminalService.ListGateways:input_type -> teleport.lib.teleterm.v1.ListGatewaysRequest - 32, // 38: teleport.lib.teleterm.v1.TerminalService.CreateGateway:input_type -> teleport.lib.teleterm.v1.CreateGatewayRequest - 35, // 39: teleport.lib.teleterm.v1.TerminalService.RemoveGateway:input_type -> teleport.lib.teleterm.v1.RemoveGatewayRequest - 36, // 40: teleport.lib.teleterm.v1.TerminalService.SetGatewayTargetSubresourceName:input_type -> teleport.lib.teleterm.v1.SetGatewayTargetSubresourceNameRequest - 37, // 41: teleport.lib.teleterm.v1.TerminalService.SetGatewayLocalPort:input_type -> teleport.lib.teleterm.v1.SetGatewayLocalPortRequest - 43, // 42: teleport.lib.teleterm.v1.TerminalService.GetAuthSettings:input_type -> teleport.lib.teleterm.v1.GetAuthSettingsRequest - 5, // 43: teleport.lib.teleterm.v1.TerminalService.GetCluster:input_type -> teleport.lib.teleterm.v1.GetClusterRequest - 24, // 44: teleport.lib.teleterm.v1.TerminalService.Login:input_type -> teleport.lib.teleterm.v1.LoginRequest - 21, // 45: teleport.lib.teleterm.v1.TerminalService.LoginPasswordless:input_type -> teleport.lib.teleterm.v1.LoginPasswordlessRequest - 6, // 46: teleport.lib.teleterm.v1.TerminalService.Logout:input_type -> teleport.lib.teleterm.v1.LogoutRequest - 22, // 47: teleport.lib.teleterm.v1.TerminalService.TransferFile:input_type -> teleport.lib.teleterm.v1.FileTransferRequest - 67, // 48: teleport.lib.teleterm.v1.TerminalService.ReportUsageEvent:input_type -> teleport.lib.teleterm.v1.ReportUsageEventRequest - 46, // 49: teleport.lib.teleterm.v1.TerminalService.UpdateHeadlessAuthenticationState:input_type -> teleport.lib.teleterm.v1.UpdateHeadlessAuthenticationStateRequest - 48, // 50: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerRole:input_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerRoleRequest - 50, // 51: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerNodeToken:input_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerNodeTokenRequest - 52, // 52: teleport.lib.teleterm.v1.TerminalService.DeleteConnectMyComputerToken:input_type -> teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenRequest - 45, // 53: teleport.lib.teleterm.v1.TerminalService.UpdateTshdEventsServerAddress:output_type -> teleport.lib.teleterm.v1.UpdateTshdEventsServerAddressResponse - 27, // 54: teleport.lib.teleterm.v1.TerminalService.ListRootClusters:output_type -> teleport.lib.teleterm.v1.ListClustersResponse - 27, // 55: teleport.lib.teleterm.v1.TerminalService.ListLeafClusters:output_type -> teleport.lib.teleterm.v1.ListClustersResponse - 40, // 56: teleport.lib.teleterm.v1.TerminalService.GetDatabases:output_type -> teleport.lib.teleterm.v1.GetDatabasesResponse - 31, // 57: teleport.lib.teleterm.v1.TerminalService.ListDatabaseUsers:output_type -> teleport.lib.teleterm.v1.ListDatabaseUsersResponse - 39, // 58: teleport.lib.teleterm.v1.TerminalService.GetServers:output_type -> teleport.lib.teleterm.v1.GetServersResponse - 10, // 59: teleport.lib.teleterm.v1.TerminalService.GetAccessRequests:output_type -> teleport.lib.teleterm.v1.GetAccessRequestsResponse - 9, // 60: teleport.lib.teleterm.v1.TerminalService.GetAccessRequest:output_type -> teleport.lib.teleterm.v1.GetAccessRequestResponse - 3, // 61: teleport.lib.teleterm.v1.TerminalService.DeleteAccessRequest:output_type -> teleport.lib.teleterm.v1.EmptyResponse - 13, // 62: teleport.lib.teleterm.v1.TerminalService.CreateAccessRequest:output_type -> teleport.lib.teleterm.v1.CreateAccessRequestResponse - 18, // 63: teleport.lib.teleterm.v1.TerminalService.ReviewAccessRequest:output_type -> teleport.lib.teleterm.v1.ReviewAccessRequestResponse - 16, // 64: teleport.lib.teleterm.v1.TerminalService.GetRequestableRoles:output_type -> teleport.lib.teleterm.v1.GetRequestableRolesResponse - 3, // 65: teleport.lib.teleterm.v1.TerminalService.AssumeRole:output_type -> teleport.lib.teleterm.v1.EmptyResponse - 42, // 66: teleport.lib.teleterm.v1.TerminalService.GetKubes:output_type -> teleport.lib.teleterm.v1.GetKubesResponse - 61, // 67: teleport.lib.teleterm.v1.TerminalService.AddCluster:output_type -> teleport.lib.teleterm.v1.Cluster - 3, // 68: teleport.lib.teleterm.v1.TerminalService.RemoveCluster:output_type -> teleport.lib.teleterm.v1.EmptyResponse - 34, // 69: teleport.lib.teleterm.v1.TerminalService.ListGateways:output_type -> teleport.lib.teleterm.v1.ListGatewaysResponse - 62, // 70: teleport.lib.teleterm.v1.TerminalService.CreateGateway:output_type -> teleport.lib.teleterm.v1.Gateway - 3, // 71: teleport.lib.teleterm.v1.TerminalService.RemoveGateway:output_type -> teleport.lib.teleterm.v1.EmptyResponse - 62, // 72: teleport.lib.teleterm.v1.TerminalService.SetGatewayTargetSubresourceName:output_type -> teleport.lib.teleterm.v1.Gateway - 62, // 73: teleport.lib.teleterm.v1.TerminalService.SetGatewayLocalPort:output_type -> teleport.lib.teleterm.v1.Gateway - 68, // 74: teleport.lib.teleterm.v1.TerminalService.GetAuthSettings:output_type -> teleport.lib.teleterm.v1.AuthSettings - 61, // 75: teleport.lib.teleterm.v1.TerminalService.GetCluster:output_type -> teleport.lib.teleterm.v1.Cluster - 3, // 76: teleport.lib.teleterm.v1.TerminalService.Login:output_type -> teleport.lib.teleterm.v1.EmptyResponse - 20, // 77: teleport.lib.teleterm.v1.TerminalService.LoginPasswordless:output_type -> teleport.lib.teleterm.v1.LoginPasswordlessResponse - 3, // 78: teleport.lib.teleterm.v1.TerminalService.Logout:output_type -> teleport.lib.teleterm.v1.EmptyResponse - 23, // 79: teleport.lib.teleterm.v1.TerminalService.TransferFile:output_type -> teleport.lib.teleterm.v1.FileTransferProgress - 3, // 80: teleport.lib.teleterm.v1.TerminalService.ReportUsageEvent:output_type -> teleport.lib.teleterm.v1.EmptyResponse - 47, // 81: teleport.lib.teleterm.v1.TerminalService.UpdateHeadlessAuthenticationState:output_type -> teleport.lib.teleterm.v1.UpdateHeadlessAuthenticationStateResponse - 49, // 82: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerRole:output_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerRoleResponse - 51, // 83: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerNodeToken:output_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerNodeTokenResponse - 53, // 84: teleport.lib.teleterm.v1.TerminalService.DeleteConnectMyComputerToken:output_type -> teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenResponse - 53, // [53:85] is the sub-list for method output_type - 21, // [21:53] is the sub-list for method input_type - 21, // [21:21] is the sub-list for extension type_name - 21, // [21:21] is the sub-list for extension extendee - 0, // [0:21] is the sub-list for field type_name + 68, // 20: teleport.lib.teleterm.v1.CreateConnectMyComputerNodeTokenResponse.labels:type_name -> teleport.lib.teleterm.v1.Label + 65, // 21: teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.server:type_name -> teleport.lib.teleterm.v1.Server + 44, // 22: teleport.lib.teleterm.v1.TerminalService.UpdateTshdEventsServerAddress:input_type -> teleport.lib.teleterm.v1.UpdateTshdEventsServerAddressRequest + 26, // 23: teleport.lib.teleterm.v1.TerminalService.ListRootClusters:input_type -> teleport.lib.teleterm.v1.ListClustersRequest + 29, // 24: teleport.lib.teleterm.v1.TerminalService.ListLeafClusters:input_type -> teleport.lib.teleterm.v1.ListLeafClustersRequest + 28, // 25: teleport.lib.teleterm.v1.TerminalService.GetDatabases:input_type -> teleport.lib.teleterm.v1.GetDatabasesRequest + 30, // 26: teleport.lib.teleterm.v1.TerminalService.ListDatabaseUsers:input_type -> teleport.lib.teleterm.v1.ListDatabaseUsersRequest + 38, // 27: teleport.lib.teleterm.v1.TerminalService.GetServers:input_type -> teleport.lib.teleterm.v1.GetServersRequest + 8, // 28: teleport.lib.teleterm.v1.TerminalService.GetAccessRequests:input_type -> teleport.lib.teleterm.v1.GetAccessRequestsRequest + 7, // 29: teleport.lib.teleterm.v1.TerminalService.GetAccessRequest:input_type -> teleport.lib.teleterm.v1.GetAccessRequestRequest + 11, // 30: teleport.lib.teleterm.v1.TerminalService.DeleteAccessRequest:input_type -> teleport.lib.teleterm.v1.DeleteAccessRequestRequest + 12, // 31: teleport.lib.teleterm.v1.TerminalService.CreateAccessRequest:input_type -> teleport.lib.teleterm.v1.CreateAccessRequestRequest + 17, // 32: teleport.lib.teleterm.v1.TerminalService.ReviewAccessRequest:input_type -> teleport.lib.teleterm.v1.ReviewAccessRequestRequest + 15, // 33: teleport.lib.teleterm.v1.TerminalService.GetRequestableRoles:input_type -> teleport.lib.teleterm.v1.GetRequestableRolesRequest + 14, // 34: teleport.lib.teleterm.v1.TerminalService.AssumeRole:input_type -> teleport.lib.teleterm.v1.AssumeRoleRequest + 41, // 35: teleport.lib.teleterm.v1.TerminalService.GetKubes:input_type -> teleport.lib.teleterm.v1.GetKubesRequest + 25, // 36: teleport.lib.teleterm.v1.TerminalService.AddCluster:input_type -> teleport.lib.teleterm.v1.AddClusterRequest + 4, // 37: teleport.lib.teleterm.v1.TerminalService.RemoveCluster:input_type -> teleport.lib.teleterm.v1.RemoveClusterRequest + 33, // 38: teleport.lib.teleterm.v1.TerminalService.ListGateways:input_type -> teleport.lib.teleterm.v1.ListGatewaysRequest + 32, // 39: teleport.lib.teleterm.v1.TerminalService.CreateGateway:input_type -> teleport.lib.teleterm.v1.CreateGatewayRequest + 35, // 40: teleport.lib.teleterm.v1.TerminalService.RemoveGateway:input_type -> teleport.lib.teleterm.v1.RemoveGatewayRequest + 36, // 41: teleport.lib.teleterm.v1.TerminalService.SetGatewayTargetSubresourceName:input_type -> teleport.lib.teleterm.v1.SetGatewayTargetSubresourceNameRequest + 37, // 42: teleport.lib.teleterm.v1.TerminalService.SetGatewayLocalPort:input_type -> teleport.lib.teleterm.v1.SetGatewayLocalPortRequest + 43, // 43: teleport.lib.teleterm.v1.TerminalService.GetAuthSettings:input_type -> teleport.lib.teleterm.v1.GetAuthSettingsRequest + 5, // 44: teleport.lib.teleterm.v1.TerminalService.GetCluster:input_type -> teleport.lib.teleterm.v1.GetClusterRequest + 24, // 45: teleport.lib.teleterm.v1.TerminalService.Login:input_type -> teleport.lib.teleterm.v1.LoginRequest + 21, // 46: teleport.lib.teleterm.v1.TerminalService.LoginPasswordless:input_type -> teleport.lib.teleterm.v1.LoginPasswordlessRequest + 6, // 47: teleport.lib.teleterm.v1.TerminalService.Logout:input_type -> teleport.lib.teleterm.v1.LogoutRequest + 22, // 48: teleport.lib.teleterm.v1.TerminalService.TransferFile:input_type -> teleport.lib.teleterm.v1.FileTransferRequest + 69, // 49: teleport.lib.teleterm.v1.TerminalService.ReportUsageEvent:input_type -> teleport.lib.teleterm.v1.ReportUsageEventRequest + 46, // 50: teleport.lib.teleterm.v1.TerminalService.UpdateHeadlessAuthenticationState:input_type -> teleport.lib.teleterm.v1.UpdateHeadlessAuthenticationStateRequest + 48, // 51: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerRole:input_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerRoleRequest + 50, // 52: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerNodeToken:input_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerNodeTokenRequest + 52, // 53: teleport.lib.teleterm.v1.TerminalService.DeleteConnectMyComputerToken:input_type -> teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenRequest + 54, // 54: teleport.lib.teleterm.v1.TerminalService.WaitForConnectMyComputerNodeJoin:input_type -> teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest + 45, // 55: teleport.lib.teleterm.v1.TerminalService.UpdateTshdEventsServerAddress:output_type -> teleport.lib.teleterm.v1.UpdateTshdEventsServerAddressResponse + 27, // 56: teleport.lib.teleterm.v1.TerminalService.ListRootClusters:output_type -> teleport.lib.teleterm.v1.ListClustersResponse + 27, // 57: teleport.lib.teleterm.v1.TerminalService.ListLeafClusters:output_type -> teleport.lib.teleterm.v1.ListClustersResponse + 40, // 58: teleport.lib.teleterm.v1.TerminalService.GetDatabases:output_type -> teleport.lib.teleterm.v1.GetDatabasesResponse + 31, // 59: teleport.lib.teleterm.v1.TerminalService.ListDatabaseUsers:output_type -> teleport.lib.teleterm.v1.ListDatabaseUsersResponse + 39, // 60: teleport.lib.teleterm.v1.TerminalService.GetServers:output_type -> teleport.lib.teleterm.v1.GetServersResponse + 10, // 61: teleport.lib.teleterm.v1.TerminalService.GetAccessRequests:output_type -> teleport.lib.teleterm.v1.GetAccessRequestsResponse + 9, // 62: teleport.lib.teleterm.v1.TerminalService.GetAccessRequest:output_type -> teleport.lib.teleterm.v1.GetAccessRequestResponse + 3, // 63: teleport.lib.teleterm.v1.TerminalService.DeleteAccessRequest:output_type -> teleport.lib.teleterm.v1.EmptyResponse + 13, // 64: teleport.lib.teleterm.v1.TerminalService.CreateAccessRequest:output_type -> teleport.lib.teleterm.v1.CreateAccessRequestResponse + 18, // 65: teleport.lib.teleterm.v1.TerminalService.ReviewAccessRequest:output_type -> teleport.lib.teleterm.v1.ReviewAccessRequestResponse + 16, // 66: teleport.lib.teleterm.v1.TerminalService.GetRequestableRoles:output_type -> teleport.lib.teleterm.v1.GetRequestableRolesResponse + 3, // 67: teleport.lib.teleterm.v1.TerminalService.AssumeRole:output_type -> teleport.lib.teleterm.v1.EmptyResponse + 42, // 68: teleport.lib.teleterm.v1.TerminalService.GetKubes:output_type -> teleport.lib.teleterm.v1.GetKubesResponse + 63, // 69: teleport.lib.teleterm.v1.TerminalService.AddCluster:output_type -> teleport.lib.teleterm.v1.Cluster + 3, // 70: teleport.lib.teleterm.v1.TerminalService.RemoveCluster:output_type -> teleport.lib.teleterm.v1.EmptyResponse + 34, // 71: teleport.lib.teleterm.v1.TerminalService.ListGateways:output_type -> teleport.lib.teleterm.v1.ListGatewaysResponse + 64, // 72: teleport.lib.teleterm.v1.TerminalService.CreateGateway:output_type -> teleport.lib.teleterm.v1.Gateway + 3, // 73: teleport.lib.teleterm.v1.TerminalService.RemoveGateway:output_type -> teleport.lib.teleterm.v1.EmptyResponse + 64, // 74: teleport.lib.teleterm.v1.TerminalService.SetGatewayTargetSubresourceName:output_type -> teleport.lib.teleterm.v1.Gateway + 64, // 75: teleport.lib.teleterm.v1.TerminalService.SetGatewayLocalPort:output_type -> teleport.lib.teleterm.v1.Gateway + 70, // 76: teleport.lib.teleterm.v1.TerminalService.GetAuthSettings:output_type -> teleport.lib.teleterm.v1.AuthSettings + 63, // 77: teleport.lib.teleterm.v1.TerminalService.GetCluster:output_type -> teleport.lib.teleterm.v1.Cluster + 3, // 78: teleport.lib.teleterm.v1.TerminalService.Login:output_type -> teleport.lib.teleterm.v1.EmptyResponse + 20, // 79: teleport.lib.teleterm.v1.TerminalService.LoginPasswordless:output_type -> teleport.lib.teleterm.v1.LoginPasswordlessResponse + 3, // 80: teleport.lib.teleterm.v1.TerminalService.Logout:output_type -> teleport.lib.teleterm.v1.EmptyResponse + 23, // 81: teleport.lib.teleterm.v1.TerminalService.TransferFile:output_type -> teleport.lib.teleterm.v1.FileTransferProgress + 3, // 82: teleport.lib.teleterm.v1.TerminalService.ReportUsageEvent:output_type -> teleport.lib.teleterm.v1.EmptyResponse + 47, // 83: teleport.lib.teleterm.v1.TerminalService.UpdateHeadlessAuthenticationState:output_type -> teleport.lib.teleterm.v1.UpdateHeadlessAuthenticationStateResponse + 49, // 84: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerRole:output_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerRoleResponse + 51, // 85: teleport.lib.teleterm.v1.TerminalService.CreateConnectMyComputerNodeToken:output_type -> teleport.lib.teleterm.v1.CreateConnectMyComputerNodeTokenResponse + 53, // 86: teleport.lib.teleterm.v1.TerminalService.DeleteConnectMyComputerToken:output_type -> teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenResponse + 55, // 87: teleport.lib.teleterm.v1.TerminalService.WaitForConnectMyComputerNodeJoin:output_type -> teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse + 55, // [55:88] is the sub-list for method output_type + 22, // [22:55] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_teleport_lib_teleterm_v1_service_proto_init() } @@ -4831,7 +4953,7 @@ func file_teleport_lib_teleterm_v1_service_proto_init() { } } file_teleport_lib_teleterm_v1_service_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginPasswordlessRequest_LoginPasswordlessRequestInit); i { + switch v := v.(*WaitForConnectMyComputerNodeJoinRequest); i { case 0: return &v.state case 1: @@ -4843,7 +4965,7 @@ func file_teleport_lib_teleterm_v1_service_proto_init() { } } file_teleport_lib_teleterm_v1_service_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginPasswordlessRequest_LoginPasswordlessPINResponse); i { + switch v := v.(*WaitForConnectMyComputerNodeJoinResponse); i { case 0: return &v.state case 1: @@ -4855,7 +4977,7 @@ func file_teleport_lib_teleterm_v1_service_proto_init() { } } file_teleport_lib_teleterm_v1_service_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginPasswordlessRequest_LoginPasswordlessCredentialResponse); i { + switch v := v.(*LoginPasswordlessRequest_LoginPasswordlessRequestInit); i { case 0: return &v.state case 1: @@ -4867,7 +4989,7 @@ func file_teleport_lib_teleterm_v1_service_proto_init() { } } file_teleport_lib_teleterm_v1_service_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginRequest_LocalParams); i { + switch v := v.(*LoginPasswordlessRequest_LoginPasswordlessPINResponse); i { case 0: return &v.state case 1: @@ -4879,6 +5001,30 @@ func file_teleport_lib_teleterm_v1_service_proto_init() { } } file_teleport_lib_teleterm_v1_service_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LoginPasswordlessRequest_LoginPasswordlessCredentialResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_lib_teleterm_v1_service_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LoginRequest_LocalParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_lib_teleterm_v1_service_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LoginRequest_SsoParams); i { case 0: return &v.state @@ -4906,7 +5052,7 @@ func file_teleport_lib_teleterm_v1_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_lib_teleterm_v1_service_proto_rawDesc, NumEnums: 3, - NumMessages: 56, + NumMessages: 58, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go index cd3904f4319b3..e0e155ab39496 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/service_grpc.pb.go @@ -65,6 +65,7 @@ const ( TerminalService_CreateConnectMyComputerRole_FullMethodName = "/teleport.lib.teleterm.v1.TerminalService/CreateConnectMyComputerRole" TerminalService_CreateConnectMyComputerNodeToken_FullMethodName = "/teleport.lib.teleterm.v1.TerminalService/CreateConnectMyComputerNodeToken" TerminalService_DeleteConnectMyComputerToken_FullMethodName = "/teleport.lib.teleterm.v1.TerminalService/DeleteConnectMyComputerToken" + TerminalService_WaitForConnectMyComputerNodeJoin_FullMethodName = "/teleport.lib.teleterm.v1.TerminalService/WaitForConnectMyComputerNodeJoin" ) // TerminalServiceClient is the client API for TerminalService service. @@ -164,6 +165,13 @@ type TerminalServiceClient interface { CreateConnectMyComputerNodeToken(ctx context.Context, in *CreateConnectMyComputerNodeTokenRequest, opts ...grpc.CallOption) (*CreateConnectMyComputerNodeTokenResponse, error) // DeleteConnectMyComputerToken deletes a join token DeleteConnectMyComputerToken(ctx context.Context, in *DeleteConnectMyComputerTokenRequest, opts ...grpc.CallOption) (*DeleteConnectMyComputerTokenResponse, error) + // WaitForConnectMyComputerNodeJoin sets up a watcher and returns a response only after detecting + // that the Connect My Computer node for the particular cluster has joined the cluster (the + // OpPut event). + // + // This RPC times out by itself after a minute to prevent the request from hanging forever, in + // case the client didn't set a deadline or doesn't abort the request. + WaitForConnectMyComputerNodeJoin(ctx context.Context, in *WaitForConnectMyComputerNodeJoinRequest, opts ...grpc.CallOption) (*WaitForConnectMyComputerNodeJoinResponse, error) } type terminalServiceClient struct { @@ -507,6 +515,15 @@ func (c *terminalServiceClient) DeleteConnectMyComputerToken(ctx context.Context return out, nil } +func (c *terminalServiceClient) WaitForConnectMyComputerNodeJoin(ctx context.Context, in *WaitForConnectMyComputerNodeJoinRequest, opts ...grpc.CallOption) (*WaitForConnectMyComputerNodeJoinResponse, error) { + out := new(WaitForConnectMyComputerNodeJoinResponse) + err := c.cc.Invoke(ctx, TerminalService_WaitForConnectMyComputerNodeJoin_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TerminalServiceServer is the server API for TerminalService service. // All implementations must embed UnimplementedTerminalServiceServer // for forward compatibility @@ -604,6 +621,13 @@ type TerminalServiceServer interface { CreateConnectMyComputerNodeToken(context.Context, *CreateConnectMyComputerNodeTokenRequest) (*CreateConnectMyComputerNodeTokenResponse, error) // DeleteConnectMyComputerToken deletes a join token DeleteConnectMyComputerToken(context.Context, *DeleteConnectMyComputerTokenRequest) (*DeleteConnectMyComputerTokenResponse, error) + // WaitForConnectMyComputerNodeJoin sets up a watcher and returns a response only after detecting + // that the Connect My Computer node for the particular cluster has joined the cluster (the + // OpPut event). + // + // This RPC times out by itself after a minute to prevent the request from hanging forever, in + // case the client didn't set a deadline or doesn't abort the request. + WaitForConnectMyComputerNodeJoin(context.Context, *WaitForConnectMyComputerNodeJoinRequest) (*WaitForConnectMyComputerNodeJoinResponse, error) mustEmbedUnimplementedTerminalServiceServer() } @@ -707,6 +731,9 @@ func (UnimplementedTerminalServiceServer) CreateConnectMyComputerNodeToken(conte func (UnimplementedTerminalServiceServer) DeleteConnectMyComputerToken(context.Context, *DeleteConnectMyComputerTokenRequest) (*DeleteConnectMyComputerTokenResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteConnectMyComputerToken not implemented") } +func (UnimplementedTerminalServiceServer) WaitForConnectMyComputerNodeJoin(context.Context, *WaitForConnectMyComputerNodeJoinRequest) (*WaitForConnectMyComputerNodeJoinResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WaitForConnectMyComputerNodeJoin not implemented") +} func (UnimplementedTerminalServiceServer) mustEmbedUnimplementedTerminalServiceServer() {} // UnsafeTerminalServiceServer may be embedded to opt out of forward compatibility for this service. @@ -1307,6 +1334,24 @@ func _TerminalService_DeleteConnectMyComputerToken_Handler(srv interface{}, ctx return interceptor(ctx, in, info, handler) } +func _TerminalService_WaitForConnectMyComputerNodeJoin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WaitForConnectMyComputerNodeJoinRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TerminalServiceServer).WaitForConnectMyComputerNodeJoin(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TerminalService_WaitForConnectMyComputerNodeJoin_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TerminalServiceServer).WaitForConnectMyComputerNodeJoin(ctx, req.(*WaitForConnectMyComputerNodeJoinRequest)) + } + return interceptor(ctx, in, info, handler) +} + // TerminalService_ServiceDesc is the grpc.ServiceDesc for TerminalService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -1434,6 +1479,10 @@ var TerminalService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteConnectMyComputerToken", Handler: _TerminalService_DeleteConnectMyComputerToken_Handler, }, + { + MethodName: "WaitForConnectMyComputerNodeJoin", + Handler: _TerminalService_WaitForConnectMyComputerNodeJoin_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.d.ts b/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.d.ts index 5999d641b9c20..45d66b0f6a65f 100644 --- a/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.d.ts +++ b/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.d.ts @@ -49,6 +49,7 @@ interface ITerminalServiceService extends grpc.ServiceDefinition { @@ -339,6 +340,15 @@ interface ITerminalServiceService_IDeleteConnectMyComputerToken extends grpc.Met responseSerialize: grpc.serialize; responseDeserialize: grpc.deserialize; } +interface ITerminalServiceService_IWaitForConnectMyComputerNodeJoin extends grpc.MethodDefinition { + path: "/teleport.lib.teleterm.v1.TerminalService/WaitForConnectMyComputerNodeJoin"; + requestStream: false; + responseStream: false; + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} export const TerminalServiceService: ITerminalServiceService; @@ -375,6 +385,7 @@ export interface ITerminalServiceServer { createConnectMyComputerRole: grpc.handleUnaryCall; createConnectMyComputerNodeToken: grpc.handleUnaryCall; deleteConnectMyComputerToken: grpc.handleUnaryCall; + waitForConnectMyComputerNodeJoin: grpc.handleUnaryCall; } export interface ITerminalServiceClient { @@ -473,6 +484,9 @@ export interface ITerminalServiceClient { deleteConnectMyComputerToken(request: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenRequest, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenResponse) => void): grpc.ClientUnaryCall; deleteConnectMyComputerToken(request: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenResponse) => void): grpc.ClientUnaryCall; deleteConnectMyComputerToken(request: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenResponse) => void): grpc.ClientUnaryCall; + waitForConnectMyComputerNodeJoin(request: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse) => void): grpc.ClientUnaryCall; + waitForConnectMyComputerNodeJoin(request: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse) => void): grpc.ClientUnaryCall; + waitForConnectMyComputerNodeJoin(request: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse) => void): grpc.ClientUnaryCall; } export class TerminalServiceClient extends grpc.Client implements ITerminalServiceClient { @@ -571,4 +585,7 @@ export class TerminalServiceClient extends grpc.Client implements ITerminalServi public deleteConnectMyComputerToken(request: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenRequest, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenResponse) => void): grpc.ClientUnaryCall; public deleteConnectMyComputerToken(request: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenResponse) => void): grpc.ClientUnaryCall; public deleteConnectMyComputerToken(request: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.DeleteConnectMyComputerTokenResponse) => void): grpc.ClientUnaryCall; + public waitForConnectMyComputerNodeJoin(request: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse) => void): grpc.ClientUnaryCall; + public waitForConnectMyComputerNodeJoin(request: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse) => void): grpc.ClientUnaryCall; + public waitForConnectMyComputerNodeJoin(request: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse) => void): grpc.ClientUnaryCall; } diff --git a/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.js b/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.js index 1913d21302124..92ae2a9bd04cf 100644 --- a/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.js +++ b/gen/proto/js/teleport/lib/teleterm/v1/service_grpc_pb.js @@ -622,6 +622,28 @@ function deserialize_teleport_lib_teleterm_v1_UpdateTshdEventsServerAddressRespo return teleport_lib_teleterm_v1_service_pb.UpdateTshdEventsServerAddressResponse.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinRequest(arg) { + if (!(arg instanceof teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest)) { + throw new Error('Expected argument of type teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinRequest(buffer_arg) { + return teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinResponse(arg) { + if (!(arg instanceof teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse)) { + throw new Error('Expected argument of type teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinResponse(buffer_arg) { + return teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + // TerminalService is used by the Electron app to communicate with the tsh daemon. // @@ -1043,6 +1065,23 @@ deleteConnectMyComputerToken: { responseSerialize: serialize_teleport_lib_teleterm_v1_DeleteConnectMyComputerTokenResponse, responseDeserialize: deserialize_teleport_lib_teleterm_v1_DeleteConnectMyComputerTokenResponse, }, + // WaitForConnectMyComputerNodeJoin sets up a watcher and returns a response only after detecting +// that the Connect My Computer node for the particular cluster has joined the cluster (the +// OpPut event). +// +// This RPC times out by itself after a minute to prevent the request from hanging forever, in +// case the client didn't set a deadline or doesn't abort the request. +waitForConnectMyComputerNodeJoin: { + path: '/teleport.lib.teleterm.v1.TerminalService/WaitForConnectMyComputerNodeJoin', + requestStream: false, + responseStream: false, + requestType: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinRequest, + responseType: teleport_lib_teleterm_v1_service_pb.WaitForConnectMyComputerNodeJoinResponse, + requestSerialize: serialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinRequest, + requestDeserialize: deserialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinRequest, + responseSerialize: serialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinResponse, + responseDeserialize: deserialize_teleport_lib_teleterm_v1_WaitForConnectMyComputerNodeJoinResponse, + }, }; exports.TerminalServiceClient = grpc.makeGenericClientConstructor(TerminalServiceService); diff --git a/gen/proto/js/teleport/lib/teleterm/v1/service_pb.d.ts b/gen/proto/js/teleport/lib/teleterm/v1/service_pb.d.ts index 1cb64157ec245..24389f270fc56 100644 --- a/gen/proto/js/teleport/lib/teleterm/v1/service_pb.d.ts +++ b/gen/proto/js/teleport/lib/teleterm/v1/service_pb.d.ts @@ -1493,6 +1493,51 @@ export namespace DeleteConnectMyComputerTokenResponse { } } +export class WaitForConnectMyComputerNodeJoinRequest extends jspb.Message { + getRootClusterUri(): string; + setRootClusterUri(value: string): WaitForConnectMyComputerNodeJoinRequest; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): WaitForConnectMyComputerNodeJoinRequest.AsObject; + static toObject(includeInstance: boolean, msg: WaitForConnectMyComputerNodeJoinRequest): WaitForConnectMyComputerNodeJoinRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: WaitForConnectMyComputerNodeJoinRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): WaitForConnectMyComputerNodeJoinRequest; + static deserializeBinaryFromReader(message: WaitForConnectMyComputerNodeJoinRequest, reader: jspb.BinaryReader): WaitForConnectMyComputerNodeJoinRequest; +} + +export namespace WaitForConnectMyComputerNodeJoinRequest { + export type AsObject = { + rootClusterUri: string, + } +} + +export class WaitForConnectMyComputerNodeJoinResponse extends jspb.Message { + + hasServer(): boolean; + clearServer(): void; + getServer(): teleport_lib_teleterm_v1_server_pb.Server | undefined; + setServer(value?: teleport_lib_teleterm_v1_server_pb.Server): WaitForConnectMyComputerNodeJoinResponse; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): WaitForConnectMyComputerNodeJoinResponse.AsObject; + static toObject(includeInstance: boolean, msg: WaitForConnectMyComputerNodeJoinResponse): WaitForConnectMyComputerNodeJoinResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: WaitForConnectMyComputerNodeJoinResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): WaitForConnectMyComputerNodeJoinResponse; + static deserializeBinaryFromReader(message: WaitForConnectMyComputerNodeJoinResponse, reader: jspb.BinaryReader): WaitForConnectMyComputerNodeJoinResponse; +} + +export namespace WaitForConnectMyComputerNodeJoinResponse { + export type AsObject = { + server?: teleport_lib_teleterm_v1_server_pb.Server.AsObject, + } +} + export enum PasswordlessPrompt { PASSWORDLESS_PROMPT_UNSPECIFIED = 0, PASSWORDLESS_PROMPT_PIN = 1, diff --git a/gen/proto/js/teleport/lib/teleterm/v1/service_pb.js b/gen/proto/js/teleport/lib/teleterm/v1/service_pb.js index 84a7bd13ec710..bb6fee60fc686 100644 --- a/gen/proto/js/teleport/lib/teleterm/v1/service_pb.js +++ b/gen/proto/js/teleport/lib/teleterm/v1/service_pb.js @@ -94,6 +94,8 @@ goog.exportSymbol('proto.teleport.lib.teleterm.v1.UpdateHeadlessAuthenticationSt goog.exportSymbol('proto.teleport.lib.teleterm.v1.UpdateHeadlessAuthenticationStateResponse', null, global); goog.exportSymbol('proto.teleport.lib.teleterm.v1.UpdateTshdEventsServerAddressRequest', null, global); goog.exportSymbol('proto.teleport.lib.teleterm.v1.UpdateTshdEventsServerAddressResponse', null, global); +goog.exportSymbol('proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest', null, global); +goog.exportSymbol('proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse', null, global); /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -1270,6 +1272,48 @@ if (goog.DEBUG && !COMPILED) { */ proto.teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenResponse.displayName = 'proto.teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenResponse'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.displayName = 'proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.displayName = 'proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse'; +} @@ -10847,6 +10891,287 @@ proto.teleport.lib.teleterm.v1.DeleteConnectMyComputerTokenResponse.serializeBin }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.prototype.toObject = function(opt_includeInstance) { + return proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.toObject = function(includeInstance, msg) { + var f, obj = { + rootClusterUri: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest; + return proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setRootClusterUri(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getRootClusterUri(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string root_cluster_uri = 1; + * @return {string} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.prototype.getRootClusterUri = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest} returns this + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinRequest.prototype.setRootClusterUri = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.prototype.toObject = function(opt_includeInstance) { + return proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.toObject = function(includeInstance, msg) { + var f, obj = { + server: (f = msg.getServer()) && teleport_lib_teleterm_v1_server_pb.Server.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse; + return proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new teleport_lib_teleterm_v1_server_pb.Server; + reader.readMessage(value,teleport_lib_teleterm_v1_server_pb.Server.deserializeBinaryFromReader); + msg.setServer(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getServer(); + if (f != null) { + writer.writeMessage( + 1, + f, + teleport_lib_teleterm_v1_server_pb.Server.serializeBinaryToWriter + ); + } +}; + + +/** + * optional Server server = 1; + * @return {?proto.teleport.lib.teleterm.v1.Server} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.prototype.getServer = function() { + return /** @type{?proto.teleport.lib.teleterm.v1.Server} */ ( + jspb.Message.getWrapperField(this, teleport_lib_teleterm_v1_server_pb.Server, 1)); +}; + + +/** + * @param {?proto.teleport.lib.teleterm.v1.Server|undefined} value + * @return {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse} returns this +*/ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.prototype.setServer = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse} returns this + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.prototype.clearServer = function() { + return this.setServer(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.teleport.lib.teleterm.v1.WaitForConnectMyComputerNodeJoinResponse.prototype.hasServer = function() { + return jspb.Message.getField(this, 1) != null; +}; + + /** * @enum {number} */ diff --git a/integration/proxy/teleterm_test.go b/integration/proxy/teleterm_test.go index c6aec06dfbd8a..7f72bbbaf13fc 100644 --- a/integration/proxy/teleterm_test.go +++ b/integration/proxy/teleterm_test.go @@ -130,6 +130,7 @@ func testGatewayCertRenewal(t *testing.T, inst *helpers.TeleInstance, username, return grpc.WithTransportCredentials(insecure.NewCredentials()), nil }, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) t.Cleanup(func() { diff --git a/integration/teleterm_test.go b/integration/teleterm_test.go index 9a26d930ec6f8..c9208b8136e00 100644 --- a/integration/teleterm_test.go +++ b/integration/teleterm_test.go @@ -19,6 +19,7 @@ import ( "fmt" "net" "os/user" + "path/filepath" "sync/atomic" "testing" "time" @@ -38,12 +39,14 @@ import ( "github.com/gravitational/teleport/integration/helpers" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/service" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/teleterm/api/uri" "github.com/gravitational/teleport/lib/teleterm/apiserver/handler" "github.com/gravitational/teleport/lib/teleterm/clusters" "github.com/gravitational/teleport/lib/teleterm/daemon" + libutils "github.com/gravitational/teleport/lib/utils" ) func TestTeleterm(t *testing.T) { @@ -97,6 +100,11 @@ func TestTeleterm(t *testing.T) { t.Parallel() testCreatingAndDeletingConnectMyComputerToken(t, pack) }) + + t.Run("WaitForConnectMyComputerNodeJoin", func(t *testing.T) { + t.Parallel() + testWaitForConnectMyComputerNodeJoin(t, pack, creds) + }) } func testAddingRootCluster(t *testing.T, pack *dbhelpers.DatabasePack, creds *helpers.UserCreds) { @@ -111,6 +119,7 @@ func testAddingRootCluster(t *testing.T, pack *dbhelpers.DatabasePack, creds *he daemonService, err := daemon.New(daemon.Config{ Storage: storage, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) t.Cleanup(func() { @@ -142,6 +151,7 @@ func testListRootClustersReturnsLoggedInUser(t *testing.T, pack *dbhelpers.Datab daemonService, err := daemon.New(daemon.Config{ Storage: storage, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) t.Cleanup(func() { @@ -223,6 +233,7 @@ func testGetClusterReturnsPropertiesFromAuthServer(t *testing.T, pack *dbhelpers daemonService, err := daemon.New(daemon.Config{ Storage: storage, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) t.Cleanup(func() { @@ -270,6 +281,7 @@ func testHeadlessWatcher(t *testing.T, pack *dbhelpers.DatabasePack, creds *help return grpc.WithTransportCredentials(insecure.NewCredentials()), nil }, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) t.Cleanup(func() { @@ -498,6 +510,7 @@ func testCreateConnectMyComputerRole(t *testing.T, pack *dbhelpers.DatabasePack) daemonService, err := daemon.New(daemon.Config{ Storage: storage, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) t.Cleanup(func() { @@ -612,6 +625,7 @@ func testCreatingAndDeletingConnectMyComputerToken(t *testing.T, pack *dbhelpers daemonService, err := daemon.New(daemon.Config{ Storage: storage, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) t.Cleanup(func() { @@ -683,6 +697,64 @@ func testCreatingAndDeletingConnectMyComputerToken(t *testing.T, pack *dbhelpers require.True(t, trace.IsNotFound(err)) } +func testWaitForConnectMyComputerNodeJoin(t *testing.T, pack *dbhelpers.DatabasePack, creds *helpers.UserCreds) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + t.Cleanup(cancel) + + tc := mustLogin(t, pack.Root.User.GetName(), pack, creds) + + storage, err := clusters.NewStorage(clusters.Config{ + Dir: tc.KeysDir, + InsecureSkipVerify: tc.InsecureSkipVerify, + }) + require.NoError(t, err) + + agentsDir := t.TempDir() + daemonService, err := daemon.New(daemon.Config{ + Storage: storage, + KubeconfigsDir: t.TempDir(), + AgentsDir: agentsDir, + }) + require.NoError(t, err) + t.Cleanup(func() { + daemonService.Stop() + }) + + handler, err := handler.New( + handler.Config{ + DaemonService: daemonService, + }, + ) + require.NoError(t, err) + + profileName, _, err := net.SplitHostPort(pack.Root.Cluster.Web) + require.NoError(t, err) + + waitForNodeJoinErr := make(chan error) + + go func() { + _, err := handler.WaitForConnectMyComputerNodeJoin(ctx, &api.WaitForConnectMyComputerNodeJoinRequest{ + RootClusterUri: uri.NewClusterURI(profileName).String(), + }) + waitForNodeJoinErr <- err + }() + + // Start the new node. + nodeConfig := newNodeConfig(t, pack.Root.Cluster.Config.Auth.ListenAddr, "token", types.JoinMethodToken) + nodeConfig.DataDir = filepath.Join(agentsDir, profileName, "data") + nodeConfig.Log = libutils.NewLoggerForTests() + nodeSvc, err := service.NewTeleport(nodeConfig) + require.NoError(t, err) + require.NoError(t, nodeSvc.Start()) + t.Cleanup(func() { require.NoError(t, nodeSvc.Close()) }) + + _, err = nodeSvc.WaitForEventTimeout(10*time.Second, service.TeleportReadyEvent) + require.NoError(t, err, "timeout waiting for node readiness") + + // Verify that WaitForConnectMyComputerNodeJoin returned with no errors. + require.NoError(t, <-waitForNodeJoinErr) +} + // mustLogin logs in as the given user by completely skipping the actual login flow and saving valid // certs to disk. clusters.Storage can then be pointed to tc.KeysDir and daemon.Service can act as // if the user was successfully logged in. diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index 72d34e16fea2d..b07682b3bd46c 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -309,7 +309,7 @@ func makeSampleSSHConfig(conf *servicecfg.Config, flags SampleFlags, enabled boo s.ListenAddress = conf.SSH.Addr.Addr s.Commands = []CommandLabel{ { - Name: "hostname", + Name: defaults.HostnameLabel, Command: []string{"hostname"}, Period: time.Minute, }, diff --git a/lib/defaults/defaults.go b/lib/defaults/defaults.go index 7ae598bdf666a..7728fe3b19208 100644 --- a/lib/defaults/defaults.go +++ b/lib/defaults/defaults.go @@ -905,6 +905,10 @@ const ( // SSHDConfigPath is the path to the sshd config file to modify // when using the agentless installer SSHDConfigPath = "/etc/ssh/sshd_config" + + // HostnameLabel is the name of the label added to the sample SSH config generated by the teleport + // node configure command. + HostnameLabel = "hostname" ) // AzureInviteTokenName is the name of the default token to use diff --git a/lib/teleterm/apiserver/handler/handler_connectmycomputer.go b/lib/teleterm/apiserver/handler/handler_connectmycomputer.go index 9dad02556d68f..ef1a0fa735343 100644 --- a/lib/teleterm/apiserver/handler/handler_connectmycomputer.go +++ b/lib/teleterm/apiserver/handler/handler_connectmycomputer.go @@ -17,10 +17,12 @@ package handler import ( "context" "strings" + "time" "github.com/gravitational/trace" api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1" + "github.com/gravitational/teleport/lib/teleterm/api/uri" ) func (s *Handler) CreateConnectMyComputerRole(ctx context.Context, req *api.CreateConnectMyComputerRoleRequest) (*api.CreateConnectMyComputerRoleResponse, error) { @@ -54,3 +56,25 @@ func (s *Handler) DeleteConnectMyComputerToken(ctx context.Context, req *api.Del res, err := s.DaemonService.DeleteConnectMyComputerToken(ctx, req) return res, trace.Wrap(err) } + +func (s *Handler) WaitForConnectMyComputerNodeJoin(ctx context.Context, req *api.WaitForConnectMyComputerNodeJoinRequest) (*api.WaitForConnectMyComputerNodeJoinResponse, error) { + // The Electron app aborts the request after a timeout that's much shorter. However, we're going + // to add an internal timeout as well to protect from requests hanging forever if a client doesn't + // set a deadline or doesn't abort the request. + timeoutCtx, close := context.WithTimeout(ctx, time.Minute) + defer close() + + rootClusterURI, err := uri.Parse(req.RootClusterUri) + if err != nil { + return nil, trace.Wrap(err) + } + + server, err := s.DaemonService.WaitForConnectMyComputerNodeJoin(timeoutCtx, rootClusterURI) + if err != nil { + return nil, trace.Wrap(err) + } + + return &api.WaitForConnectMyComputerNodeJoinResponse{ + Server: newAPIServer(server), + }, err +} diff --git a/lib/teleterm/config.go b/lib/teleterm/config.go index 5d5a477251781..409f457febc0a 100644 --- a/lib/teleterm/config.go +++ b/lib/teleterm/config.go @@ -38,6 +38,8 @@ type Config struct { // KubeconfigsDir is the directory containing kubeconfigs for Kubernetes // Acesss. KubeconfigsDir string + // AgentsDir contains agent config files and data directories for Connect My Computer. + AgentsDir string } // CheckAndSetDefaults checks and sets default config values. @@ -67,5 +69,9 @@ func (c *Config) CheckAndSetDefaults() error { return trace.BadParameter("missing kubeconfigs directory") } + if c.AgentsDir == "" { + return trace.BadParameter("missing agents directory") + } + return nil } diff --git a/lib/teleterm/daemon/config.go b/lib/teleterm/daemon/config.go index 5b19141b54118..9007fb60299d0 100644 --- a/lib/teleterm/daemon/config.go +++ b/lib/teleterm/daemon/config.go @@ -39,6 +39,8 @@ type Config struct { // KubeconfigsDir is the directory containing kubeconfigs for Kubernetes // Acesss. KubeconfigsDir string + // AgentsDir contains agent config files and data directories for Connect My Computer. + AgentsDir string GatewayCreator GatewayCreator DBCLICommandProvider gateway.CLICommandProvider @@ -50,6 +52,7 @@ type Config struct { ConnectMyComputerRoleSetup *connectmycomputer.RoleSetup ConnectMyComputerTokenProvisioner *connectmycomputer.TokenProvisioner + ConnectMyComputerNodeJoinWait *connectmycomputer.NodeJoinWait } type CreateTshdEventsClientCredsFunc func() (grpc.DialOption, error) @@ -64,6 +67,10 @@ func (c *Config) CheckAndSetDefaults() error { return trace.BadParameter("missing kubeconfigs directory") } + if c.AgentsDir == "" { + return trace.BadParameter("missing agents directory") + } + if c.GatewayCreator == nil { c.GatewayCreator = clusters.NewGatewayCreator(c.Storage) } @@ -91,5 +98,17 @@ func (c *Config) CheckAndSetDefaults() error { if c.ConnectMyComputerTokenProvisioner == nil { c.ConnectMyComputerTokenProvisioner = connectmycomputer.NewTokenProvisioner(&connectmycomputer.TokenProvisionerConfig{Clock: c.Storage.Clock}) } + + if c.ConnectMyComputerNodeJoinWait == nil { + nodeJoinWait, err := connectmycomputer.NewNodeJoinWait(&connectmycomputer.NodeJoinWaitConfig{ + AgentsDir: c.AgentsDir, + }) + if err != nil { + return trace.Wrap(err) + } + + c.ConnectMyComputerNodeJoinWait = nodeJoinWait + } + return nil } diff --git a/lib/teleterm/daemon/daemon.go b/lib/teleterm/daemon/daemon.go index ee0dc06293009..67e474aae231e 100644 --- a/lib/teleterm/daemon/daemon.go +++ b/lib/teleterm/daemon/daemon.go @@ -201,11 +201,17 @@ func (s *Service) ResolveCluster(path string) (*clusters.Cluster, *client.Telepo if err != nil { return nil, nil, trace.Wrap(err) } - cluster, clusterClient, err := s.resolveCluster(resourceURI) + cluster, clusterClient, err := s.ResolveClusterURI(resourceURI) return cluster, clusterClient, trace.Wrap(err) } -func (s *Service) resolveCluster(uri uri.ResourceURI) (*clusters.Cluster, *client.TeleportClient, error) { +// ResolveClusterURI is like ResolveCluster, but it accepts an already parsed URI instead of a +// string. +// +// In the future, we should migrate towards ResolveClusterURI. Transforming strings into URIs should +// be done on the outermost layer, that is the gRPC handlers, so that the inner core doesn't have to +// worry about parsing URIs and can assume they are correct. +func (s *Service) ResolveClusterURI(uri uri.ResourceURI) (*clusters.Cluster, *client.TeleportClient, error) { cluster, clusterClient, err := s.cfg.Storage.GetByResourceURI(uri) return cluster, clusterClient, trace.Wrap(err) } @@ -327,7 +333,7 @@ func (s *Service) reissueGatewayCerts(ctx context.Context, g gateway.Gateway) er } reissueDBCerts := func() error { - cluster, _, err := s.resolveCluster(g.TargetURI()) + cluster, _, err := s.ResolveClusterURI(g.TargetURI()) if err != nil { return trace.Wrap(err) } @@ -774,7 +780,7 @@ func (s *Service) CreateConnectMyComputerNodeToken(ctx context.Context, rootClus return nodeToken, trace.Wrap(err) } -// DeleteConnectMyComputerToken deletes a join token +// DeleteConnectMyComputerToken deletes a join token. func (s *Service) DeleteConnectMyComputerToken(ctx context.Context, req *api.DeleteConnectMyComputerTokenRequest) (*api.DeleteConnectMyComputerTokenResponse, error) { _, clusterClient, err := s.ResolveCluster(req.RootClusterUri) if err != nil { @@ -801,6 +807,39 @@ func (s *Service) DeleteConnectMyComputerToken(ctx context.Context, req *api.Del return response, trace.Wrap(err) } +// WaitForConnectMyComputerNodeJoin returns a response only after detecting that a Connect My +// Computer node for the given cluster has joined the cluster. +func (s *Service) WaitForConnectMyComputerNodeJoin(ctx context.Context, rootClusterURI uri.ResourceURI) (clusters.Server, error) { + cluster, clusterClient, err := s.ResolveClusterURI(rootClusterURI) + if err != nil { + return clusters.Server{}, trace.Wrap(err) + } + + var server clusters.Server + err = clusters.AddMetadataToRetryableError(ctx, func() error { + proxyClient, err := clusterClient.ConnectToProxy(ctx) + if err != nil { + return trace.Wrap(err) + } + defer proxyClient.Close() + + authClient, err := proxyClient.ConnectToCluster(ctx, clusterClient.SiteName) + if err != nil { + return trace.Wrap(err) + } + defer authClient.Close() + + server, err = s.cfg.ConnectMyComputerNodeJoinWait.Run(ctx, authClient, cluster) + if err != nil { + return trace.Wrap(err) + } + + return nil + }) + + return server, trace.Wrap(err) +} + func (s *Service) shouldReuseGateway(targetURI uri.ResourceURI) (gateway.Gateway, bool) { // A single gateway can be shared for all terminals of the same kube // cluster. diff --git a/lib/teleterm/daemon/daemon_test.go b/lib/teleterm/daemon/daemon_test.go index f3996c4248918..a72ae38435593 100644 --- a/lib/teleterm/daemon/daemon_test.go +++ b/lib/teleterm/daemon/daemon_test.go @@ -257,6 +257,7 @@ func TestGatewayCRUD(t *testing.T) { Storage: storage, GatewayCreator: mockGatewayCreator, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) @@ -303,6 +304,7 @@ func TestUpdateTshdEventsServerAddress(t *testing.T) { Storage: storage, CreateTshdEventsClientCredsFunc: createTshdEventsClientCredsFunc, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) @@ -334,6 +336,7 @@ func TestUpdateTshdEventsServerAddress_CredsErr(t *testing.T) { Storage: storage, CreateTshdEventsClientCredsFunc: createTshdEventsClientCredsFunc, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) @@ -432,6 +435,7 @@ func TestRetryWithRelogin(t *testing.T) { return grpc.WithTransportCredentials(insecure.NewCredentials()), nil }, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) @@ -481,6 +485,7 @@ func TestImportantModalSemaphore(t *testing.T) { return grpc.WithTransportCredentials(insecure.NewCredentials()), nil }, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) diff --git a/lib/teleterm/services/connectmycomputer/connectmycomputer.go b/lib/teleterm/services/connectmycomputer/connectmycomputer.go index 5a8f61d1cc762..b5e30c16eb770 100644 --- a/lib/teleterm/services/connectmycomputer/connectmycomputer.go +++ b/lib/teleterm/services/connectmycomputer/connectmycomputer.go @@ -17,7 +17,10 @@ package connectmycomputer import ( "context" "fmt" + "os" "os/user" + "path/filepath" + "strings" "time" "github.com/google/uuid" @@ -27,10 +30,12 @@ import ( "golang.org/x/exp/slices" "github.com/gravitational/teleport" + apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/teleterm/clusters" "github.com/gravitational/teleport/lib/utils" ) @@ -205,54 +210,23 @@ const resourceUpdateTimeout = 15 * time.Second // syncResourceUpdate calls a function which updates the given resource and then waits until the // cache propagates the change. func (s *RoleSetup) syncResourceUpdate(ctx context.Context, accessAndIdentity AccessAndIdentity, resource types.Resource, updateFunc func(context.Context) error) error { - watcher, err := accessAndIdentity.NewWatcher(ctx, types.Watch{ - Kinds: []types.WatchKind{ - {Kind: resource.GetKind()}, - }, - }) + watcher, err := initializeWatcher(ctx, accessAndIdentity, resource.GetKind()) if err != nil { return trace.Wrap(err) } defer watcher.Close() - // Wait for OpInit. - select { - case <-ctx.Done(): - return trace.Wrap(ctx.Err(), "initializing watcher") - case <-watcher.Done(): - return trace.Wrap(watcher.Error(), "initializing watcher") - case event := <-watcher.Events(): - if event.Type != types.OpInit { - return trace.Errorf("unexpected event type %q received from resource watcher", event.Type) - } - } - err = updateFunc(ctx) if err != nil { return trace.Wrap(err, "calling update function") } - for { - select { - case <-ctx.Done(): - return trace.Wrap(ctx.Err(), "waiting for OpPut event for %v", resource.GetName()) - case <-watcher.Done(): - return trace.Wrap(watcher.Error(), "waiting for OpPut event for %v", resource.GetName()) - case event := <-watcher.Events(): - if event.Type != types.OpPut { - continue - } - - // Kind + name combo is enough to uniquely identify a resource within a single cluster. - if event.Resource.GetKind() == resource.GetKind() && event.Resource.GetName() == resource.GetName() { - return nil - } - } - } + _, err = waitForOpPut(ctx, watcher, resource.GetKind(), resource.GetName()) + return trace.Wrap(err) } -// AccessAndIdentity represents services.Access, services.Identity and auth.Cache methods used -// by [RoleSetup]. During a normal operation, auth.ClientI is passed as this interface. +// AccessAndIdentity represents services.Access, services.Identity, services.Presence and auth.Cache +// methods used by [RoleSetup]. During a normal operation, auth.ClientI is passed as this interface. type AccessAndIdentity interface { // See services.Access.GetRole. GetRole(ctx context.Context, name string) (types.Role, error) @@ -265,6 +239,9 @@ type AccessAndIdentity interface { GetUser(name string, withSecrets bool) (types.User, error) // See services.Identity.UpdateUser. UpdateUser(context.Context, types.User) error + + // See services.Presence.GetNode. + GetNode(ctx context.Context, namespace, name string) (types.Server, error) } // CertManager enables the usage of only select methods from [client.ProxyClient] so that there @@ -354,3 +331,199 @@ type Provisioner interface { // See services.Provisioner.DeleteToken. DeleteToken(ctx context.Context, token string) error } + +type NodeJoinWait struct { + cfg *NodeJoinWaitConfig +} + +func NewNodeJoinWait(cfg *NodeJoinWaitConfig) (*NodeJoinWait, error) { + err := cfg.CheckAndSetDefaults() + if err != nil { + return nil, err + } + + return &NodeJoinWait{cfg: cfg}, nil +} + +// Run grabs the host UUID of an agent from disk and then waits for the node with the given name to +// show up in the cluster. +// +// The Electron app calls this method soon after starting the agent process. +func (n *NodeJoinWait) Run(ctx context.Context, accessAndIdentity AccessAndIdentity, cluster *clusters.Cluster) (clusters.Server, error) { + nodeName, err := n.getNodeNameFromHostUUIDFile(ctx, cluster) + if err != nil { + return clusters.Server{}, err + } + + server, err := n.waitForNode(ctx, accessAndIdentity, cluster, nodeName) + if err != nil { + return clusters.Server{}, trace.Wrap(err) + } + + // The default config generated by `teleport node config` during the setup of Connect My Computer + // includes a command label with a hostname. Immediately after the node joins the cluster, the + // label is most likely going to be empty. It takes a couple of seconds for it to update with the + // actual hostname of the device. + // + // To work around that, we fill it out with os.Hostname if it's empty. + err = n.fillOutHostnameLabelIfBlank(server) + if err != nil { + return clusters.Server{}, trace.Wrap(err) + } + + return clusters.Server{ + URI: cluster.URI.AppendServer(server.GetName()), + Server: server, + }, nil +} + +func (n *NodeJoinWait) getNodeNameFromHostUUIDFile(ctx context.Context, cluster *clusters.Cluster) (string, error) { + dataDir := filepath.Join(n.cfg.AgentsDir, cluster.ProfileName, "data") + + // NodeJoinWait gets executed when the agent is booting up, so the host UUID file might not exist + // on disk yet. Use a ticker to periodically check for its existence. + ticker := time.NewTicker(50 * time.Millisecond) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + // We're reading the host UUID file by ourselves rather than using utils.ReadHostUUID, because + // that function returns NotFound both when the file doesn't exist and when the host UUID in + // the file is empty. + // + // Here we need to be able to distinguish between both of those two cases. + out, err := utils.ReadPath(utils.GetHostUUIDPath(dataDir)) + if err != nil { + if trace.IsNotFound(err) { + continue + } + return "", trace.Wrap(err) + } + + id := strings.TrimSpace(string(out)) + if id == "" { + return "", trace.NotFound("host UUID is empty") + } + + return id, nil + case <-ctx.Done(): + return "", trace.Wrap(ctx.Err(), "waiting for host UUID file to be created") + } + } +} + +func (n *NodeJoinWait) waitForNode(ctx context.Context, accessAndIdentity AccessAndIdentity, + cluster *clusters.Cluster, nodeName string) (types.Server, error) { + watcher, err := initializeWatcher(ctx, accessAndIdentity, types.KindNode) + if err != nil { + return nil, trace.Wrap(err) + } + defer watcher.Close() + + // Attempt to fetch the node from the cluster manually, in case it's joined the cluster before we + // started the watcher. + // + // This means that we might return immediately if the node is still in the cache, even if + // technically the agent has not joined the cluster yet. We're fine with this edge case. + server, err := accessAndIdentity.GetNode(ctx, apidefaults.Namespace, nodeName) + if err != nil { + if !trace.IsNotFound(err) { + return nil, trace.Wrap(err) + } + // Continue in case of NotFound error. + } else { + return server, nil + } + + resource, err := waitForOpPut(ctx, watcher, types.KindNode, nodeName) + if err != nil { + return nil, trace.Wrap(err) + } + server, ok := resource.(*types.ServerV2) + if !ok { + return nil, trace.Errorf("cannot cast event resource to server") + } + + return server, nil +} + +func (n *NodeJoinWait) fillOutHostnameLabelIfBlank(server types.Server) error { + labels := server.GetCmdLabels() + hostnameLabel, ok := labels[defaults.HostnameLabel] + if ok && hostnameLabel.GetResult() == "" { + hostname, err := os.Hostname() + if err != nil { + return trace.Wrap(err) + } + + hostnameLabel.SetResult(hostname) + server.SetCmdLabels(labels) + } + + return nil +} + +type NodeJoinWaitConfig struct { + // AgentsDir contains agent config files and data directories for Connect My Computer. + AgentsDir string +} + +func (c *NodeJoinWaitConfig) CheckAndSetDefaults() error { + if c.AgentsDir == "" { + return trace.BadParameter("missing agents dir") + } + + return nil +} + +// initializeWatcher creates a new watcher and waits for OpInit. The caller must remember to close +// the watcher. +func initializeWatcher(ctx context.Context, accessAndIdentity AccessAndIdentity, kind string) (types.Watcher, error) { + watcher, err := accessAndIdentity.NewWatcher(ctx, types.Watch{ + Kinds: []types.WatchKind{ + {Kind: kind}, + }, + }) + if err != nil { + return nil, trace.Wrap(err) + } + + // Wait for OpInit. + select { + case <-ctx.Done(): + watcher.Close() + return nil, trace.Wrap(ctx.Err(), "waiting for OpInit event") + case <-watcher.Done(): + return nil, trace.Wrap(watcher.Error(), "waiting for OpInit event") + case event := <-watcher.Events(): + if event.Type != types.OpInit { + watcher.Close() + return nil, trace.Errorf("unexpected event type %q received from %s watcher", event.Type, kind) + } + } + + return watcher, nil +} + +// waitForOpPut blocks until the watcher receives an OpPut event with a resource watching the given +// kind and name. +func waitForOpPut(ctx context.Context, watcher types.Watcher, kind string, name string) (types.Resource, error) { + for { + select { + case <-ctx.Done(): + return nil, trace.Wrap(ctx.Err(), "waiting for OpPut event for %v", name) + case <-watcher.Done(): + return nil, trace.Wrap(watcher.Error(), "waiting for OpPut event for %v", name) + case event := <-watcher.Events(): + if event.Type != types.OpPut { + continue + } + + // Kind + name combo is enough to uniquely identify a resource within a single cluster. + if event.Resource.GetKind() == kind && event.Resource.GetName() == name { + return event.Resource, nil + } + } + } +} diff --git a/lib/teleterm/services/connectmycomputer/connectmycomputer_test.go b/lib/teleterm/services/connectmycomputer/connectmycomputer_test.go index e0ec44ef0897e..b9443cac1e517 100644 --- a/lib/teleterm/services/connectmycomputer/connectmycomputer_test.go +++ b/lib/teleterm/services/connectmycomputer/connectmycomputer_test.go @@ -16,16 +16,21 @@ package connectmycomputer import ( "context" + "os" + "path/filepath" "sync" "testing" + "time" "github.com/gravitational/trace" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/teleterm/api/uri" "github.com/gravitational/teleport/lib/teleterm/clusters" + "github.com/gravitational/teleport/lib/utils" ) func TestRoleSetupRun_WithNonLocalUser(t *testing.T) { @@ -82,11 +87,146 @@ func TestRoleSetupRun_Idempotency(t *testing.T) { require.Equal(t, 1, accessAndIdentity.callCounts["UpdateUser"], "expected two runs to update the user only once") } +const nodejoinWaitTestTimeout = 10 * time.Second + +func TestNodeJoinWaitRun_WaitsForHostUUIDFileToBeCreatedAndFetchesNodeFromCluster(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), nodejoinWaitTestTimeout) + t.Cleanup(cancel) + + cluster := &clusters.Cluster{URI: uri.NewClusterURI("foo"), ProfileName: "foo"} + events := &mockEvents{} + node, err := types.NewServer("1234", types.KindNode, types.ServerSpecV2{ + CmdLabels: types.LabelsToV2(map[string]types.CommandLabel{ + defaults.HostnameLabel: &types.CommandLabelV2{Result: ""}, + }), + }) + require.NoError(t, err) + accessAndIdentity := &mockAccessAndIdentity{ + callCounts: make(map[string]int), + events: events, + node: node, + } + + nodeJoinWait, err := NewNodeJoinWait(&NodeJoinWaitConfig{AgentsDir: t.TempDir()}) + require.NoError(t, err) + + runErr := make(chan error) + serverC := make(chan clusters.Server) + + go func() { + server, err := nodeJoinWait.Run(ctx, accessAndIdentity, cluster) + runErr <- err + serverC <- server + }() + + // Make sure NodeJoinWait.Run doesn't see the file on the first tick. + time.Sleep(10 * time.Millisecond) + + // Create the UUID file while NodeJoinWait.Run is executed in a separate goroutine to verify that + // it continuously attempts to read the host UUID file, rather than reading it only once. + mustMakeHostUUIDFile(t, nodeJoinWait.cfg.AgentsDir, cluster.ProfileName) + + // Verify that NodeJoinWait.Run used GetNode and not a watcher to fetch the node. + require.NoError(t, <-runErr) + server := <-serverC + require.Equal(t, node.GetName(), server.GetName()) + + // Verify that the empty hostname label gets filled out. + hostname, err := os.Hostname() + require.NoError(t, err) + require.Contains(t, server.GetCmdLabels(), defaults.HostnameLabel) + require.Equal(t, hostname, server.GetCmdLabels()[defaults.HostnameLabel].GetResult()) +} + +func TestNodeJoinWaitRun_WatchesForOpPutIfNodeWasNotFound(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), nodejoinWaitTestTimeout) + t.Cleanup(cancel) + + cluster := &clusters.Cluster{URI: uri.NewClusterURI("foo"), ProfileName: "foo"} + events := &mockEvents{} + accessAndIdentity := &mockAccessAndIdentity{ + callCounts: make(map[string]int), + events: events, + } + + nodeJoinWait, err := NewNodeJoinWait(&NodeJoinWaitConfig{AgentsDir: t.TempDir()}) + require.NoError(t, err) + + hostUUID := mustMakeHostUUIDFile(t, nodeJoinWait.cfg.AgentsDir, cluster.ProfileName) + eventServer, err := types.NewServer(hostUUID, types.KindNode, types.ServerSpecV2{ + CmdLabels: types.LabelsToV2(map[string]types.CommandLabel{ + defaults.HostnameLabel: &types.CommandLabelV2{Result: ""}, + }), + }) + require.NoError(t, err) + bogusEventServer, err := types.NewServer("1234", types.KindNode, types.ServerSpecV2{}) + require.NoError(t, err) + + runErr := make(chan error) + serverC := make(chan clusters.Server) + + go func() { + server, err := nodeJoinWait.Run(ctx, accessAndIdentity, cluster) + runErr <- err + serverC <- server + }() + + err = accessAndIdentity.events.WaitSomeWatchers(ctx) + require.NoError(t, err) + // Fire an event with another node first to verify that NodeJoinWait does the comparison correctly. + accessAndIdentity.events.Fire(types.Event{ + Type: types.OpPut, + Resource: bogusEventServer, + }) + accessAndIdentity.events.Fire(types.Event{ + Type: types.OpPut, + Resource: eventServer, + }) + + // Verify that NodeJoinWait.Run returns as soon as it receives an event with a matching server. + require.NoError(t, <-runErr) + server := <-serverC + require.Equal(t, eventServer.GetName(), server.GetName()) + + // Verify that the empty hostname label gets filled out. + hostname, err := os.Hostname() + require.NoError(t, err) + require.Contains(t, server.GetCmdLabels(), defaults.HostnameLabel) + require.Equal(t, hostname, server.GetCmdLabels()[defaults.HostnameLabel].GetResult()) +} + +func TestNodeJoinWaitRun_ReturnsEarlyIfGetNodeReturnsErrorOtherThanNotFound(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithTimeout(context.Background(), nodejoinWaitTestTimeout) + t.Cleanup(cancel) + + cluster := &clusters.Cluster{URI: uri.NewClusterURI("foo"), ProfileName: "foo"} + events := &mockEvents{} + nodeErr := trace.Errorf("something went wrong") + accessAndIdentity := &mockAccessAndIdentity{ + callCounts: make(map[string]int), + events: events, + nodeErr: nodeErr, + } + + nodeJoinWait, err := NewNodeJoinWait(&NodeJoinWaitConfig{AgentsDir: t.TempDir()}) + require.NoError(t, err) + + mustMakeHostUUIDFile(t, nodeJoinWait.cfg.AgentsDir, cluster.ProfileName) + + _, err = nodeJoinWait.Run(ctx, accessAndIdentity, cluster) + require.Equal(t, nodeErr, err) +} + type mockAccessAndIdentity struct { user types.User role types.Role callCounts map[string]int events *mockEvents + node types.Server + nodeErr error } func (m *mockAccessAndIdentity) GetUser(name string, withSecrets bool) (types.User, error) { @@ -131,6 +271,17 @@ func (m *mockAccessAndIdentity) UpdateUser(ctx context.Context, user types.User) return nil } +func (m *mockAccessAndIdentity) GetNode(ctx context.Context, namespace, name string) (types.Server, error) { + if m.nodeErr != nil { + return nil, m.nodeErr + } + + if m.node != nil { + return m.node, nil + } + return nil, trace.NotFound("node not found") +} + type mockCertManager struct{} func (m *mockCertManager) ReissueUserCerts(context.Context, client.CertCachePolicy, client.ReissueParams) error { @@ -164,6 +315,25 @@ func (e *mockEvents) Fire(event types.Event) { } } +// WaitSomeWatchers blocks until either some watcher is subscribed or context is done. +func (e *mockEvents) WaitSomeWatchers(ctx context.Context) error { + ticker := time.NewTicker(5 * time.Millisecond) + defer ticker.Stop() + for { + select { + case <-ticker.C: + e.Lock() + n := len(e.channels) + e.Unlock() + if n > 0 { + return nil + } + case <-ctx.Done(): + return trace.Wrap(ctx.Err()) + } + } +} + // mockWatcher is copied from integrations/lib/watcherjob/helpers_test.go. type mockWatcher struct { events <-chan types.Event @@ -191,3 +361,18 @@ func (w mockWatcher) Close() error { func (w mockWatcher) Error() error { return trace.Wrap(w.ctx.Err()) } + +func mustMakeHostUUIDFile(t *testing.T, agentsDir string, profileName string) string { + dataDir := filepath.Join(agentsDir, profileName, "data") + + agentsDirStat, err := os.Stat(agentsDir) + require.NoError(t, err) + + err = os.MkdirAll(dataDir, agentsDirStat.Mode()) + require.NoError(t, err) + + hostUUID, err := utils.ReadOrMakeHostUUID(dataDir) + require.NoError(t, err) + + return hostUUID +} diff --git a/lib/teleterm/teleterm.go b/lib/teleterm/teleterm.go index 70041d164a5dc..ee4b4f1f9276e 100644 --- a/lib/teleterm/teleterm.go +++ b/lib/teleterm/teleterm.go @@ -56,6 +56,7 @@ func Serve(ctx context.Context, cfg Config) error { CreateTshdEventsClientCredsFunc: grpcCredentials.tshdEvents, PrehogAddr: cfg.PrehogAddr, KubeconfigsDir: cfg.KubeconfigsDir, + AgentsDir: cfg.AgentsDir, }) if err != nil { return trace.Wrap(err) diff --git a/lib/teleterm/teleterm_test.go b/lib/teleterm/teleterm_test.go index 85c170c9df671..aa703ed5ea157 100644 --- a/lib/teleterm/teleterm_test.go +++ b/lib/teleterm/teleterm_test.go @@ -114,6 +114,7 @@ func TestStart(t *testing.T) { PrehogAddr: "https://prehog:9999", ListeningC: listeningC, KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), } ctx, cancel := context.WithCancel(context.Background()) diff --git a/lib/utils/utils.go b/lib/utils/utils.go index 0acea1524393e..31fa29885ede1 100644 --- a/lib/utils/utils.go +++ b/lib/utils/utils.go @@ -441,6 +441,11 @@ func GetFreeTCPPorts(n int, offset ...int) (PortList, error) { return PortList{ports: list}, nil } +// GetHostUUIDPath returns the path to the host UUID file given the data directory. +func GetHostUUIDPath(dataDir string) string { + return filepath.Join(dataDir, HostUUIDFile) +} + // HostUUIDExistsLocally checks if dataDir/host_uuid file exists in local storage. func HostUUIDExistsLocally(dataDir string) bool { _, err := ReadHostUUID(dataDir) @@ -449,7 +454,7 @@ func HostUUIDExistsLocally(dataDir string) bool { // ReadHostUUID reads host UUID from the file in the data dir func ReadHostUUID(dataDir string) (string, error) { - out, err := ReadPath(filepath.Join(dataDir, HostUUIDFile)) + out, err := ReadPath(GetHostUUIDPath(dataDir)) if err != nil { if errors.Is(err, fs.ErrPermission) { //do not convert to system error as this loses the ability to compare that it is a permission error @@ -466,7 +471,7 @@ func ReadHostUUID(dataDir string) (string, error) { // WriteHostUUID writes host UUID into a file func WriteHostUUID(dataDir string, id string) error { - err := os.WriteFile(filepath.Join(dataDir, HostUUIDFile), []byte(id), os.ModeExclusive|0400) + err := os.WriteFile(GetHostUUIDPath(dataDir), []byte(id), os.ModeExclusive|0400) if err != nil { if errors.Is(err, fs.ErrPermission) { //do not convert to system error as this loses the ability to compare that it is a permission error diff --git a/proto/teleport/lib/teleterm/v1/service.proto b/proto/teleport/lib/teleterm/v1/service.proto index 9b320c3968f16..209b3fe1d0481 100644 --- a/proto/teleport/lib/teleterm/v1/service.proto +++ b/proto/teleport/lib/teleterm/v1/service.proto @@ -133,6 +133,13 @@ service TerminalService { rpc CreateConnectMyComputerNodeToken(CreateConnectMyComputerNodeTokenRequest) returns (CreateConnectMyComputerNodeTokenResponse); // DeleteConnectMyComputerToken deletes a join token rpc DeleteConnectMyComputerToken(DeleteConnectMyComputerTokenRequest) returns (DeleteConnectMyComputerTokenResponse); + // WaitForConnectMyComputerNodeJoin sets up a watcher and returns a response only after detecting + // that the Connect My Computer node for the particular cluster has joined the cluster (the + // OpPut event). + // + // This RPC times out by itself after a minute to prevent the request from hanging forever, in + // case the client didn't set a deadline or doesn't abort the request. + rpc WaitForConnectMyComputerNodeJoin(WaitForConnectMyComputerNodeJoinRequest) returns (WaitForConnectMyComputerNodeJoinResponse); } message EmptyResponse {} @@ -471,5 +478,11 @@ message DeleteConnectMyComputerTokenRequest { string root_cluster_uri = 1; string token = 2; } - message DeleteConnectMyComputerTokenResponse {} + +message WaitForConnectMyComputerNodeJoinRequest { + string root_cluster_uri = 1; +} +message WaitForConnectMyComputerNodeJoinResponse { + Server server = 1; +} diff --git a/tool/tsh/common/daemon.go b/tool/tsh/common/daemon.go index 8c6ca1e8198c2..e82c8d077c504 100644 --- a/tool/tsh/common/daemon.go +++ b/tool/tsh/common/daemon.go @@ -46,6 +46,7 @@ func onDaemonStart(cf *CLIConf) error { InsecureSkipVerify: cf.InsecureSkipVerify, PrehogAddr: cf.DaemonPrehogAddr, KubeconfigsDir: cf.DaemonKubeconfigsDir, + AgentsDir: cf.DaemonAgentsDir, }) if err != nil { return trace.Wrap(err) diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index 811fac60b5f73..5de21c69d052d 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -181,6 +181,7 @@ type CLIConf struct { SiteName string // KubernetesCluster specifies the kubernetes cluster to login to. KubernetesCluster string + // DaemonAddr is the daemon listening address. DaemonAddr string // DaemonCertsDir is the directory containing certs used to create secure gRPC connection with daemon service @@ -190,8 +191,11 @@ type CLIConf struct { // DaemonKubeconfigsDir is the directory "Directory containing kubeconfig // for Kubernetes Access. DaemonKubeconfigsDir string - // DaemonPid is the PID to be stopped + // DaemonAgentsDir contains agent config files and data directories for Connect My Computer. + DaemonAgentsDir string + // DaemonPid is the PID to be stopped by tsh daemon stop. DaemonPid int + // DatabaseService specifies the database proxy server to log into. DatabaseService string // DatabaseUser specifies database user to embed in the certificate. @@ -722,6 +726,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { daemonStart.Flag("certs-dir", "Directory containing certs used to create secure gRPC connection with daemon service").StringVar(&cf.DaemonCertsDir) daemonStart.Flag("prehog-addr", "URL where prehog events should be submitted").StringVar(&cf.DaemonPrehogAddr) daemonStart.Flag("kubeconfigs-dir", "Directory containing kubeconfig for Kubernetes Access").StringVar(&cf.DaemonKubeconfigsDir) + daemonStart.Flag("agents-dir", "Directory containing agent config files and data directories for Connect My Computer").StringVar(&cf.DaemonAgentsDir) daemonStop := daemon.Command("stop", "Gracefully stops a process on Windows by sending Ctrl-Break to it.").Hidden() daemonStop.Flag("pid", "PID to be stopped").IntVar(&cf.DaemonPid) diff --git a/web/packages/build/package.json b/web/packages/build/package.json index 62afd0dfca945..39608512a2c6a 100644 --- a/web/packages/build/package.json +++ b/web/packages/build/package.json @@ -74,7 +74,7 @@ "eslint-plugin-react": "^7.27.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-testing-library": "^5.6.0", - "events": "1.0.2", + "events": "3.3.0", "fork-ts-checker-webpack-plugin": "^8.0.0", "html-webpack-plugin": "^5.5.0", "jest": "^27.3.1", diff --git a/web/packages/shared/utils/wait.ts b/web/packages/shared/utils/wait.ts index 8c625d9f995f8..9644743937ec4 100644 --- a/web/packages/shared/utils/wait.ts +++ b/web/packages/shared/utils/wait.ts @@ -16,13 +16,21 @@ /** Resolves after a given duration */ export function wait(ms: number, abortSignal?: AbortSignal): Promise { + if (abortSignal?.aborted) { + return Promise.reject(new DOMException('Wait was aborted.', 'AbortError')); + } + return new Promise((resolve, reject) => { - const timeout = setTimeout(resolve, ms); - if (abortSignal) { - abortSignal.onabort = () => { - clearTimeout(timeout); - reject(new DOMException('Wait was aborted.', 'AbortError')); - }; - } + const abort = () => { + clearTimeout(timeout); + reject(new DOMException('Wait was aborted.', 'AbortError')); + }; + const done = () => { + abortSignal?.removeEventListener('abort', abort); + resolve(); + }; + + const timeout = setTimeout(done, ms); + abortSignal?.addEventListener('abort', abort, { once: true }); }); } diff --git a/web/packages/teleterm/src/mainProcess/agentRunner/agentRunner.ts b/web/packages/teleterm/src/mainProcess/agentRunner/agentRunner.ts index 6162969924a4f..ea5e31e418d38 100644 --- a/web/packages/teleterm/src/mainProcess/agentRunner/agentRunner.ts +++ b/web/packages/teleterm/src/mainProcess/agentRunner/agentRunner.ts @@ -35,6 +35,10 @@ export class AgentRunner { { process: ChildProcess; state: AgentProcessState; + /** + * logs contains last 10 lines of logs from stderr of the agent. + */ + logs: string; } >(); @@ -81,6 +85,7 @@ export class AgentRunner { this.agentProcesses.set(rootClusterUri, { process: agentProcess, state: { status: 'not-started' }, + logs: '', }); this.addAgentListeners(rootClusterUri, agentProcess); this.setupCleanupDaemon(rootClusterUri, agentProcess); @@ -92,6 +97,10 @@ export class AgentRunner { return this.agentProcesses.get(rootClusterUri)?.state; } + getLogs(rootClusterUri: RootClusterUri): string | undefined { + return this.agentProcesses.get(rootClusterUri)?.logs; + } + async kill(rootClusterUri: RootClusterUri): Promise { const agent = this.agentProcesses.get(rootClusterUri); if (!agent) { @@ -111,12 +120,15 @@ export class AgentRunner { rootClusterUri: RootClusterUri, process: ChildProcess ): void { - // Teleport logs output to stderr. let stderrOutput = ''; + this.agentProcesses.get(rootClusterUri).logs = stderrOutput; + + // Teleport logs output to stderr. process.stderr.setEncoding('utf-8'); process.stderr.on('data', (error: string) => { stderrOutput += error; stderrOutput = processAgentOutput(stderrOutput); + this.agentProcesses.get(rootClusterUri).logs = stderrOutput; }); const spawnHandler = () => { diff --git a/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts b/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts index 870b5e3f62e4f..a9d96ac7209e3 100644 --- a/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts +++ b/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts @@ -117,16 +117,27 @@ export function generateAgentConfigPaths( }; } +export function getAgentsDir(userDataDir: string): string { + // Why not put agentsDir into runtimeSettings? That's because we don't want the renderer to have + // access to this value as it could lead to bad security practices. + // + // If agentsDir was sent from the renderer to tshd and the main process, those recipients could + // not trust that agentsDir has not been tampered with. Instead, the renderer should merely send + // the root cluster URI and the recipients should build the path to the specific agent dir from + // that, with agentsDir being supplied out of band. + return path.resolve(userDataDir, 'agents'); +} + function getAgentDirectoryOrThrow( userDataDir: string, profileName: string ): string { - const agentsDirectory = path.resolve(userDataDir, 'agents'); - const resolved = path.resolve(agentsDirectory, profileName); + const agentsDir = getAgentsDir(userDataDir); + const resolved = path.resolve(agentsDir, profileName); // check if the path doesn't contain any unexpected segments const isValidPath = - path.dirname(resolved) === agentsDirectory && + path.dirname(resolved) === agentsDir && path.basename(resolved) === profileName; if (!isValidPath) { throw new Error(`The agent config path is incorrect: ${resolved}`); diff --git a/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts b/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts index 9818843806e03..67e584206c08a 100644 --- a/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts +++ b/web/packages/teleterm/src/mainProcess/fixtures/mocks.ts @@ -108,6 +108,10 @@ export class MockMainProcessClient implements MainProcessClient { return { status: 'not-started' }; } + getAgentLogs(): string { + return ''; + } + subscribeToAgentUpdate() { return { cleanup: () => undefined }; } diff --git a/web/packages/teleterm/src/mainProcess/mainProcess.ts b/web/packages/teleterm/src/mainProcess/mainProcess.ts index bf3859eb56731..2a47bb74a16be 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcess.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcess.ts @@ -361,6 +361,18 @@ export default class MainProcess { } ); + ipcMain.on( + 'main-process-connect-my-computer-get-agent-logs', + ( + event, + args: { + rootClusterUri: RootClusterUri; + } + ) => { + event.returnValue = this.agentRunner.getLogs(args.rootClusterUri); + } + ); + subscribeToTerminalContextMenuEvent(); subscribeToTabContextMenuEvent(); subscribeToConfigServiceEvents(this.configService); diff --git a/web/packages/teleterm/src/mainProcess/mainProcessClient.ts b/web/packages/teleterm/src/mainProcess/mainProcessClient.ts index 87f62f6d29e49..260fe26776dfe 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcessClient.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcessClient.ts @@ -112,6 +112,12 @@ export default function createMainProcessClient(): MainProcessClient { clusterProperties ); }, + getAgentLogs(clusterProperties: { rootClusterUri: RootClusterUri }) { + return ipcRenderer.sendSync( + 'main-process-connect-my-computer-get-agent-logs', + clusterProperties + ); + }, subscribeToAgentUpdate: (rootClusterUri, listener) => { const onChange = ( _, diff --git a/web/packages/teleterm/src/mainProcess/runtimeSettings.ts b/web/packages/teleterm/src/mainProcess/runtimeSettings.ts index d29868ec36e8f..2441fe61cd0fb 100644 --- a/web/packages/teleterm/src/mainProcess/runtimeSettings.ts +++ b/web/packages/teleterm/src/mainProcess/runtimeSettings.ts @@ -25,6 +25,7 @@ import { staticConfig } from 'teleterm/staticConfig'; import { GrpcServerAddresses, RuntimeSettings } from './types'; import { loadInstallationId } from './loadInstallationId'; +import { getAgentsDir } from './createAgentConfigFile'; const { argv, env } = process; @@ -64,6 +65,8 @@ export function getRuntimeSettings(): RuntimeSettings { // Before switching to the recommended path, we need to investigate the impact of this change. // https://www.electronjs.org/docs/latest/api/app#appgetpathname const logsDir = path.join(userDataDir, 'logs'); + // DO NOT expose agentsDir through RuntimeSettings. See the comment in getAgentsDir. + const agentsDir = getAgentsDir(userDataDir); const tshd = { insecure: isInsecure, @@ -79,6 +82,7 @@ export function getRuntimeSettings(): RuntimeSettings { `--certs-dir=${getCertsDir()}`, `--prehog-addr=${staticConfig.prehogAddress}`, `--kubeconfigs-dir=${kubeConfigsDir}`, + `--agents-dir=${agentsDir}`, ], }; const sharedProcess = { diff --git a/web/packages/teleterm/src/mainProcess/types.ts b/web/packages/teleterm/src/mainProcess/types.ts index a0efd99cd4250..4d04a7d6869f1 100644 --- a/web/packages/teleterm/src/mainProcess/types.ts +++ b/web/packages/teleterm/src/mainProcess/types.ts @@ -109,6 +109,7 @@ export type MainProcessClient = { }): Promise; killAgent(args: { rootClusterUri: RootClusterUri }): Promise; getAgentState(args: { rootClusterUri: RootClusterUri }): AgentProcessState; + getAgentLogs(args: { rootClusterUri: RootClusterUri }): string; subscribeToAgentUpdate: SubscribeToAgentUpdate; }; diff --git a/web/packages/teleterm/src/services/tshd/createAbortController.test.ts b/web/packages/teleterm/src/services/tshd/createAbortController.test.ts new file mode 100644 index 0000000000000..4efeba8195833 --- /dev/null +++ b/web/packages/teleterm/src/services/tshd/createAbortController.test.ts @@ -0,0 +1,44 @@ +/** + * Copyright 2023 Gravitational, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import createAbortController from './createAbortController'; + +test('abort controller emits the abort event only once', () => { + const listener = jest.fn(); + const controller = createAbortController(); + + controller.signal.addEventListener(listener); + controller.abort(); + + // This makes sure that the implementation doesn't depend solely on `emitter.once('abort', cb)` to + // implement this. Once a signal has been aborted, its state changes and it cannot be reused. + // + // This mirrors the browser implementation. + const listenerAddedAfterAbort = jest.fn(); + controller.signal.addEventListener(listenerAddedAfterAbort); + controller.abort(); + + expect(listener).toHaveBeenCalledTimes(1); + expect(listenerAddedAfterAbort).not.toHaveBeenCalled(); +}); + +test('abort updates signal.aborted', () => { + const controller = createAbortController(); + expect(controller.signal.aborted).toBe(false); + + controller.abort(); + expect(controller.signal.aborted).toBe(true); +}); diff --git a/web/packages/teleterm/src/services/tshd/createAbortController.ts b/web/packages/teleterm/src/services/tshd/createAbortController.ts index 79f219fa1eda6..37214a1695f17 100644 --- a/web/packages/teleterm/src/services/tshd/createAbortController.ts +++ b/web/packages/teleterm/src/services/tshd/createAbortController.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +// This file works both in the browser and Node.js. +// In Node environment, it imports the built-in events module. +// In browser environment, it imports the events package. import { EventEmitter } from 'events'; import { TshAbortController } from './types'; @@ -25,8 +28,14 @@ export default function createAbortController(): TshAbortController { const emitter = new EventEmitter(); const signal = { + aborted: false, + // TODO(ravicious): Consider aligning the interface of TshAbortSignal with the interface of + // browser's AbortSignal so that those two can be used interchangeably, for example in the wait + // function from the shared package. + // + // TshAbortSignal doesn't accept the event name as the first argument. addEventListener(cb: (...args: any[]) => void) { - emitter.addListener('abort', cb); + emitter.once('abort', cb); }, removeEventListener(cb: (...args: any[]) => void) { @@ -37,6 +46,13 @@ export default function createAbortController(): TshAbortController { return { signal, abort() { + // Once abort() has been called and the signal becomes aborted, it cannot be reused. + // https://dom.spec.whatwg.org/#abortsignal-signal-abort + if (signal.aborted) { + return; + } + + signal.aborted = true; emitter.emit('abort'); }, }; diff --git a/web/packages/teleterm/src/services/tshd/createClient.ts b/web/packages/teleterm/src/services/tshd/createClient.ts index d6bfe86a5d685..70fa51ca2a76c 100644 --- a/web/packages/teleterm/src/services/tshd/createClient.ts +++ b/web/packages/teleterm/src/services/tshd/createClient.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ChannelCredentials, ClientDuplexStream } from '@grpc/grpc-js'; +import grpc from '@grpc/grpc-js'; import * as api from 'gen-proto-js/teleport/lib/teleterm/v1/service_pb'; import { TerminalServiceClient } from 'gen-proto-js/teleport/lib/teleterm/v1/service_grpc_pb'; import { @@ -37,7 +37,7 @@ import { export default function createClient( addr: string, - credentials: ChannelCredentials + credentials: grpc.ChannelCredentials ) { const logger = new Logger('tshd'); const tshd = middleware(new TerminalServiceClient(addr, credentials), [ @@ -437,7 +437,7 @@ export default function createClient( return new Promise((resolve, reject) => { callRef.current = tshd.loginPasswordless(); - const stream = callRef.current as ClientDuplexStream< + const stream = callRef.current as grpc.ClientDuplexStream< api.LoginPasswordlessRequest, api.LoginPasswordlessResponse >; @@ -707,6 +707,37 @@ export default function createClient( }); }, + waitForConnectMyComputerNodeJoin( + uri: uri.RootClusterUri, + abortSignal: types.TshAbortSignal + ) { + const req = + new api.WaitForConnectMyComputerNodeJoinRequest().setRootClusterUri( + uri + ); + + return withAbort( + abortSignal, + callRef => + new Promise( + (resolve, reject) => { + callRef.current = tshd.waitForConnectMyComputerNodeJoin( + req, + (err, response) => { + if (err) { + reject(err); + } else { + resolve( + response.toObject() as types.WaitForConnectMyComputerNodeJoinResponse + ); + } + } + ); + } + ) + ); + }, + async updateHeadlessAuthenticationState( params: UpdateHeadlessAuthenticationStateParams, abortSignal?: types.TshAbortSignal diff --git a/web/packages/teleterm/src/services/tshd/createFileTransferStream.ts b/web/packages/teleterm/src/services/tshd/createFileTransferStream.ts index 4acb66f92ab95..322feab73395f 100644 --- a/web/packages/teleterm/src/services/tshd/createFileTransferStream.ts +++ b/web/packages/teleterm/src/services/tshd/createFileTransferStream.ts @@ -23,7 +23,7 @@ import { TshAbortSignal } from './types'; export function createFileTransferStream( stream: ClientReadableStream, - abortSignal?: TshAbortSignal + abortSignal: TshAbortSignal ): FileTransferListeners { abortSignal.addEventListener(() => stream.cancel()); diff --git a/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts b/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts index dbf757bc0c7ac..00fa3cf9674a3 100644 --- a/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts +++ b/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts @@ -16,86 +16,71 @@ import { makeRootCluster } from 'teleterm/services/tshd/testHelpers'; -import { - AuthSettings, - AccessRequest, - Cluster, - CreateAccessRequestParams, - CreateGatewayParams, - Gateway, - GetDatabasesResponse, - GetKubesResponse, - GetRequestableRolesParams, - GetServersResponse, - LoginLocalParams, - LoginPasswordlessParams, - LoginSsoParams, - ReviewAccessRequestParams, - GetResourcesParams, - TshAbortController, - TshAbortSignal, - TshClient, - GetRequestableRolesResponse, - UpdateHeadlessAuthenticationStateParams, -} from '../types'; +import * as types from '../types'; -export class MockTshClient implements TshClient { - listRootClusters: () => Promise; +export class MockTshClient implements types.TshClient { + listRootClusters: () => Promise; listLeafClusters = () => Promise.resolve([]); - getKubes: (params: GetResourcesParams) => Promise; - getDatabases: (params: GetResourcesParams) => Promise; + getKubes: ( + params: types.GetResourcesParams + ) => Promise; + getDatabases: ( + params: types.GetResourcesParams + ) => Promise; listDatabaseUsers: (dbUri: string) => Promise; getRequestableRoles: ( - params: GetRequestableRolesParams - ) => Promise; - getServers: (params: GetResourcesParams) => Promise; + params: types.GetRequestableRolesParams + ) => Promise; + getServers: ( + params: types.GetResourcesParams + ) => Promise; assumeRole: ( clusterUri: string, requestIds: string[], dropIds: string[] ) => Promise; deleteAccessRequest: (clusterUri: string, requestId: string) => Promise; - getAccessRequests: (clusterUri: string) => Promise; + getAccessRequests: (clusterUri: string) => Promise; getAccessRequest: ( clusterUri: string, requestId: string - ) => Promise; + ) => Promise; reviewAccessRequest: ( clusterUri: string, - params: ReviewAccessRequestParams - ) => Promise; + params: types.ReviewAccessRequestParams + ) => Promise; createAccessRequest: ( - params: CreateAccessRequestParams - ) => Promise; - createAbortController: () => TshAbortController; - addRootCluster: (addr: string) => Promise; + params: types.CreateAccessRequestParams + ) => Promise; + createAbortController: () => types.TshAbortController; + addRootCluster: (addr: string) => Promise; - listGateways: () => Promise; - createGateway: (params: CreateGatewayParams) => Promise; + listGateways: () => Promise; + createGateway: (params: types.CreateGatewayParams) => Promise; removeGateway: (gatewayUri: string) => Promise; setGatewayTargetSubresourceName: ( gatewayUri: string, targetSubresourceName: string - ) => Promise; + ) => Promise; setGatewayLocalPort: ( gatewayUri: string, localPort: string - ) => Promise; + ) => Promise; getCluster = () => Promise.resolve(makeRootCluster()); - getAuthSettings: (clusterUri: string) => Promise; + getAuthSettings: (clusterUri: string) => Promise; removeCluster = () => Promise.resolve(); loginLocal: ( - params: LoginLocalParams, - abortSignal?: TshAbortSignal + params: types.LoginLocalParams, + abortSignal?: types.TshAbortSignal ) => Promise; loginSso: ( - params: LoginSsoParams, - abortSignal?: TshAbortSignal + params: types.LoginSsoParams, + abortSignal?: types.TshAbortSignal ) => Promise; loginPasswordless: ( - params: LoginPasswordlessParams, - abortSignal?: TshAbortSignal + params: types.LoginPasswordlessParams, + abortSignal?: types.TshAbortSignal ) => Promise; logout = () => Promise.resolve(); transferFile: () => undefined; @@ -105,8 +90,9 @@ export class MockTshClient implements TshClient { createConnectMyComputerNodeToken = () => Promise.resolve({ token: 'abc', labelsList: [] }); deleteConnectMyComputerToken = () => Promise.resolve(); + waitForConnectMyComputerNodeJoin: () => Promise; updateHeadlessAuthenticationState: ( - params: UpdateHeadlessAuthenticationStateParams + params: types.UpdateHeadlessAuthenticationStateParams ) => Promise; } diff --git a/web/packages/teleterm/src/services/tshd/types.ts b/web/packages/teleterm/src/services/tshd/types.ts index 3b7dd6ff8c384..55b686e7efcd7 100644 --- a/web/packages/teleterm/src/services/tshd/types.ts +++ b/web/packages/teleterm/src/services/tshd/types.ts @@ -247,6 +247,10 @@ export type TshClient = { clusterUri: uri.RootClusterUri, token: string ) => Promise; + waitForConnectMyComputerNodeJoin: ( + rootClusterUri: uri.RootClusterUri, + abortSignal: TshAbortSignal + ) => Promise; updateHeadlessAuthenticationState: ( params: UpdateHeadlessAuthenticationStateParams, @@ -260,6 +264,7 @@ export type TshAbortController = { }; export type TshAbortSignal = { + readonly aborted: boolean; addEventListener(cb: (...args: any[]) => void): void; removeEventListener(cb: (...args: any[]) => void): void; }; @@ -341,9 +346,12 @@ export type Label = apiLabel.Label.AsObject; export type CreateConnectMyComputerRoleResponse = apiService.CreateConnectMyComputerRoleResponse.AsObject; - export type CreateConnectMyComputerNodeTokenResponse = apiService.CreateConnectMyComputerNodeTokenResponse.AsObject; +export type WaitForConnectMyComputerNodeJoinResponse = + apiService.WaitForConnectMyComputerNodeJoinResponse.AsObject & { + server: Server; + }; // Replaces object property with a new type type Modify = Omit & R; diff --git a/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerSetup/DocumentConnectMyComputerSetup.tsx b/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerSetup/DocumentConnectMyComputerSetup.tsx index c17d0469a9240..3fe4545ab02d7 100644 --- a/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerSetup/DocumentConnectMyComputerSetup.tsx +++ b/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerSetup/DocumentConnectMyComputerSetup.tsx @@ -26,6 +26,7 @@ import { useWorkspaceContext } from 'teleterm/ui/Documents'; import { retryWithRelogin } from 'teleterm/ui/utils'; import { AgentProcessError, + NodeWaitJoinTimeout, useConnectMyComputerContext, } from 'teleterm/ui/ConnectMyComputer'; import Logger from 'teleterm/logger'; @@ -107,31 +108,33 @@ function AgentSetup({ rootClusterUri }: { rootClusterUri: RootClusterUri }) { const [createRoleAttempt, runCreateRoleAttempt, setCreateRoleAttempt] = useAsync( - useCallback(async () => { - retryWithRelogin(ctx, rootClusterUri, async () => { - let certsReloaded = false; + useCallback( + () => + retryWithRelogin(ctx, rootClusterUri, async () => { + let certsReloaded = false; - try { - const response = await ctx.connectMyComputerService.createRole( - rootClusterUri - ); - certsReloaded = response.certsReloaded; - } catch (error) { - if (isAccessDeniedError(error)) { - throw new Error( - 'Access denied. Contact your administrator for permissions to manage users and roles.' + try { + const response = await ctx.connectMyComputerService.createRole( + rootClusterUri ); + certsReloaded = response.certsReloaded; + } catch (error) { + if (isAccessDeniedError(error)) { + throw new Error( + 'Access denied. Contact your administrator for permissions to manage users and roles.' + ); + } + throw error; } - throw error; - } - // If tshd reloaded the certs to refresh the role list, the Electron app must resync details - // of the cluster to also update the role list in the UI. - if (certsReloaded) { - await ctx.clustersService.syncRootCluster(rootClusterUri); - } - }); - }, [ctx, rootClusterUri]) + // If tshd reloaded the certs to refresh the role list, the Electron app must resync details + // of the cluster to also update the role list in the UI. + if (certsReloaded) { + await ctx.clustersService.syncRootCluster(rootClusterUri); + } + }), + [ctx, rootClusterUri] + ) ); const [ generateConfigFileAttempt, @@ -192,6 +195,20 @@ function AgentSetup({ rootClusterUri }: { rootClusterUri: RootClusterUri }) { return; } + if (joinClusterAttempt.error instanceof NodeWaitJoinTimeout) { + return ( + <> + + + + ); + } + if (!(joinClusterAttempt.error instanceof AgentProcessError)) { return ; } diff --git a/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerStatus/DocumentConnectMyComputerStatus.tsx b/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerStatus/DocumentConnectMyComputerStatus.tsx index 61130bbac16b9..8f0bf12e368b0 100644 --- a/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerStatus/DocumentConnectMyComputerStatus.tsx +++ b/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputerStatus/DocumentConnectMyComputerStatus.tsx @@ -37,6 +37,7 @@ import Indicator from 'design/Indicator'; import { AgentProcessError, CurrentAction, + NodeWaitJoinTimeout, useConnectMyComputerContext, } from 'teleterm/ui/ConnectMyComputer'; import { assertUnreachable } from 'teleterm/ui/utils'; @@ -272,6 +273,16 @@ function prettifyCurrentAction(currentAction: CurrentAction): { }; } case 'error': { + if (currentAction.attempt.error instanceof NodeWaitJoinTimeout) { + return { + Icon: StyledWarning, + title: 'Failed to start agent', + error: + 'The agent did not join the cluster within the timeout window.', + logs: currentAction.attempt.error.logs, + }; + } + if (!(currentAction.attempt.error instanceof AgentProcessError)) { return { Icon: StyledWarning, diff --git a/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.test.tsx b/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.test.tsx index 18a232989e0d6..b6275ea8566a2 100644 --- a/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.test.tsx +++ b/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.test.tsx @@ -81,6 +81,14 @@ test('startAgent re-throws errors that are thrown while spawning the process', a status: 'error', message: 'ENOENT', }; + + jest + .spyOn(appContext.connectMyComputerService, 'waitForNodeToJoin') + .mockImplementation( + // Hang until abort. + (rootClusterUri, abortSignal) => + new Promise((resolve, reject) => abortSignal.addEventListener(reject)) + ); jest .spyOn(appContext.mainProcessClient, 'getAgentState') .mockImplementation(() => errorStatus); @@ -114,7 +122,6 @@ test('startAgent re-throws errors that are thrown while spawning the process', a await act(async () => { [, error] = await result.current.startAgent(); }); - expect(error).toBeInstanceOf(AgentProcessError); expect(result.current.currentAction).toStrictEqual({ kind: 'start', attempt: makeErrorAttempt(new AgentProcessError()), @@ -123,10 +130,16 @@ test('startAgent re-throws errors that are thrown while spawning the process', a message: 'ENOENT', }, }); + expect(error).toBeInstanceOf(AgentProcessError); }); test('starting the agent flips the workspace autoStart flag to true', async () => { const { appContext, rootCluster } = getMocksWithConnectMyComputerEnabled(); + + jest + .spyOn(appContext.connectMyComputerService, 'waitForNodeToJoin') + .mockResolvedValue(makeServer()); + const { result } = renderHook(() => useConnectMyComputerContext(), { wrapper: ({ children }) => ( @@ -139,7 +152,10 @@ test('starting the agent flips the workspace autoStart flag to true', async () = ), }); - await act(() => result.current.startAgent()); + await act(async () => { + const [, error] = await result.current.startAgent(); + expect(error).toBeFalsy(); + }); expect( appContext.workspacesService.getConnectMyComputerAutoStart(rootCluster.uri) diff --git a/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx b/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx index d8bcb9a15d22a..9d3d54d5d2996 100644 --- a/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx +++ b/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx @@ -23,9 +23,6 @@ import React, { useMemo, useState, } from 'react'; - -import { wait } from 'shared/utils/wait'; - import { Attempt, makeSuccessAttempt, @@ -35,8 +32,8 @@ import { import { RootClusterUri } from 'teleterm/ui/uri'; import { useAppContext } from 'teleterm/ui/appContextProvider'; - -import { Server } from 'teleterm/services/tshd/types'; +import { Server, TshAbortSignal } from 'teleterm/services/tshd/types'; +import createAbortController from 'teleterm/services/tshd/createAbortController'; import { assertUnreachable } from '../utils'; @@ -86,7 +83,7 @@ const ConnectMyComputerContext = createContext(null); export const ConnectMyComputerContextProvider: FC<{ rootClusterUri: RootClusterUri; -}> = props => { +}> = ({ rootClusterUri, children }) => { const { mainProcessClient, connectMyComputerService, @@ -103,16 +100,15 @@ export const ConnectMyComputerContextProvider: FC<{ setAgentConfiguredAttempt, ] = useAsync( useCallback( - () => - connectMyComputerService.isAgentConfigFileCreated(props.rootClusterUri), - [connectMyComputerService, props.rootClusterUri] + () => connectMyComputerService.isAgentConfigFileCreated(rootClusterUri), + [connectMyComputerService, rootClusterUri] ) ); const isAgentConfigured = isAgentConfiguredAttempt.status === 'success' && isAgentConfiguredAttempt.data; - const rootCluster = clustersService.findCluster(props.rootClusterUri); + const rootCluster = clustersService.findCluster(rootClusterUri); const canUse = useMemo(() => { const isFeatureFlagEnabled = configService.get( 'feature.connectMyComputer' @@ -133,7 +129,7 @@ export const ConnectMyComputerContextProvider: FC<{ const [agentProcessState, setAgentProcessState] = useState( () => mainProcessClient.getAgentState({ - rootClusterUri: props.rootClusterUri, + rootClusterUri, }) || { status: 'not-started', } @@ -150,36 +146,33 @@ export const ConnectMyComputerContextProvider: FC<{ const [startAgentAttempt, startAgent] = useAsync( useCallback(async () => { setCurrentActionKind('start'); - await connectMyComputerService.runAgent(props.rootClusterUri); - const abortController = new AbortController(); + await connectMyComputerService.runAgent(rootClusterUri); + + const abortController = createAbortController(); try { const server = await Promise.race([ connectMyComputerService.waitForNodeToJoin( - props.rootClusterUri, + rootClusterUri, abortController.signal ), throwOnAgentProcessErrors( mainProcessClient, - props.rootClusterUri, + rootClusterUri, abortController.signal ), wait(20_000, abortController.signal).then(() => { - throw new Error( - 'The agent did not manage to join the cluster within 20 seconds.' - ); + const logs = mainProcessClient.getAgentLogs({ rootClusterUri }); + throw new NodeWaitJoinTimeout(logs); }), ]); setCurrentActionKind('observe-process'); - workspacesService.setConnectMyComputerAutoStart( - props.rootClusterUri, - true - ); - usageService.captureConnectMyComputerAgentStart(props.rootClusterUri); + workspacesService.setConnectMyComputerAutoStart(rootClusterUri, true); + usageService.captureConnectMyComputerAgentStart(rootClusterUri); return server; } catch (error) { // in case of any error kill the agent - await connectMyComputerService.killAgent(props.rootClusterUri); + await connectMyComputerService.killAgent(rootClusterUri); throw error; } finally { abortController.abort(); @@ -187,7 +180,7 @@ export const ConnectMyComputerContextProvider: FC<{ }, [ connectMyComputerService, mainProcessClient, - props.rootClusterUri, + rootClusterUri, usageService, workspacesService, ]) @@ -204,13 +197,10 @@ export const ConnectMyComputerContextProvider: FC<{ const [killAgentAttempt, killAgent] = useAsync( useCallback(async () => { setCurrentActionKind('kill'); - await connectMyComputerService.killAgent(props.rootClusterUri); + await connectMyComputerService.killAgent(rootClusterUri); setCurrentActionKind('observe-process'); - workspacesService.setConnectMyComputerAutoStart( - props.rootClusterUri, - false - ); - }, [connectMyComputerService, props.rootClusterUri, workspacesService]) + workspacesService.setConnectMyComputerAutoStart(rootClusterUri, false); + }, [connectMyComputerService, rootClusterUri, workspacesService]) ); const markAgentAsConfigured = useCallback(() => { @@ -223,11 +213,11 @@ export const ConnectMyComputerContextProvider: FC<{ useEffect(() => { const { cleanup } = mainProcessClient.subscribeToAgentUpdate( - props.rootClusterUri, + rootClusterUri, setAgentProcessState ); return cleanup; - }, [mainProcessClient, props.rootClusterUri]); + }, [mainProcessClient, rootClusterUri]); let currentAction: CurrentAction; const kind = currentActionKind; @@ -275,7 +265,7 @@ export const ConnectMyComputerContextProvider: FC<{ const shouldAutoStartAgent = isAgentConfigured && canUse && - workspacesService.getConnectMyComputerAutoStart(props.rootClusterUri) && + workspacesService.getConnectMyComputerAutoStart(rootClusterUri) && agentIsNotStarted; if (shouldAutoStartAgent) { downloadAndStartAgent(); @@ -285,7 +275,7 @@ export const ConnectMyComputerContextProvider: FC<{ downloadAndStartAgent, agentIsNotStarted, isAgentConfigured, - props.rootClusterUri, + rootClusterUri, workspacesService, ]); @@ -306,7 +296,7 @@ export const ConnectMyComputerContextProvider: FC<{ markAgentAsNotConfigured, isAgentConfiguredAttempt, }} - children={props.children} + children={children} /> ); }; @@ -329,7 +319,7 @@ export const useConnectMyComputerContext = () => { function throwOnAgentProcessErrors( mainProcessClient: MainProcessClient, rootClusterUri: RootClusterUri, - abortSignal: AbortSignal + abortSignal: TshAbortSignal ): Promise { return new Promise((_, reject) => { const rejectOnError = (agentProcessState: AgentProcessState) => { @@ -348,12 +338,12 @@ function throwOnAgentProcessErrors( rootClusterUri, rejectOnError ); - abortSignal.onabort = () => { + abortSignal.addEventListener(() => { cleanup(); reject( new DOMException('throwOnAgentProcessErrors was aborted', 'AbortError') ); - }; + }); // the state may have changed before we started listening, we have to check the current state rejectOnError( @@ -370,3 +360,35 @@ export class AgentProcessError extends Error { this.name = 'AgentProcessError'; } } + +export class NodeWaitJoinTimeout extends Error { + constructor(public readonly logs: string) { + super('NodeWaitJoinTimeout'); + this.name = 'NodeWaitJoinTimeout'; + } +} + +/** + * wait is like wait from the shared package, but it works with TshAbortSignal. + * TODO(ravicious): Refactor TshAbortSignal so that its interface is the same as AbortSignal. + * See the comment in createAbortController for more details. + */ +function wait(ms: number, abortSignal: TshAbortSignal): Promise { + if (abortSignal.aborted) { + return Promise.reject(new DOMException('Wait was aborted.', 'AbortError')); + } + + return new Promise((resolve, reject) => { + const abort = () => { + clearTimeout(timeout); + reject(new DOMException('Wait was aborted.', 'AbortError')); + }; + const done = () => { + abortSignal.removeEventListener(abort); + resolve(); + }; + + const timeout = setTimeout(done, ms); + abortSignal.addEventListener(abort); + }); +} diff --git a/web/packages/teleterm/src/ui/services/connectMyComputer/connectMyComputerService.ts b/web/packages/teleterm/src/ui/services/connectMyComputer/connectMyComputerService.ts index bee7b7a05956f..1b03d5e4ecfd1 100644 --- a/web/packages/teleterm/src/ui/services/connectMyComputer/connectMyComputerService.ts +++ b/web/packages/teleterm/src/ui/services/connectMyComputer/connectMyComputerService.ts @@ -14,13 +14,12 @@ * limitations under the License. */ -import { wait } from 'shared/utils/wait'; - import { MainProcessClient } from 'teleterm/mainProcess/types'; import { Cluster, CreateConnectMyComputerRoleResponse, Server, + TshAbortSignal, TshClient, } from 'teleterm/services/tshd/types'; @@ -83,26 +82,13 @@ export class ConnectMyComputerService { async waitForNodeToJoin( rootClusterUri: uri.RootClusterUri, - abortSignal: AbortSignal + abortSignal: TshAbortSignal ): Promise { - // TODO(gzdunek): Replace with waiting for the node to join. - await wait(1_000, abortSignal); - return { - uri: `${rootClusterUri}/servers/178ef081-259b-4aa5-a018-449b5ea7e694`, - tunnel: false, - name: '178ef081-259b-4aa5-a018-449b5ea7e694', - hostname: 'foo', - addr: '127.0.0.1:3022', - labelsList: [ - { - name: 'hostname', - value: 'mbp.home', - }, - { - name: 'teleport.dev/connect-my-computer', - value: 'abcd@goteleport.com', - }, - ], - }; + const response = await this.tshClient.waitForConnectMyComputerNodeJoin( + rootClusterUri, + abortSignal + ); + + return response.server; } } diff --git a/web/packages/teleterm/src/ui/services/fileTransferClient/fileTransferService.ts b/web/packages/teleterm/src/ui/services/fileTransferClient/fileTransferService.ts index ad48ac154bc32..011759913b09b 100644 --- a/web/packages/teleterm/src/ui/services/fileTransferClient/fileTransferService.ts +++ b/web/packages/teleterm/src/ui/services/fileTransferClient/fileTransferService.ts @@ -34,6 +34,7 @@ export class FileTransferService { abortController: AbortController ): FileTransferListeners { const abortSignal = { + aborted: false, addEventListener: (cb: (...args: any[]) => void) => { abortController.signal.addEventListener('abort', cb); }, @@ -41,6 +42,13 @@ export class FileTransferService { abortController.signal.removeEventListener('abort', cb); }, }; + abortController.signal.addEventListener( + 'abort', + () => { + abortSignal.aborted = true; + }, + { once: true } + ); const listeners = this.tshClient.transferFile(options, abortSignal); if ( options.direction === diff --git a/yarn.lock b/yarn.lock index 03304f37e1054..12482aa9c72ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7589,12 +7589,7 @@ eventemitter3@^4.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/events/-/events-1.0.2.tgz#75849dcfe93d10fb057c30055afdbd51d06a8e24" - integrity sha1-dYSdz+k9EPsFfDAFWv29UdBqjiQ= - -events@^3.2.0, events@^3.3.0: +events@3.3.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==