Compare commits
55 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c54eac28fd | |
|
|
6caf0e77fa | |
|
|
1ad1dc15dd | |
|
|
f575e91b2c | |
|
|
ed672b2bc9 | |
|
|
eab6c910a6 | |
|
|
60e53f304f | |
|
|
f093930c85 | |
|
|
7ad93a42d9 | |
|
|
7155fb6211 | |
|
|
f28d506cea | |
|
|
58ccc6321e | |
|
|
b519ffc959 | |
|
|
3a8fa31879 | |
|
|
614b1687cf | |
|
|
30f87c1323 | |
|
|
78655b4786 | |
|
|
d00c28104b | |
|
|
9e3e083f29 | |
|
|
c32936d71e | |
|
|
f3c8ec2564 | |
|
|
7e1a6c9068 | |
|
|
b9a11e9fea | |
|
|
bc5cf811a0 | |
|
|
fb49f049e6 | |
|
|
cdb43b08fa | |
|
|
56181ba330 | |
|
|
46c38b351a | |
|
|
a05d48d6dd | |
|
|
fc63514da1 | |
|
|
e14d9f769a | |
|
|
239dde4a62 | |
|
|
80e833a557 | |
|
|
6fccd7757e | |
|
|
400fa5f2d3 | |
|
|
0e13e85e65 | |
|
|
07361b21ea | |
|
|
8e76884d21 | |
|
|
805ce40c63 | |
|
|
93ea011b36 | |
|
|
5592211a41 | |
|
|
184c8f70b5 | |
|
|
149a93e0ec | |
|
|
252b57fd45 | |
|
|
24b80dfed8 | |
|
|
334e3f56de | |
|
|
f4157743ed | |
|
|
79fb35f680 | |
|
|
7ccaf0a21f | |
|
|
6093b09afa | |
|
|
70c215f7e2 | |
|
|
28c0ee28f0 | |
|
|
bc2944de97 | |
|
|
7a845ca5e9 | |
|
|
c17f0782f7 |
|
|
@ -9,22 +9,22 @@ shared_configs:
|
|||
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
|
||||
version: 2.1
|
||||
jobs:
|
||||
build-1-18:
|
||||
build-1-23:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: cimg/go:1.18
|
||||
- image: cimg/go:1.23
|
||||
steps: *simple_job_steps
|
||||
|
||||
build-1-19:
|
||||
build-1-24:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: cimg/go:1.19
|
||||
- image: cimg/go:1.24
|
||||
steps: *simple_job_steps
|
||||
|
||||
build-1-20:
|
||||
build-1-25:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: cimg/go:1.20
|
||||
- image: cimg/go:1.25
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
|
|
@ -32,16 +32,9 @@ jobs:
|
|||
command: |
|
||||
make ci
|
||||
|
||||
build-1-21:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: cimg/go:1.21
|
||||
steps: *simple_job_steps
|
||||
|
||||
workflows:
|
||||
pr-build-test:
|
||||
jobs:
|
||||
- build-1-18
|
||||
- build-1-19
|
||||
- build-1-20
|
||||
- build-1-21
|
||||
- build-1-23
|
||||
- build-1-24
|
||||
- build-1-25
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ dist/
|
|||
.idea/
|
||||
VERSION
|
||||
.tmp/
|
||||
*.snap
|
||||
|
|
|
|||
|
|
@ -8,14 +8,23 @@ builds:
|
|||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
- s390x
|
||||
- ppc64le
|
||||
goarm:
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: darwin
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- goos: darwin
|
||||
goarch: s390x
|
||||
- goos: windows
|
||||
|
|
@ -29,12 +38,26 @@ builds:
|
|||
|
||||
archives:
|
||||
- format: tar.gz
|
||||
name_template: >-
|
||||
{{ .Binary }}_{{ .Version }}_
|
||||
{{- if eq .Os "darwin" }}osx{{ else }}{{ .Os }}{{ end }}_
|
||||
{{- if eq .Arch "amd64" }}x86_64
|
||||
{{- else if eq .Arch "386" }}x86_32
|
||||
{{- else }}{{ .Arch }}{{ end }}
|
||||
{{- with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
replacements:
|
||||
amd64: x86_64
|
||||
386: x86_32
|
||||
darwin: osx
|
||||
files:
|
||||
- LICENSE
|
||||
|
||||
nfpms:
|
||||
- vendor: Fullstory
|
||||
homepage: https://github.com/fullstorydev/grpcurl/
|
||||
maintainer: Engineering at Fullstory <fixme@fixme>
|
||||
description: 'Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers'
|
||||
license: MIT
|
||||
id: nfpms
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
FROM golang:1.18-alpine as builder
|
||||
MAINTAINER FullStory Engineering
|
||||
FROM golang:1.25-alpine AS builder
|
||||
LABEL maintainer="Fullstory Engineering"
|
||||
|
||||
# create non-privileged group and user
|
||||
RUN addgroup -S grpcurl && adduser -S grpcurl -G grpcurl
|
||||
|
|
@ -16,7 +16,7 @@ RUN go build -o /grpcurl \
|
|||
-ldflags "-w -extldflags \"-static\" -X \"main.version=$(cat VERSION)\"" \
|
||||
./cmd/grpcurl
|
||||
|
||||
FROM alpine:3 as alpine
|
||||
FROM alpine:3 AS alpine
|
||||
WORKDIR /
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /etc/passwd /etc/passwd
|
||||
|
|
|
|||
4
LICENSE
4
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 FullStory, Inc
|
||||
Copyright (c) 2017 Fullstory, Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
|
|
|||
29
Makefile
29
Makefile
|
|
@ -1,8 +1,11 @@
|
|||
dev_build_version=$(shell git describe --tags --always --dirty)
|
||||
|
||||
export PATH := $(shell pwd)/.tmp/protoc/bin:$(PATH)
|
||||
|
||||
export PROTOC_VERSION := 22.0
|
||||
# Disable CGO for improved compatibility across distros
|
||||
export CGO_ENABLED=0
|
||||
export GOFLAGS=-trimpath
|
||||
export GOWORK=off
|
||||
|
||||
# TODO: run golint and errcheck, but only to catch *new* violations and
|
||||
# decide whether to change code or not (e.g. we need to be able to whitelist
|
||||
|
|
@ -15,10 +18,12 @@ ci: deps checkgofmt checkgenerate vet staticcheck ineffassign predeclared test
|
|||
.PHONY: deps
|
||||
deps:
|
||||
go get -d -v -t ./...
|
||||
go mod tidy
|
||||
|
||||
.PHONY: updatedeps
|
||||
updatedeps:
|
||||
go get -d -v -t -u -f ./...
|
||||
go mod tidy
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
|
|
@ -26,8 +31,8 @@ install:
|
|||
|
||||
.PHONY: release
|
||||
release:
|
||||
@go install github.com/goreleaser/goreleaser@v1.10.0
|
||||
goreleaser release --rm-dist
|
||||
@go install github.com/goreleaser/goreleaser@v1.21.0
|
||||
goreleaser release --clean
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
|
|
@ -39,13 +44,15 @@ docker:
|
|||
generate: .tmp/protoc/bin/protoc
|
||||
@go install google.golang.org/protobuf/cmd/protoc-gen-go@a709e31e5d12
|
||||
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1.0
|
||||
@go install github.com/jhump/protoreflect/desc/sourceinfo/cmd/protoc-gen-gosrcinfo@v1.14.1
|
||||
go generate ./...
|
||||
go mod tidy
|
||||
|
||||
.PHONY: checkgenerate
|
||||
checkgenerate: generate
|
||||
git status --porcelain
|
||||
@if [ -n "$$(git status --porcelain)" ]; then \
|
||||
git diff; \
|
||||
git status --porcelain -- '**/*.go'
|
||||
@if [ -n "$$(git status --porcelain -- '**/*.go')" ]; then \
|
||||
git diff -- '**/*.go'; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
|
@ -62,8 +69,8 @@ vet:
|
|||
|
||||
.PHONY: staticcheck
|
||||
staticcheck:
|
||||
@go install honnef.co/go/tools/cmd/staticcheck@v0.4.3
|
||||
staticcheck ./...
|
||||
@go install honnef.co/go/tools/cmd/staticcheck@2025.1.1
|
||||
staticcheck -checks "inherit,-SA1019" ./...
|
||||
|
||||
.PHONY: ineffassign
|
||||
ineffassign:
|
||||
|
|
@ -72,7 +79,7 @@ ineffassign:
|
|||
|
||||
.PHONY: predeclared
|
||||
predeclared:
|
||||
@go install github.com/nishanths/predeclared@5f2f810c9ae6
|
||||
@go install github.com/nishanths/predeclared@51e8c974458a0f93dc03fe356f91ae1a6d791e6f
|
||||
predeclared ./...
|
||||
|
||||
# Intentionally omitted from CI, but target here for ad-hoc reports.
|
||||
|
|
@ -88,8 +95,8 @@ errcheck:
|
|||
errcheck ./...
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race ./...
|
||||
test: deps
|
||||
CGO_ENABLED=1 go test -race ./...
|
||||
|
||||
.tmp/protoc/bin/protoc: ./Makefile ./download_protoc.sh
|
||||
./download_protoc.sh
|
||||
|
|
|
|||
21
README.md
21
README.md
|
|
@ -1,6 +1,7 @@
|
|||
# gRPCurl
|
||||
[](https://circleci.com/gh/fullstorydev/grpcurl/tree/master)
|
||||
[](https://goreportcard.com/report/github.com/fullstorydev/grpcurl)
|
||||
[](https://snapcraft.io/grpcurl)
|
||||
|
||||
`grpcurl` is a command-line tool that lets you interact with gRPC servers. It's
|
||||
basically `curl` for gRPC servers.
|
||||
|
|
@ -79,6 +80,12 @@ of environments, including Windows and myriad Linux distributions.
|
|||
You can see more details and the full list of other packages for `grpcurl` at _repology.org_:
|
||||
https://repology.org/project/grpcurl/information
|
||||
|
||||
### Snap
|
||||
|
||||
You can install `grpcurl` using the snap package:
|
||||
|
||||
`snap install grpcurl`
|
||||
|
||||
### From Source
|
||||
If you already have the [Go SDK](https://golang.org/doc/install) installed, you can use the `go`
|
||||
tool to install `grpcurl`:
|
||||
|
|
@ -145,6 +152,13 @@ grpcurl -d @ grpc.server.com:443 my.custom.server.Service/Method <<EOM
|
|||
}
|
||||
EOM
|
||||
```
|
||||
### Adding Headers/Metadata to Request
|
||||
Adding of headers / metadata to a rpc request is possible via the `-H name:value` command line option. Multiple headers can be added in a similar fashion.
|
||||
Example :
|
||||
```shell
|
||||
grpcurl -H header1:value1 -H header2:value2 -d '{"id": 1234, "tags": ["foo","bar"]}' grpc.server.com:443 my.custom.server.Service/Method
|
||||
```
|
||||
For more usage guide, check out the help docs via `grpcurl -help`
|
||||
|
||||
### Listing Services
|
||||
To list all services exposed by a server, use the "list" verb. When using `.proto` source
|
||||
|
|
@ -159,6 +173,13 @@ grpcurl -protoset my-protos.bin list
|
|||
|
||||
# Using proto sources
|
||||
grpcurl -import-path ../protos -proto my-stuff.proto list
|
||||
|
||||
# Export proto files (use -proto-out-dir to specify the output directory)
|
||||
grpcurl -plaintext -proto-out-dir "out_protos" "localhost:8787" describe my.custom.server.Service
|
||||
|
||||
# Export protoset file (use -protoset-out to specify the output file)
|
||||
grpcurl -plaintext -protoset-out "out.protoset" "localhost:8787" describe my.custom.server.Service
|
||||
|
||||
```
|
||||
|
||||
The "list" verb also lets you see all methods in a particular service:
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
//go:build go1.10
|
||||
// +build go1.10
|
||||
|
||||
package main
|
||||
|
||||
func indent() string {
|
||||
// In Go 1.10 and up, the flag package automatically
|
||||
// adds the right indentation.
|
||||
return ""
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
//go:build !go1.10
|
||||
// +build !go1.10
|
||||
|
||||
package main
|
||||
|
||||
func indent() string {
|
||||
// In Go 1.9 and older, we need to add indentation
|
||||
// after newlines in the flag doc strings.
|
||||
return " \t"
|
||||
}
|
||||
|
|
@ -8,17 +8,19 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 required to use APIs in other grpcurl package
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/alts"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
|
@ -32,9 +34,9 @@ import (
|
|||
"github.com/fullstorydev/grpcurl"
|
||||
)
|
||||
|
||||
// To avoid confusion between program error codes and the gRPC resonse
|
||||
// To avoid confusion between program error codes and the gRPC response
|
||||
// status codes 'Cancelled' and 'Unknown', 1 and 2 respectively,
|
||||
// the response status codes emitted use an offest of 64
|
||||
// the response status codes emitted use an offset of 64
|
||||
const statusCodeOffset = 64
|
||||
|
||||
const noVersion = "dev build <no version set>"
|
||||
|
|
@ -52,11 +54,14 @@ var (
|
|||
Print usage instructions and exit.`))
|
||||
printVersion = flags.Bool("version", false, prettify(`
|
||||
Print version.`))
|
||||
|
||||
plaintext = flags.Bool("plaintext", false, prettify(`
|
||||
Use plain-text HTTP/2 when connecting to server (no TLS).`))
|
||||
insecure = flags.Bool("insecure", false, prettify(`
|
||||
Skip server certificate and domain verification. (NOT SECURE!) Not
|
||||
valid with -plaintext option.`))
|
||||
|
||||
// TLS Options
|
||||
cacert = flags.String("cacert", "", prettify(`
|
||||
File containing trusted root certificates for verifying the server.
|
||||
Ignored if -insecure is specified.`))
|
||||
|
|
@ -66,6 +71,13 @@ var (
|
|||
key = flags.String("key", "", prettify(`
|
||||
File containing client private key, to present to the server. Not valid
|
||||
with -plaintext option. Must also provide -cert option.`))
|
||||
|
||||
// ALTS Options
|
||||
usealts = flags.Bool("alts", false, prettify(`
|
||||
Use Application Layer Transport Security (ALTS) when connecting to server.`))
|
||||
altsHandshakerServiceAddress = flags.String("alts-handshaker-service", "", prettify(`If set, this server will be used to do the ATLS handshaking.`))
|
||||
altsTargetServiceAccounts multiString
|
||||
|
||||
protoset multiString
|
||||
protoFiles multiString
|
||||
importPaths multiString
|
||||
|
|
@ -86,7 +98,8 @@ var (
|
|||
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.`))
|
||||
positional arguments, or 'localhost' in the case of a unix domain
|
||||
socket.`))
|
||||
userAgent = flags.String("user-agent", "", prettify(`
|
||||
If set, the specified value will be added to the User-Agent header set
|
||||
by the grpc-go library.
|
||||
|
|
@ -122,9 +135,11 @@ var (
|
|||
is received for this same period then the connection is closed and the
|
||||
operation fails.`))
|
||||
maxTime = flags.Float64("max-time", 0, prettify(`
|
||||
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.`))
|
||||
The maximum total time the operation can take, in seconds. This sets a
|
||||
timeout on the gRPC context, allowing both client and server to give up
|
||||
after the deadline has past. 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.`))
|
||||
maxMsgSz = flags.Int("max-msg-sz", 0, prettify(`
|
||||
The maximum encoded size of a response message, in bytes, that grpcurl
|
||||
will accept. If not specified, defaults to 4,194,304 (4 megabytes).`))
|
||||
|
|
@ -137,12 +152,20 @@ var (
|
|||
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.`))
|
||||
protoOut = flags.String("proto-out-dir", "", prettify(`
|
||||
The name of a directory where the generated .proto files will be written.
|
||||
With the list and describe verbs, the listed or described elements and
|
||||
their transitive dependencies will be written as .proto files in the
|
||||
specified directory 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 generated .proto files in the
|
||||
output directory.`))
|
||||
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
||||
When describing messages, show a template of input data.`))
|
||||
verbose = flags.Bool("v", false, prettify(`
|
||||
Enable verbose output.`))
|
||||
veryVerbose = flags.Bool("vv", false, prettify(`
|
||||
Enable very verbose output.`))
|
||||
Enable very verbose output (includes timing data).`))
|
||||
serverName = flags.String("servername", "", prettify(`
|
||||
Override server name when validating TLS certificate. This flag is
|
||||
ignored if -plaintext or -insecure is used.
|
||||
|
|
@ -199,6 +222,14 @@ func init() {
|
|||
-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.`))
|
||||
flags.Var(&altsTargetServiceAccounts, "alts-target-service-account", prettify(`
|
||||
The full email address of the service account that the server is
|
||||
expected to be using when ALTS is used. You can specify this option
|
||||
multiple times to indicate multiple allowed service accounts. If the
|
||||
server authenticates with a service account that is not one of the
|
||||
expected accounts, the RPC will not be issued. If no such arguments are
|
||||
provided, no check will be performed, and the RPC will be issued
|
||||
regardless of the server's service account.`))
|
||||
}
|
||||
|
||||
type multiString []string
|
||||
|
|
@ -255,6 +286,32 @@ func (cs compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDe
|
|||
return exts, nil
|
||||
}
|
||||
|
||||
type timingData struct {
|
||||
Title string
|
||||
Start time.Time
|
||||
Value time.Duration
|
||||
Parent *timingData
|
||||
Sub []*timingData
|
||||
}
|
||||
|
||||
func (d *timingData) Child(title string) *timingData {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
child := &timingData{Title: title, Start: time.Now()}
|
||||
d.Sub = append(d.Sub, child)
|
||||
return child
|
||||
}
|
||||
|
||||
func (d *timingData) Done() {
|
||||
if d == nil {
|
||||
return
|
||||
}
|
||||
if d.Value == 0 {
|
||||
d.Value = time.Since(d.Start)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flags.Usage = usage
|
||||
flags.Parse(os.Args[1:])
|
||||
|
|
@ -267,6 +324,9 @@ func main() {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
// default behavior is to use tls
|
||||
usetls := !*plaintext && !*usealts
|
||||
|
||||
// Do extra validation on arguments and figure out what user asked us to do.
|
||||
if *connectTimeout < 0 {
|
||||
fail(nil, "The -connect-timeout argument must not be negative.")
|
||||
|
|
@ -280,18 +340,27 @@ func main() {
|
|||
if *maxMsgSz < 0 {
|
||||
fail(nil, "The -max-msg-sz argument must not be negative.")
|
||||
}
|
||||
if *plaintext && *insecure {
|
||||
fail(nil, "The -plaintext and -insecure arguments are mutually exclusive.")
|
||||
if *plaintext && *usealts {
|
||||
fail(nil, "The -plaintext and -alts arguments are mutually exclusive.")
|
||||
}
|
||||
if *plaintext && *cert != "" {
|
||||
fail(nil, "The -plaintext and -cert arguments are mutually exclusive.")
|
||||
if *insecure && !usetls {
|
||||
fail(nil, "The -insecure argument can only be used with TLS.")
|
||||
}
|
||||
if *plaintext && *key != "" {
|
||||
fail(nil, "The -plaintext and -key arguments are mutually exclusive.")
|
||||
if *cert != "" && !usetls {
|
||||
fail(nil, "The -cert argument can only be used with TLS.")
|
||||
}
|
||||
if *key != "" && !usetls {
|
||||
fail(nil, "The -key argument can only be used with TLS.")
|
||||
}
|
||||
if (*key == "") != (*cert == "") {
|
||||
fail(nil, "The -cert and -key arguments must be used together and both be present.")
|
||||
}
|
||||
if *altsHandshakerServiceAddress != "" && !*usealts {
|
||||
fail(nil, "The -alts-handshaker-service argument must be used with the -alts argument.")
|
||||
}
|
||||
if len(altsTargetServiceAccounts) > 0 && !*usealts {
|
||||
fail(nil, "The -alts-target-service-account argument must be used with the -alts argument.")
|
||||
}
|
||||
if *format != "json" && *format != "text" {
|
||||
fail(nil, "The -format option must be 'json' or 'text'.")
|
||||
}
|
||||
|
|
@ -328,8 +397,16 @@ func main() {
|
|||
if *verbose {
|
||||
verbosityLevel = 1
|
||||
}
|
||||
|
||||
var rootTiming *timingData
|
||||
if *veryVerbose {
|
||||
verbosityLevel = 2
|
||||
|
||||
rootTiming = &timingData{Title: "Timing Data", Start: time.Now()}
|
||||
defer func() {
|
||||
rootTiming.Done()
|
||||
dumpTiming(rootTiming, 0)
|
||||
}()
|
||||
}
|
||||
|
||||
var symbol string
|
||||
|
|
@ -381,22 +458,24 @@ func main() {
|
|||
|
||||
ctx := context.Background()
|
||||
if *maxTime > 0 {
|
||||
timeout := time.Duration(*maxTime * float64(time.Second))
|
||||
timeout := floatSecondsToDuration(*maxTime)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
dial := func() *grpc.ClientConn {
|
||||
dialTiming := rootTiming.Child("Dial")
|
||||
defer dialTiming.Done()
|
||||
dialTime := 10 * time.Second
|
||||
if *connectTimeout > 0 {
|
||||
dialTime = time.Duration(*connectTimeout * float64(time.Second))
|
||||
dialTime = floatSecondsToDuration(*connectTimeout)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, dialTime)
|
||||
defer cancel()
|
||||
var opts []grpc.DialOption
|
||||
if *keepaliveTime > 0 {
|
||||
timeout := time.Duration(*keepaliveTime * float64(time.Second))
|
||||
timeout := floatSecondsToDuration(*keepaliveTime)
|
||||
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: timeout,
|
||||
Timeout: timeout,
|
||||
|
|
@ -405,8 +484,31 @@ func main() {
|
|||
if *maxMsgSz > 0 {
|
||||
opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(*maxMsgSz)))
|
||||
}
|
||||
if isUnixSocket != nil && isUnixSocket() && !strings.HasPrefix(target, "unix://") {
|
||||
// prepend unix:// to the address if it's not already there
|
||||
// this is to maintain backwards compatibility because the custom dialer is replaced by
|
||||
// the default dialer in grpc-go.
|
||||
// https://github.com/fullstorydev/grpcurl/pull/480
|
||||
target = "unix://" + target
|
||||
}
|
||||
var creds credentials.TransportCredentials
|
||||
if !*plaintext {
|
||||
if *plaintext {
|
||||
if *authority != "" {
|
||||
opts = append(opts, grpc.WithAuthority(*authority))
|
||||
}
|
||||
} else if *usealts {
|
||||
clientOptions := alts.DefaultClientOptions()
|
||||
if len(altsTargetServiceAccounts) > 0 {
|
||||
clientOptions.TargetServiceAccounts = altsTargetServiceAccounts
|
||||
}
|
||||
if *altsHandshakerServiceAddress != "" {
|
||||
clientOptions.HandshakerServiceAddress = *altsHandshakerServiceAddress
|
||||
}
|
||||
creds = alts.NewClientCreds(clientOptions)
|
||||
} else if usetls {
|
||||
tlsTiming := dialTiming.Child("TLS Setup")
|
||||
defer tlsTiming.Done()
|
||||
|
||||
tlsConf, err := grpcurl.ClientTLSConfig(*insecure, *cacert, *cert, *key)
|
||||
if err != nil {
|
||||
fail(err, "Failed to create TLS config")
|
||||
|
|
@ -439,8 +541,9 @@ func main() {
|
|||
if overrideName != "" {
|
||||
opts = append(opts, grpc.WithAuthority(overrideName))
|
||||
}
|
||||
} else if *authority != "" {
|
||||
opts = append(opts, grpc.WithAuthority(*authority))
|
||||
tlsTiming.Done()
|
||||
} else {
|
||||
panic("Should have defaulted to use TLS.")
|
||||
}
|
||||
|
||||
grpcurlUA := "grpcurl/" + version
|
||||
|
|
@ -452,11 +555,9 @@ func main() {
|
|||
}
|
||||
opts = append(opts, grpc.WithUserAgent(grpcurlUA))
|
||||
|
||||
network := "tcp"
|
||||
if isUnixSocket != nil && isUnixSocket() {
|
||||
network = "unix"
|
||||
}
|
||||
cc, err := grpcurl.BlockingDial(ctx, network, target, creds, opts...)
|
||||
blockingDialTiming := dialTiming.Child("BlockingDial")
|
||||
defer blockingDialTiming.Done()
|
||||
cc, err := grpcurl.BlockingDial(ctx, "", target, creds, opts...)
|
||||
if err != nil {
|
||||
fail(err, "Failed to dial target host %q", target)
|
||||
}
|
||||
|
|
@ -508,6 +609,7 @@ func main() {
|
|||
refCtx := metadata.NewOutgoingContext(ctx, md)
|
||||
cc = dial()
|
||||
refClient = grpcreflect.NewClientAuto(refCtx, cc)
|
||||
refClient.AllowMissingFileDescriptors()
|
||||
reflSource := grpcurl.DescriptorSourceFromServer(ctx, refClient)
|
||||
if fileSource != nil {
|
||||
descSource = compositeSource{reflSource, fileSource}
|
||||
|
|
@ -552,6 +654,9 @@ func main() {
|
|||
if err := writeProtoset(descSource, svcs...); err != nil {
|
||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||
}
|
||||
if err := writeProtos(descSource, svcs...); err != nil {
|
||||
fail(err, "Failed to write protos to %s", *protoOut)
|
||||
}
|
||||
} else {
|
||||
methods, err := grpcurl.ListMethods(descSource, symbol)
|
||||
if err != nil {
|
||||
|
|
@ -567,6 +672,9 @@ func main() {
|
|||
if err := writeProtoset(descSource, symbol); err != nil {
|
||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||
}
|
||||
if err := writeProtos(descSource, symbol); err != nil {
|
||||
fail(err, "Failed to write protos to %s", *protoOut)
|
||||
}
|
||||
}
|
||||
|
||||
} else if describe {
|
||||
|
|
@ -671,6 +779,9 @@ func main() {
|
|||
if err := writeProtoset(descSource, symbols...); err != nil {
|
||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||
}
|
||||
if err := writeProtos(descSource, symbol); err != nil {
|
||||
fail(err, "Failed to write protos to %s", *protoOut)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Invoke an RPC
|
||||
|
|
@ -703,7 +814,9 @@ func main() {
|
|||
VerbosityLevel: verbosityLevel,
|
||||
}
|
||||
|
||||
invokeTiming := rootTiming.Child("InvokeRPC")
|
||||
err = grpcurl.InvokeRPC(ctx, descSource, cc, symbol, append(addlHeaders, rpcHeaders...), h, rf.Next)
|
||||
invokeTiming.Done()
|
||||
if err != nil {
|
||||
if errStatus, ok := status.FromError(err); ok && *formatError {
|
||||
h.Status = errStatus
|
||||
|
|
@ -734,6 +847,17 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func dumpTiming(td *timingData, lvl int) {
|
||||
var ind strings.Builder
|
||||
for x := 0; x < lvl; x++ {
|
||||
ind.WriteString(" ")
|
||||
}
|
||||
fmt.Printf("%s%s: %s\n", ind.String(), td.Title, td.Value)
|
||||
for _, sd := range td.Sub {
|
||||
dumpTiming(sd, lvl+1)
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, `Usage:
|
||||
%s [flags] [address] [list|describe] [symbol]
|
||||
|
|
@ -781,7 +905,7 @@ func prettify(docString string) string {
|
|||
j++
|
||||
}
|
||||
|
||||
return strings.Join(parts[:j], "\n"+indent())
|
||||
return strings.Join(parts[:j], "\n")
|
||||
}
|
||||
|
||||
func warn(msg string, args ...interface{}) {
|
||||
|
|
@ -817,6 +941,13 @@ func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error
|
|||
return grpcurl.WriteProtoset(f, descSource, symbols...)
|
||||
}
|
||||
|
||||
func writeProtos(descSource grpcurl.DescriptorSource, symbols ...string) error {
|
||||
if *protoOut == "" {
|
||||
return nil
|
||||
}
|
||||
return grpcurl.WriteProtoFiles(*protoOut, descSource, symbols...)
|
||||
}
|
||||
|
||||
type optionalBoolFlag struct {
|
||||
set, val bool
|
||||
}
|
||||
|
|
@ -841,3 +972,12 @@ func (f *optionalBoolFlag) Set(s string) error {
|
|||
func (f *optionalBoolFlag) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func floatSecondsToDuration(seconds float64) time.Duration {
|
||||
durationFloat := seconds * float64(time.Second)
|
||||
if durationFloat > math.MaxInt64 {
|
||||
// Avoid overflow
|
||||
return math.MaxInt64
|
||||
}
|
||||
return time.Duration(durationFloat)
|
||||
}
|
||||
|
|
|
|||
103
desc_source.go
103
desc_source.go
|
|
@ -5,13 +5,15 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/desc/protoparse"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/desc/protoparse" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/desc/protoprint"
|
||||
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
|
@ -41,7 +43,7 @@ type DescriptorSource interface {
|
|||
func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) {
|
||||
files := &descriptorpb.FileDescriptorSet{}
|
||||
for _, fileName := range fileNames {
|
||||
b, err := ioutil.ReadFile(fileName)
|
||||
b, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err)
|
||||
}
|
||||
|
|
@ -258,19 +260,9 @@ func reflectionSupport(err error) error {
|
|||
// 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())
|
||||
}
|
||||
filenames, fds, err := getFileDescriptors(symbols, descSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// now expand that to include transitive dependencies in topologically sorted
|
||||
// order (such that file always appears after its dependencies)
|
||||
|
|
@ -302,3 +294,76 @@ func addFilesToSet(allFiles []*descriptorpb.FileDescriptorProto, expanded map[st
|
|||
}
|
||||
return append(allFiles, fd.AsFileDescriptorProto())
|
||||
}
|
||||
|
||||
// WriteProtoFiles will use the given descriptor source to resolve all the given
|
||||
// symbols and write proto files with their definitions to the given output directory.
|
||||
func WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource, symbols ...string) error {
|
||||
filenames, fds, err := getFileDescriptors(symbols, descSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 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))
|
||||
allFileDescriptors := make([]*desc.FileDescriptor, 0, len(fds))
|
||||
for _, filename := range filenames {
|
||||
allFileDescriptors = addFilesToFileDescriptorList(allFileDescriptors, expandedFiles, fds[filename])
|
||||
}
|
||||
pr := protoprint.Printer{}
|
||||
// now we can serialize to files
|
||||
for i := range allFileDescriptors {
|
||||
if err := writeProtoFile(outProtoDirPath, allFileDescriptors[i], &pr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeProtoFile(outProtoDirPath string, fd *desc.FileDescriptor, pr *protoprint.Printer) error {
|
||||
outFile := filepath.Join(outProtoDirPath, fd.GetFullyQualifiedName())
|
||||
outDir := filepath.Dir(outFile)
|
||||
if err := os.MkdirAll(outDir, 0777); err != nil {
|
||||
return fmt.Errorf("failed to create directory %q: %w", outDir, err)
|
||||
}
|
||||
|
||||
f, err := os.Create(outFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create proto file %q: %w", outFile, err)
|
||||
}
|
||||
defer f.Close()
|
||||
if err := pr.PrintProtoFile(fd, f); err != nil {
|
||||
return fmt.Errorf("failed to write proto file %q: %w", outFile, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileDescriptors(symbols []string, descSource DescriptorSource) ([]string, map[string]*desc.FileDescriptor, 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 nil, nil, 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())
|
||||
}
|
||||
}
|
||||
return filenames, fds, nil
|
||||
}
|
||||
|
||||
func addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expanded map[string]struct{}, fd *desc.FileDescriptor) []*desc.FileDescriptor {
|
||||
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 = addFilesToFileDescriptorList(allFiles, expanded, dep)
|
||||
}
|
||||
return append(allFiles, fd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package grpcurl
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
|
|
@ -34,7 +34,7 @@ func TestWriteProtoset(t *testing.T) {
|
|||
}
|
||||
|
||||
func loadProtoset(path string) (*descriptorpb.FileDescriptorSet, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
|
|
|||
45
go.mod
45
go.mod
|
|
@ -1,31 +1,32 @@
|
|||
module github.com/fullstorydev/grpcurl
|
||||
|
||||
go 1.18
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/jhump/protoreflect v1.15.2
|
||||
google.golang.org/grpc v1.57.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/jhump/protoreflect v1.18.0
|
||||
google.golang.org/grpc v1.66.2
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.19.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/bufbuild/protocompile v0.6.0 // indirect
|
||||
cel.dev/expr v0.15.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
golang.org/x/oauth2 v0.7.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
||||
)
|
||||
|
|
|
|||
150
go.sum
150
go.sum
|
|
@ -1,106 +1,56 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
|
||||
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
|
||||
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w=
|
||||
cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk=
|
||||
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA=
|
||||
github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
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.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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/jhump/protoreflect v1.15.2 h1:7YppbATX94jEt9KLAc5hICx4h6Yt3SaavhQRsIUEHP0=
|
||||
github.com/jhump/protoreflect v1.15.2/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155 h1:IgJPqnrlY2Mr4pYB6oaMKvFvwJ9H+X6CCY5x1vCTcpc=
|
||||
github.com/envoyproxy/go-control-plane v0.12.1-0.20240621013728-1eb8caab5155/go.mod h1:5Wkq+JduFtdAXihLmeTJf+tRYIT4KBc2vPXDhwVo1pA=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jhump/protoreflect v1.18.0 h1:TOz0MSR/0JOZ5kECB/0ufGnC2jdsgZ123Rd/k4Z5/2w=
|
||||
github.com/jhump/protoreflect v1.18.0/go.mod h1:ezWcltJIVF4zYdIFM+D/sHV4Oh5LNU08ORzCGfwvTz8=
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.1 h1:Dw1rslK/VotaUGYsv53XVWITr+5RCPXfvvlGrM/+B6w=
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.1/go.mod h1:D9LBEowZyv8/iSu97FU2zmXG3JxVTmNw21mu63niFzU=
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
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-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
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/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
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/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M=
|
||||
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
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.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
||||
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
|
||||
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
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=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
92
grpcurl.go
92
grpcurl.go
|
|
@ -14,20 +14,22 @@ import (
|
|||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/desc/protoprint"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
xdsCredentials "google.golang.org/grpc/credentials/xds"
|
||||
_ "google.golang.org/grpc/health" // import grpc/health to enable transparent client side checking
|
||||
"google.golang.org/grpc/metadata"
|
||||
protov2 "google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
|
|
@ -449,11 +451,9 @@ func makeTemplate(md *desc.MessageDescriptor, path []*desc.MessageDescriptor) pr
|
|||
dm := dynamic.NewMessage(md)
|
||||
|
||||
// if the message is a recursive structure, we don't want to blow the stack
|
||||
for _, seen := range path {
|
||||
if seen == md {
|
||||
// already visited this type; avoid infinite recursion
|
||||
return dm
|
||||
}
|
||||
if slices.Contains(path, md) {
|
||||
// already visited this type; avoid infinite recursion
|
||||
return dm
|
||||
}
|
||||
path = append(path, dm.GetMessageDescriptor())
|
||||
|
||||
|
|
@ -544,7 +544,7 @@ func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, client
|
|||
} else if cacertFile != "" {
|
||||
// Create a certificate pool from the certificate authority
|
||||
certPool := x509.NewCertPool()
|
||||
ca, err := ioutil.ReadFile(cacertFile)
|
||||
ca, err := os.ReadFile(cacertFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read ca certificate: %v", err)
|
||||
}
|
||||
|
|
@ -581,7 +581,7 @@ func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string
|
|||
if cacertFile != "" {
|
||||
// Create a certificate pool from the certificate authority
|
||||
certPool := x509.NewCertPool()
|
||||
ca, err := ioutil.ReadFile(cacertFile)
|
||||
ca, err := os.ReadFile(cacertFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read ca certificate: %v", err)
|
||||
}
|
||||
|
|
@ -608,7 +608,24 @@ func ServerTransportCredentials(cacertFile, serverCertFile, serverKeyFile string
|
|||
// BlockingDial is a helper method to dial the given address, using optional TLS credentials,
|
||||
// and blocking until the returned connection is ready. If the given credentials are nil, the
|
||||
// connection will be insecure (plain-text).
|
||||
// The network parameter should be left empty in most cases when your address is a RFC 3986
|
||||
// compliant URI. The resolver from grpc-go will resolve the correct network type.
|
||||
func BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
if creds == nil {
|
||||
creds = insecure.NewCredentials()
|
||||
}
|
||||
|
||||
var err error
|
||||
if strings.HasPrefix(address, "xds:///") {
|
||||
// The xds:/// prefix is used to signal to the gRPC client to use an xDS server to resolve the
|
||||
// target. The relevant credentials will be automatically pulled from the GRPC_XDS_BOOTSTRAP or
|
||||
// GRPC_XDS_BOOTSTRAP_CONFIG env vars.
|
||||
creds, err = xdsCredentials.NewClientCredentials(xdsCredentials.ClientOptions{FallbackCreds: creds})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// grpc.Dial doesn't provide any information on permanent connection errors (like
|
||||
// TLS handshake failures). So in order to provide good error messages, we need a
|
||||
// custom dialer that can provide that info. That means we manage the TLS handshake.
|
||||
|
|
@ -624,25 +641,37 @@ 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,
|
||||
}
|
||||
creds = &errSignalingCreds{
|
||||
TransportCredentials: creds,
|
||||
writeResult: writeResult,
|
||||
}
|
||||
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 insecure.NewCredentials() dial transport
|
||||
// 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)
|
||||
if err != nil {
|
||||
writeResult(err)
|
||||
|
||||
switch network {
|
||||
case "":
|
||||
// no-op, use address as-is
|
||||
case "tcp":
|
||||
if strings.HasPrefix(address, "unix://") {
|
||||
return nil, fmt.Errorf("tcp network type cannot use unix address %s", address)
|
||||
}
|
||||
return conn, err
|
||||
case "unix":
|
||||
if !strings.HasPrefix(address, "unix://") {
|
||||
// prepend unix:// to the address if it's not already there
|
||||
// this is to maintain backwards compatibility because the custom dialer is replaced by
|
||||
// the default dialer in grpc-go.
|
||||
// https://github.com/fullstorydev/grpcurl/pull/480
|
||||
address = "unix://" + address
|
||||
}
|
||||
default:
|
||||
// custom dialer for other networks
|
||||
dialer := func(ctx context.Context, address string) (net.Conn, error) {
|
||||
conn, err := (&net.Dialer{}).DialContext(ctx, network, address)
|
||||
if err != nil {
|
||||
// capture the error so we can provide a better message
|
||||
writeResult(err)
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
opts = append([]grpc.DialOption{grpc.WithContextDialer(dialer)}, opts...)
|
||||
}
|
||||
|
||||
// Even with grpc.FailOnNonTempDialError, this call will usually timeout in
|
||||
|
|
@ -655,13 +684,8 @@ func BlockingDial(ctx context.Context, network, address string, creds credential
|
|||
opts = append([]grpc.DialOption{grpc.FailOnNonTempDialError(true)}, opts...)
|
||||
// But we don't want caller to be able to override these two, so we put
|
||||
// them *after* the explicitly provided options.
|
||||
opts = append(opts, grpc.WithBlock(), grpc.WithContextDialer(dialer))
|
||||
opts = append(opts, grpc.WithBlock(), grpc.WithTransportCredentials(creds))
|
||||
|
||||
if creds == nil {
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
} else {
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
}
|
||||
conn, err := grpc.DialContext(ctx, address, opts...)
|
||||
var res interface{}
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
|
@ -130,7 +129,7 @@ type svr struct {
|
|||
}
|
||||
|
||||
func (s *svr) load() error {
|
||||
accts, err := ioutil.ReadFile(s.datafile)
|
||||
accts, err := os.ReadFile(s.datafile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
|
@ -162,7 +161,7 @@ func (s *svr) flush() {
|
|||
|
||||
if b, err := json.Marshal(accounts); err != nil {
|
||||
grpclog.Errorf("failed to save data to %q", s.datafile)
|
||||
} else if err := ioutil.WriteFile(s.datafile, b, 0666); err != nil {
|
||||
} else if err := os.WriteFile(s.datafile, b, 0666); err != nil {
|
||||
grpclog.Errorf("failed to save data to %q", s.datafile)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/jhump/protoreflect/dynamic"
|
||||
"github.com/golang/protobuf/jsonpb" //lint:ignore SA1019 we have to import these because some of their types appear in exported API
|
||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/desc" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/dynamic" //lint:ignore SA1019 same as above
|
||||
"github.com/jhump/protoreflect/dynamic/grpcdynamic"
|
||||
"github.com/jhump/protoreflect/grpcreflect"
|
||||
"google.golang.org/grpc"
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ The last step is to update the Homebrew recipe to use the latest version. First,
|
|||
|
||||
```sh
|
||||
# download the source archive from GitHub
|
||||
URL=https://github.com/fullstorydev/grpcurl/archive/v2.3.4.tar.gz
|
||||
URL=https://github.com/fullstorydev/grpcurl/archive/refs/tags/v2.3.4.tar.gz
|
||||
curl -L -o tmp.tgz $URL
|
||||
# and compute the SHA
|
||||
SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')"
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ rm VERSION
|
|||
|
||||
# Homebrew release
|
||||
|
||||
URL="https://github.com/fullstorydev/grpcurl/archive/${VERSION}.tar.gz"
|
||||
URL="https://github.com/fullstorydev/grpcurl/archive/refs/tags/${VERSION}.tar.gz"
|
||||
curl -L -o tmp.tgz "$URL"
|
||||
SHA="$(sha256sum < tmp.tgz | awk '{ print $1 }')"
|
||||
rm tmp.tgz
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# packing and releasing
|
||||
To pack the current branch to a snap package:
|
||||
|
||||
`snapcraft pack`
|
||||
|
||||
To install the package locally:
|
||||
|
||||
`snap install ./grpcurl_v[version tag]_amd64.snap --devmode`
|
||||
|
||||
To upload the snap to the edge channel:
|
||||
|
||||
`snapcraft upload --release edge ./grpcurl_v[version tag]_amd64.snap`
|
||||
|
||||
(you need to own the package name registration for this!)
|
||||
|
||||
# ownership
|
||||
The snap's current owner is `pietro.pasotti@canonical.com`; who is very happy to support with maintaining the snap distribution and/or transfer its ownership to the developers.
|
||||
|
||||
Please reach out to me for questions regarding the snap; including:
|
||||
- adding support for other architectures
|
||||
- automating the release
|
||||
|
||||
Cheers and thanks for the awesome tool!
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
name: grpcurl
|
||||
base: core24
|
||||
# allow grpcurl part to call craftctl set-version
|
||||
adopt-info: grpcurl
|
||||
summary: grpcurl is a command-line tool that lets you interact with gRPC servers.
|
||||
|
||||
description: |
|
||||
grpcurl is a command-line tool that lets you interact with gRPC servers.
|
||||
It's basically curl for gRPC servers.
|
||||
|
||||
grade: stable
|
||||
confinement: strict
|
||||
license: MIT
|
||||
|
||||
platforms:
|
||||
amd64:
|
||||
build-on:
|
||||
- amd64
|
||||
build-for:
|
||||
- amd64
|
||||
arm64:
|
||||
build-on:
|
||||
- amd64
|
||||
- arm64
|
||||
build-for:
|
||||
- arm64
|
||||
|
||||
apps:
|
||||
grpcurl:
|
||||
command: grpcurl
|
||||
plugs:
|
||||
- network
|
||||
|
||||
parts:
|
||||
grpcurl:
|
||||
plugin: go
|
||||
build-snaps: [go/latest/stable]
|
||||
source: https://github.com/fullstorydev/grpcurl
|
||||
source-type: git
|
||||
override-build: |
|
||||
tag="$(git describe --tags --abbrev=0)"
|
||||
craftctl set version="$tag"
|
||||
|
||||
go build -o $CRAFT_PART_INSTALL/grpcurl ./cmd/grpcurl/grpcurl.go
|
||||
|
||||
# adjust the permissions
|
||||
chmod 0755 $CRAFT_PART_INSTALL/grpcurl
|
||||
|
|
@ -253,8 +253,12 @@ func TestBrokenTLS_ClientNotTrusted(t *testing.T) {
|
|||
e.Close()
|
||||
t.Fatal("expecting TLS failure setting up server and client")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "bad certificate") {
|
||||
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
||||
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)
|
||||
// Go 1.24: "bad certificate"
|
||||
// Go 1.25: "handshake failure"
|
||||
errMsg := err.Error()
|
||||
if !strings.Contains(errMsg, "bad certificate") && !strings.Contains(errMsg, "handshake failure") {
|
||||
t.Fatalf("expecting a specific TLS certificate or handshake error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -293,8 +297,12 @@ func TestBrokenTLS_RequireClientCertButNonePresented(t *testing.T) {
|
|||
e.Close()
|
||||
t.Fatal("expecting TLS failure setting up server and client")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "bad certificate") {
|
||||
t.Fatalf("expecting TLS certificate error, got: %v", err)
|
||||
// Check for either the old error (Go <=1.24) or the new one (Go 1.25+)
|
||||
// Go 1.24: "bad certificate"
|
||||
// Go 1.25: "handshake failure"
|
||||
errMsg := err.Error()
|
||||
if !strings.Contains(errMsg, "bad certificate") && !strings.Contains(errMsg, "handshake failure") {
|
||||
t.Fatalf("expecting a specific TLS certificate or handshake error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue