mirror of
https://github.com/fullstorydev/grpcurl.git
synced 2026-06-12 22:11:45 +03:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96cfd48e32 | ||
|
|
d30f3a01b7 | ||
|
|
0d669e78d0 | ||
|
|
9572bd4525 | ||
|
|
ccc9007156 | ||
|
|
9248ea0963 |
12
.travis.yml
12
.travis.yml
@@ -3,17 +3,17 @@ sudo: false
|
|||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- go: "1.9"
|
- go: 1.9.x
|
||||||
- go: "1.10"
|
- go: 1.10.x
|
||||||
- go: "1.11"
|
- go: 1.11.x
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=off
|
- GO111MODULE=off
|
||||||
- VET=1
|
- VET=1
|
||||||
- go: "1.11"
|
- go: 1.11.x
|
||||||
env: GO111MODULE=on
|
env: GO111MODULE=on
|
||||||
- go: "1.12"
|
- go: 1.12.x
|
||||||
env: GO111MODULE=off
|
env: GO111MODULE=off
|
||||||
- go: "1.12"
|
- go: 1.12.x
|
||||||
env: GO111MODULE=on
|
env: GO111MODULE=on
|
||||||
- go: tip
|
- go: tip
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import (
|
|||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
|
||||||
|
|
||||||
|
// Register gzip compressor so compressed responses will work:
|
||||||
|
_ "google.golang.org/grpc/encoding/gzip"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "dev build <no version set>"
|
var version = "dev build <no version set>"
|
||||||
@@ -52,15 +55,27 @@ var (
|
|||||||
key = flags.String("key", "", prettify(`
|
key = flags.String("key", "", prettify(`
|
||||||
File containing client private key, to present to the server. Not valid
|
File containing client private key, to present to the server. Not valid
|
||||||
with -plaintext option. Must also provide -cert option.`))
|
with -plaintext option. Must also provide -cert option.`))
|
||||||
protoset multiString
|
protoset multiString
|
||||||
protoFiles multiString
|
protoFiles multiString
|
||||||
importPaths multiString
|
importPaths multiString
|
||||||
addlHeaders multiString
|
addlHeaders multiString
|
||||||
rpcHeaders multiString
|
rpcHeaders multiString
|
||||||
reflHeaders multiString
|
reflHeaders multiString
|
||||||
authority = flags.String("authority", "", prettify(`
|
expandHeaders = flags.Bool("expand-headers", false, prettify(`
|
||||||
Value of :authority pseudo-header to be use with underlying HTTP/2
|
If set, headers may use '${NAME}' syntax to reference environment
|
||||||
requests. It defaults to the given address.`))
|
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.`))
|
||||||
|
authority = flags.String("authority", "", prettify(`
|
||||||
|
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.`))
|
||||||
data = flags.String("d", "", prettify(`
|
data = flags.String("d", "", prettify(`
|
||||||
Data for request contents. If the value is '@' then the request contents
|
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
|
are read from stdin. For calls that accept a stream of requests, the
|
||||||
@@ -93,12 +108,24 @@ var (
|
|||||||
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
|
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
|
||||||
emitDefaults = flags.Bool("emit-defaults", false, prettify(`
|
emitDefaults = flags.Bool("emit-defaults", false, prettify(`
|
||||||
Emit default values for JSON-encoded responses.`))
|
Emit default values for JSON-encoded responses.`))
|
||||||
|
protosetOut = flags.String("protoset-out", "", prettify(`
|
||||||
|
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.`))
|
||||||
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
||||||
When describing messages, show a template of input data.`))
|
When describing messages, show a template of input data.`))
|
||||||
verbose = flags.Bool("v", false, prettify(`
|
verbose = flags.Bool("v", false, prettify(`
|
||||||
Enable verbose output.`))
|
Enable verbose output.`))
|
||||||
serverName = flags.String("servername", "", prettify(`
|
serverName = flags.String("servername", "", prettify(`
|
||||||
Override server name when validating TLS certificate.`))
|
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).`))
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -286,9 +313,6 @@ func main() {
|
|||||||
if *maxMsgSz > 0 {
|
if *maxMsgSz > 0 {
|
||||||
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
|
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
|
||||||
}
|
}
|
||||||
if *authority != "" {
|
|
||||||
opts = append(opts, grpc.WithAuthority(*authority))
|
|
||||||
}
|
|
||||||
var creds credentials.TransportCredentials
|
var creds credentials.TransportCredentials
|
||||||
if !*plaintext {
|
if !*plaintext {
|
||||||
var err error
|
var err error
|
||||||
@@ -296,11 +320,27 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fail(err, "Failed to configure transport credentials")
|
fail(err, "Failed to configure transport credentials")
|
||||||
}
|
}
|
||||||
if *serverName != "" {
|
|
||||||
if err := creds.OverrideServerName(*serverName); err != nil {
|
// can use either -servername or -authority; but not both
|
||||||
fail(err, "Failed to override server name as %q", *serverName)
|
if *serverName != "" && *authority != "" {
|
||||||
|
if *serverName == *authority {
|
||||||
|
warn("Both -servername and -authority are present; prefer only -authority.")
|
||||||
|
} else {
|
||||||
|
fail(nil, "Cannot specify different values for -servername and -authority.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
overrideName := *serverName
|
||||||
|
if overrideName == "" {
|
||||||
|
overrideName = *authority
|
||||||
|
}
|
||||||
|
|
||||||
|
if overrideName != "" {
|
||||||
|
if err := creds.OverrideServerName(overrideName); err != nil {
|
||||||
|
fail(err, "Failed to override server name as %q", overrideName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if *authority != "" {
|
||||||
|
opts = append(opts, grpc.WithAuthority(*authority))
|
||||||
}
|
}
|
||||||
network := "tcp"
|
network := "tcp"
|
||||||
if isUnixSocket != nil && isUnixSocket() {
|
if isUnixSocket != nil && isUnixSocket() {
|
||||||
@@ -313,6 +353,22 @@ func main() {
|
|||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *expandHeaders {
|
||||||
|
var err error
|
||||||
|
addlHeaders, err = grpcurl.ExpandHeaders(addlHeaders)
|
||||||
|
if err != nil {
|
||||||
|
fail(err, "Failed to expand additional headers")
|
||||||
|
}
|
||||||
|
rpcHeaders, err = grpcurl.ExpandHeaders(rpcHeaders)
|
||||||
|
if err != nil {
|
||||||
|
fail(err, "Failed to expand rpc headers")
|
||||||
|
}
|
||||||
|
reflHeaders, err = grpcurl.ExpandHeaders(reflHeaders)
|
||||||
|
if err != nil {
|
||||||
|
fail(err, "Failed to expand reflection headers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var cc *grpc.ClientConn
|
var cc *grpc.ClientConn
|
||||||
var descSource grpcurl.DescriptorSource
|
var descSource grpcurl.DescriptorSource
|
||||||
var refClient *grpcreflect.Client
|
var refClient *grpcreflect.Client
|
||||||
@@ -367,6 +423,9 @@ func main() {
|
|||||||
fmt.Printf("%s\n", svc)
|
fmt.Printf("%s\n", svc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := writeProtoset(descSource, svcs...); err != nil {
|
||||||
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
methods, err := grpcurl.ListMethods(descSource, symbol)
|
methods, err := grpcurl.ListMethods(descSource, symbol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -379,6 +438,9 @@ func main() {
|
|||||||
fmt.Printf("%s\n", m)
|
fmt.Printf("%s\n", m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := writeProtoset(descSource, symbol); err != nil {
|
||||||
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if describe {
|
} else if describe {
|
||||||
@@ -479,6 +541,9 @@ func main() {
|
|||||||
fmt.Println(str)
|
fmt.Println(str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := writeProtoset(descSource, symbols...); err != nil {
|
||||||
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Invoke an RPC
|
// Invoke an RPC
|
||||||
@@ -595,3 +660,15 @@ func fail(err error, msg string, args ...interface{}) {
|
|||||||
exit(2)
|
exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error {
|
||||||
|
if *protosetOut == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f, err := os.Create(*protosetOut)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return grpcurl.WriteProtoset(f, descSource, symbols...)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package grpcurl
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -251,3 +252,53 @@ func reflectionSupport(err error) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteProtoset will use the given descriptor source to resolve all of the given
|
||||||
|
// symbols and write a proto file descriptor set with their definitions to the
|
||||||
|
// given output. The output will include descriptors for all files in which the
|
||||||
|
// symbols are defined as well as their transitive dependencies.
|
||||||
|
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
|
||||||
|
// compute set of file descriptors
|
||||||
|
filenames := make([]string, 0, len(symbols))
|
||||||
|
fds := make(map[string]*desc.FileDescriptor, len(symbols))
|
||||||
|
for _, sym := range symbols {
|
||||||
|
d, err := descSource.FindSymbol(sym)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
|
||||||
|
}
|
||||||
|
fd := d.GetFile()
|
||||||
|
if _, ok := fds[fd.GetName()]; !ok {
|
||||||
|
fds[fd.GetName()] = fd
|
||||||
|
filenames = append(filenames, fd.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now expand that to include transitive dependencies in topologically sorted
|
||||||
|
// order (such that file always appears after its dependencies)
|
||||||
|
expandedFiles := make(map[string]struct{}, len(fds))
|
||||||
|
allFilesSlice := make([]*descpb.FileDescriptorProto, 0, len(fds))
|
||||||
|
for _, filename := range filenames {
|
||||||
|
allFilesSlice = addFilesToSet(allFilesSlice, expandedFiles, fds[filename])
|
||||||
|
}
|
||||||
|
// now we can serialize to file
|
||||||
|
b, err := proto.Marshal(&descpb.FileDescriptorSet{File: allFilesSlice})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to serialize file descriptor set: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := out.Write(b); err != nil {
|
||||||
|
return fmt.Errorf("failed to write file descriptor set: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFilesToSet(allFiles []*descpb.FileDescriptorProto, expanded map[string]struct{}, fd *desc.FileDescriptor) []*descpb.FileDescriptorProto {
|
||||||
|
if _, ok := expanded[fd.GetName()]; ok {
|
||||||
|
// already seen this one
|
||||||
|
return allFiles
|
||||||
|
}
|
||||||
|
expanded[fd.GetName()] = struct{}{}
|
||||||
|
// add all dependencies first
|
||||||
|
for _, dep := range fd.GetDependencies() {
|
||||||
|
allFiles = addFilesToSet(allFiles, expanded, dep)
|
||||||
|
}
|
||||||
|
return append(allFiles, fd.AsFileDescriptorProto())
|
||||||
|
}
|
||||||
|
|||||||
62
desc_source_test.go
Normal file
62
desc_source_test.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package grpcurl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWriteProtoset(t *testing.T) {
|
||||||
|
exampleProtoset, err := loadProtoset("./testing/example.protoset")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to load example.protoset: %v", err)
|
||||||
|
}
|
||||||
|
testProtoset, err := loadProtoset("./testing/test.protoset")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to load test.protoset: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedProtoset := &descriptor.FileDescriptorSet{
|
||||||
|
File: append(exampleProtoset.File, testProtoset.File...),
|
||||||
|
}
|
||||||
|
|
||||||
|
descSrc, err := DescriptorSourceFromFileDescriptorSet(mergedProtoset)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create descriptor source: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkWriteProtoset(t, descSrc, exampleProtoset, "TestService")
|
||||||
|
checkWriteProtoset(t, descSrc, testProtoset, "grpc.testing.TestService")
|
||||||
|
checkWriteProtoset(t, descSrc, mergedProtoset, "TestService", "grpc.testing.TestService")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadProtoset(path string) (*descriptor.FileDescriptorSet, error) {
|
||||||
|
b, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var protoset descriptor.FileDescriptorSet
|
||||||
|
if err := proto.Unmarshal(b, &protoset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &protoset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkWriteProtoset(t *testing.T, descSrc DescriptorSource, protoset *descriptor.FileDescriptorSet, symbols ...string) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := WriteProtoset(&buf, descSrc, symbols...); err != nil {
|
||||||
|
t.Fatalf("failed to write protoset: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result descriptor.FileDescriptorSet
|
||||||
|
if err := proto.Unmarshal(buf.Bytes(), &result); err != nil {
|
||||||
|
t.Fatalf("failed to unmarshal written protoset: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !proto.Equal(protoset, &result) {
|
||||||
|
t.Fatalf("written protoset not equal to input:\nExpecting: %s\nActual: %s", protoset, &result)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
go.mod
4
go.mod
@@ -1,8 +1,8 @@
|
|||||||
module github.com/fullstorydev/grpcurl
|
module github.com/fullstorydev/grpcurl
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.3.1
|
github.com/golang/protobuf v1.3.5
|
||||||
github.com/jhump/protoreflect v1.5.0
|
github.com/jhump/protoreflect v1.5.0
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
||||||
google.golang.org/grpc v1.21.0
|
google.golang.org/grpc v1.28.0
|
||||||
)
|
)
|
||||||
|
|||||||
36
go.sum
36
go.sum
@@ -1,30 +1,58 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc=
|
github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc=
|
||||||
github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|||||||
81
grpcurl.go
81
grpcurl.go
@@ -15,6 +15,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
_ "google.golang.org/grpc/xds/experimental"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListServices uses the given descriptor source to return a sorted list of fully-qualified
|
// ListServices uses the given descriptor source to return a sorted list of fully-qualified
|
||||||
@@ -161,6 +164,36 @@ func MetadataFromHeaders(headers []string) metadata.MD {
|
|||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var envVarRegex = regexp.MustCompile(`\${\w+}`)
|
||||||
|
|
||||||
|
// ExpandHeaders expands environment variables contained in the header string.
|
||||||
|
// If no corresponding environment variable is found an error is returned.
|
||||||
|
// TODO: Add escaping for `${`
|
||||||
|
func ExpandHeaders(headers []string) ([]string, error) {
|
||||||
|
expandedHeaders := make([]string, len(headers))
|
||||||
|
for idx, header := range headers {
|
||||||
|
if header == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results := envVarRegex.FindAllString(header, -1)
|
||||||
|
if len(results) == 0 {
|
||||||
|
expandedHeaders[idx] = headers[idx]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expandedHeader := header
|
||||||
|
for _, result := range results {
|
||||||
|
envVarName := result[2 : len(result)-1] // strip leading `${` and trailing `}`
|
||||||
|
envVarValue, ok := os.LookupEnv(envVarName)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("header %q refers to missing environment variable %q", header, envVarName)
|
||||||
|
}
|
||||||
|
expandedHeader = strings.Replace(expandedHeader, result, envVarValue, -1)
|
||||||
|
}
|
||||||
|
expandedHeaders[idx] = expandedHeader
|
||||||
|
}
|
||||||
|
return expandedHeaders, nil
|
||||||
|
}
|
||||||
|
|
||||||
var base64Codecs = []*base64.Encoding{base64.StdEncoding, base64.URLEncoding, base64.RawStdEncoding, base64.RawURLEncoding}
|
var base64Codecs = []*base64.Encoding{base64.StdEncoding, base64.URLEncoding, base64.RawStdEncoding, base64.RawURLEncoding}
|
||||||
|
|
||||||
func decode(val string) (string, error) {
|
func decode(val string) (string, error) {
|
||||||
@@ -573,33 +606,44 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// custom credentials and dialer will notify on error via the
|
||||||
|
// writeResult function
|
||||||
|
if creds != nil {
|
||||||
|
creds = &errSignalingCreds{
|
||||||
|
TransportCredentials: creds,
|
||||||
|
writeResult: writeResult,
|
||||||
|
}
|
||||||
|
}
|
||||||
dialer := func(ctx context.Context, address string) (net.Conn, error) {
|
dialer := func(ctx context.Context, address string) (net.Conn, error) {
|
||||||
|
// NB: We *could* handle the TLS handshake ourselves, in the custom
|
||||||
|
// dialer (instead of customizing both the dialer and the credentials).
|
||||||
|
// But that requires using WithInsecure dial option (so that the gRPC
|
||||||
|
// library doesn't *also* try to do a handshake). And that would mean
|
||||||
|
// that the library would send the wrong ":scheme" metaheader to
|
||||||
|
// servers: it would send "http" instead of "https" because it is
|
||||||
|
// unaware that TLS is actually in use.
|
||||||
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
|
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeResult(err)
|
writeResult(err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
if creds != nil {
|
return conn, err
|
||||||
conn, _, err = creds.ClientHandshake(ctx, address, conn)
|
|
||||||
if err != nil {
|
|
||||||
writeResult(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even with grpc.FailOnNonTempDialError, this call will usually timeout in
|
// Even with grpc.FailOnNonTempDialError, this call will usually timeout in
|
||||||
// the face of TLS handshake errors. So we can't rely on grpc.WithBlock() to
|
// the face of TLS handshake errors. So we can't rely on grpc.WithBlock() to
|
||||||
// know when we're done. So we run it in a goroutine and then use result
|
// know when we're done. So we run it in a goroutine and then use result
|
||||||
// channel to either get the channel or fail-fast.
|
// channel to either get the connection or fail-fast.
|
||||||
go func() {
|
go func() {
|
||||||
opts = append(opts,
|
opts = append(opts,
|
||||||
grpc.WithBlock(),
|
grpc.WithBlock(),
|
||||||
grpc.FailOnNonTempDialError(true),
|
grpc.FailOnNonTempDialError(true),
|
||||||
grpc.WithContextDialer(dialer),
|
grpc.WithContextDialer(dialer),
|
||||||
grpc.WithInsecure(), // we are handling TLS, so tell grpc not to
|
|
||||||
)
|
)
|
||||||
|
if creds == nil {
|
||||||
|
opts = append(opts, grpc.WithInsecure())
|
||||||
|
} else {
|
||||||
|
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||||
|
}
|
||||||
conn, err := grpc.DialContext(ctx, address, opts...)
|
conn, err := grpc.DialContext(ctx, address, opts...)
|
||||||
var res interface{}
|
var res interface{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -620,3 +664,18 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
|||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// errSignalingCreds is a wrapper around a TransportCredentials value, but
|
||||||
|
// it will use the writeResult function to notify on error.
|
||||||
|
type errSignalingCreds struct {
|
||||||
|
credentials.TransportCredentials
|
||||||
|
writeResult func(res interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *errSignalingCreds) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
|
||||||
|
conn, auth, err := c.TransportCredentials.ClientHandshake(ctx, addr, rawConn)
|
||||||
|
if err != nil {
|
||||||
|
c.writeResult(err)
|
||||||
|
}
|
||||||
|
return conn, auth, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -300,6 +300,33 @@ func TestGetAllFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExpandHeaders(t *testing.T) {
|
||||||
|
inHeaders := []string{"key1: ${value}", "key2: bar", "key3: ${woo", "key4: woo}", "key5: ${TEST}",
|
||||||
|
"key6: ${TEST_VAR}", "${TEST}: ${TEST_VAR}", "key8: ${EMPTY}"}
|
||||||
|
os.Setenv("value", "value")
|
||||||
|
os.Setenv("TEST", "value5")
|
||||||
|
os.Setenv("TEST_VAR", "value6")
|
||||||
|
os.Setenv("EMPTY", "")
|
||||||
|
expectedHeaders := map[string]bool{"key1: value": true, "key2: bar": true, "key3: ${woo": true, "key4: woo}": true,
|
||||||
|
"key5: value5": true, "key6: value6": true, "value5: value6": true, "key8: ": true}
|
||||||
|
|
||||||
|
outHeaders, err := ExpandHeaders(inHeaders)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("The ExpandHeaders function generated an unexpected error %s", err)
|
||||||
|
}
|
||||||
|
for _, expandedHeader := range outHeaders {
|
||||||
|
if _, ok := expectedHeaders[expandedHeader]; !ok {
|
||||||
|
t.Errorf("The ExpandHeaders function has returned an unexpected header. Received unexpected header %s", expandedHeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
badHeaders := []string{"key: ${DNE}"}
|
||||||
|
_, err = ExpandHeaders(badHeaders)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("The ExpandHeaders function should return an error for missing environment variables %q", badHeaders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func fileNames(files []*desc.FileDescriptor) []string {
|
func fileNames(files []*desc.FileDescriptor) []string {
|
||||||
names := make([]string, len(files))
|
names := make([]string, len(files))
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
|
|||||||
Reference in New Issue
Block a user