Add PingHosts RPC with async multi-host ping support

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Kaien 2026-04-15 15:05:14 +03:00
parent 22309e99c2
commit 557243af1c
3 changed files with 267 additions and 19 deletions

View File

@ -529,6 +529,178 @@ func (x *GetServerStatusResponse) GetPeersCount() int32 {
return 0 return 0
} }
type PingHostsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Hosts []string `protobuf:"bytes,1,rep,name=hosts,proto3" json:"hosts,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PingHostsRequest) Reset() {
*x = PingHostsRequest{}
mi := &file_proto_agent_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PingHostsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PingHostsRequest) ProtoMessage() {}
func (x *PingHostsRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_agent_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PingHostsRequest.ProtoReflect.Descriptor instead.
func (*PingHostsRequest) Descriptor() ([]byte, []int) {
return file_proto_agent_proto_rawDescGZIP(), []int{9}
}
func (x *PingHostsRequest) GetHosts() []string {
if x != nil {
return x.Hosts
}
return nil
}
type PingResult struct {
state protoimpl.MessageState `protogen:"open.v1"`
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
LatencyMs float64 `protobuf:"fixed64,2,opt,name=latency_ms,json=latencyMs,proto3" json:"latency_ms,omitempty"` // -1 if unreachable
Reachable bool `protobuf:"varint,3,opt,name=reachable,proto3" json:"reachable,omitempty"`
Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PingResult) Reset() {
*x = PingResult{}
mi := &file_proto_agent_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PingResult) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PingResult) ProtoMessage() {}
func (x *PingResult) ProtoReflect() protoreflect.Message {
mi := &file_proto_agent_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PingResult.ProtoReflect.Descriptor instead.
func (*PingResult) Descriptor() ([]byte, []int) {
return file_proto_agent_proto_rawDescGZIP(), []int{10}
}
func (x *PingResult) GetHost() string {
if x != nil {
return x.Host
}
return ""
}
func (x *PingResult) GetLatencyMs() float64 {
if x != nil {
return x.LatencyMs
}
return 0
}
func (x *PingResult) GetReachable() bool {
if x != nil {
return x.Reachable
}
return false
}
func (x *PingResult) GetError() string {
if x != nil {
return x.Error
}
return ""
}
type PingHostsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
Results []*PingResult `protobuf:"bytes,3,rep,name=results,proto3" json:"results,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *PingHostsResponse) Reset() {
*x = PingHostsResponse{}
mi := &file_proto_agent_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *PingHostsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PingHostsResponse) ProtoMessage() {}
func (x *PingHostsResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_agent_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PingHostsResponse.ProtoReflect.Descriptor instead.
func (*PingHostsResponse) Descriptor() ([]byte, []int) {
return file_proto_agent_proto_rawDescGZIP(), []int{11}
}
func (x *PingHostsResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
func (x *PingHostsResponse) GetError() string {
if x != nil {
return x.Error
}
return ""
}
func (x *PingHostsResponse) GetResults() []*PingResult {
if x != nil {
return x.Results
}
return nil
}
var File_proto_agent_proto protoreflect.FileDescriptor var File_proto_agent_proto protoreflect.FileDescriptor
const file_proto_agent_proto_rawDesc = "" + const file_proto_agent_proto_rawDesc = "" +
@ -573,12 +745,26 @@ const file_proto_agent_proto_rawDesc = "" +
"public_key\x18\x04 \x01(\tR\tpublicKey\x12%\n" + "public_key\x18\x04 \x01(\tR\tpublicKey\x12%\n" +
"\x0elistening_port\x18\x05 \x01(\tR\rlisteningPort\x12\x1f\n" + "\x0elistening_port\x18\x05 \x01(\tR\rlisteningPort\x12\x1f\n" +
"\vpeers_count\x18\x06 \x01(\x05R\n" + "\vpeers_count\x18\x06 \x01(\x05R\n" +
"peersCount2\xdb\x02\n" + "peersCount\"(\n" +
"\x10PingHostsRequest\x12\x14\n" +
"\x05hosts\x18\x01 \x03(\tR\x05hosts\"s\n" +
"\n" +
"PingResult\x12\x12\n" +
"\x04host\x18\x01 \x01(\tR\x04host\x12\x1d\n" +
"\n" +
"latency_ms\x18\x02 \x01(\x01R\tlatencyMs\x12\x1c\n" +
"\treachable\x18\x03 \x01(\bR\treachable\x12\x14\n" +
"\x05error\x18\x04 \x01(\tR\x05error\"p\n" +
"\x11PingHostsResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess\x12\x14\n" +
"\x05error\x18\x02 \x01(\tR\x05error\x12+\n" +
"\aresults\x18\x03 \x03(\v2\x11.agent.PingResultR\aresults2\x9b\x03\n" +
"\x0eWireGuardAgent\x12S\n" + "\x0eWireGuardAgent\x12S\n" +
"\x10GetClientConfigs\x12\x1e.agent.GetClientConfigsRequest\x1a\x1f.agent.GetClientConfigsResponse\x12M\n" + "\x10GetClientConfigs\x12\x1e.agent.GetClientConfigsRequest\x1a\x1f.agent.GetClientConfigsResponse\x12M\n" +
"\x0eRegisterClient\x12\x1c.agent.RegisterClientRequest\x1a\x1d.agent.RegisterClientResponse\x12S\n" + "\x0eRegisterClient\x12\x1c.agent.RegisterClientRequest\x1a\x1d.agent.RegisterClientResponse\x12S\n" +
"\x10UnregisterClient\x12\x1e.agent.UnregisterClientRequest\x1a\x1f.agent.UnregisterClientResponse\x12P\n" + "\x10UnregisterClient\x12\x1e.agent.UnregisterClientRequest\x1a\x1f.agent.UnregisterClientResponse\x12P\n" +
"\x0fGetServerStatus\x12\x1d.agent.GetServerStatusRequest\x1a\x1e.agent.GetServerStatusResponseB\x13Z\x11l_vpn_proto/protob\x06proto3" "\x0fGetServerStatus\x12\x1d.agent.GetServerStatusRequest\x1a\x1e.agent.GetServerStatusResponse\x12>\n" +
"\tPingHosts\x12\x17.agent.PingHostsRequest\x1a\x18.agent.PingHostsResponseB\x13Z\x11l_vpn_proto/protob\x06proto3"
var ( var (
file_proto_agent_proto_rawDescOnce sync.Once file_proto_agent_proto_rawDescOnce sync.Once
@ -592,7 +778,7 @@ func file_proto_agent_proto_rawDescGZIP() []byte {
return file_proto_agent_proto_rawDescData return file_proto_agent_proto_rawDescData
} }
var file_proto_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_proto_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_proto_agent_proto_goTypes = []any{ var file_proto_agent_proto_goTypes = []any{
(*GetClientConfigsRequest)(nil), // 0: agent.GetClientConfigsRequest (*GetClientConfigsRequest)(nil), // 0: agent.GetClientConfigsRequest
(*ClientConfig)(nil), // 1: agent.ClientConfig (*ClientConfig)(nil), // 1: agent.ClientConfig
@ -603,23 +789,29 @@ var file_proto_agent_proto_goTypes = []any{
(*UnregisterClientResponse)(nil), // 6: agent.UnregisterClientResponse (*UnregisterClientResponse)(nil), // 6: agent.UnregisterClientResponse
(*GetServerStatusRequest)(nil), // 7: agent.GetServerStatusRequest (*GetServerStatusRequest)(nil), // 7: agent.GetServerStatusRequest
(*GetServerStatusResponse)(nil), // 8: agent.GetServerStatusResponse (*GetServerStatusResponse)(nil), // 8: agent.GetServerStatusResponse
(*PingHostsRequest)(nil), // 9: agent.PingHostsRequest
(*PingResult)(nil), // 10: agent.PingResult
(*PingHostsResponse)(nil), // 11: agent.PingHostsResponse
} }
var file_proto_agent_proto_depIdxs = []int32{ var file_proto_agent_proto_depIdxs = []int32{
1, // 0: agent.GetClientConfigsResponse.clients:type_name -> agent.ClientConfig 1, // 0: agent.GetClientConfigsResponse.clients:type_name -> agent.ClientConfig
1, // 1: agent.RegisterClientResponse.client:type_name -> agent.ClientConfig 1, // 1: agent.RegisterClientResponse.client:type_name -> agent.ClientConfig
0, // 2: agent.WireGuardAgent.GetClientConfigs:input_type -> agent.GetClientConfigsRequest 10, // 2: agent.PingHostsResponse.results:type_name -> agent.PingResult
3, // 3: agent.WireGuardAgent.RegisterClient:input_type -> agent.RegisterClientRequest 0, // 3: agent.WireGuardAgent.GetClientConfigs:input_type -> agent.GetClientConfigsRequest
5, // 4: agent.WireGuardAgent.UnregisterClient:input_type -> agent.UnregisterClientRequest 3, // 4: agent.WireGuardAgent.RegisterClient:input_type -> agent.RegisterClientRequest
7, // 5: agent.WireGuardAgent.GetServerStatus:input_type -> agent.GetServerStatusRequest 5, // 5: agent.WireGuardAgent.UnregisterClient:input_type -> agent.UnregisterClientRequest
2, // 6: agent.WireGuardAgent.GetClientConfigs:output_type -> agent.GetClientConfigsResponse 7, // 6: agent.WireGuardAgent.GetServerStatus:input_type -> agent.GetServerStatusRequest
4, // 7: agent.WireGuardAgent.RegisterClient:output_type -> agent.RegisterClientResponse 9, // 7: agent.WireGuardAgent.PingHosts:input_type -> agent.PingHostsRequest
6, // 8: agent.WireGuardAgent.UnregisterClient:output_type -> agent.UnregisterClientResponse 2, // 8: agent.WireGuardAgent.GetClientConfigs:output_type -> agent.GetClientConfigsResponse
8, // 9: agent.WireGuardAgent.GetServerStatus:output_type -> agent.GetServerStatusResponse 4, // 9: agent.WireGuardAgent.RegisterClient:output_type -> agent.RegisterClientResponse
6, // [6:10] is the sub-list for method output_type 6, // 10: agent.WireGuardAgent.UnregisterClient:output_type -> agent.UnregisterClientResponse
2, // [2:6] is the sub-list for method input_type 8, // 11: agent.WireGuardAgent.GetServerStatus:output_type -> agent.GetServerStatusResponse
2, // [2:2] is the sub-list for extension type_name 11, // 12: agent.WireGuardAgent.PingHosts:output_type -> agent.PingHostsResponse
2, // [2:2] is the sub-list for extension extendee 8, // [8:13] is the sub-list for method output_type
0, // [0:2] is the sub-list for field type_name 3, // [3:8] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
} }
func init() { file_proto_agent_proto_init() } func init() { file_proto_agent_proto_init() }
@ -633,7 +825,7 @@ func file_proto_agent_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_agent_proto_rawDesc), len(file_proto_agent_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_agent_proto_rawDesc), len(file_proto_agent_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 9, NumMessages: 12,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -9,6 +9,7 @@ service WireGuardAgent {
rpc RegisterClient(RegisterClientRequest) returns (RegisterClientResponse); rpc RegisterClient(RegisterClientRequest) returns (RegisterClientResponse);
rpc UnregisterClient(UnregisterClientRequest) returns (UnregisterClientResponse); rpc UnregisterClient(UnregisterClientRequest) returns (UnregisterClientResponse);
rpc GetServerStatus(GetServerStatusRequest) returns (GetServerStatusResponse); rpc GetServerStatus(GetServerStatusRequest) returns (GetServerStatusResponse);
rpc PingHosts(PingHostsRequest) returns (PingHostsResponse);
} }
message GetClientConfigsRequest {} message GetClientConfigsRequest {}
@ -58,3 +59,20 @@ message GetServerStatusResponse {
string listening_port = 5; string listening_port = 5;
int32 peers_count = 6; int32 peers_count = 6;
} }
message PingHostsRequest {
repeated string hosts = 1;
}
message PingResult {
string host = 1;
double latency_ms = 2; // -1 if unreachable
bool reachable = 3;
string error = 4;
}
message PingHostsResponse {
bool success = 1;
string error = 2;
repeated PingResult results = 3;
}

View File

@ -23,6 +23,7 @@ const (
WireGuardAgent_RegisterClient_FullMethodName = "/agent.WireGuardAgent/RegisterClient" WireGuardAgent_RegisterClient_FullMethodName = "/agent.WireGuardAgent/RegisterClient"
WireGuardAgent_UnregisterClient_FullMethodName = "/agent.WireGuardAgent/UnregisterClient" WireGuardAgent_UnregisterClient_FullMethodName = "/agent.WireGuardAgent/UnregisterClient"
WireGuardAgent_GetServerStatus_FullMethodName = "/agent.WireGuardAgent/GetServerStatus" WireGuardAgent_GetServerStatus_FullMethodName = "/agent.WireGuardAgent/GetServerStatus"
WireGuardAgent_PingHosts_FullMethodName = "/agent.WireGuardAgent/PingHosts"
) )
// WireGuardAgentClient is the client API for WireGuardAgent service. // WireGuardAgentClient is the client API for WireGuardAgent service.
@ -33,6 +34,7 @@ type WireGuardAgentClient interface {
RegisterClient(ctx context.Context, in *RegisterClientRequest, opts ...grpc.CallOption) (*RegisterClientResponse, error) RegisterClient(ctx context.Context, in *RegisterClientRequest, opts ...grpc.CallOption) (*RegisterClientResponse, error)
UnregisterClient(ctx context.Context, in *UnregisterClientRequest, opts ...grpc.CallOption) (*UnregisterClientResponse, error) UnregisterClient(ctx context.Context, in *UnregisterClientRequest, opts ...grpc.CallOption) (*UnregisterClientResponse, error)
GetServerStatus(ctx context.Context, in *GetServerStatusRequest, opts ...grpc.CallOption) (*GetServerStatusResponse, error) GetServerStatus(ctx context.Context, in *GetServerStatusRequest, opts ...grpc.CallOption) (*GetServerStatusResponse, error)
PingHosts(ctx context.Context, in *PingHostsRequest, opts ...grpc.CallOption) (*PingHostsResponse, error)
} }
type wireGuardAgentClient struct { type wireGuardAgentClient struct {
@ -83,6 +85,16 @@ func (c *wireGuardAgentClient) GetServerStatus(ctx context.Context, in *GetServe
return out, nil return out, nil
} }
func (c *wireGuardAgentClient) PingHosts(ctx context.Context, in *PingHostsRequest, opts ...grpc.CallOption) (*PingHostsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(PingHostsResponse)
err := c.cc.Invoke(ctx, WireGuardAgent_PingHosts_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// WireGuardAgentServer is the server API for WireGuardAgent service. // WireGuardAgentServer is the server API for WireGuardAgent service.
// All implementations must embed UnimplementedWireGuardAgentServer // All implementations must embed UnimplementedWireGuardAgentServer
// for forward compatibility. // for forward compatibility.
@ -91,6 +103,7 @@ type WireGuardAgentServer interface {
RegisterClient(context.Context, *RegisterClientRequest) (*RegisterClientResponse, error) RegisterClient(context.Context, *RegisterClientRequest) (*RegisterClientResponse, error)
UnregisterClient(context.Context, *UnregisterClientRequest) (*UnregisterClientResponse, error) UnregisterClient(context.Context, *UnregisterClientRequest) (*UnregisterClientResponse, error)
GetServerStatus(context.Context, *GetServerStatusRequest) (*GetServerStatusResponse, error) GetServerStatus(context.Context, *GetServerStatusRequest) (*GetServerStatusResponse, error)
PingHosts(context.Context, *PingHostsRequest) (*PingHostsResponse, error)
mustEmbedUnimplementedWireGuardAgentServer() mustEmbedUnimplementedWireGuardAgentServer()
} }
@ -113,6 +126,9 @@ func (UnimplementedWireGuardAgentServer) UnregisterClient(context.Context, *Unre
func (UnimplementedWireGuardAgentServer) GetServerStatus(context.Context, *GetServerStatusRequest) (*GetServerStatusResponse, error) { func (UnimplementedWireGuardAgentServer) GetServerStatus(context.Context, *GetServerStatusRequest) (*GetServerStatusResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetServerStatus not implemented") return nil, status.Error(codes.Unimplemented, "method GetServerStatus not implemented")
} }
func (UnimplementedWireGuardAgentServer) PingHosts(context.Context, *PingHostsRequest) (*PingHostsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method PingHosts not implemented")
}
func (UnimplementedWireGuardAgentServer) mustEmbedUnimplementedWireGuardAgentServer() {} func (UnimplementedWireGuardAgentServer) mustEmbedUnimplementedWireGuardAgentServer() {}
func (UnimplementedWireGuardAgentServer) testEmbeddedByValue() {} func (UnimplementedWireGuardAgentServer) testEmbeddedByValue() {}
@ -206,6 +222,24 @@ func _WireGuardAgent_GetServerStatus_Handler(srv interface{}, ctx context.Contex
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _WireGuardAgent_PingHosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PingHostsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WireGuardAgentServer).PingHosts(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: WireGuardAgent_PingHosts_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WireGuardAgentServer).PingHosts(ctx, req.(*PingHostsRequest))
}
return interceptor(ctx, in, info, handler)
}
// WireGuardAgent_ServiceDesc is the grpc.ServiceDesc for WireGuardAgent service. // WireGuardAgent_ServiceDesc is the grpc.ServiceDesc for WireGuardAgent service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -229,6 +263,10 @@ var WireGuardAgent_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetServerStatus", MethodName: "GetServerStatus",
Handler: _WireGuardAgent_GetServerStatus_Handler, Handler: _WireGuardAgent_GetServerStatus_Handler,
}, },
{
MethodName: "PingHosts",
Handler: _WireGuardAgent_PingHosts_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "proto/agent.proto", Metadata: "proto/agent.proto",