Skip to content

Instantly share code, notes, and snippets.

@tringuyen-yw
Last active August 12, 2020 12:47
Show Gist options
  • Save tringuyen-yw/bf119a10b7810bd298fff344e1da9aaa to your computer and use it in GitHub Desktop.
Save tringuyen-yw/bf119a10b7810bd298fff344e1da9aaa to your computer and use it in GitHub Desktop.
GrpCurl Practice

GrpCurl Help

The below is the copy from grpcurl -help. Output current as of June 2020, using

$ grpcurl -version
grpcurl 1.6.0

Usage:

  grpcurl [flags] [address] [list|describe] [symbol]

The address is only optional when used with list or describe and a protoset or proto flag is provided.

If list is indicated, the symbol (if present) should be a fully-qualified service name. If present, all methods of that service are listed. If not present, all exposed services are listed, or all services defined in protosets.

If describe is indicated, the descriptor for the given symbol is shown. The symbol should be a fully-qualified service, enum, or message name. If no symbol is given then the descriptors for all exposed or known services are shown.

If neither verb is present, the symbol must be a fully-qualified method name in service/method or service.method format. In this case, the request body will be used to invoke the named method. If no body is given but one is required (i.e. the method is unary or server-streaming), an empty instance of the method`s request type will be sent.

The address will typically be in the form "host:port" where host can be an IP address or a hostname and port is a numeric port or service name. If an IPv6 address is given, it must be surrounded by brackets, like "[2001:db8::1]". For Unix variants, if a -unix=true flag is present, then the address must be the path to the domain socket.

Available flags:
-H value

Additional headers in name: value format. May specify more than one via multiple flags. These headers will also be included in reflection requests requests to a server.

-authority string

The authoritative name of the remote server. This value is passed as the value of the ":authority" pseudo-header in the HTTP/2 protocol. When TLS is used, this will also be used as the server name when verifying the server`s certificate. It defaults to the address that is provided in the positional arguments.

-cacert string

File containing trusted root certificates for verifying the server. Ignored if -insecure is specified.

-cert string

File containing client certificate (public key), to present to the server. Not valid with -plaintext option. Must also provide -key option.

-connect-timeout float

The maximum time, in seconds, to wait for connection to be established. Defaults to 10 seconds.

-d string

Data for request contents. If the value is @ then the request contents are read from stdin. For calls that accept a stream of requests, the contents should include all such request messages concatenated together (possibly delimited; see -format).

-emit-defaults

Emit default values for JSON-encoded responses.

-expand-headers

If set, headers may use ${NAME} syntax to reference environment variables. These will be expanded to the actual environment variable value before sending to the server. For example, if there is an environment variable defined like FOO=bar, then a header of key: ${FOO} would expand to key: bar. This applies to -H, -rpc-header, and -reflect-header options. No other expansion/escaping is performed. This can be used to supply credentials/secrets without having to put them in command-line arguments.

-format string

The format of request data. The allowed values are json or text. For json, the input data must be in JSON format. Multiple request values may be concatenated (messages with a JSON representation other than object must be separated by whitespace, such as a newline). For text, the input data must be in the protobuf text format, in which case multiple request values must be separated by the "record separator" ASCII character: 0x1E. The stream should not end in a record separator. If it does, it will be interpreted as a final, blank message after the separator. (default "json")

-format-error

When a non-zero status is returned, format the response using the value set by the -format flag .

-help

Print usage instructions and exit.

-import-path value

The path to a directory from which proto sources can be imported, for use with -proto flags. Multiple import paths can be configured by specifying multiple -import-path flags. Paths will be searched in the order given. If no import paths are given, all files (including all imports) must be provided as -proto flags, and grpcurl will attempt to resolve all import statements from the set of file names given.

-insecure

Skip server certificate and domain verification. (NOT SECURE!) Not valid with -plaintext option.

-keepalive-time float

If present, the maximum idle time in seconds, after which a keepalive probe is sent. If the connection remains idle and no keepalive response is received for this same period then the connection is closed and the operation fails.

-key string

File containing client private key, to present to the server. Not valid with -plaintext option. Must also provide -cert option.

-max-msg-sz int

The maximum encoded size of a response message, in bytes, that grpcurl will accept. If not specified, defaults to 4,194,304 (4 megabytes).

-max-time float

The maximum total time the operation can take, in seconds. This is useful for preventing batch jobs that use grpcurl from hanging due to slow or bad network links or due to incorrect stream method usage.

-msg-template

When describing messages, show a template of input data.

-plaintext

Use plain-text HTTP/2 when connecting to server (no TLS).

-proto value

The name of a proto source file. Source files given will be used to determine the RPC schema instead of querying for it from the remote server via the gRPC reflection API. When set: the list action lists the services found in the given files and their imports (vs. those exposed by the remote server), and the describe action describes symbols found in the given files. May specify more than one via multiple -proto flags. Imports will be resolved using the given -import-path flags. Multiple proto files can be specified by specifying multiple -proto flags. It is an error to use both -protoset and -proto flags.

-protoset value

The name of a file containing an encoded FileDescriptorSet. This file`s contents will be used to determine the RPC schema instead of querying for it from the remote server via the gRPC reflection API. When set: the list action lists the services found in the given descriptors (vs. those exposed by the remote server), and the describe action describes symbols found in the given descriptors. May specify more than one via multiple -protoset flags. It is an error to use both -protoset and -proto flags.

-protoset-out string

The name of a file to be written that will contain a FileDescriptorSet proto. With the list and describe verbs, the listed or described elements and their transitive dependencies will be written to the named file if this option is given. When invoking an RPC and this option is given, the method being invoked and its transitive dependencies will be included in the output file.

-reflect-header value

Additional reflection headers in name: value format. May specify more than one via multiple flags. These headers will only be used during reflection requests and will be excluded when invoking the requested RPC method.

-rpc-header value

Additional RPC headers in name: value format. May specify more than one via multiple flags. These headers will only be used when invoking the requested RPC method. They are excluded from reflection requests.

-servername string

Override server name when validating TLS certificate. This flag is ignored if -plaintext or -insecure is used. NOTE: Prefer -authority. This flag may be removed in the future. It is an error to use both -authority and -servername (though this will be permitted if they are both set to the same value, to increase backwards compatibility with earlier releases that allowed both to be set).

-unix

Indicates that the server address is the path to a Unix domain socket.

-use-reflection

When true, server reflection will be used to determine the RPC schema. Defaults to true unless a -proto or -protoset option is provided. If -use-reflection is used in combination with a -proto or -protoset flag, the provided descriptor sources will be used in addition to server reflection to resolve messages and extensions.

-v

Enable verbose output.

-version

Print version.

(end)

Testing GRPC Service with GRPCurl

Documentation

The grpcurl -help command shows the syntax help on the console. You may need to refer to this help quite often when experimenting with grpcurl at the begining. For convenience a copy of grpcurl -help content is provided here.

Discover the services exposed by a GRPC server

Here is the friends.proto content. We are going to use grpcurl to query the friends grpc server to match that proto descriptor.

syntax = "proto3";

import "cloudstate/entity_key.proto";

package cloudstate.samples.chat.friends;

message Friend{
    string user = 1;
    string avatar = 2;
}

message FriendRequest {
    string user = 1 [(.cloudstate.entity_key) = true];
    Friend friend = 2;
}

message User {
    string user = 1 [(.cloudstate.entity_key) = true];
}

message FriendsList {
    repeated Friend friends = 1;
}

message Empty {
}

service Friends {
    rpc Add(FriendRequest) returns (Empty);
    rpc Remove(FriendRequest) returns (Empty);
    rpc GetFriends(User) returns (FriendsList);
}

Assumption: the Cloudstate chat JS sample has been deployed on a K8S cluster https://github.com/lightbend/cloudstate-samples/tree/master/chat

run this cmd on one of the node on the K8S cluster using the ClusterIP of the friends service obtained by

$ kubectl get service/friends

NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
friends   ClusterIP   10.152.183.173   <none>        80/TCP    9d

We see that the friends service is exposed on the CLusterIP 10.152.183.173 and listening to port 80 Now we would like to discover the services exposed by the friends GRPC server

ℹ️
the -plaintext means plain-text HTTP/2 when connecting to the server hosting the GRPC service. TLS (Transport Layer Security) is the default protocol when communicating with a GRPC server.
$ grpcurl -plaintext 10.152.183.173:80 list

cloudstate.samples.chat.friends.Friends
grpc.reflection.v1alpha.ServerReflection

Query a GRPC service metadata

Among the services exposed by the GRPC server at 10.152.183.173:80. We see indeed a service named Friends under the package name cloudstate.samples.chat.friends as defined in the service descriptor friends.proto. Thus its fully qualified named cloudstate.samples.chat.friends.Friends. Let’s explore the Friends service further.

# list all services (server supports reflection)
$ grpcurl -plaintext 10.152.183.173:80 list

cloudstate.samples.chat.friends.Friends
grpc.reflection.v1alpha.ServerReflection


#---"list serviceName": show the methods of serviceName
$ grpcurl -plaintext 10.152.183.173:80 list cloudstate.samples.chat.friends.Friends

cloudstate.samples.chat.friends.Friends.Add
cloudstate.samples.chat.friends.Friends.GetFriends
cloudstate.samples.chat.friends.Friends.Remove

The "describe" verb will print the type of any symbol that the server knows about or that is found in a given protoset file. It also prints a description of that symbol, in the form of snippets of proto source. It won’t necessarily be the original source that defined the element, but it will be equivalent.

#---"describe serviceName": show the protobuf Service Descriptor of serviceName
#   which details the method signature and return type
$ grpcurl -plaintext 10.152.183.173:80 describe cloudstate.samples.chat.friends.Friends

cloudstate.samples.chat.friends.Friends is a service:
service Friends {
  rpc Add ( .cloudstate.samples.chat.friends.FriendRequest ) returns ( .cloudstate.samples.chat.friends.Empty );
  rpc GetFriends ( .cloudstate.samples.chat.friends.User ) returns ( .cloudstate.samples.chat.friends.FriendsList );
  rpc Remove ( .cloudstate.samples.chat.friends.FriendRequest ) returns ( .cloudstate.samples.chat.friends.Empty );
}


$ grpcurl -plaintext 192.168.1.98:31380 describe cloudstate.samples.chat.chat.Chat

cloudstate.samples.chat.chat.Chat is a service:
service Chat {
  rpc GetBundleImg ( .cloudstate.samples.chat.chat.ImgRequest ) returns ( .google.api.HttpBody ) {
    option (.google.api.http) = { get:"/pages/imgs/{img}"  };
  }
  rpc GetBundleJs ( .cloudstate.samples.chat.chat.Empty ) returns ( .google.api.HttpBody ) {
    option (.google.api.http) = { get:"/pages/build/bundle.js"  };
  }
  rpc GetChatPage ( .cloudstate.samples.chat.chat.Empty ) returns ( .google.api.HttpBody ) {
    option (.google.api.http) = { get:"/pages/chat.html"  };
  }
}

#---describe a single method
$ grpcurl -plaintext 192.168.1.98:31380 describe cloudstate.samples.chat.chat.Chat.GetChatPage

    cloudstate.samples.chat.chat.Chat.GetChatPage is a method:
    rpc GetChatPage ( .cloudstate.samples.chat.chat.Empty ) returns ( .google.api.HttpBody ) {
      option (.google.api.http) = { get:"/pages/chat.html"  };
    }

#---describe a message
$ grpcurl -plaintext 10.152.183.173:80 describe cloudstate.samples.chat.friends.Friend

    cloudstate.samples.chat.friends.Friend is a message:
    message Friend {
      string user = 1;
      string avatar = 2;
    }

Invoking GRPC methods

Now we would like to test some methods, for example Add and GetFriends. As seen above when we "describe" the Friends service, the methods require to pass parameters of type FriendRequest and User. From the command line, grpcrul pass the parameters values serialized as JSON string. We need to explore the protobuf definition of those complex types until we reach the basic JSON data types

$ grpcurl -plaintext 10.152.183.173:80 describe cloudstate.samples.chat.friends.FriendRequest

    cloudstate.samples.chat.friends.FriendRequest is a message:
    message FriendRequest {
      string user = 1 [(.cloudstate.entity_key) = true];
      .cloudstate.samples.chat.friends.Friend friend = 2;
    }

$ grpcurl -plaintext 10.152.183.173:80 describe cloudstate.samples.chat.friends.Friend

    cloudstate.samples.chat.friends.Friend is a message:
    message Friend {
      string user = 1;
      string avatar = 2;
    }

$ grpcurl -plaintext 10.152.183.173:80 describe cloudstate.samples.chat.friends.User

    cloudstate.samples.chat.friends.User is a message:
    message User {
      string user = 1 [(.cloudstate.entity_key) = true];
    }

We can serialize a FriendRequest value as JSON string:

{
  "user": "Tri",
  "friend": {
    "user": "Justin",
    "avatar": "Octopus"
  }
}

Now let’s try to invoke the Add method of the Friends service. The details about the method signature was described previously at Query a GRPC service metadata

$ grpcurl -plaintext -d '{"user": "Tri", "friend": {"user":"Justin", "avatar":"Octopus"}}' \
  10.152.183.173:80 \
  cloudstate.samples.chat.friends.Friends/Add

$ grpcurl -plaintext -d '{"user": "Tri", "friend": {"user":"SpongeBob", "avatar":"Lobster"}}' \
  10.152.183.173:80 \
  cloudstate.samples.chat.friends.Friends/Add

# screen output:
{
}

Now we can verify that user "Tri" has two friends "Justin" and "SpongeBob". By can invoking the GetFriends method which returns a FriendsList

$ grpcurl -plaintext -d '{"user": "Tri"}' \
  10.152.183.173:80 \
  cloudstate.samples.chat.friends.Friends/GetFriends

# screen output:
{
  "friends": [
    {
      "user": "Justin",
      "avatar": "Octopus"
    },
    {
      "user": "SpongeBob",
      "avatar": "Lobster"
    }
  ]
}

For completeness, let’s also test the Remove method

$ grpcurl -plaintext -d '{"user": "Tri", "friend": {"user":"SpongeBob", "avatar":"Lobster"}}' \
  10.152.183.173:80 \
  cloudstate.samples.chat.friends.Friends/Remove

#---Verification: GetFriends should not list the removed `FriendRequest`
$ grpcurl -plaintext -d '{"user": "Tri"}' 10.152.183.173:80 cloudstate.samples.chat.friends.Friends/GetFriends

{
  "friends": [
    {
      "user": "Justin",
      "avatar": "Octopus"
    }
  ]
}

Summary

  • You have designed a GRPC service

  • You deploy the service on Kubernetes

  • You want to test your service by sending requests using curl, as you used to do with REST services. But curl is not compatible with gRPC wire format.

  • grpcurl is a command line tool designed to test gRPC service ala curl. grpcurl allows you to:

    • query the metadata of your gRPC service and the schema of your data defined in protobuf format

    • invoke the methods of your GRPC service

    • grpcrul supports many more powerful features of GRPC (TLS authentication, streaming & bi-directional streaming request/response). Discover the fullk features of gRPCurl on GitHub

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment