RPC: introduce Resolve() method to support "tart ip --resolver=agent"
This commit is contained in:
parent
50b54ac5c9
commit
d1969ded96
|
|
@ -11,6 +11,8 @@ Currently implemented features:
|
|||
* `tart exec` support (`--run-rpc`)
|
||||
* it's recommended to invoke it as a launchd [global agent](https://launchd.info/) because fewer privileges will be available to commands started via `tart exec`
|
||||
* however, you can also invoke it as a launchd [global daemon](https://launchd.info/) if running commands started via `tart exec` as `root` is desired
|
||||
* `tart ip --resolver=agent` support (`--run-rpc`)
|
||||
* allows resolving VM's IP address without relying on DHCP leases and/or an ARP table
|
||||
|
||||
To run all features appropriate for a given context, use component groups:
|
||||
|
||||
|
|
|
|||
|
|
@ -314,6 +314,86 @@ func (x *IOChunk) GetData() []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
type ResolveIPRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResolveIPRequest) Reset() {
|
||||
*x = ResolveIPRequest{}
|
||||
mi := &file_rpc_agent_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResolveIPRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResolveIPRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ResolveIPRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_rpc_agent_proto_msgTypes[4]
|
||||
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 ResolveIPRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ResolveIPRequest) Descriptor() ([]byte, []int) {
|
||||
return file_rpc_agent_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
type ResolveIPResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ResolveIPResponse) Reset() {
|
||||
*x = ResolveIPResponse{}
|
||||
mi := &file_rpc_agent_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ResolveIPResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResolveIPResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ResolveIPResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_rpc_agent_proto_msgTypes[5]
|
||||
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 ResolveIPResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ResolveIPResponse) Descriptor() ([]byte, []int) {
|
||||
return file_rpc_agent_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *ResolveIPResponse) GetIp() string {
|
||||
if x != nil {
|
||||
return x.Ip
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ExecRequest_Command struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
|
|
@ -327,7 +407,7 @@ type ExecRequest_Command struct {
|
|||
|
||||
func (x *ExecRequest_Command) Reset() {
|
||||
*x = ExecRequest_Command{}
|
||||
mi := &file_rpc_agent_proto_msgTypes[4]
|
||||
mi := &file_rpc_agent_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -339,7 +419,7 @@ func (x *ExecRequest_Command) String() string {
|
|||
func (*ExecRequest_Command) ProtoMessage() {}
|
||||
|
||||
func (x *ExecRequest_Command) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_rpc_agent_proto_msgTypes[4]
|
||||
mi := &file_rpc_agent_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -399,7 +479,7 @@ type ExecResponse_Exit struct {
|
|||
|
||||
func (x *ExecResponse_Exit) Reset() {
|
||||
*x = ExecResponse_Exit{}
|
||||
mi := &file_rpc_agent_proto_msgTypes[5]
|
||||
mi := &file_rpc_agent_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -411,7 +491,7 @@ func (x *ExecResponse_Exit) String() string {
|
|||
func (*ExecResponse_Exit) ProtoMessage() {}
|
||||
|
||||
func (x *ExecResponse_Exit) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_rpc_agent_proto_msgTypes[5]
|
||||
mi := &file_rpc_agent_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -461,9 +541,13 @@ const file_rpc_agent_proto_rawDesc = "" +
|
|||
"\x04rows\x18\x01 \x01(\rR\x04rows\x12\x12\n" +
|
||||
"\x04cols\x18\x02 \x01(\rR\x04cols\"\x1d\n" +
|
||||
"\aIOChunk\x12\x12\n" +
|
||||
"\x04data\x18\x01 \x01(\fR\x04data20\n" +
|
||||
"\x04data\x18\x01 \x01(\fR\x04data\"\x12\n" +
|
||||
"\x10ResolveIPRequest\"#\n" +
|
||||
"\x11ResolveIPResponse\x12\x0e\n" +
|
||||
"\x02ip\x18\x01 \x01(\tR\x02ip2d\n" +
|
||||
"\x05Agent\x12'\n" +
|
||||
"\x04Exec\x12\f.ExecRequest\x1a\r.ExecResponse(\x010\x01B5Z3github.com/cirruslabs/tart-guest-agent/internal/rpcb\x06proto3"
|
||||
"\x04Exec\x12\f.ExecRequest\x1a\r.ExecResponse(\x010\x01\x122\n" +
|
||||
"\tResolveIP\x12\x11.ResolveIPRequest\x1a\x12.ResolveIPResponseB5Z3github.com/cirruslabs/tart-guest-agent/internal/rpcb\x06proto3"
|
||||
|
||||
var (
|
||||
file_rpc_agent_proto_rawDescOnce sync.Once
|
||||
|
|
@ -477,27 +561,31 @@ func file_rpc_agent_proto_rawDescGZIP() []byte {
|
|||
return file_rpc_agent_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_rpc_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_rpc_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_rpc_agent_proto_goTypes = []any{
|
||||
(*ExecRequest)(nil), // 0: ExecRequest
|
||||
(*ExecResponse)(nil), // 1: ExecResponse
|
||||
(*TerminalSize)(nil), // 2: TerminalSize
|
||||
(*IOChunk)(nil), // 3: IOChunk
|
||||
(*ExecRequest_Command)(nil), // 4: ExecRequest.Command
|
||||
(*ExecResponse_Exit)(nil), // 5: ExecResponse.Exit
|
||||
(*ResolveIPRequest)(nil), // 4: ResolveIPRequest
|
||||
(*ResolveIPResponse)(nil), // 5: ResolveIPResponse
|
||||
(*ExecRequest_Command)(nil), // 6: ExecRequest.Command
|
||||
(*ExecResponse_Exit)(nil), // 7: ExecResponse.Exit
|
||||
}
|
||||
var file_rpc_agent_proto_depIdxs = []int32{
|
||||
4, // 0: ExecRequest.command:type_name -> ExecRequest.Command
|
||||
6, // 0: ExecRequest.command:type_name -> ExecRequest.Command
|
||||
3, // 1: ExecRequest.standard_input:type_name -> IOChunk
|
||||
2, // 2: ExecRequest.terminal_resize:type_name -> TerminalSize
|
||||
5, // 3: ExecResponse.exit:type_name -> ExecResponse.Exit
|
||||
7, // 3: ExecResponse.exit:type_name -> ExecResponse.Exit
|
||||
3, // 4: ExecResponse.standard_output:type_name -> IOChunk
|
||||
3, // 5: ExecResponse.standard_error:type_name -> IOChunk
|
||||
2, // 6: ExecRequest.Command.terminal_size:type_name -> TerminalSize
|
||||
0, // 7: Agent.Exec:input_type -> ExecRequest
|
||||
1, // 8: Agent.Exec:output_type -> ExecResponse
|
||||
8, // [8:9] is the sub-list for method output_type
|
||||
7, // [7:8] is the sub-list for method input_type
|
||||
4, // 8: Agent.ResolveIP:input_type -> ResolveIPRequest
|
||||
1, // 9: Agent.Exec:output_type -> ExecResponse
|
||||
5, // 10: Agent.ResolveIP:output_type -> ResolveIPResponse
|
||||
9, // [9:11] is the sub-list for method output_type
|
||||
7, // [7:9] is the sub-list for method input_type
|
||||
7, // [7:7] is the sub-list for extension type_name
|
||||
7, // [7:7] is the sub-list for extension extendee
|
||||
0, // [0:7] is the sub-list for field type_name
|
||||
|
|
@ -524,7 +612,7 @@ func file_rpc_agent_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_rpc_agent_proto_rawDesc), len(file_rpc_agent_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 6,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import (
|
|||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
Agent_Exec_FullMethodName = "/Agent/Exec"
|
||||
Agent_Exec_FullMethodName = "/Agent/Exec"
|
||||
Agent_ResolveIP_FullMethodName = "/Agent/ResolveIP"
|
||||
)
|
||||
|
||||
// AgentClient is the client API for Agent service.
|
||||
|
|
@ -27,6 +28,7 @@ const (
|
|||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type AgentClient interface {
|
||||
Exec(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ExecRequest, ExecResponse], error)
|
||||
ResolveIP(ctx context.Context, in *ResolveIPRequest, opts ...grpc.CallOption) (*ResolveIPResponse, error)
|
||||
}
|
||||
|
||||
type agentClient struct {
|
||||
|
|
@ -50,11 +52,22 @@ func (c *agentClient) Exec(ctx context.Context, opts ...grpc.CallOption) (grpc.B
|
|||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Agent_ExecClient = grpc.BidiStreamingClient[ExecRequest, ExecResponse]
|
||||
|
||||
func (c *agentClient) ResolveIP(ctx context.Context, in *ResolveIPRequest, opts ...grpc.CallOption) (*ResolveIPResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ResolveIPResponse)
|
||||
err := c.cc.Invoke(ctx, Agent_ResolveIP_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AgentServer is the server API for Agent service.
|
||||
// All implementations must embed UnimplementedAgentServer
|
||||
// for forward compatibility.
|
||||
type AgentServer interface {
|
||||
Exec(grpc.BidiStreamingServer[ExecRequest, ExecResponse]) error
|
||||
ResolveIP(context.Context, *ResolveIPRequest) (*ResolveIPResponse, error)
|
||||
mustEmbedUnimplementedAgentServer()
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +81,9 @@ type UnimplementedAgentServer struct{}
|
|||
func (UnimplementedAgentServer) Exec(grpc.BidiStreamingServer[ExecRequest, ExecResponse]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Exec not implemented")
|
||||
}
|
||||
func (UnimplementedAgentServer) ResolveIP(context.Context, *ResolveIPRequest) (*ResolveIPResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ResolveIP not implemented")
|
||||
}
|
||||
func (UnimplementedAgentServer) mustEmbedUnimplementedAgentServer() {}
|
||||
func (UnimplementedAgentServer) testEmbeddedByValue() {}
|
||||
|
||||
|
|
@ -96,13 +112,36 @@ func _Agent_Exec_Handler(srv interface{}, stream grpc.ServerStream) error {
|
|||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Agent_ExecServer = grpc.BidiStreamingServer[ExecRequest, ExecResponse]
|
||||
|
||||
func _Agent_ResolveIP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResolveIPRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AgentServer).ResolveIP(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Agent_ResolveIP_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgentServer).ResolveIP(ctx, req.(*ResolveIPRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Agent_ServiceDesc is the grpc.ServiceDesc for Agent service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Agent_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "Agent",
|
||||
HandlerType: (*AgentServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ResolveIP",
|
||||
Handler: _Agent_ResolveIP_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Exec",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func (rpc *RPC) ResolveIP(ctx context.Context, _ *ResolveIPRequest) (*ResolveIPResponse, error) {
|
||||
ifaceAddrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve interface addresses: %w", err)
|
||||
}
|
||||
|
||||
for _, ifaceAddr := range ifaceAddrs {
|
||||
// Addresses returned by net.InterfaceAddrs()
|
||||
// generally are of type *net.IPNet
|
||||
ipNet, ok := ifaceAddr.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only interested in IPv4 addresses
|
||||
if ipNet.IP.To4() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only interested in global unicast addresses
|
||||
//
|
||||
// Note that Golang's "net" package also includes
|
||||
// IPv4 private address space in this definition.
|
||||
if !ipNet.IP.IsGlobalUnicast() {
|
||||
continue
|
||||
}
|
||||
|
||||
return &ResolveIPResponse{
|
||||
Ip: ipNet.IP.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("cannot identify VMs IP address")
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ option go_package = "github.com/cirruslabs/tart-guest-agent/internal/rpc";
|
|||
|
||||
service Agent {
|
||||
rpc Exec(stream ExecRequest) returns (stream ExecResponse);
|
||||
rpc ResolveIP(ResolveIPRequest) returns (ResolveIPResponse);
|
||||
}
|
||||
|
||||
message ExecRequest {
|
||||
|
|
@ -44,3 +45,11 @@ message TerminalSize {
|
|||
message IOChunk {
|
||||
bytes data = 1;
|
||||
}
|
||||
|
||||
message ResolveIPRequest {
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
message ResolveIPResponse {
|
||||
string ip = 1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue