I have written a Python GRPC client that can connect to a number of GRPC Golang services. I have been able to make this work like so:
from alphausblue.connection.conn import grpc_client_connection
from alphausblue.iam.v1.iam_pb2 import WhoAmIRequest
from alphausblue.iam.v1.iam_pb2_grpc import IamStub
async def main():
conn = grpc_client_connection(svc = "blue")
stub = IamStub(conn)
resp = await stub.WhoAmI(WhoAmIRequest())
print(resp)
where the service name is blue
. However, if I were to try to connect to a different service to request data like this:
from alphausblue.connection.conn import grpc_client_connection
from alphausblue.cost.v1.cost_pb2 import ListAccountsRequest
from alphausblue.cost.v1.cost_pb2_grpc import CostStub
async def main():
conn = grpc_client_connection(svc = "cost")
stub = CostStub(conn)
account = await stub.GetAccount(GetAccountRequest(vendor = 'aws', id = '731058950257'))
print(account)
I get an UNIMPLEMENTED response. This would make sense if the service didn't exist but it does and my Golang client connects to it just fine. Moreover, when I check the server's logs I can clearly see that the request reached the server. Doing some more research, I discovered this code on my server:
type service struct {
UserInfo *blueinterceptors.UserData
cost.UnimplementedCostServer
}
func (s *service) GetAccount(ctx context.Context, in *cost.GetAccountRequest) (*api.Account, error) {
switch in.Vendor {
case "aws":
// Do stuff
default:
return status.Errorf(codes.Unimplemented, "not implemented")
}
}
What this tells me is that the function is being called but the payload is being deserialized is missing the vendor
field. However, when debugging I can see this line:
src/core/lib/security/transport/secure_endpoint.cc:296] WRITE 0000018E2C62FB80: 00 00 00 13 0a 03 61 77 73 12 0c 37 33 31 30 35 38 39 35 30 32 35 37 '......aws..731058950257'
So, the data is being sent over GRPC to the server but is being deserialized into an object with missing fields. What, then is the cause of this?
Update
I looked at the definitions for GetAccountRequest
for both the Python and Golang clients as suggested by @blackgreen.
Golang client code:
// Request message for the Cost.GetAccount rpc.
type GetAccountRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Vendor string `protobuf:"bytes,1,opt,name=vendor,proto3" json:"vendor,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
}
Python client code:
_GETACCOUNTREQUEST = _descriptor.Descriptor(
name='GetAccountRequest',
full_name='blueapi.cost.v1.GetAccountRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='vendor', full_name='blueapi.cost.v1.GetAccountRequest.vendor', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='id', full_name='blueapi.cost.v1.GetAccountRequest.id', index=1,
number=2, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=946,
serialized_end=993,
)
It's clear that the fields are in the same order here so I don't think that's the issue unless GRPC is using index
rather than number
.