Compare commits

..

171 Commits

Author SHA1 Message Date
Doug Fawley 4af0faa7a0
transport: add test case for zero second timeout (#8452) 2025-07-18 09:33:24 -07:00
Burkov Egor cc46259771
xdsclient: typed config better nil checks (#8412) 2025-07-18 08:20:15 -07:00
Doug Fawley c7b188f361
Retract v1.74.0 and v1.74.1 (#8456) 2025-07-17 12:54:20 -07:00
eshitachandwani 0a12fb0d84
Revert "credentials: allow audience to be configured (#8421) (#8442)" (#8450)
This reverts commit 7208cdc423.
2025-07-16 15:19:22 +05:30
Arjan Singh Bal 52d9f91b2d
transport: release mutex before returning on expired deadlines in server streams (#8451) 2025-07-16 10:46:15 +05:30
Arjan Singh Bal bed551a435
xds: add a test for deadlocks in nested xDS channels (#8448) 2025-07-15 11:25:39 +05:30
Doug Fawley b64eaf8684
endpointsharding: shuffle endpoint order before updating children (#8438) 2025-07-14 15:29:30 -07:00
Chris Staite 7208cdc423
credentials: allow audience to be configured (#8421) (#8442)
There are competing specifications around whether a method should be included in a JWT audience or not.  For example #4713 specifically excluded the method referencing https://google.aip.dev/auth/4111 whereas GCE IAP requires the full URI https://cloud.google.com/iap/docs/authentication-howto.

In order to facilitate both methods, we introduce a new environment variable, namely GRPC_AUDIENCE_IS_FULL_PATH, to allow the method stripping to be disabled.  This defaults to the existing behaviour of stripping the method, but can be set to avoid this.
2025-07-14 13:52:09 -04:00
Richard Belleville af2600d31c
Move erm-g to Emeritus Maintainer (#8418) 2025-07-11 15:11:52 -07:00
Richard Belleville cf1a8619d2
Remove inactive maintainers (#8416) 2025-07-11 15:11:39 -07:00
Doug Fawley eb4a783fd5
xds: give up pool lock before closing xdsclient channel (#8445) 2025-07-11 13:28:15 -07:00
Luwei Ge 3d0cb79a78
alts: improve alts handshaker error logs (#8444)
* improve alts handshaker error logs
2025-07-10 14:07:22 -07:00
Doug Fawley 12f9d9c0da
server: allow 0s grpc-timeout header values, as java is known to be able to send them (#8439) 2025-07-09 07:35:47 -07:00
Arjan Singh Bal a809a4644b
deps: update dependencies for all modules (#8434) 2025-07-09 11:09:21 +05:30
eshitachandwani a21e37488e
xds/cdsbalancer: correctly remove the unwanted cds watchers (#8428) 2025-07-08 08:12:35 +05:30
Ashesh Vidyut 64a6b623ba
grpctest: minor improvements to the test logger implementation (#8370) 2025-07-07 11:55:00 +05:30
Easwar Swaminathan aa57e6af6c
xds: cleanup internal testing functions for env vars that have long been removed (#8413) 2025-07-02 17:54:14 +05:30
Purnesh Dixit f9cf0f67e6
xdsclient: relay marshalled bytes of complete resource proto to decoders (#8422) 2025-07-02 09:51:43 +05:30
Doug Fawley 8acde50e5b
dns: add environment variable to disable TXT lookups in DNS resolver (#8377) 2025-07-01 14:33:13 -07:00
Arjan Singh Bal de72c21442
xds: Avoid error logs when setting fallback bootstrap config (#8419) 2025-07-02 02:38:29 +05:30
eshitachandwani bb4b6d5b98
add grpctester (#8423) 2025-07-01 18:55:23 +05:30
Doug Fawley dd718e42f4
github: delete mergeable configuration (#8415) 2025-06-26 12:36:11 -07:00
Doug Fawley bfc1981f6a
github: Restrict repo contents permissions to read-only in pr-validation (#8414) 2025-06-26 12:36:00 -07:00
Easwar Swaminathan 62071420ce
xdsclient: preserve original bytes for decoding when the resource is wrapped (#8411) 2025-06-25 03:50:29 -07:00
eshitachandwani a2d6045916
Change version to 1.75.0-dev (#8409) 2025-06-25 12:44:42 +05:30
Easwar Swaminathan 1787f94275
xdsclient: export genericResourceTypeDecoder (#8406) 2025-06-24 21:07:51 -07:00
Easwar Swaminathan 15299ccca3
xdsclient: make a function to return the supported resource type implementations (#8405) 2025-06-23 23:00:14 -07:00
Arjan Singh Bal 20bd1e7dfa
grpc: revert #8278: Fix cardinality violations in non-server streaming RPCs (#8404)
This reverts commit a64d9333af.
2025-06-24 00:20:59 +05:30
vinothkumarr227 bdbe6a2b5d
examples/opentelemetry: demonstrate enabling experimental metrics (#8388) 2025-06-23 11:16:35 +05:30
Arjan Singh Bal 0100d21c8f
outlierdetection: cleanup temporary pickfirst health listener attribute (#8402) 2025-06-19 11:20:35 +05:30
vinothkumarr227 bbaca7a088
stub: Add child balancer in stub.BalancerData (#8393) 2025-06-19 10:48:14 +05:30
Arjan Singh Bal e5de1e2cac
xdsclient_test: Avoid restarting listener in TestServerFailureMetrics_AfterResponseRecv (#8399) 2025-06-17 10:02:42 +05:30
Arjan Singh Bal 9c62b1c9f1
xds: Fix flaky test HandleListenerUpdate_ErrorUpdate (#8397) 2025-06-17 00:22:57 +05:30
Arjan Singh Bal 042139c86d
xds_test: Avoid buffering ack requests in ADS streams (#8395) 2025-06-17 00:18:00 +05:30
Purnesh Dixit 082a9275c7
xds: Roll forward xdsclient migration (#8391) 2025-06-17 00:09:58 +05:30
Arjan Singh Bal 5f8fe4fa6c
github: Add workflow to replace mergeable (#8401) 2025-06-16 23:57:11 +05:30
Arjan Singh Bal 57400b4e69
roundrobin: Remove unnecessary ExitIdle override (#8390) 2025-06-11 21:56:22 +05:30
Pranjali-2501 a64d9333af
grpc: Fix cardinality violations in non-server streaming RPCs (#8278) 2025-06-11 16:49:42 +05:30
Arjan Singh Bal d2e836604b
xds: revert #8310: migration of xdsclient to use generic client and dedicated LRS client
This reverts commit 996aabeb3f.
2025-06-09 21:46:02 +05:30
eshitachandwani af0f88e01d
add spiffe config (#8384) 2025-06-09 11:04:27 +05:30
Purnesh Dixit 996aabeb3f
xds: migrate internal xdsclient to use generic client and dedicated LRS client (#8310) 2025-06-06 11:15:42 +05:30
Antoine Tollenaere ec91b2e05e
xds: Remove temporary environment variable for least request (#8248) 2025-06-05 22:10:48 +05:30
Mikhail Mazurskiy 9319d72162
cmd/protoc-gen-go-grpc: use `Error()` since no formatting is performed (#8378) 2025-06-05 09:10:58 +05:30
Gregory Cooke f6bf86cc7e
Add flag guarding SPIFFE Bundle provider (#8343)
* Add flag guarding SPIFFE Bundle provider

* remove the log

* vet

* address PR comments

* add comment

* fix typo

* rename flag

* add test

* vet

* add other flag check

* remove check from watcher

* add tests for new section where the spiffe bundle map file is set to empty string

* vet
2025-06-04 13:31:41 -04:00
Arjan Singh Bal 6dfe07c8c3
balancer: Make ExitIdle compulsory for Balancers (#8367) 2025-06-03 09:15:35 +05:30
Arjan Singh Bal 8d1e6e2335
deps: update dependencies for all modules and fix revive findings (#8372) 2025-06-03 08:59:34 +05:30
vinothkumarr227 9b7bd34139
grpc: introduce new Dial and Server Options to set static window size (#8283) 2025-06-02 12:42:34 -07:00
Purnesh Dixit 643bd63bf7
xds/internal: update generic grpctransport codec name to proto (#8368) 2025-06-02 22:26:09 +05:30
Arjan Singh Bal 4275c5bdd8
transport: Re-use slice buffer reader for a stream (#8360) 2025-05-30 00:09:14 +05:30
Pranjali-2501 ec4810caeb
grpc: Fix cardinality violations in client streaming and unary RPCs (#8330) 2025-05-28 16:06:43 +05:30
Arjan Singh Bal fb223f78b8
transport: Optimize heap allocations (#8361) 2025-05-28 15:21:53 +05:30
Arjan Singh Bal f947a86ebc
balancer/ringhash: Add experimental notice in package comment (#8364) 2025-05-28 14:41:32 +05:30
apolcyn 05d49d0147
[interop client] provide a flag to set google-c2p resolver universe domain (#8145)
* provide a flag on interop_client to set google-c2p resolver universe domai
2025-05-28 11:02:07 +05:30
Purnesh Dixit 28128e0b1f
xdsclient: Fix flakyness in `TestResourceUpdateMetrics` in the case of repeated NACKs (#8363) 2025-05-28 09:41:01 +05:30
Elric.Lim 4cab0e6dc6
balacergroup: cleanup exitIdle() (#8347) 2025-05-26 21:20:28 +05:30
Purnesh Dixit e3ca7f9077
xdsclient: fix unexpectedly large LoadReportInterval in initial load report request (#8348) 2025-05-26 10:05:11 +05:30
eshitachandwani 443caad4d7
delegatingresolver: avoid proxy for resolved addresses in NO_PROXY env (#8329) 2025-05-23 21:02:11 +05:30
Michael Lumish 32e57de3f8
Rename PSM interop fallback test suite to light (#8350)
This is part of a cross-repository change to generalize the fallback
test suite to support other tests, and to change the name for
clarity. See also https://github.com/grpc/psm-interop/pull/179.

RELEASE NOTES: n/a
2025-05-22 15:45:21 -07:00
Arjan Singh Bal 6995ef2ab6
internal/transport: Wait for server goroutines to exit during shutdown in test (#8306) 2025-05-21 09:24:41 +05:30
Purnesh Dixit aaabd60df2
deps: update dependencies for all modules (#8331) 2025-05-19 23:14:07 +05:30
Antoine Tollenaere 0c24af1c70
balancer/least_request : Fix panic while handling resolver errors (#8333) 2025-05-19 13:42:16 +05:30
Purnesh Dixit f2d3e11f30
Change version to 1.74.0-dev (#8324) 2025-05-15 08:07:34 -07:00
Purnesh Dixit 1ecde18f59
xds: generic xds client ads stream tests (#8307) 2025-05-15 09:44:12 +05:30
eshitachandwani 5c0d552444
removing unused code (#8316) 2025-05-14 21:41:45 +05:30
Purnesh Dixit af5146b696
grpc: update contributing.md (#8318) 2025-05-14 14:37:25 +05:30
Purnesh Dixit 09166b665e
cleanup: remove unused constants in generic xdsclient (#8315) 2025-05-14 09:53:59 +05:30
Arjan Singh Bal e3f13e75a6
transport: Prevent sending negative timeouts (#8312) 2025-05-14 09:15:09 +05:30
Pranjali-2501 b89909b7bd
leakcheck: Fix flaky test TestCheck (#8309) 2025-05-13 15:23:49 +05:30
Arjan Singh Bal 709023de87
grpcsync/event: Simplify synchronization (#8308) 2025-05-13 00:01:48 +05:30
Arjan Singh Bal d36b02efcc
transport: Propagate status code on receiving RST_STREAM during message read (#8289) 2025-05-12 23:23:14 +05:30
eshitachandwani ee7f0b65fd
resolver/delegatingresolver: wait for proxy resolver build before update in tests (#8304) 2025-05-12 22:24:17 +05:30
Evan Jones 96e31dbc85
transport: Reject non-positive timeout values in server (#8290) 2025-05-12 09:23:29 -07:00
janardhanvissa d3d2702d29
cleanup: replace dial with newclient (#8196) 2025-05-12 10:57:47 +05:30
Doug Fawley d46d6d8962
Update CONTRIBUTING.md (#8300) 2025-05-09 09:57:52 -07:00
Marcos Huck 950a7cfdfd
health: Add List method to gRPC Health service (#8155) 2025-05-09 09:02:21 +05:30
eshitachandwani 4680429852
credentials/local: implement ValidateAuthority (#8291) 2025-05-09 02:24:49 +05:30
Purnesh Dixit b3d63b180c
xds: add MetricsReporter for generic xds client (#8274) 2025-05-08 22:48:00 +05:30
eshitachandwani d00f4acc38
resolver/delegatingresolver: wait for proxy resolver to be built in test (#8302) 2025-05-08 14:55:43 +05:30
Purnesh Dixit 0e656b20dd
xds: modify generic clients grpctransport to accept optional custom grpc new client function (#8301) 2025-05-08 14:54:25 +05:30
Arjan Singh Bal c84fab05de
grpc: Update ClientStream.CloseSend docs (#8292) 2025-05-08 00:15:51 +05:30
Evan Jones c7aec4defb
transport: skip Status.Proto() without details in writeStatus (#8282) 2025-05-07 23:50:46 +05:30
Arjan Singh Bal 35aea9cd90
weightedroundrobin: Remove nil embedded SubConn from endpointWeight (#8297) 2025-05-07 23:42:11 +05:30
Luwei Ge 41095aeec6
[alts] add keepalive params to the alts handshaker client dial option (#8293)
* add keepalive params to the alts handshaker client dial option

* no need to permit without stream

* address comment

* add env var protection

* go vet
2025-05-07 09:48:59 -07:00
eshitachandwani ee8a53a220
internal/delegatingresolver: avoid proxy if networktype of target address is not tcp (#8215) 2025-05-07 10:25:16 +05:30
Arjan Singh Bal 7fb5738f99
xds_test: Wait for server to enter serving mode in RBAC test (#8287) 2025-05-06 00:28:58 +05:30
janardhanvissa d2f02e5612
stats/opentelemetry: separate out interceptors for tracing and metrics (#8063) 2025-05-05 23:58:14 +05:30
Matthew Stevenson 00be1e1383
[alts] Add plumbing for the bound access token field in the ALTS StartClient request. (#8284) 2025-05-05 08:07:34 -07:00
vinothkumarr227 763d093ac8
otel: Test streaming rpc sequence numbers (#8272) 2025-05-05 15:18:16 +05:30
Purnesh Dixit 75d25ee2c3
xds: generic lrs client for load reporting (#8250) 2025-05-05 10:14:32 +05:30
eshitachandwani 080f9563df
credentials, transport, grpc : add a call option to override the :authority header on a per-RPC basis (#8068) 2025-04-30 14:41:28 +05:30
eshitachandwani 6821606f35
grpc: regenerate protos (#8277) 2025-04-29 16:59:06 +05:30
Arjan Singh Bal 399e2d048c
credentials/alts: Optimize Reads (Roll forward #8236) (#8271) 2025-04-29 11:19:39 +05:30
eshitachandwani 4cedec40eb
grpc_test: add tests for client streaming (#8120) 2025-04-25 13:06:52 +05:30
Arjan Singh Bal 030938e543
xds: Remove redundant proto checks (#8273) 2025-04-24 15:07:19 +05:30
Sebastian French 515f377af2
github: replace actions/upload-release-asset@v1 with gh cli (#8264) 2025-04-23 11:29:31 -07:00
Purnesh Dixit ec2d624ac9
xds: generic xds client resource watching e2e (#8183) 2025-04-23 11:23:29 +05:30
Purnesh Dixit 82e25c77f2
xds: fix TestServer_Security_WithValidAndInvalidSecurityConfiguration data race (#8269) 2025-04-23 11:03:21 +05:30
Arjan Singh Bal 2640dd7b09
atls: Clarify usage of dst in ALTSRecordCrypto interface docs (#8266) 2025-04-22 23:35:19 +05:30
Gregory Cooke 58d1a72b99
[Security] Add verification logic using SPIFFE Bundle Maps in XDS (#8229)
Add verification logic using SPIFFE Bundle Maps in XDS
2025-04-22 13:43:29 -04:00
Vadim Shtayura f7d488de75
credentials: expose NewContextWithRequestInfo publicly (#8198) 2025-04-21 16:30:52 -07:00
Antoine Tollenaere 54e7e26a1f
balancer/ringhash: move LB policy from xds/internal to exported path (#8249) 2025-04-18 10:23:10 -07:00
Doug Fawley 223149bb45
github: add printing of new packages to dependency checker (#8263) 2025-04-18 10:20:57 -07:00
Purnesh Dixit aec13815d3
cleanup: status formatting bug and comment grammar fix (#8260) 2025-04-17 20:01:42 +05:30
Antoine Tollenaere 7d68bf62e2
ringhash: fix flaky e2e tests (#8257) 2025-04-17 17:07:45 +05:30
Arjan Singh Bal 718c4d8452
xds: Make locality ID string representation consistent with A78 (#8256) 2025-04-17 12:41:55 +05:30
janardhanvissa eb4b687764
examples/features/opentelemetry: demonstrate tracing using OpenTelemetry plugin (#8056) 2025-04-17 10:42:51 +05:30
vinothkumarr227 8b2dbbbb83
New A72 changes for OpenTelemetry #8216 (#8226) 2025-04-17 09:52:52 +05:30
Antoine Tollenaere cb1613cf09
xds: make least request available by default (#8253) 2025-04-16 13:51:02 -07:00
Arjan Singh Bal d36887b369
balancer/pickfirstleaf: Avoid reading Address.Metadata (#8227) 2025-04-16 21:54:45 +05:30
Gregory Cooke 560ca642f8
xds: fix data file name in test (#8254) 2025-04-16 21:45:42 +05:30
alingse f0676ea45d
Update lrs_stream.go fix use of wrong err (#8224) 2025-04-14 15:31:50 -07:00
Yousuk Seung 6319a2c1cd
ringhash: normalize uppercase in requestHashHeader from service config (#8243) 2025-04-14 14:30:33 +05:30
Purnesh Dixit 68205d5d0a
xdsclient: update watcher API as per gRFC A88 (#7977) 2025-04-14 11:12:28 +05:30
Yash Tibrewal 732f3f32f5
stats/opentelemetry: fix trace attributes message sequence numbers to start from 0 (#8237) 2025-04-09 10:26:59 +05:30
Arjan Singh Bal 6bfa0ca35b
Rollback #8232 and #8204 (#8236)
* Revert "credentials/alts: Add comments to clarify buffer sizing (#8232)"

This reverts commit be25d96c52.

* Revert "credentials/alts: Optimize reads (#8204)"

This reverts commit b368379ef8.
2025-04-08 23:21:49 +05:30
Antoine Tollenaere 25c750934e
ringhash: implement gRFC A76 (#8159) 2025-04-08 15:50:54 +05:30
Arjan Singh Bal 09dd4ba0fb
testdata: Wrap lines to 80 columns in markdown file (#8235) 2025-04-08 14:32:55 +05:30
Arjan Singh Bal be25d96c52
credentials/alts: Add comments to clarify buffer sizing (#8232) 2025-04-08 09:03:28 +05:30
Arjan Singh Bal db81a2cb4f
benchmark: Specify passthrough resolver to avoid resolution failures (#8231) 2025-04-08 09:02:40 +05:30
Arjan Singh Bal b368379ef8
credentials/alts: Optimize reads (#8204) 2025-04-07 11:51:14 +05:30
Gregory Cooke 4b5505d301
[Security] Add support for SPIFFE Bundle Maps in XDS bundles (#8180)
This adds support for configuring SPIFFE Bundle Maps inside of credentials via xds bundles.

See the gRFC for more detail grpc/proposal#462
2025-04-04 13:12:53 -04:00
vinothkumarr227 ce35fd41c5
stats/opentelemetry: add trace event for name resolution delay (#8074) 2025-04-04 09:55:45 +05:30
eshitachandwani 52c643eb74
deps: update dependencies for all modules (#8221)
* update deps

* protos update
2025-04-04 09:54:18 +05:30
eshitachandwani 51d6a43ec5
Change version to 1.73.0-dev (#8220) 2025-04-03 15:23:17 +05:30
Purnesh Dixit 57a2605e35
xdsclient: fix TestServerFailureMetrics_BeforeResponseRecv test to wait for watch to start before stopping the listener (#8217) 2025-04-03 09:45:55 +05:30
Purnesh Dixit 5edab9e554
xdsclient: add grpc.xds_client.server_failure counter mertric (#8203) 2025-03-28 22:17:11 +05:30
Purnesh Dixit 78ba6616c1
regenerate protos (#8208) 2025-03-28 21:37:37 +05:30
Arjan Singh Bal 6819ed796f
delegatingresolver: Stop calls into delegates once the parent resolver is closed (#8195) 2025-03-26 22:30:16 +05:30
Doug Fawley a51009d1d7
resolver: convert EndpointMap to use generics (#8189) 2025-03-24 09:37:36 -07:00
Doug Fawley b0d1203846
resolver: create AddressMapV2 with generics to replace AddressMap (#8187) 2025-03-21 13:09:52 -07:00
eshitachandwani 43a4a84abc
internal/balancer/clusterimpl: replace testpb with testgrpc (#8188) 2025-03-21 17:22:28 +05:30
Doug Fawley d8924ac46a
xds: fix support for load reporting in LOGICAL_DNS clusters (#8170) 2025-03-20 09:58:32 -07:00
Doug Fawley ce2fded1f3
xds: fix support for circuit breakers in LOGICAL_DNS clusters (#8169) 2025-03-20 09:39:49 -07:00
Arjan Singh Bal eb744dec5d
resolver: Make EndpointMap's Get, Set and Delete operations O(1) (#8179) 2025-03-19 11:22:09 -07:00
Ryan Blaney 8d8571e474
stats: Improved sequencing documentation for server-side stats events and added tests. (#7885) 2025-03-19 22:23:17 +05:30
Doug Fawley 0af5a164e0
grpc: fix bug causing an extra Read if a compressed message is the same size as the limit (#8178) 2025-03-18 15:08:29 -07:00
Purnesh Dixit 1703656ba5
xds: generic xDS client transport channel and ads stream implementation (#8144) 2025-03-18 12:33:18 +05:30
Purnesh Dixit c27e6dc312
xdsclient: read bootstrap config before creating the first xDS client in DefaultPool (#8164) 2025-03-18 09:36:56 +05:30
Gregory Cooke 1f6b0cff02
[Security] Add support for SPIFFE Bundle Maps in certificate providers (#8167) 2025-03-17 14:39:04 -04:00
vinothkumarr227 775150f68c
stats/opentelemetry: use TextMapProvider and TracerProvider from TraceOptions instead of otel global (#8166) 2025-03-13 11:54:06 +05:30
Purnesh Dixit d860daa75b
example/features/retry: fix grpc.NewClient call in documentation (#8163) 2025-03-13 10:33:28 +05:30
Gregory Cooke 75d4a60639
[Security] Add support for parsing SPIFFE Bundle Maps (#8124)
This adds a dependency on go-spiffe in order to parse SPIFFE bundles. More specifically, that library does not yet support SPIFFE bundle maps, but it does support SPIFFE bundles. This adds parsing of these maps to grpc-go
2025-03-12 13:32:01 -04:00
MV Shiva 5ac9042795
balancer/rls: allow maxAge to exceed 5m if staleAge is set (#8137) 2025-03-12 07:21:16 -07:00
Easwar Swaminathan bdba42f3a7
xds: emit resource-not-found logs at Warning level (#8158) 2025-03-11 15:32:47 -07:00
Easwar Swaminathan a0a739f794
xds: ensure node ID is populated in errors from the server (#8140)
* xds: ensure node ID is populated in errors from the server

* xds: cleanup server e2e tests

* don't wrap status errors
2025-03-10 15:05:05 -07:00
Doug Fawley 5668c66bc6
resolver/manual: allow calling UpdateState with an un-Built resolver (#8150) 2025-03-06 13:39:48 -08:00
Arjan Singh Bal 5199327135
grpc: Add endpoints in resolverWrapper.NewAddresses (#8149) 2025-03-06 22:55:15 +05:30
chressie f49c747db7
balancer/pickfirst/pickfirstleaf: fix race condition in tests (#8148) 2025-03-06 20:55:28 +05:30
Purnesh Dixit af078150db
xds: introduce generic xds clients xDS and LRS Client API signatures (#8042) 2025-03-06 09:35:04 +05:30
Arjan Singh Bal 8c080da92c
priority: Send and validate connection error in test (#8143) 2025-03-05 23:44:04 +05:30
Arjan Singh Bal e8c412da15
*: Regenerate protos (#8142) 2025-03-05 21:38:53 +05:30
Arjan Singh Bal 0914bba6c5
interop: Wait for server to become ready in alts interop tests (#8141) 2025-03-05 12:37:38 +05:30
Easwar Swaminathan bffa4be817
xds: ensure xDS node ID is populated in errors from xds resolver and cds lb policy (#8131) 2025-03-04 17:32:04 -08:00
Arjan Singh Bal 8ae4b7db91
clusterresolver: Lower log level when ExitIdle is called with no child (#8133) 2025-03-01 00:23:48 +05:30
Arjan Singh Bal 0d6e39f679
transport: Send RST stream from the server when deadline expires (#8071) 2025-02-28 22:49:18 +05:30
Purnesh Dixit 7505bf2855
xds: introduce simple grpc transport for generic xds clients (#8066) 2025-02-28 15:05:21 +05:30
Purnesh Dixit 01080d57f3
stats/openetelemetry: refactor and make e2e test stats verification deterministic (#8077) 2025-02-28 14:42:19 +05:30
janardhanvissa b0f5027011
cleanup: replace dial with newclient (#7970) 2025-02-28 13:53:16 +05:30
janardhanvissa 52a257e680
cleanup: replace dial with newclient (#7967) 2025-02-27 16:03:14 -08:00
Arjan Singh Bal d48317fafe
github: change test action to cover the legacy pickfirst balancer (#8129) 2025-02-27 08:21:51 -08:00
Arjan Singh Bal a510cf5d4d
xds, pickfirst: Enable additional addresses in xDS, set new pick_first as default (#8126) 2025-02-27 10:22:22 +05:30
Easwar Swaminathan e9c0617119
xds: simplify code handling certain error conditions in the resolver (#8123) 2025-02-26 15:16:32 -08:00
Easwar Swaminathan feaf942a79
cds: stop child policies on resource-not-found errors (#8122) 2025-02-26 15:05:37 -08:00
Arjan Singh Bal dbf92b436d
deps: update dependencies for all modules (#8108) 2025-02-25 15:21:44 -08:00
Arjan Singh Bal aa629e0ef3
balancergroup: Make closing terminal (#8095) 2025-02-25 11:29:05 +05:30
Arjan Singh Bal e0ac3acff4
xdsclient: Add error type for NACKed resources (#8117) 2025-02-25 11:15:16 +05:30
Arjan Singh Bal 65c6718afb
examples/features/dualstack: Demonstrate Dual Stack functionality (#8098) 2025-02-21 12:12:25 +05:30
Matthieu MOREL c75fc8edec
chore: enable early-return and unnecessary-stmt and useless-break from revive (#8100) 2025-02-20 14:10:09 -08:00
Easwar Swaminathan c7db760171
xdsclient: ensure xDS node ID in included in NACK and connectivity errors (#8103) 2025-02-20 06:09:24 -08:00
Arjan Singh Bal 42fc25a9b4
weightedroundrobin: Move functions to manage Endpoint weights into a new internal package (#8087) 2025-02-19 22:14:21 +05:30
Arjan Singh Bal 607565d68c
Change version to 1.72.0-dev (#8107) 2025-02-19 21:47:18 +05:30
508 changed files with 29381 additions and 13670 deletions

21
.github/mergeable.yml vendored
View File

@ -1,21 +0,0 @@
version: 2
mergeable:
- when: pull_request.*
validate:
- do: label
must_include:
regex: '^Type:'
- do: description
must_include:
# Allow:
# RELEASE NOTES: none (case insensitive)
#
# RELEASE NOTES: N/A (case insensitive)
#
# RELEASE NOTES:
# * <text>
regex: '^RELEASE NOTES:\s*([Nn][Oo][Nn][Ee]|[Nn]/[Aa]|\n(\*|-)\s*.+)$'
regex_flag: 'm'
- do: milestone
must_include:
regex: 'Release$'

View File

@ -19,8 +19,8 @@ jobs:
- name: Run coverage - name: Run coverage
run: go test -coverprofile=coverage.out -coverpkg=./... ./... run: go test -coverprofile=coverage.out -coverpkg=./... ./...
- name: Run coverage with new pickfirst - name: Run coverage with old pickfirst
run: GRPC_EXPERIMENTAL_ENABLE_NEW_PICK_FIRST=true go test -coverprofile=coverage_new_pickfirst.out -coverpkg=./... ./... run: GRPC_EXPERIMENTAL_ENABLE_NEW_PICK_FIRST=false go test -coverprofile=coverage_old_pickfirst.out -coverpkg=./... ./...
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v4

View File

@ -42,8 +42,19 @@ jobs:
mkdir "${TEMP_DIR}/before" mkdir "${TEMP_DIR}/before"
scripts/gen-deps.sh "${TEMP_DIR}/before" scripts/gen-deps.sh "${TEMP_DIR}/before"
echo "Comparing dependencies..." echo -e " \nComparing dependencies..."
cd "${TEMP_DIR}" cd "${TEMP_DIR}"
# Run grep in a sub-shell since bash does not support ! in the middle of a pipe # Run grep in a sub-shell since bash does not support ! in the middle of a pipe.
diff -u0 -r "before" "after" | bash -c '! grep -v "@@"' if diff -u0 -r "before" "after" | bash -c '! grep -v "@@"'; then
echo "No changes detected." echo "No changes detected."
exit 0
fi
# Print packages in `after` but not `before`.
for x in $(ls -1 after | grep -vF "$(ls -1 before)"); do
echo -e " \nDependencies of new package $x:"
cat "after/$x"
done
echo -e " \nChanges detected; exiting with error."
exit 1

55
.github/workflows/pr-validation.yml vendored Normal file
View File

@ -0,0 +1,55 @@
name: PR Validation
on:
pull_request:
types: [opened, edited, synchronize, labeled, unlabeled, milestoned, demilestoned]
permissions:
contents: read
jobs:
validate:
name: Validate PR
runs-on: ubuntu-latest
steps:
- name: Validate Label
uses: actions/github-script@v6
with:
script: |
const labels = context.payload.pull_request.labels.map(label => label.name);
const requiredRegex = new RegExp('^Type:');
const hasRequiredLabel = labels.some(label => requiredRegex.test(label));
if (!hasRequiredLabel) {
core.setFailed("This PR must have a label starting with 'Type:'.");
}
- name: Validate Description
uses: actions/github-script@v6
with:
script: |
const body = context.payload.pull_request.body;
const requiredRegex = new RegExp('^RELEASE NOTES:\\s*([Nn][Oo][Nn][Ee]|[Nn]/[Aa]|\\n(\\*|-)\\s*.+)$', 'm');
if (!requiredRegex.test(body)) {
core.setFailed(`
The PR description must include a RELEASE NOTES section.
It should be in one of the following formats:
- "RELEASE NOTES: none" (case-insensitive)
- "RELEASE NOTES: N/A" (case-insensitive)
- A bulleted list under "RELEASE NOTES:", for example:
RELEASE NOTES:
* my_package: Fix bug causing crash...
`);
}
- name: Validate Milestone
uses: actions/github-script@v6
with:
script: |
const milestone = context.payload.pull_request.milestone;
if (!milestone) {
core.setFailed("This PR must be associated with a milestone.");
} else {
const requiredRegex = new RegExp('Release$');
if (!requiredRegex.test(milestone.title)) {
core.setFailed("The milestone for this PR must end with 'Release'.");
}
}

View File

@ -57,11 +57,7 @@ jobs:
echo "name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT echo "name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT
- name: Upload asset - name: Upload asset
uses: actions/upload-release-asset@v1 run: |
gh release upload ${{ github.event.release.tag_name }} ./${{ steps.package.outputs.name }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./${{ steps.package.outputs.name }}
asset_name: ${{ steps.package.outputs.name }}
asset_content_type: application/gzip

View File

@ -27,7 +27,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: '1.23' go-version: '1.24'
cache-dependency-path: "**/go.sum" cache-dependency-path: "**/go.sum"
# Run the vet-proto checks. # Run the vet-proto checks.
@ -43,33 +43,33 @@ jobs:
matrix: matrix:
include: include:
- type: vet - type: vet
goversion: '1.22' goversion: '1.23'
- type: extras - type: extras
goversion: '1.23' goversion: '1.24'
- type: tests - type: tests
goversion: '1.23' goversion: '1.24'
- type: tests - type: tests
goversion: '1.23' goversion: '1.24'
testflags: -race testflags: -race
- type: tests - type: tests
goversion: '1.23' goversion: '1.24'
goarch: 386 goarch: 386
- type: tests - type: tests
goversion: '1.23' goversion: '1.24'
goarch: arm64 goarch: arm64
- type: tests - type: tests
goversion: '1.22' goversion: '1.23'
- type: tests - type: tests
goversion: '1.23' goversion: '1.24'
testflags: -race testflags: -race
grpcenv: 'GRPC_EXPERIMENTAL_ENABLE_NEW_PICK_FIRST=true' grpcenv: 'GRPC_EXPERIMENTAL_ENABLE_NEW_PICK_FIRST=false'
steps: steps:
# Setup the environment. # Setup the environment.
@ -107,7 +107,7 @@ jobs:
if: matrix.type == 'tests' if: matrix.type == 'tests'
run: | run: |
go version go version
go test ${{ matrix.testflags }} -cpu 1,4 -timeout 7m google.golang.org/grpc/... go test ${{ matrix.testflags }} -cpu 1,4 -timeout 7m ./...
cd "${GITHUB_WORKSPACE}" cd "${GITHUB_WORKSPACE}"
for MOD_FILE in $(find . -name 'go.mod' | grep -Ev '^\./go\.mod'); do for MOD_FILE in $(find . -name 'go.mod' | grep -Ev '^\./go\.mod'); do
pushd "$(dirname ${MOD_FILE})" pushd "$(dirname ${MOD_FILE})"

View File

@ -1,73 +1,102 @@
# How to contribute # How to contribute
We definitely welcome your patches and contributions to gRPC! Please read the gRPC We welcome your patches and contributions to gRPC! Please read the gRPC
organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md) organization's [governance
and [contribution guidelines](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md) before proceeding. rules](https://github.com/grpc/grpc-community/blob/master/governance.md) before
proceeding.
If you are new to GitHub, please start by reading [Pull Request howto](https://help.github.com/articles/about-pull-requests/) If you are new to GitHub, please start by reading [Pull Request howto](https://help.github.com/articles/about-pull-requests/)
## Legal requirements ## Legal requirements
In order to protect both you and ourselves, you will need to sign the In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://identity.linuxfoundation.org/projects/cncf). [Contributor License
Agreement](https://identity.linuxfoundation.org/projects/cncf). When you create
your first PR, a link will be added as a comment that contains the steps needed
to complete this process.
## Getting Started
A great way to start is by searching through our open issues. [Unassigned issues
labeled as "help
wanted"](https://github.com/grpc/grpc-go/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20label%3A%22Status%3A%20Help%20Wanted%22%20no%3Aassignee)
are especially nice for first-time contributors, as they should be well-defined
problems that already have agreed-upon solutions.
## Code Style
We follow [Google's published Go style
guide](https://google.github.io/styleguide/go/). Note that there are three
primary documents that make up this style guide; please follow them as closely
as possible. If a reviewer recommends something that contradicts those
guidelines, there may be valid reasons to do so, but it should be rare.
## Guidelines for Pull Requests ## Guidelines for Pull Requests
How to get your contributions merged smoothly and quickly.
How to get your contributions merged smoothly and quickly:
- Create **small PRs** that are narrowly focused on **addressing a single - Create **small PRs** that are narrowly focused on **addressing a single
concern**. We often times receive PRs that are trying to fix several things at concern**. We often receive PRs that attempt to fix several things at the same
a time, but only one fix is considered acceptable, nothing gets merged and time, and if one part of the PR has a problem, that will hold up the entire
both author's & review's time is wasted. Create more PRs to address different PR.
concerns and everyone will be happy.
- If you are searching for features to work on, issues labeled [Status: Help - For **speculative changes**, consider opening an issue and discussing it
Wanted](https://github.com/grpc/grpc-go/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Status%3A+Help+Wanted%22) first. If you are suggesting a behavioral or API change, consider starting
is a great place to start. These issues are well-documented and usually can be with a [gRFC proposal](https://github.com/grpc/proposal). Many new features
resolved with a single pull request. that are not bug fixes will require cross-language agreement.
- If you are adding a new file, make sure it has the copyright message template - If you want to fix **formatting or style**, consider whether your changes are
at the top as a comment. You can copy over the message from an existing file an obvious improvement or might be considered a personal preference. If a
and update the year. style change is based on preference, it likely will not be accepted. If it
corrects widely agreed-upon anti-patterns, then please do create a PR and
explain the benefits of the change.
- The grpc package should only depend on standard Go packages and a small number - For correcting **misspellings**, please be aware that we use some terms that
of exceptions. If your contribution introduces new dependencies which are NOT are sometimes flagged by spell checkers. As an example, "if an only if" is
in the [list](https://godoc.org/google.golang.org/grpc?imports), you need a often written as "iff". Please do not make spelling correction changes unless
discussion with gRPC-Go authors and consultants. you are certain they are misspellings.
- For speculative changes, consider opening an issue and discussing it first. If
you are suggesting a behavioral or API change, consider starting with a [gRFC
proposal](https://github.com/grpc/proposal).
- Provide a good **PR description** as a record of **what** change is being made - Provide a good **PR description** as a record of **what** change is being made
and **why** it was made. Link to a GitHub issue if it exists. and **why** it was made. Link to a GitHub issue if it exists.
- If you want to fix formatting or style, consider whether your changes are an - Maintain a **clean commit history** and use **meaningful commit messages**.
obvious improvement or might be considered a personal preference. If a style PRs with messy commit histories are difficult to review and won't be merged.
change is based on preference, it likely will not be accepted. If it corrects Before sending your PR, ensure your changes are based on top of the latest
widely agreed-upon anti-patterns, then please do create a PR and explain the `upstream/master` commits, and avoid rebasing in the middle of a code review.
benefits of the change. You should **never use `git push -f`** unless absolutely necessary during a
review, as it can interfere with GitHub's tracking of comments.
- Unless your PR is trivial, you should expect there will be reviewer comments
that you'll need to address before merging. We'll mark it as `Status: Requires
Reporter Clarification` if we expect you to respond to these comments in a
timely manner. If the PR remains inactive for 6 days, it will be marked as
`stale` and automatically close 7 days after that if we don't hear back from
you.
- Maintain **clean commit history** and use **meaningful commit messages**. PRs
with messy commit history are difficult to review and won't be merged. Use
`rebase -i upstream/master` to curate your commit history and/or to bring in
latest changes from master (but avoid rebasing in the middle of a code
review).
- Keep your PR up to date with upstream/master (if there are merge conflicts, we
can't really merge your change).
- **All tests need to be passing** before your change can be merged. We - **All tests need to be passing** before your change can be merged. We
recommend you **run tests locally** before creating your PR to catch breakages recommend you run tests locally before creating your PR to catch breakages
early on. early on:
- `./scripts/vet.sh` to catch vet errors
- `go test -cpu 1,4 -timeout 7m ./...` to run the tests
- `go test -race -cpu 1,4 -timeout 7m ./...` to run tests in race mode
- Exceptions to the rules can be made if there's a compelling reason for doing so. - `./scripts/vet.sh` to catch vet errors.
- `go test -cpu 1,4 -timeout 7m ./...` to run the tests.
- `go test -race -cpu 1,4 -timeout 7m ./...` to run tests in race mode.
Note that we have a multi-module repo, so `go test` commands may need to be
run from the root of each module in order to cause all tests to run.
*Alternatively*, you may find it easier to push your changes to your fork on
GitHub, which will trigger a GitHub Actions run that you can use to verify
everything is passing.
- If you are adding a new file, make sure it has the **copyright message**
template at the top as a comment. You can copy the message from an existing
file and update the year.
- The grpc package should only depend on standard Go packages and a small number
of exceptions. **If your contribution introduces new dependencies**, you will
need a discussion with gRPC-Go maintainers. A GitHub action check will run on
every PR, and will flag any transitive dependency changes from any public
package.
- Unless your PR is trivial, you should **expect reviewer comments** that you
will need to address before merging. We'll label the PR as `Status: Requires
Reporter Clarification` if we expect you to respond to these comments in a
timely manner. If the PR remains inactive for 6 days, it will be marked as
`stale`, and we will automatically close it after 7 days if we don't hear back
from you. Please feel free to ping issues or bugs if you do not get a response
within a week.
- Exceptions to the rules can be made if there's a compelling reason to do so.

View File

@ -57,7 +57,7 @@ As a reminder, all `CallOption`s may be converted into `DialOption`s that become
the default for all RPCs sent through a client using `grpc.WithDefaultCallOptions`: the default for all RPCs sent through a client using `grpc.WithDefaultCallOptions`:
```go ```go
myclient := grpc.Dial(ctx, target, grpc.WithDefaultCallOptions(grpc.CallContentSubtype("mycodec"))) myclient := grpc.NewClient(target, grpc.WithDefaultCallOptions(grpc.CallContentSubtype("mycodec")))
``` ```
When specified in either of these ways, messages will be encoded using this When specified in either of these ways, messages will be encoded using this
@ -132,7 +132,7 @@ As a reminder, all `CallOption`s may be converted into `DialOption`s that become
the default for all RPCs sent through a client using `grpc.WithDefaultCallOptions`: the default for all RPCs sent through a client using `grpc.WithDefaultCallOptions`:
```go ```go
myclient := grpc.Dial(ctx, target, grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip"))) myclient := grpc.NewClient(target, grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")))
``` ```
When specified in either of these ways, messages will be compressed using this When specified in either of these ways, messages will be compressed using this

View File

@ -5,7 +5,7 @@ As outlined in the [gRPC authentication guide](https://grpc.io/docs/guides/auth.
# Enabling TLS on a gRPC client # Enabling TLS on a gRPC client
```Go ```Go
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))) conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")))
``` ```
# Enabling TLS on a gRPC server # Enabling TLS on a gRPC server
@ -63,7 +63,7 @@ to prevent any insecure transmission of tokens.
## Google Compute Engine (GCE) ## Google Compute Engine (GCE)
```Go ```Go
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithPerRPCCredentials(oauth.NewComputeEngine())) conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithPerRPCCredentials(oauth.NewComputeEngine()))
``` ```
## JWT ## JWT
@ -73,6 +73,6 @@ jwtCreds, err := oauth.NewServiceAccountFromFile(*serviceAccountKeyFile, *oauthS
if err != nil { if err != nil {
log.Fatalf("Failed to create JWT credentials: %v", err) log.Fatalf("Failed to create JWT credentials: %v", err)
} }
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithPerRPCCredentials(jwtCreds)) conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithPerRPCCredentials(jwtCreds))
``` ```

View File

@ -9,21 +9,19 @@ for general contribution guidelines.
## Maintainers (in alphabetical order) ## Maintainers (in alphabetical order)
- [aranjans](https://github.com/aranjans), Google LLC
- [arjan-bal](https://github.com/arjan-bal), Google LLC - [arjan-bal](https://github.com/arjan-bal), Google LLC
- [arvindbr8](https://github.com/arvindbr8), Google LLC - [arvindbr8](https://github.com/arvindbr8), Google LLC
- [atollena](https://github.com/atollena), Datadog, Inc. - [atollena](https://github.com/atollena), Datadog, Inc.
- [dfawley](https://github.com/dfawley), Google LLC - [dfawley](https://github.com/dfawley), Google LLC
- [easwars](https://github.com/easwars), Google LLC - [easwars](https://github.com/easwars), Google LLC
- [erm-g](https://github.com/erm-g), Google LLC
- [gtcooke94](https://github.com/gtcooke94), Google LLC - [gtcooke94](https://github.com/gtcooke94), Google LLC
- [purnesh42h](https://github.com/purnesh42h), Google LLC
- [zasweq](https://github.com/zasweq), Google LLC
## Emeritus Maintainers (in alphabetical order) ## Emeritus Maintainers (in alphabetical order)
- [adelez](https://github.com/adelez) - [adelez](https://github.com/adelez)
- [aranjans](https://github.com/aranjans)
- [canguler](https://github.com/canguler) - [canguler](https://github.com/canguler)
- [cesarghali](https://github.com/cesarghali) - [cesarghali](https://github.com/cesarghali)
- [erm-g](https://github.com/erm-g)
- [iamqizhao](https://github.com/iamqizhao) - [iamqizhao](https://github.com/iamqizhao)
- [jeanbza](https://github.com/jeanbza) - [jeanbza](https://github.com/jeanbza)
- [jtattermusch](https://github.com/jtattermusch) - [jtattermusch](https://github.com/jtattermusch)
@ -32,5 +30,7 @@ for general contribution guidelines.
- [matt-kwong](https://github.com/matt-kwong) - [matt-kwong](https://github.com/matt-kwong)
- [menghanl](https://github.com/menghanl) - [menghanl](https://github.com/menghanl)
- [nicolasnoble](https://github.com/nicolasnoble) - [nicolasnoble](https://github.com/nicolasnoble)
- [purnesh42h](https://github.com/purnesh42h)
- [srini100](https://github.com/srini100) - [srini100](https://github.com/srini100)
- [yongni](https://github.com/yongni) - [yongni](https://github.com/yongni)
- [zasweq](https://github.com/zasweq)

View File

@ -32,6 +32,7 @@ import "google.golang.org/grpc"
- [Low-level technical docs](Documentation) from this repository - [Low-level technical docs](Documentation) from this repository
- [Performance benchmark][] - [Performance benchmark][]
- [Examples](examples) - [Examples](examples)
- [Contribution guidelines](CONTRIBUTING.md)
## FAQ ## FAQ

View File

@ -68,9 +68,9 @@ func RunRegisterTests(t *testing.T, ec ExpectedStatusCodes) {
server.Serve(lis) server.Serve(lis)
}() }()
conn, err := grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil { if err != nil {
t.Fatalf("cannot connect to server: %v", err) t.Fatalf("grpc.NewClient(%q) = %v", lis.Addr().String(), err)
} }
t.Run("channelz", func(t *testing.T) { t.Run("channelz", func(t *testing.T) {

View File

@ -78,7 +78,7 @@ func (lb *loggerBuilder) Build(audit.LoggerConfig) audit.Logger {
} }
} }
func (*loggerBuilder) ParseLoggerConfig(config json.RawMessage) (audit.LoggerConfig, error) { func (*loggerBuilder) ParseLoggerConfig(json.RawMessage) (audit.LoggerConfig, error) {
return nil, nil return nil, nil
} }
@ -246,7 +246,7 @@ func (s) TestAuditLogger(t *testing.T) {
serverCreds := loadServerCreds(t) serverCreds := loadServerCreds(t)
clientCreds := loadClientCreds(t) clientCreds := loadClientCreds(t)
ss := &stubserver.StubServer{ ss := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error { FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {

View File

@ -295,7 +295,7 @@ func (s) TestStaticPolicyEnd2End(t *testing.T) {
i, _ := authz.NewStatic(test.authzPolicy) i, _ := authz.NewStatic(test.authzPolicy)
stub := &stubserver.StubServer{ stub := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
StreamingInputCallF: func(stream testgrpc.TestService_StreamingInputCallServer) error { StreamingInputCallF: func(stream testgrpc.TestService_StreamingInputCallServer) error {
@ -374,7 +374,7 @@ func (s) TestAllowsRPCRequestWithPrincipalsFieldOnTLSAuthenticatedConnection(t *
} }
stub := &stubserver.StubServer{ stub := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
S: grpc.NewServer(grpc.Creds(creds), grpc.ChainUnaryInterceptor(i.UnaryInterceptor)), S: grpc.NewServer(grpc.Creds(creds), grpc.ChainUnaryInterceptor(i.UnaryInterceptor)),
@ -436,7 +436,7 @@ func (s) TestAllowsRPCRequestWithPrincipalsFieldOnMTLSAuthenticatedConnection(t
ClientCAs: certPool, ClientCAs: certPool,
}) })
stub := &stubserver.StubServer{ stub := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
S: grpc.NewServer(grpc.Creds(creds), grpc.ChainUnaryInterceptor(i.UnaryInterceptor)), S: grpc.NewServer(grpc.Creds(creds), grpc.ChainUnaryInterceptor(i.UnaryInterceptor)),
@ -486,7 +486,7 @@ func (s) TestFileWatcherEnd2End(t *testing.T) {
defer i.Close() defer i.Close()
stub := &stubserver.StubServer{ stub := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
StreamingInputCallF: func(stream testgrpc.TestService_StreamingInputCallServer) error { StreamingInputCallF: func(stream testgrpc.TestService_StreamingInputCallServer) error {
@ -563,7 +563,7 @@ func (s) TestFileWatcher_ValidPolicyRefresh(t *testing.T) {
defer i.Close() defer i.Close()
stub := &stubserver.StubServer{ stub := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
// Start a gRPC server with gRPC authz unary server interceptor. // Start a gRPC server with gRPC authz unary server interceptor.
@ -608,7 +608,7 @@ func (s) TestFileWatcher_InvalidPolicySkipReload(t *testing.T) {
defer i.Close() defer i.Close()
stub := &stubserver.StubServer{ stub := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
// Start a gRPC server with gRPC authz unary server interceptors. // Start a gRPC server with gRPC authz unary server interceptors.
@ -656,7 +656,7 @@ func (s) TestFileWatcher_RecoversFromReloadFailure(t *testing.T) {
defer i.Close() defer i.Close()
stub := &stubserver.StubServer{ stub := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, req *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(context.Context, *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{}, nil return &testpb.SimpleResponse{}, nil
}, },
S: grpc.NewServer(grpc.ChainUnaryInterceptor(i.UnaryInterceptor)), S: grpc.NewServer(grpc.ChainUnaryInterceptor(i.UnaryInterceptor)),

View File

@ -360,6 +360,10 @@ type Balancer interface {
// call SubConn.Shutdown for its existing SubConns; however, this will be // call SubConn.Shutdown for its existing SubConns; however, this will be
// required in a future release, so it is recommended. // required in a future release, so it is recommended.
Close() Close()
// ExitIdle instructs the LB policy to reconnect to backends / exit the
// IDLE state, if appropriate and possible. Note that SubConns that enter
// the IDLE state will not reconnect until SubConn.Connect is called.
ExitIdle()
} }
// ExitIdler is an optional interface for balancers to implement. If // ExitIdler is an optional interface for balancers to implement. If
@ -367,8 +371,8 @@ type Balancer interface {
// the ClientConn is idle. If unimplemented, ClientConn.Connect will cause // the ClientConn is idle. If unimplemented, ClientConn.Connect will cause
// all SubConns to connect. // all SubConns to connect.
// //
// Notice: it will be required for all balancers to implement this in a future // Deprecated: All balancers must implement this interface. This interface will
// release. // be removed in a future release.
type ExitIdler interface { type ExitIdler interface {
// ExitIdle instructs the LB policy to reconnect to backends / exit the // ExitIdle instructs the LB policy to reconnect to backends / exit the
// IDLE state, if appropriate and possible. Note that SubConns that enter // IDLE state, if appropriate and possible. Note that SubConns that enter

View File

@ -41,7 +41,7 @@ func (bb *baseBuilder) Build(cc balancer.ClientConn, _ balancer.BuildOptions) ba
cc: cc, cc: cc,
pickerBuilder: bb.pickerBuilder, pickerBuilder: bb.pickerBuilder,
subConns: resolver.NewAddressMap(), subConns: resolver.NewAddressMapV2[balancer.SubConn](),
scStates: make(map[balancer.SubConn]connectivity.State), scStates: make(map[balancer.SubConn]connectivity.State),
csEvltr: &balancer.ConnectivityStateEvaluator{}, csEvltr: &balancer.ConnectivityStateEvaluator{},
config: bb.config, config: bb.config,
@ -65,7 +65,7 @@ type baseBalancer struct {
csEvltr *balancer.ConnectivityStateEvaluator csEvltr *balancer.ConnectivityStateEvaluator
state connectivity.State state connectivity.State
subConns *resolver.AddressMap subConns *resolver.AddressMapV2[balancer.SubConn]
scStates map[balancer.SubConn]connectivity.State scStates map[balancer.SubConn]connectivity.State
picker balancer.Picker picker balancer.Picker
config Config config Config
@ -100,7 +100,7 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
// Successful resolution; clear resolver error and ensure we return nil. // Successful resolution; clear resolver error and ensure we return nil.
b.resolverErr = nil b.resolverErr = nil
// addrsSet is the set converted from addrs, it's used for quick lookup of an address. // addrsSet is the set converted from addrs, it's used for quick lookup of an address.
addrsSet := resolver.NewAddressMap() addrsSet := resolver.NewAddressMapV2[any]()
for _, a := range s.ResolverState.Addresses { for _, a := range s.ResolverState.Addresses {
addrsSet.Set(a, nil) addrsSet.Set(a, nil)
if _, ok := b.subConns.Get(a); !ok { if _, ok := b.subConns.Get(a); !ok {
@ -122,8 +122,7 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
} }
} }
for _, a := range b.subConns.Keys() { for _, a := range b.subConns.Keys() {
sci, _ := b.subConns.Get(a) sc, _ := b.subConns.Get(a)
sc := sci.(balancer.SubConn)
// a was removed by resolver. // a was removed by resolver.
if _, ok := addrsSet.Get(a); !ok { if _, ok := addrsSet.Get(a); !ok {
sc.Shutdown() sc.Shutdown()
@ -173,8 +172,7 @@ func (b *baseBalancer) regeneratePicker() {
// Filter out all ready SCs from full subConn map. // Filter out all ready SCs from full subConn map.
for _, addr := range b.subConns.Keys() { for _, addr := range b.subConns.Keys() {
sci, _ := b.subConns.Get(addr) sc, _ := b.subConns.Get(addr)
sc := sci.(balancer.SubConn)
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready { if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[sc] = SubConnInfo{Address: addr} readySCs[sc] = SubConnInfo{Address: addr}
} }

View File

@ -37,6 +37,8 @@ import (
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
) )
var randIntN = rand.IntN
// ChildState is the balancer state of a child along with the endpoint which // ChildState is the balancer state of a child along with the endpoint which
// identifies the child balancer. // identifies the child balancer.
type ChildState struct { type ChildState struct {
@ -45,7 +47,15 @@ type ChildState struct {
// Balancer exposes only the ExitIdler interface of the child LB policy. // Balancer exposes only the ExitIdler interface of the child LB policy.
// Other methods of the child policy are called only by endpointsharding. // Other methods of the child policy are called only by endpointsharding.
Balancer balancer.ExitIdler Balancer ExitIdler
}
// ExitIdler provides access to only the ExitIdle method of the child balancer.
type ExitIdler interface {
// ExitIdle instructs the LB policy to reconnect to backends / exit the
// IDLE state, if appropriate and possible. Note that SubConns that enter
// the IDLE state will not reconnect until SubConn.Connect is called.
ExitIdle()
} }
// Options are the options to configure the behaviour of the // Options are the options to configure the behaviour of the
@ -73,7 +83,7 @@ func NewBalancer(cc balancer.ClientConn, opts balancer.BuildOptions, childBuilde
esOpts: esOpts, esOpts: esOpts,
childBuilder: childBuilder, childBuilder: childBuilder,
} }
es.children.Store(resolver.NewEndpointMap()) es.children.Store(resolver.NewEndpointMap[*balancerWrapper]())
return es return es
} }
@ -90,7 +100,7 @@ type endpointSharding struct {
// calls into a child. To avoid deadlocks, do not acquire childMu while // calls into a child. To avoid deadlocks, do not acquire childMu while
// holding mu. // holding mu.
childMu sync.Mutex childMu sync.Mutex
children atomic.Pointer[resolver.EndpointMap] // endpoint -> *balancerWrapper children atomic.Pointer[resolver.EndpointMap[*balancerWrapper]]
// inhibitChildUpdates is set during UpdateClientConnState/ResolverError // inhibitChildUpdates is set during UpdateClientConnState/ResolverError
// calls (calls to children will each produce an update, only want one // calls (calls to children will each produce an update, only want one
@ -104,6 +114,21 @@ type endpointSharding struct {
mu sync.Mutex mu sync.Mutex
} }
// rotateEndpoints returns a slice of all the input endpoints rotated a random
// amount.
func rotateEndpoints(es []resolver.Endpoint) []resolver.Endpoint {
les := len(es)
if les == 0 {
return es
}
r := randIntN(les)
// Make a copy to avoid mutating data beyond the end of es.
ret := make([]resolver.Endpoint, les)
copy(ret, es[r:])
copy(ret[les-r:], es[:r])
return ret
}
// UpdateClientConnState creates a child for new endpoints and deletes children // UpdateClientConnState creates a child for new endpoints and deletes children
// for endpoints that are no longer present. It also updates all the children, // for endpoints that are no longer present. It also updates all the children,
// and sends a single synchronous update of the childrens' aggregated state at // and sends a single synchronous update of the childrens' aggregated state at
@ -122,18 +147,17 @@ func (es *endpointSharding) UpdateClientConnState(state balancer.ClientConnState
var ret error var ret error
children := es.children.Load() children := es.children.Load()
newChildren := resolver.NewEndpointMap() newChildren := resolver.NewEndpointMap[*balancerWrapper]()
// Update/Create new children. // Update/Create new children.
for _, endpoint := range state.ResolverState.Endpoints { for _, endpoint := range rotateEndpoints(state.ResolverState.Endpoints) {
if _, ok := newChildren.Get(endpoint); ok { if _, ok := newChildren.Get(endpoint); ok {
// Endpoint child was already created, continue to avoid duplicate // Endpoint child was already created, continue to avoid duplicate
// update. // update.
continue continue
} }
var childBalancer *balancerWrapper childBalancer, ok := children.Get(endpoint)
if val, ok := children.Get(endpoint); ok { if ok {
childBalancer = val.(*balancerWrapper)
// Endpoint attributes may have changed, update the stored endpoint. // Endpoint attributes may have changed, update the stored endpoint.
es.mu.Lock() es.mu.Lock()
childBalancer.childState.Endpoint = endpoint childBalancer.childState.Endpoint = endpoint
@ -166,7 +190,7 @@ func (es *endpointSharding) UpdateClientConnState(state balancer.ClientConnState
for _, e := range children.Keys() { for _, e := range children.Keys() {
child, _ := children.Get(e) child, _ := children.Get(e)
if _, ok := newChildren.Get(e); !ok { if _, ok := newChildren.Get(e); !ok {
child.(*balancerWrapper).closeLocked() child.closeLocked()
} }
} }
es.children.Store(newChildren) es.children.Store(newChildren)
@ -189,7 +213,7 @@ func (es *endpointSharding) ResolverError(err error) {
}() }()
children := es.children.Load() children := es.children.Load()
for _, child := range children.Values() { for _, child := range children.Values() {
child.(*balancerWrapper).resolverErrorLocked(err) child.resolverErrorLocked(err)
} }
} }
@ -202,7 +226,17 @@ func (es *endpointSharding) Close() {
defer es.childMu.Unlock() defer es.childMu.Unlock()
children := es.children.Load() children := es.children.Load()
for _, child := range children.Values() { for _, child := range children.Values() {
child.(*balancerWrapper).closeLocked() child.closeLocked()
}
}
func (es *endpointSharding) ExitIdle() {
es.childMu.Lock()
defer es.childMu.Unlock()
for _, bw := range es.children.Load().Values() {
if !bw.isClosed {
bw.child.ExitIdle()
}
} }
} }
@ -222,8 +256,7 @@ func (es *endpointSharding) updateState() {
childStates := make([]ChildState, 0, children.Len()) childStates := make([]ChildState, 0, children.Len())
for _, child := range children.Values() { for _, child := range children.Values() {
bw := child.(*balancerWrapper) childState := child.childState
childState := bw.childState
childStates = append(childStates, childState) childStates = append(childStates, childState)
childPicker := childState.State.Picker childPicker := childState.State.Picker
switch childState.State.ConnectivityState { switch childState.State.ConnectivityState {
@ -263,7 +296,7 @@ func (es *endpointSharding) updateState() {
p := &pickerWithChildStates{ p := &pickerWithChildStates{
pickers: pickers, pickers: pickers,
childStates: childStates, childStates: childStates,
next: uint32(rand.IntN(len(pickers))), next: uint32(randIntN(len(pickers))),
} }
es.cc.UpdateState(balancer.State{ es.cc.UpdateState(balancer.State{
ConnectivityState: aggState, ConnectivityState: aggState,
@ -328,15 +361,13 @@ func (bw *balancerWrapper) UpdateState(state balancer.State) {
// ExitIdle pings an IDLE child balancer to exit idle in a new goroutine to // ExitIdle pings an IDLE child balancer to exit idle in a new goroutine to
// avoid deadlocks due to synchronous balancer state updates. // avoid deadlocks due to synchronous balancer state updates.
func (bw *balancerWrapper) ExitIdle() { func (bw *balancerWrapper) ExitIdle() {
if ei, ok := bw.child.(balancer.ExitIdler); ok { go func() {
go func() { bw.es.childMu.Lock()
bw.es.childMu.Lock() if !bw.isClosed {
if !bw.isClosed { bw.child.ExitIdle()
ei.ExitIdle() }
} bw.es.childMu.Unlock()
bw.es.childMu.Unlock() }()
}()
}
} }
// updateClientConnStateLocked delivers the ClientConnState to the child // updateClientConnStateLocked delivers the ClientConnState to the child

View File

@ -0,0 +1,353 @@
/*
*
* Copyright 2024 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package endpointsharding_test
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/endpointsharding"
"google.golang.org/grpc/balancer/pickfirst/pickfirstleaf"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/balancer/stub"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/roundrobin"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
"google.golang.org/grpc/serviceconfig"
"google.golang.org/grpc/status"
testgrpc "google.golang.org/grpc/interop/grpc_testing"
testpb "google.golang.org/grpc/interop/grpc_testing"
)
var (
defaultTestTimeout = time.Second * 10
defaultTestShortTimeout = time.Millisecond * 10
)
type s struct {
grpctest.Tester
}
func Test(t *testing.T) {
grpctest.RunSubTests(t, s{})
}
var logger = grpclog.Component("endpoint-sharding-test")
func init() {
balancer.Register(fakePetioleBuilder{})
}
const fakePetioleName = "fake_petiole"
type fakePetioleBuilder struct{}
func (fakePetioleBuilder) Name() string {
return fakePetioleName
}
func (fakePetioleBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
fp := &fakePetiole{
ClientConn: cc,
bOpts: opts,
}
fp.Balancer = endpointsharding.NewBalancer(fp, opts, balancer.Get(pickfirstleaf.Name).Build, endpointsharding.Options{})
return fp
}
func (fakePetioleBuilder) ParseConfig(json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
return nil, nil
}
// fakePetiole is a load balancer that wraps the endpointShardingBalancer, and
// forwards ClientConnUpdates with a child config of graceful switch that wraps
// pick first. It also intercepts UpdateState to make sure it can access the
// child state maintained by EndpointSharding.
type fakePetiole struct {
balancer.Balancer
balancer.ClientConn
bOpts balancer.BuildOptions
}
func (fp *fakePetiole) UpdateClientConnState(state balancer.ClientConnState) error {
if el := state.ResolverState.Endpoints; len(el) != 2 {
return fmt.Errorf("UpdateClientConnState wants two endpoints, got: %v", el)
}
return fp.Balancer.UpdateClientConnState(state)
}
func (fp *fakePetiole) UpdateState(state balancer.State) {
childStates := endpointsharding.ChildStatesFromPicker(state.Picker)
// Both child states should be present in the child picker. States and
// picker change over the lifecycle of test, but there should always be two.
if len(childStates) != 2 {
logger.Fatal(fmt.Errorf("length of child states received: %v, want 2", len(childStates)))
}
fp.ClientConn.UpdateState(state)
}
// TestEndpointShardingBasic tests the basic functionality of the endpoint
// sharding balancer. It specifies a petiole policy that is essentially a
// wrapper around the endpoint sharder. Two backends are started, with each
// backend's address specified in an endpoint. The petiole does not have a
// special picker, so it should fallback to the default behavior, which is to
// round_robin amongst the endpoint children that are in the aggregated state.
// It also verifies the petiole has access to the raw child state in case it
// wants to implement a custom picker. The test sends a resolver error to the
// endpointsharding balancer and verifies an error picker from the children
// is used while making an RPC.
func (s) TestEndpointShardingBasic(t *testing.T) {
backend1 := stubserver.StartTestService(t, nil)
defer backend1.Stop()
backend2 := stubserver.StartTestService(t, nil)
defer backend2.Stop()
mr := manual.NewBuilderWithScheme("e2e-test")
defer mr.Close()
json := fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, fakePetioleName)
sc := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(json)
mr.InitialState(resolver.State{
Endpoints: []resolver.Endpoint{
{Addresses: []resolver.Address{{Addr: backend1.Address}}},
{Addresses: []resolver.Address{{Addr: backend2.Address}}},
},
ServiceConfig: sc,
})
dOpts := []grpc.DialOption{
grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()),
// Use a large backoff delay to avoid the error picker being updated
// too quickly.
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.Config{
BaseDelay: 2 * defaultTestTimeout,
Multiplier: float64(0),
Jitter: float64(0),
MaxDelay: 2 * defaultTestTimeout,
},
}),
}
cc, err := grpc.NewClient(mr.Scheme()+":///", dOpts...)
if err != nil {
t.Fatalf("Failed to create new client: %v", err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
client := testgrpc.NewTestServiceClient(cc)
// Assert a round robin distribution between the two spun up backends. This
// requires a poll and eventual consistency as both endpoint children do not
// start in state READY.
if err = roundrobin.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: backend1.Address}, {Addr: backend2.Address}}); err != nil {
t.Fatalf("error in expected round robin: %v", err)
}
// Stopping both the backends should make the channel enter
// TransientFailure.
backend1.Stop()
backend2.Stop()
testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
// When the resolver reports an error, the picker should get updated to
// return the resolver error.
mr.CC().ReportError(errors.New("test error"))
testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
for ; ctx.Err() == nil; <-time.After(time.Millisecond) {
_, err := client.EmptyCall(ctx, &testpb.Empty{})
if err == nil {
t.Fatalf("EmptyCall succeeded when expected to fail with %q", "test error")
}
if strings.Contains(err.Error(), "test error") {
break
}
}
if ctx.Err() != nil {
t.Fatalf("Context timed out waiting for picker with resolver error.")
}
}
// Tests that endpointsharding doesn't automatically re-connect IDLE children.
// The test creates an endpoint with two servers and another with a single
// server. The active service in endpoint 1 is closed to make the child
// pickfirst enter IDLE state. The test verifies that the child pickfirst
// doesn't connect to the second address in the endpoint.
func (s) TestEndpointShardingReconnectDisabled(t *testing.T) {
backend1 := stubserver.StartTestService(t, nil)
defer backend1.Stop()
backend2 := stubserver.StartTestService(t, nil)
defer backend2.Stop()
backend3 := stubserver.StartTestService(t, nil)
defer backend3.Stop()
mr := manual.NewBuilderWithScheme("e2e-test")
defer mr.Close()
name := strings.ReplaceAll(strings.ToLower(t.Name()), "/", "")
bf := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) {
epOpts := endpointsharding.Options{DisableAutoReconnect: true}
bd.ChildBalancer = endpointsharding.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build, epOpts)
},
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.ChildBalancer.UpdateClientConnState(ccs)
},
Close: func(bd *stub.BalancerData) {
bd.ChildBalancer.Close()
},
}
stub.Register(name, bf)
json := fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, name)
sc := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(json)
mr.InitialState(resolver.State{
Endpoints: []resolver.Endpoint{
{Addresses: []resolver.Address{{Addr: backend1.Address}, {Addr: backend2.Address}}},
{Addresses: []resolver.Address{{Addr: backend3.Address}}},
},
ServiceConfig: sc,
})
cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatalf("Failed to create new client: %v", err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
client := testgrpc.NewTestServiceClient(cc)
// Assert a round robin distribution between the two spun up backends. This
// requires a poll and eventual consistency as both endpoint children do not
// start in state READY.
if err = roundrobin.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: backend1.Address}, {Addr: backend3.Address}}); err != nil {
t.Fatalf("error in expected round robin: %v", err)
}
// On closing the first server, the first child balancer should enter
// IDLE. Since endpointsharding is configured not to auto-reconnect, it will
// remain IDLE and will not try to connect to the second backend in the same
// endpoint.
backend1.Stop()
// CheckRoundRobinRPCs waits for all the backends to become reachable, we
// call it to ensure the picker no longer sends RPCs to closed backend.
if err = roundrobin.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: backend3.Address}}); err != nil {
t.Fatalf("error in expected round robin: %v", err)
}
// Verify requests go only to backend3 for a short time.
shortCtx, cancel := context.WithTimeout(ctx, defaultTestShortTimeout)
defer cancel()
for ; shortCtx.Err() == nil; <-time.After(time.Millisecond) {
var peer peer.Peer
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(&peer)); err != nil {
if status.Code(err) != codes.DeadlineExceeded {
t.Fatalf("EmptyCall() returned unexpected error %v", err)
}
break
}
if got, want := peer.Addr.String(), backend3.Address; got != want {
t.Fatalf("EmptyCall() went to unexpected backend: got %q, want %q", got, want)
}
}
}
// Tests that endpointsharding doesn't automatically re-connect IDLE children
// until cc.Connect() is called. The test creates an endpoint with a single
// address. The client is connected and the active server is closed to make the
// child pickfirst enter IDLE state. The test verifies that the child pickfirst
// doesn't re-connect automatically. The test calls cc.Connect() and verified
// that the balancer connects causing the channel to enter TransientFailure.
func (s) TestEndpointShardingExitIdle(t *testing.T) {
backend := stubserver.StartTestService(t, nil)
defer backend.Stop()
mr := manual.NewBuilderWithScheme("e2e-test")
defer mr.Close()
name := strings.ReplaceAll(strings.ToLower(t.Name()), "/", "")
bf := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) {
epOpts := endpointsharding.Options{DisableAutoReconnect: true}
bd.ChildBalancer = endpointsharding.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build, epOpts)
},
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.ChildBalancer.UpdateClientConnState(ccs)
},
Close: func(bd *stub.BalancerData) {
bd.ChildBalancer.Close()
},
ExitIdle: func(bd *stub.BalancerData) {
bd.ChildBalancer.ExitIdle()
},
}
stub.Register(name, bf)
json := fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, name)
sc := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(json)
mr.InitialState(resolver.State{
Endpoints: []resolver.Endpoint{
{Addresses: []resolver.Address{{Addr: backend.Address}}},
},
ServiceConfig: sc,
})
cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatalf("Failed to create new client: %v", err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
client := testgrpc.NewTestServiceClient(cc)
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
t.Errorf("client.EmptyCall() returned unexpected error: %v", err)
}
// On closing the first server, the first child balancer should enter
// IDLE. Since endpointsharding is configured not to auto-reconnect, it will
// remain IDLE and will not try to re-connect
backend.Stop()
testutils.AwaitState(ctx, t, cc, connectivity.Idle)
shortCtx, shortCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
defer shortCancel()
testutils.AwaitNoStateChange(shortCtx, t, cc, connectivity.Idle)
// The balancer should try to re-connect and fail.
cc.Connect()
testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
}

View File

@ -1,6 +1,6 @@
/* /*
* *
* Copyright 2024 gRPC authors. * Copyright 2025 gRPC authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,46 +16,14 @@
* *
*/ */
package endpointsharding_test package endpointsharding
import ( import (
"context"
"encoding/json"
"errors"
"fmt" "fmt"
"log"
"strings"
"testing" "testing"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/endpointsharding"
"google.golang.org/grpc/balancer/pickfirst/pickfirstleaf"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/balancer/stub"
"google.golang.org/grpc/internal/grpctest" "google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/roundrobin"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
"google.golang.org/grpc/serviceconfig"
"google.golang.org/grpc/status"
testgrpc "google.golang.org/grpc/interop/grpc_testing"
testpb "google.golang.org/grpc/interop/grpc_testing"
)
var (
defaultTestTimeout = time.Second * 10
defaultTestShortTimeout = time.Millisecond * 10
) )
type s struct { type s struct {
@ -66,223 +34,50 @@ func Test(t *testing.T) {
grpctest.RunSubTests(t, s{}) grpctest.RunSubTests(t, s{})
} }
var logger = grpclog.Component("endpoint-sharding-test") func (s) TestRotateEndpoints(t *testing.T) {
ep := func(addr string) resolver.Endpoint {
func init() { return resolver.Endpoint{Addresses: []resolver.Address{{Addr: addr}}}
balancer.Register(fakePetioleBuilder{})
}
const fakePetioleName = "fake_petiole"
type fakePetioleBuilder struct{}
func (fakePetioleBuilder) Name() string {
return fakePetioleName
}
func (fakePetioleBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
fp := &fakePetiole{
ClientConn: cc,
bOpts: opts,
} }
fp.Balancer = endpointsharding.NewBalancer(fp, opts, balancer.Get(pickfirstleaf.Name).Build, endpointsharding.Options{}) endpoints := []resolver.Endpoint{ep("1"), ep("2"), ep("3"), ep("4"), ep("5")}
return fp testCases := []struct {
} rval int
want []resolver.Endpoint
func (fakePetioleBuilder) ParseConfig(json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { }{
return nil, nil {
} rval: 0,
want: []resolver.Endpoint{ep("1"), ep("2"), ep("3"), ep("4"), ep("5")},
// fakePetiole is a load balancer that wraps the endpointShardingBalancer, and
// forwards ClientConnUpdates with a child config of graceful switch that wraps
// pick first. It also intercepts UpdateState to make sure it can access the
// child state maintained by EndpointSharding.
type fakePetiole struct {
balancer.Balancer
balancer.ClientConn
bOpts balancer.BuildOptions
}
func (fp *fakePetiole) UpdateClientConnState(state balancer.ClientConnState) error {
if el := state.ResolverState.Endpoints; len(el) != 2 {
return fmt.Errorf("UpdateClientConnState wants two endpoints, got: %v", el)
}
return fp.Balancer.UpdateClientConnState(state)
}
func (fp *fakePetiole) UpdateState(state balancer.State) {
childStates := endpointsharding.ChildStatesFromPicker(state.Picker)
// Both child states should be present in the child picker. States and
// picker change over the lifecycle of test, but there should always be two.
if len(childStates) != 2 {
logger.Fatal(fmt.Errorf("length of child states received: %v, want 2", len(childStates)))
}
fp.ClientConn.UpdateState(state)
}
// TestEndpointShardingBasic tests the basic functionality of the endpoint
// sharding balancer. It specifies a petiole policy that is essentially a
// wrapper around the endpoint sharder. Two backends are started, with each
// backend's address specified in an endpoint. The petiole does not have a
// special picker, so it should fallback to the default behavior, which is to
// round_robin amongst the endpoint children that are in the aggregated state.
// It also verifies the petiole has access to the raw child state in case it
// wants to implement a custom picker. The test sends a resolver error to the
// endpointsharding balancer and verifies an error picker from the children
// is used while making an RPC.
func (s) TestEndpointShardingBasic(t *testing.T) {
backend1 := stubserver.StartTestService(t, nil)
defer backend1.Stop()
backend2 := stubserver.StartTestService(t, nil)
defer backend2.Stop()
mr := manual.NewBuilderWithScheme("e2e-test")
defer mr.Close()
json := fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, fakePetioleName)
sc := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(json)
mr.InitialState(resolver.State{
Endpoints: []resolver.Endpoint{
{Addresses: []resolver.Address{{Addr: backend1.Address}}},
{Addresses: []resolver.Address{{Addr: backend2.Address}}},
}, },
ServiceConfig: sc, {
}) rval: 1,
want: []resolver.Endpoint{ep("2"), ep("3"), ep("4"), ep("5"), ep("1")},
dOpts := []grpc.DialOption{
grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()),
// Use a large backoff delay to avoid the error picker being updated
// too quickly.
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.Config{
BaseDelay: 2 * defaultTestTimeout,
Multiplier: float64(0),
Jitter: float64(0),
MaxDelay: 2 * defaultTestTimeout,
},
}),
}
cc, err := grpc.NewClient(mr.Scheme()+":///", dOpts...)
if err != nil {
log.Fatalf("Failed to create new client: %v", err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
client := testgrpc.NewTestServiceClient(cc)
// Assert a round robin distribution between the two spun up backends. This
// requires a poll and eventual consistency as both endpoint children do not
// start in state READY.
if err = roundrobin.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: backend1.Address}, {Addr: backend2.Address}}); err != nil {
t.Fatalf("error in expected round robin: %v", err)
}
// Stopping both the backends should make the channel enter
// TransientFailure.
backend1.Stop()
backend2.Stop()
testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
// When the resolver reports an error, the picker should get updated to
// return the resolver error.
mr.ReportError(errors.New("test error"))
testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
for ; ctx.Err() == nil; <-time.After(time.Millisecond) {
_, err := client.EmptyCall(ctx, &testpb.Empty{})
if err == nil {
t.Fatalf("EmptyCall succeeded when expected to fail with %q", "test error")
}
if strings.Contains(err.Error(), "test error") {
break
}
}
if ctx.Err() != nil {
t.Fatalf("Context timed out waiting for picker with resolver error.")
}
}
// Tests that endpointsharding doesn't automatically re-connect IDLE children.
// The test creates an endpoint with two servers and another with a single
// server. The active service in endpoint 1 is closed to make the child
// pickfirst enter IDLE state. The test verifies that the child pickfirst
// doesn't connect to the second address in the endpoint.
func (s) TestEndpointShardingReconnectDisabled(t *testing.T) {
backend1 := stubserver.StartTestService(t, nil)
defer backend1.Stop()
backend2 := stubserver.StartTestService(t, nil)
defer backend2.Stop()
backend3 := stubserver.StartTestService(t, nil)
defer backend3.Stop()
mr := manual.NewBuilderWithScheme("e2e-test")
defer mr.Close()
name := strings.ReplaceAll(strings.ToLower(t.Name()), "/", "")
bf := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) {
epOpts := endpointsharding.Options{DisableAutoReconnect: true}
bd.Data = endpointsharding.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build, epOpts)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { {
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) rval: 2,
want: []resolver.Endpoint{ep("3"), ep("4"), ep("5"), ep("1"), ep("2")},
}, },
Close: func(bd *stub.BalancerData) { {
bd.Data.(balancer.Balancer).Close() rval: 3,
want: []resolver.Endpoint{ep("4"), ep("5"), ep("1"), ep("2"), ep("3")},
},
{
rval: 4,
want: []resolver.Endpoint{ep("5"), ep("1"), ep("2"), ep("3"), ep("4")},
}, },
} }
stub.Register(name, bf)
json := fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, name) defer func(r func(int) int) {
sc := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(json) randIntN = r
mr.InitialState(resolver.State{ }(randIntN)
Endpoints: []resolver.Endpoint{
{Addresses: []resolver.Address{{Addr: backend1.Address}, {Addr: backend2.Address}}},
{Addresses: []resolver.Address{{Addr: backend3.Address}}},
},
ServiceConfig: sc,
})
cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials())) for _, tc := range testCases {
if err != nil { t.Run(fmt.Sprint(tc.rval), func(t *testing.T) {
log.Fatalf("Failed to create new client: %v", err) randIntN = func(int) int {
} return tc.rval
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
client := testgrpc.NewTestServiceClient(cc)
// Assert a round robin distribution between the two spun up backends. This
// requires a poll and eventual consistency as both endpoint children do not
// start in state READY.
if err = roundrobin.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: backend1.Address}, {Addr: backend3.Address}}); err != nil {
t.Fatalf("error in expected round robin: %v", err)
}
// On closing the first server, the first child balancer should enter
// IDLE. Since endpointsharding is configured not to auto-reconnect, it will
// remain IDLE and will not try to connect to the second backend in the same
// endpoint.
backend1.Stop()
// CheckRoundRobinRPCs waits for all the backends to become reachable, we
// call it to ensure the picker no longer sends RPCs to closed backend.
if err = roundrobin.CheckRoundRobinRPCs(ctx, client, []resolver.Address{{Addr: backend3.Address}}); err != nil {
t.Fatalf("error in expected round robin: %v", err)
}
// Verify requests go only to backend3 for a short time.
shortCtx, cancel := context.WithTimeout(ctx, defaultTestShortTimeout)
defer cancel()
for ; shortCtx.Err() == nil; <-time.After(time.Millisecond) {
var peer peer.Peer
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(&peer)); err != nil {
if status.Code(err) != codes.DeadlineExceeded {
t.Fatalf("EmptyCall() returned unexpected error %v", err)
} }
break got := rotateEndpoints(endpoints)
} if fmt.Sprint(got) != fmt.Sprint(tc.want) {
if got, want := peer.Addr.String(), backend3.Address; got != want { t.Fatalf("rand=%v; rotateEndpoints(%v) = %v; want %v", tc.rval, endpoints, got, tc.want)
t.Fatalf("EmptyCall() went to unexpected backend: got %q, want %q", got, want) }
} })
} }
} }

View File

@ -19,7 +19,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.4 // protoc-gen-go v1.36.6
// protoc v5.27.1 // protoc v5.27.1
// source: grpc/lb/v1/load_balancer.proto // source: grpc/lb/v1/load_balancer.proto
@ -642,115 +642,47 @@ func (x *Server) GetDrop() bool {
var File_grpc_lb_v1_load_balancer_proto protoreflect.FileDescriptor var File_grpc_lb_v1_load_balancer_proto protoreflect.FileDescriptor
var file_grpc_lb_v1_load_balancer_proto_rawDesc = string([]byte{ const file_grpc_lb_v1_load_balancer_proto_rawDesc = "" +
0x0a, 0x1e, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6c, 0x62, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x6f, 0x61, "\n" +
0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, "\x1egrpc/lb/v1/load_balancer.proto\x12\n" +
0x12, 0x0a, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x1a, 0x1e, 0x67, 0x6f, "grpc.lb.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xc1\x01\n" +
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, "\x12LoadBalanceRequest\x12P\n" +
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, "\x0finitial_request\x18\x01 \x01(\v2%.grpc.lb.v1.InitialLoadBalanceRequestH\x00R\x0einitialRequest\x12<\n" +
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, "\fclient_stats\x18\x02 \x01(\v2\x17.grpc.lb.v1.ClientStatsH\x00R\vclientStatsB\x1b\n" +
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc1, 0x01, "\x19load_balance_request_type\"/\n" +
0x0a, 0x12, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, "\x19InitialLoadBalanceRequest\x12\x12\n" +
0x75, 0x65, 0x73, 0x74, 0x12, 0x50, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, "\x04name\x18\x01 \x01(\tR\x04name\"`\n" +
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, "\x13ClientStatsPerToken\x12,\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, "\x12load_balance_token\x18\x01 \x01(\tR\x10loadBalanceToken\x12\x1b\n" +
0x61, 0x6c, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, "\tnum_calls\x18\x02 \x01(\x03R\bnumCalls\"\xb0\x03\n" +
0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, "\vClientStats\x128\n" +
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, "\ttimestamp\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\x12*\n" +
0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, "\x11num_calls_started\x18\x02 \x01(\x03R\x0fnumCallsStarted\x12,\n" +
0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, "\x12num_calls_finished\x18\x03 \x01(\x03R\x10numCallsFinished\x12]\n" +
0x53, 0x74, 0x61, 0x74, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, "-num_calls_finished_with_client_failed_to_send\x18\x06 \x01(\x03R&numCallsFinishedWithClientFailedToSend\x12H\n" +
0x74, 0x61, 0x74, 0x73, 0x42, 0x1b, 0x0a, 0x19, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, "!num_calls_finished_known_received\x18\a \x01(\x03R\x1dnumCallsFinishedKnownReceived\x12X\n" +
0x61, 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, "\x18calls_finished_with_drop\x18\b \x03(\v2\x1f.grpc.lb.v1.ClientStatsPerTokenR\x15callsFinishedWithDropJ\x04\b\x04\x10\x05J\x04\b\x05\x10\x06\"\x90\x02\n" +
0x65, 0x22, 0x2f, 0x0a, 0x19, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x6f, 0x61, 0x64, "\x13LoadBalanceResponse\x12S\n" +
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, "\x10initial_response\x18\x01 \x01(\v2&.grpc.lb.v1.InitialLoadBalanceResponseH\x00R\x0finitialResponse\x129\n" +
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, "\vserver_list\x18\x02 \x01(\v2\x16.grpc.lb.v1.ServerListH\x00R\n" +
0x6d, 0x65, 0x22, 0x60, 0x0a, 0x13, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, "serverList\x12K\n" +
0x73, 0x50, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, 0x61, "\x11fallback_response\x18\x03 \x01(\v2\x1c.grpc.lb.v1.FallbackResponseH\x00R\x10fallbackResponseB\x1c\n" +
0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, "\x1aload_balance_response_type\"\x12\n" +
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, "\x10FallbackResponse\"~\n" +
0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x63, "\x1aInitialLoadBalanceResponse\x12Z\n" +
0x61, 0x6c, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x43, "\x1cclient_stats_report_interval\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\x19clientStatsReportIntervalJ\x04\b\x01\x10\x02\"@\n" +
0x61, 0x6c, 0x6c, 0x73, 0x22, 0xb0, 0x03, 0x0a, 0x0b, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, "\n" +
0x74, 0x61, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, "ServerList\x12,\n" +
0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, "\aservers\x18\x01 \x03(\v2\x12.grpc.lb.v1.ServerR\aserversJ\x04\b\x03\x10\x04\"\x83\x01\n" +
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, "\x06Server\x12\x1d\n" +
0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, "\n" +
0x0a, 0x11, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, "ip_address\x18\x01 \x01(\fR\tipAddress\x12\x12\n" +
0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6e, 0x75, 0x6d, 0x43, 0x61, "\x04port\x18\x02 \x01(\x05R\x04port\x12,\n" +
0x6c, 0x6c, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x75, "\x12load_balance_token\x18\x03 \x01(\tR\x10loadBalanceToken\x12\x12\n" +
0x6d, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, "\x04drop\x18\x04 \x01(\bR\x04dropJ\x04\b\x05\x10\x062b\n" +
0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x73, "\fLoadBalancer\x12R\n" +
0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x5d, 0x0a, 0x2d, 0x6e, 0x75, 0x6d, 0x5f, "\vBalanceLoad\x12\x1e.grpc.lb.v1.LoadBalanceRequest\x1a\x1f.grpc.lb.v1.LoadBalanceResponse(\x010\x01BW\n" +
0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x77, "\rio.grpc.lb.v1B\x11LoadBalancerProtoP\x01Z1google.golang.org/grpc/balancer/grpclb/grpc_lb_v1b\x06proto3"
0x69, 0x74, 0x68, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65,
0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52,
0x26, 0x6e, 0x75, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65,
0x64, 0x57, 0x69, 0x74, 0x68, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65,
0x64, 0x54, 0x6f, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x48, 0x0a, 0x21, 0x6e, 0x75, 0x6d, 0x5f, 0x63,
0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6b, 0x6e,
0x6f, 0x77, 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01,
0x28, 0x03, 0x52, 0x1d, 0x6e, 0x75, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x46, 0x69, 0x6e, 0x69,
0x73, 0x68, 0x65, 0x64, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
0x64, 0x12, 0x58, 0x0a, 0x18, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x73,
0x68, 0x65, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x18, 0x08, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31,
0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x50, 0x65, 0x72, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x15, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x73,
0x68, 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, 0x44, 0x72, 0x6f, 0x70, 0x4a, 0x04, 0x08, 0x04, 0x10,
0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x90, 0x02, 0x0a, 0x13, 0x4c, 0x6f, 0x61, 0x64,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x53, 0x0a, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x72, 0x70, 0x63,
0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x6f,
0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x48, 0x00, 0x52, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6c,
0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x72, 0x70, 0x63,
0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x73,
0x74, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12,
0x4b, 0x0a, 0x11, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x70,
0x63, 0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x66, 0x61, 0x6c, 0x6c,
0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x1c, 0x0a, 0x1a,
0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x46, 0x61,
0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7e,
0x0a, 0x1a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c,
0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x1c,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x72, 0x65, 0x70,
0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x19, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74,
0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x40,
0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x07,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e,
0x67, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65,
0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04,
0x22, 0x83, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x69,
0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f,
0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2c,
0x0a, 0x12, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x6f, 0x61, 0x64,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04,
0x64, 0x72, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x72, 0x6f, 0x70,
0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x32, 0x62, 0x0a, 0x0c, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x12, 0x52, 0x0a, 0x0b, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x4c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e,
0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e,
0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x57, 0x0a, 0x0d, 0x69, 0x6f,
0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x62, 0x2e, 0x76, 0x31, 0x42, 0x11, 0x4c, 0x6f, 0x61,
0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x31, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e,
0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
0x72, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x6c, 0x62, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x6c, 0x62,
0x5f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var ( var (
file_grpc_lb_v1_load_balancer_proto_rawDescOnce sync.Once file_grpc_lb_v1_load_balancer_proto_rawDescOnce sync.Once

View File

@ -86,7 +86,7 @@ type LoadBalancerServer interface {
type UnimplementedLoadBalancerServer struct{} type UnimplementedLoadBalancerServer struct{}
func (UnimplementedLoadBalancerServer) BalanceLoad(grpc.BidiStreamingServer[LoadBalanceRequest, LoadBalanceResponse]) error { func (UnimplementedLoadBalancerServer) BalanceLoad(grpc.BidiStreamingServer[LoadBalanceRequest, LoadBalanceResponse]) error {
return status.Errorf(codes.Unimplemented, "method BalanceLoad not implemented") return status.Error(codes.Unimplemented, "method BalanceLoad not implemented")
} }
func (UnimplementedLoadBalancerServer) testEmbeddedByValue() {} func (UnimplementedLoadBalancerServer) testEmbeddedByValue() {}

View File

@ -260,10 +260,11 @@ func (lb *lbBalancer) newRemoteBalancerCCWrapper() error {
// The grpclb server addresses will set field ServerName, and creds will // The grpclb server addresses will set field ServerName, and creds will
// receive ServerName as authority. // receive ServerName as authority.
target := lb.manualResolver.Scheme() + ":///grpclb.subClientConn" target := lb.manualResolver.Scheme() + ":///grpclb.subClientConn"
cc, err := grpc.Dial(target, dopts...) cc, err := grpc.NewClient(target, dopts...)
if err != nil { if err != nil {
return fmt.Errorf("grpc.Dial(%s): %v", target, err) return fmt.Errorf("grpc.NewClient(%s): %v", target, err)
} }
cc.Connect()
ccw := &remoteBalancerCCWrapper{ ccw := &remoteBalancerCCWrapper{
cc: cc, cc: cc,
lb: lb, lb: lb,

View File

@ -859,7 +859,7 @@ func (s) TestGRPCLB_Fallback(t *testing.T) {
// Push another update to the resolver, this time with a valid balancer // Push another update to the resolver, this time with a valid balancer
// address in the attributes field. // address in the attributes field.
rs = resolver.State{ rs = resolver.State{
ServiceConfig: r.CC.ParseServiceConfig(grpclbConfig), ServiceConfig: r.CC().ParseServiceConfig(grpclbConfig),
Addresses: []resolver.Address{{Addr: beLis.Addr().String()}}, Addresses: []resolver.Address{{Addr: beLis.Addr().String()}},
} }
rs = grpclbstate.Set(rs, &grpclbstate.State{BalancerAddresses: []resolver.Address{{Addr: tss.lbAddr, ServerName: lbServerName}}}) rs = grpclbstate.Set(rs, &grpclbstate.State{BalancerAddresses: []resolver.Address{{Addr: tss.lbAddr, ServerName: lbServerName}}})
@ -1023,7 +1023,7 @@ func (s) TestGRPCLB_FallBackWithNoServerAddress(t *testing.T) {
// fallback and use the fallback backend. // fallback and use the fallback backend.
r.UpdateState(resolver.State{ r.UpdateState(resolver.State{
Addresses: []resolver.Address{{Addr: beLis.Addr().String()}}, Addresses: []resolver.Address{{Addr: beLis.Addr().String()}},
ServiceConfig: r.CC.ParseServiceConfig(grpclbConfig), ServiceConfig: r.CC().ParseServiceConfig(grpclbConfig),
}) })
sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
@ -1051,7 +1051,7 @@ func (s) TestGRPCLB_FallBackWithNoServerAddress(t *testing.T) {
// be used. // be used.
rs := resolver.State{ rs := resolver.State{
Addresses: []resolver.Address{{Addr: beLis.Addr().String()}}, Addresses: []resolver.Address{{Addr: beLis.Addr().String()}},
ServiceConfig: r.CC.ParseServiceConfig(grpclbConfig), ServiceConfig: r.CC().ParseServiceConfig(grpclbConfig),
} }
rs = grpclbstate.Set(rs, &grpclbstate.State{BalancerAddresses: []resolver.Address{{Addr: tss.lbAddr, ServerName: lbServerName}}}) rs = grpclbstate.Set(rs, &grpclbstate.State{BalancerAddresses: []resolver.Address{{Addr: tss.lbAddr, ServerName: lbServerName}}})
r.UpdateState(rs) r.UpdateState(rs)
@ -1112,7 +1112,7 @@ func (s) TestGRPCLB_PickFirst(t *testing.T) {
// Push a service config with grpclb as the load balancing policy and // Push a service config with grpclb as the load balancing policy and
// configure pick_first as its child policy. // configure pick_first as its child policy.
rs := resolver.State{ServiceConfig: r.CC.ParseServiceConfig(`{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}`)} rs := resolver.State{ServiceConfig: r.CC().ParseServiceConfig(`{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}`)}
// Push a resolver update with the remote balancer address specified via // Push a resolver update with the remote balancer address specified via
// attributes. // attributes.
@ -1152,7 +1152,7 @@ func (s) TestGRPCLB_PickFirst(t *testing.T) {
}, },
}, },
} }
rs = grpclbstate.Set(resolver.State{ServiceConfig: r.CC.ParseServiceConfig(grpclbConfig)}, s) rs = grpclbstate.Set(resolver.State{ServiceConfig: r.CC().ParseServiceConfig(grpclbConfig)}, s)
r.UpdateState(rs) r.UpdateState(rs)
testC := testgrpc.NewTestServiceClient(cc) testC := testgrpc.NewTestServiceClient(cc)
if err := roundrobin.CheckRoundRobinRPCs(ctx, testC, beServerAddrs[1:]); err != nil { if err := roundrobin.CheckRoundRobinRPCs(ctx, testC, beServerAddrs[1:]); err != nil {
@ -1261,7 +1261,7 @@ func testGRPCLBEmptyServerList(t *testing.T, svcfg string) {
}, },
}, },
} }
rs := grpclbstate.Set(resolver.State{ServiceConfig: r.CC.ParseServiceConfig(svcfg)}, s) rs := grpclbstate.Set(resolver.State{ServiceConfig: r.CC().ParseServiceConfig(svcfg)}, s)
r.UpdateState(rs) r.UpdateState(rs)
t.Log("Perform an initial RPC and expect it to succeed...") t.Log("Perform an initial RPC and expect it to succeed...")
if _, err := testC.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { if _, err := testC.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
@ -1329,7 +1329,7 @@ func (s) TestGRPCLBWithTargetNameFieldInConfig(t *testing.T) {
// Push a resolver update with grpclb configuration which does not contain the // Push a resolver update with grpclb configuration which does not contain the
// target_name field. Our fake remote balancer is configured to always // target_name field. Our fake remote balancer is configured to always
// expect `beServerName` as the server name in the initial request. // expect `beServerName` as the server name in the initial request.
rs := grpclbstate.Set(resolver.State{ServiceConfig: r.CC.ParseServiceConfig(grpclbConfig)}, rs := grpclbstate.Set(resolver.State{ServiceConfig: r.CC().ParseServiceConfig(grpclbConfig)},
&grpclbstate.State{BalancerAddresses: []resolver.Address{{ &grpclbstate.State{BalancerAddresses: []resolver.Address{{
Addr: tss.lbAddr, Addr: tss.lbAddr,
ServerName: lbServerName, ServerName: lbServerName,
@ -1366,7 +1366,7 @@ func (s) TestGRPCLBWithTargetNameFieldInConfig(t *testing.T) {
}, },
}, },
} }
rs = grpclbstate.Set(resolver.State{ServiceConfig: r.CC.ParseServiceConfig(lbCfg)}, s) rs = grpclbstate.Set(resolver.State{ServiceConfig: r.CC().ParseServiceConfig(lbCfg)}, s)
r.UpdateState(rs) r.UpdateState(rs)
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -1432,7 +1432,7 @@ func runAndCheckStats(t *testing.T, drop bool, statsChan chan *lbpb.ClientStats,
cc.Connect() cc.Connect()
defer cc.Close() defer cc.Close()
rstate := resolver.State{ServiceConfig: r.CC.ParseServiceConfig(grpclbConfig)} rstate := resolver.State{ServiceConfig: r.CC().ParseServiceConfig(grpclbConfig)}
r.UpdateState(grpclbstate.Set(rstate, &grpclbstate.State{BalancerAddresses: []resolver.Address{{ r.UpdateState(grpclbstate.Set(rstate, &grpclbstate.State{BalancerAddresses: []resolver.Address{{
Addr: tss.lbAddr, Addr: tss.lbAddr,
ServerName: lbServerName, ServerName: lbServerName,

View File

@ -125,9 +125,7 @@ func (lb *lazyBalancer) ExitIdle() {
lb.mu.Lock() lb.mu.Lock()
defer lb.mu.Unlock() defer lb.mu.Unlock()
if lb.delegate != nil { if lb.delegate != nil {
if d, ok := lb.delegate.(balancer.ExitIdler); ok { lb.delegate.ExitIdle()
d.ExitIdle()
}
return return
} }
lb.delegate = lb.childBuilder(lb.cc, lb.buildOptions) lb.delegate = lb.childBuilder(lb.cc, lb.buildOptions)

View File

@ -79,19 +79,19 @@ func (s) TestExitIdle(t *testing.T) {
bf := stub.BalancerFuncs{ bf := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build) bd.ChildBalancer = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build)
}, },
ExitIdle: func(bd *stub.BalancerData) { ExitIdle: func(bd *stub.BalancerData) {
bd.Data.(balancer.ExitIdler).ExitIdle() bd.ChildBalancer.ExitIdle()
}, },
ResolverError: func(bd *stub.BalancerData, err error) { ResolverError: func(bd *stub.BalancerData, err error) {
bd.Data.(balancer.Balancer).ResolverError(err) bd.ChildBalancer.ResolverError(err)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
} }
stub.Register(t.Name(), bf) stub.Register(t.Name(), bf)
@ -144,16 +144,16 @@ func (s) TestPicker(t *testing.T) {
bf := stub.BalancerFuncs{ bf := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build) bd.ChildBalancer = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build)
}, },
ExitIdle: func(bd *stub.BalancerData) { ExitIdle: func(*stub.BalancerData) {
t.Log("Ignoring call to ExitIdle, calling the picker should make the lazy balancer exit IDLE state.") t.Log("Ignoring call to ExitIdle, calling the picker should make the lazy balancer exit IDLE state.")
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
} }
@ -201,24 +201,24 @@ func (s) TestGoodUpdateThenResolverError(t *testing.T) {
childBF := stub.BalancerFuncs{ childBF := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = balancer.Get(pickfirstleaf.Name).Build(bd.ClientConn, bd.BuildOptions) bd.ChildBalancer = balancer.Get(pickfirstleaf.Name).Build(bd.ClientConn, bd.BuildOptions)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
if resolverErrorReceived.HasFired() { if resolverErrorReceived.HasFired() {
t.Error("Received resolver error before resolver state.") t.Error("Received resolver error before resolver state.")
} }
resolverStateReceived = true resolverStateReceived = true
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
ResolverError: func(bd *stub.BalancerData, err error) { ResolverError: func(bd *stub.BalancerData, err error) {
if !resolverStateReceived { if !resolverStateReceived {
t.Error("Received resolver error before resolver state.") t.Error("Received resolver error before resolver state.")
} }
resolverErrorReceived.Fire() resolverErrorReceived.Fire()
bd.Data.(balancer.Balancer).ResolverError(err) bd.ChildBalancer.ResolverError(err)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
} }
@ -227,19 +227,19 @@ func (s) TestGoodUpdateThenResolverError(t *testing.T) {
topLevelBF := stub.BalancerFuncs{ topLevelBF := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(childBalName).Build) bd.ChildBalancer = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(childBalName).Build)
}, },
ExitIdle: func(bd *stub.BalancerData) { ExitIdle: func(*stub.BalancerData) {
t.Log("Ignoring call to ExitIdle to delay lazy child creation until RPC time.") t.Log("Ignoring call to ExitIdle to delay lazy child creation until RPC time.")
}, },
ResolverError: func(bd *stub.BalancerData, err error) { ResolverError: func(bd *stub.BalancerData, err error) {
bd.Data.(balancer.Balancer).ResolverError(err) bd.ChildBalancer.ResolverError(err)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
} }
@ -271,7 +271,7 @@ func (s) TestGoodUpdateThenResolverError(t *testing.T) {
defer cc.Close() defer cc.Close()
cc.Connect() cc.Connect()
mr.ReportError(errors.New("test error")) mr.CC().ReportError(errors.New("test error"))
// The channel should remain in IDLE as the ExitIdle calls are not // The channel should remain in IDLE as the ExitIdle calls are not
// propagated to the lazy balancer from the stub balancer. // propagated to the lazy balancer from the stub balancer.
shortCtx, shortCancel := context.WithTimeout(ctx, defaultTestShortTimeout) shortCtx, shortCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
@ -306,17 +306,17 @@ func (s) TestResolverErrorThenGoodUpdate(t *testing.T) {
childBF := stub.BalancerFuncs{ childBF := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = balancer.Get(pickfirstleaf.Name).Build(bd.ClientConn, bd.BuildOptions) bd.ChildBalancer = balancer.Get(pickfirstleaf.Name).Build(bd.ClientConn, bd.BuildOptions)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
ResolverError: func(bd *stub.BalancerData, err error) { ResolverError: func(bd *stub.BalancerData, err error) {
t.Error("Received unexpected resolver error.") t.Error("Received unexpected resolver error.")
bd.Data.(balancer.Balancer).ResolverError(err) bd.ChildBalancer.ResolverError(err)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
} }
@ -325,16 +325,16 @@ func (s) TestResolverErrorThenGoodUpdate(t *testing.T) {
topLevelBF := stub.BalancerFuncs{ topLevelBF := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(childBalName).Build) bd.ChildBalancer = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(childBalName).Build)
}, },
ExitIdle: func(bd *stub.BalancerData) { ExitIdle: func(*stub.BalancerData) {
t.Log("Ignoring call to ExitIdle to delay lazy child creation until RPC time.") t.Log("Ignoring call to ExitIdle to delay lazy child creation until RPC time.")
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
} }
@ -367,7 +367,7 @@ func (s) TestResolverErrorThenGoodUpdate(t *testing.T) {
cc.Connect() cc.Connect()
// Send an error followed by a good update. // Send an error followed by a good update.
mr.ReportError(errors.New("test error")) mr.CC().ReportError(errors.New("test error"))
mr.UpdateState(resolver.State{ mr.UpdateState(resolver.State{
Endpoints: []resolver.Endpoint{ Endpoints: []resolver.Endpoint{
{Addresses: []resolver.Address{{Addr: backend.Address}}}, {Addresses: []resolver.Address{{Addr: backend.Address}}},
@ -407,19 +407,19 @@ func (s) TestExitIdlePassthrough(t *testing.T) {
bf := stub.BalancerFuncs{ bf := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build) bd.ChildBalancer = lazy.NewBalancer(bd.ClientConn, bd.BuildOptions, balancer.Get(pickfirstleaf.Name).Build)
}, },
ExitIdle: func(bd *stub.BalancerData) { ExitIdle: func(bd *stub.BalancerData) {
bd.Data.(balancer.ExitIdler).ExitIdle() bd.ChildBalancer.ExitIdle()
}, },
ResolverError: func(bd *stub.BalancerData, err error) { ResolverError: func(bd *stub.BalancerData, err error) {
bd.Data.(balancer.Balancer).ResolverError(err) bd.ChildBalancer.ResolverError(err)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
} }
stub.Register(t.Name(), bf) stub.Register(t.Name(), bf)

View File

@ -88,7 +88,7 @@ func (bb) Name() string {
func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer { func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer {
b := &leastRequestBalancer{ b := &leastRequestBalancer{
ClientConn: cc, ClientConn: cc,
endpointRPCCounts: resolver.NewEndpointMap(), endpointRPCCounts: resolver.NewEndpointMap[*atomic.Int32](),
} }
b.child = endpointsharding.NewBalancer(b, bOpts, balancer.Get(pickfirstleaf.Name).Build, endpointsharding.Options{}) b.child = endpointsharding.NewBalancer(b, bOpts, balancer.Get(pickfirstleaf.Name).Build, endpointsharding.Options{})
b.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[%p] ", b)) b.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[%p] ", b))
@ -97,11 +97,8 @@ func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Ba
} }
type leastRequestBalancer struct { type leastRequestBalancer struct {
// Embeds balancer.Balancer because needs to intercept UpdateClientConnState // Embeds balancer.ClientConn because we need to intercept UpdateState
// to learn about choiceCount. // calls from the child balancer.
balancer.Balancer
// Embeds balancer.ClientConn because needs to intercept UpdateState calls
// from the child balancer.
balancer.ClientConn balancer.ClientConn
child balancer.Balancer child balancer.Balancer
logger *internalgrpclog.PrefixLogger logger *internalgrpclog.PrefixLogger
@ -110,7 +107,7 @@ type leastRequestBalancer struct {
choiceCount uint32 choiceCount uint32
// endpointRPCCounts holds RPC counts to keep track for subsequent picker // endpointRPCCounts holds RPC counts to keep track for subsequent picker
// updates. // updates.
endpointRPCCounts *resolver.EndpointMap // endpoint -> *atomic.Int32 endpointRPCCounts *resolver.EndpointMap[*atomic.Int32]
} }
func (lrb *leastRequestBalancer) Close() { func (lrb *leastRequestBalancer) Close() {
@ -118,6 +115,19 @@ func (lrb *leastRequestBalancer) Close() {
lrb.endpointRPCCounts = nil lrb.endpointRPCCounts = nil
} }
func (lrb *leastRequestBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
lrb.logger.Errorf("UpdateSubConnState(%v, %+v) called unexpectedly", sc, state)
}
func (lrb *leastRequestBalancer) ResolverError(err error) {
// Will cause inline picker update from endpoint sharding.
lrb.child.ResolverError(err)
}
func (lrb *leastRequestBalancer) ExitIdle() {
lrb.child.ExitIdle()
}
func (lrb *leastRequestBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error { func (lrb *leastRequestBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error {
lrCfg, ok := ccs.BalancerConfig.(*LBConfig) lrCfg, ok := ccs.BalancerConfig.(*LBConfig)
if !ok { if !ok {
@ -164,7 +174,7 @@ func (lrb *leastRequestBalancer) UpdateState(state balancer.State) {
} }
// Reconcile endpoints. // Reconcile endpoints.
newEndpoints := resolver.NewEndpointMap() // endpoint -> nil newEndpoints := resolver.NewEndpointMap[any]()
for _, child := range readyEndpoints { for _, child := range readyEndpoints {
newEndpoints.Set(child.Endpoint, nil) newEndpoints.Set(child.Endpoint, nil)
} }
@ -179,13 +189,11 @@ func (lrb *leastRequestBalancer) UpdateState(state balancer.State) {
// Copy refs to counters into picker. // Copy refs to counters into picker.
endpointStates := make([]endpointState, 0, len(readyEndpoints)) endpointStates := make([]endpointState, 0, len(readyEndpoints))
for _, child := range readyEndpoints { for _, child := range readyEndpoints {
var counter *atomic.Int32 counter, ok := lrb.endpointRPCCounts.Get(child.Endpoint)
if val, ok := lrb.endpointRPCCounts.Get(child.Endpoint); !ok { if !ok {
// Create new counts if needed. // Create new counts if needed.
counter = new(atomic.Int32) counter = new(atomic.Int32)
lrb.endpointRPCCounts.Set(child.Endpoint, counter) lrb.endpointRPCCounts.Set(child.Endpoint, counter)
} else {
counter = val.(*atomic.Int32)
} }
endpointStates = append(endpointStates, endpointState{ endpointStates = append(endpointStates, endpointState{
picker: child.State.Picker, picker: child.State.Picker,

View File

@ -27,12 +27,13 @@ import (
"time" "time"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/grpctest" "google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/stubserver" "google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/internal/testutils"
testgrpc "google.golang.org/grpc/interop/grpc_testing" testgrpc "google.golang.org/grpc/interop/grpc_testing"
testpb "google.golang.org/grpc/interop/grpc_testing" testpb "google.golang.org/grpc/interop/grpc_testing"
"google.golang.org/grpc/peer" "google.golang.org/grpc/peer"
@ -42,7 +43,8 @@ import (
) )
const ( const (
defaultTestTimeout = 5 * time.Second defaultTestTimeout = 5 * time.Second
defaultTestShortTimeout = 10 * time.Millisecond
) )
type s struct { type s struct {
@ -706,3 +708,63 @@ func (s) TestLeastRequestEndpoints_MultipleAddresses(t *testing.T) {
t.Fatalf("error in expected round robin: %v", err) t.Fatalf("error in expected round robin: %v", err)
} }
} }
// Test tests that the least request balancer properly surfaces resolver
// errors.
func (s) TestLeastRequestEndpoints_ResolverError(t *testing.T) {
const sc = `{"loadBalancingConfig": [{"least_request_experimental": {}}]}`
mr := manual.NewBuilderWithScheme("lr-e2e")
defer mr.Close()
cc, err := grpc.NewClient(
mr.Scheme()+":///",
grpc.WithResolvers(mr),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultServiceConfig(sc),
)
if err != nil {
t.Fatalf("grpc.NewClient() failed: %v", err)
}
defer cc.Close()
// We need to pass an endpoint with a valid address to the resolver before
// reporting an error - otherwise endpointsharding does not report the
// error through.
lis, err := testutils.LocalTCPListener()
if err != nil {
t.Fatalf("net.Listen() failed: %v", err)
}
// Act like a server that closes the connection without sending a server
// preface.
go func() {
conn, err := lis.Accept()
if err != nil {
t.Errorf("Unexpected error when accepting a connection: %v", err)
}
conn.Close()
}()
mr.UpdateState(resolver.State{
Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: lis.Addr().String()}}}},
})
cc.Connect()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
// Report an error through the resolver
resolverErr := fmt.Errorf("simulated resolver error")
mr.CC().ReportError(resolverErr)
// Ensure the client returns the expected resolver error.
testServiceClient := testgrpc.NewTestServiceClient(cc)
for ; ctx.Err() == nil; <-time.After(defaultTestShortTimeout) {
_, err = testServiceClient.EmptyCall(ctx, &testpb.Empty{})
if strings.Contains(err.Error(), resolverErr.Error()) {
break
}
}
if ctx.Err() != nil {
t.Fatalf("Timeout when waiting for RPCs to fail with error containing %s. Last error: %v", resolverErr, err)
}
}

View File

@ -73,7 +73,7 @@ func Test(t *testing.T) {
func parseServiceConfig(t *testing.T, r *manual.Resolver, sc string) *serviceconfig.ParseResult { func parseServiceConfig(t *testing.T, r *manual.Resolver, sc string) *serviceconfig.ParseResult {
t.Helper() t.Helper()
scpr := r.CC.ParseServiceConfig(sc) scpr := r.CC().ParseServiceConfig(sc)
if scpr.Err != nil { if scpr.Err != nil {
t.Fatalf("Failed to parse service config %q: %v", sc, scpr.Err) t.Fatalf("Failed to parse service config %q: %v", sc, scpr.Err)
} }
@ -756,7 +756,7 @@ func (s) TestPickFirst_ResolverError_NoPreviousUpdate(t *testing.T) {
cc, r, _ := setupPickFirst(t, 0) cc, r, _ := setupPickFirst(t, 0)
nrErr := errors.New("error from name resolver") nrErr := errors.New("error from name resolver")
r.ReportError(nrErr) r.CC().ReportError(nrErr)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel() defer cancel()
@ -789,7 +789,7 @@ func (s) TestPickFirst_ResolverError_WithPreviousUpdate_Ready(t *testing.T) {
} }
nrErr := errors.New("error from name resolver") nrErr := errors.New("error from name resolver")
r.ReportError(nrErr) r.CC().ReportError(nrErr)
// Ensure that RPCs continue to succeed for the next second. // Ensure that RPCs continue to succeed for the next second.
client := testgrpc.NewTestServiceClient(cc) client := testgrpc.NewTestServiceClient(cc)
@ -848,7 +848,7 @@ func (s) TestPickFirst_ResolverError_WithPreviousUpdate_Connecting(t *testing.T)
testutils.AwaitState(ctx, t, cc, connectivity.Connecting) testutils.AwaitState(ctx, t, cc, connectivity.Connecting)
nrErr := errors.New("error from name resolver") nrErr := errors.New("error from name resolver")
r.ReportError(nrErr) r.CC().ReportError(nrErr)
// RPCs should fail with deadline exceed error as long as they are in // RPCs should fail with deadline exceed error as long as they are in
// CONNECTING and not the error returned by the name resolver. // CONNECTING and not the error returned by the name resolver.
@ -909,7 +909,7 @@ func (s) TestPickFirst_ResolverError_WithPreviousUpdate_TransientFailure(t *test
// error instead of the old error that caused the channel to move to // error instead of the old error that caused the channel to move to
// TRANSIENT_FAILURE in the first place. // TRANSIENT_FAILURE in the first place.
nrErr := errors.New("error from name resolver") nrErr := errors.New("error from name resolver")
r.ReportError(nrErr) r.CC().ReportError(nrErr)
client := testgrpc.NewTestServiceClient(cc) client := testgrpc.NewTestServiceClient(cc)
for ; ctx.Err() == nil; <-time.After(defaultTestShortTimeout) { for ; ctx.Err() == nil; <-time.After(defaultTestShortTimeout) {
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); strings.Contains(err.Error(), nrErr.Error()) { if _, err := client.EmptyCall(ctx, &testpb.Empty{}); strings.Contains(err.Error(), nrErr.Error()) {

View File

@ -65,7 +65,7 @@ func (s) TestPickFirstMetrics(t *testing.T) {
defer cancel() defer cancel()
ss := &stubserver.StubServer{ ss := &stubserver.StubServer{
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
return &testpb.Empty{}, nil return &testpb.Empty{}, nil
}, },
} }
@ -155,7 +155,7 @@ func (s) TestPickFirstMetricsE2E(t *testing.T) {
defer cancel() defer cancel()
ss := &stubserver.StubServer{ ss := &stubserver.StubServer{
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
return &testpb.Empty{}, nil return &testpb.Empty{}, nil
}, },
} }

View File

@ -54,18 +54,9 @@ func init() {
balancer.Register(pickfirstBuilder{}) balancer.Register(pickfirstBuilder{})
} }
type ( // enableHealthListenerKeyType is a unique key type used in resolver
// enableHealthListenerKeyType is a unique key type used in resolver // attributes to indicate whether the health listener usage is enabled.
// attributes to indicate whether the health listener usage is enabled. type enableHealthListenerKeyType struct{}
enableHealthListenerKeyType struct{}
// managedByPickfirstKeyType is an attribute key type to inform Outlier
// Detection that the generic health listener is being used.
// TODO: https://github.com/grpc/grpc-go/issues/7915 - Remove this when
// implementing the dualstack design. This is a hack. Once Dualstack is
// completed, outlier detection will stop sending ejection updates through
// the connectivity listener.
managedByPickfirstKeyType struct{}
)
var ( var (
logger = grpclog.Component("pick-first-leaf-lb") logger = grpclog.Component("pick-first-leaf-lb")
@ -122,7 +113,7 @@ func (pickfirstBuilder) Build(cc balancer.ClientConn, bo balancer.BuildOptions)
target: bo.Target.String(), target: bo.Target.String(),
metricsRecorder: cc.MetricsRecorder(), metricsRecorder: cc.MetricsRecorder(),
subConns: resolver.NewAddressMap(), subConns: resolver.NewAddressMapV2[*scData](),
state: connectivity.Connecting, state: connectivity.Connecting,
cancelConnectionTimer: func() {}, cancelConnectionTimer: func() {},
} }
@ -149,17 +140,6 @@ func EnableHealthListener(state resolver.State) resolver.State {
return state return state
} }
// IsManagedByPickfirst returns whether an address belongs to a SubConn
// managed by the pickfirst LB policy.
// TODO: https://github.com/grpc/grpc-go/issues/7915 - This is a hack to disable
// outlier_detection via the with connectivity listener when using pick_first.
// Once Dualstack changes are complete, all SubConns will be created by
// pick_first and outlier detection will only use the health listener for
// ejection. This hack can then be removed.
func IsManagedByPickfirst(addr resolver.Address) bool {
return addr.BalancerAttributes.Value(managedByPickfirstKeyType{}) != nil
}
type pfConfig struct { type pfConfig struct {
serviceconfig.LoadBalancingConfig `json:"-"` serviceconfig.LoadBalancingConfig `json:"-"`
@ -186,7 +166,6 @@ type scData struct {
} }
func (b *pickfirstBalancer) newSCData(addr resolver.Address) (*scData, error) { func (b *pickfirstBalancer) newSCData(addr resolver.Address) (*scData, error) {
addr.BalancerAttributes = addr.BalancerAttributes.WithValue(managedByPickfirstKeyType{}, true)
sd := &scData{ sd := &scData{
rawConnectivityState: connectivity.Idle, rawConnectivityState: connectivity.Idle,
effectiveState: connectivity.Idle, effectiveState: connectivity.Idle,
@ -220,7 +199,7 @@ type pickfirstBalancer struct {
// updates. // updates.
state connectivity.State state connectivity.State
// scData for active subonns mapped by address. // scData for active subonns mapped by address.
subConns *resolver.AddressMap subConns *resolver.AddressMapV2[*scData]
addressList addressList addressList addressList
firstPass bool firstPass bool
numTF int numTF int
@ -319,7 +298,7 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState
prevAddr := b.addressList.currentAddress() prevAddr := b.addressList.currentAddress()
prevSCData, found := b.subConns.Get(prevAddr) prevSCData, found := b.subConns.Get(prevAddr)
prevAddrsCount := b.addressList.size() prevAddrsCount := b.addressList.size()
isPrevRawConnectivityStateReady := found && prevSCData.(*scData).rawConnectivityState == connectivity.Ready isPrevRawConnectivityStateReady := found && prevSCData.rawConnectivityState == connectivity.Ready
b.addressList.updateAddrs(newAddrs) b.addressList.updateAddrs(newAddrs)
// If the previous ready SubConn exists in new address list, // If the previous ready SubConn exists in new address list,
@ -381,21 +360,21 @@ func (b *pickfirstBalancer) startFirstPassLocked() {
b.numTF = 0 b.numTF = 0
// Reset the connection attempt record for existing SubConns. // Reset the connection attempt record for existing SubConns.
for _, sd := range b.subConns.Values() { for _, sd := range b.subConns.Values() {
sd.(*scData).connectionFailedInFirstPass = false sd.connectionFailedInFirstPass = false
} }
b.requestConnectionLocked() b.requestConnectionLocked()
} }
func (b *pickfirstBalancer) closeSubConnsLocked() { func (b *pickfirstBalancer) closeSubConnsLocked() {
for _, sd := range b.subConns.Values() { for _, sd := range b.subConns.Values() {
sd.(*scData).subConn.Shutdown() sd.subConn.Shutdown()
} }
b.subConns = resolver.NewAddressMap() b.subConns = resolver.NewAddressMapV2[*scData]()
} }
// deDupAddresses ensures that each address appears only once in the slice. // deDupAddresses ensures that each address appears only once in the slice.
func deDupAddresses(addrs []resolver.Address) []resolver.Address { func deDupAddresses(addrs []resolver.Address) []resolver.Address {
seenAddrs := resolver.NewAddressMap() seenAddrs := resolver.NewAddressMapV2[*scData]()
retAddrs := []resolver.Address{} retAddrs := []resolver.Address{}
for _, addr := range addrs { for _, addr := range addrs {
@ -481,7 +460,7 @@ func addressFamily(address string) ipAddrFamily {
// This ensures that the subchannel map accurately reflects the current set of // This ensures that the subchannel map accurately reflects the current set of
// addresses received from the name resolver. // addresses received from the name resolver.
func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address) { func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address) {
newAddrsMap := resolver.NewAddressMap() newAddrsMap := resolver.NewAddressMapV2[bool]()
for _, addr := range newAddrs { for _, addr := range newAddrs {
newAddrsMap.Set(addr, true) newAddrsMap.Set(addr, true)
} }
@ -491,7 +470,7 @@ func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address)
continue continue
} }
val, _ := b.subConns.Get(oldAddr) val, _ := b.subConns.Get(oldAddr)
val.(*scData).subConn.Shutdown() val.subConn.Shutdown()
b.subConns.Delete(oldAddr) b.subConns.Delete(oldAddr)
} }
} }
@ -500,13 +479,12 @@ func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address)
// becomes ready, which means that all other subConn must be shutdown. // becomes ready, which means that all other subConn must be shutdown.
func (b *pickfirstBalancer) shutdownRemainingLocked(selected *scData) { func (b *pickfirstBalancer) shutdownRemainingLocked(selected *scData) {
b.cancelConnectionTimer() b.cancelConnectionTimer()
for _, v := range b.subConns.Values() { for _, sd := range b.subConns.Values() {
sd := v.(*scData)
if sd.subConn != selected.subConn { if sd.subConn != selected.subConn {
sd.subConn.Shutdown() sd.subConn.Shutdown()
} }
} }
b.subConns = resolver.NewAddressMap() b.subConns = resolver.NewAddressMapV2[*scData]()
b.subConns.Set(selected.addr, selected) b.subConns.Set(selected.addr, selected)
} }
@ -539,18 +517,17 @@ func (b *pickfirstBalancer) requestConnectionLocked() {
b.subConns.Set(curAddr, sd) b.subConns.Set(curAddr, sd)
} }
scd := sd.(*scData) switch sd.rawConnectivityState {
switch scd.rawConnectivityState {
case connectivity.Idle: case connectivity.Idle:
scd.subConn.Connect() sd.subConn.Connect()
b.scheduleNextConnectionLocked() b.scheduleNextConnectionLocked()
return return
case connectivity.TransientFailure: case connectivity.TransientFailure:
// The SubConn is being re-used and failed during a previous pass // The SubConn is being re-used and failed during a previous pass
// over the addressList. It has not completed backoff yet. // over the addressList. It has not completed backoff yet.
// Mark it as having failed and try the next address. // Mark it as having failed and try the next address.
scd.connectionFailedInFirstPass = true sd.connectionFailedInFirstPass = true
lastErr = scd.lastErr lastErr = sd.lastErr
continue continue
case connectivity.Connecting: case connectivity.Connecting:
// Wait for the connection attempt to complete or the timer to fire // Wait for the connection attempt to complete or the timer to fire
@ -558,7 +535,7 @@ func (b *pickfirstBalancer) requestConnectionLocked() {
b.scheduleNextConnectionLocked() b.scheduleNextConnectionLocked()
return return
default: default:
b.logger.Errorf("SubConn with unexpected state %v present in SubConns map.", scd.rawConnectivityState) b.logger.Errorf("SubConn with unexpected state %v present in SubConns map.", sd.rawConnectivityState)
return return
} }
@ -753,8 +730,7 @@ func (b *pickfirstBalancer) endFirstPassIfPossibleLocked(lastErr error) {
} }
// Connect() has been called on all the SubConns. The first pass can be // Connect() has been called on all the SubConns. The first pass can be
// ended if all the SubConns have reported a failure. // ended if all the SubConns have reported a failure.
for _, v := range b.subConns.Values() { for _, sd := range b.subConns.Values() {
sd := v.(*scData)
if !sd.connectionFailedInFirstPass { if !sd.connectionFailedInFirstPass {
return return
} }
@ -765,8 +741,7 @@ func (b *pickfirstBalancer) endFirstPassIfPossibleLocked(lastErr error) {
Picker: &picker{err: lastErr}, Picker: &picker{err: lastErr},
}) })
// Start re-connecting all the SubConns that are already in IDLE. // Start re-connecting all the SubConns that are already in IDLE.
for _, v := range b.subConns.Values() { for _, sd := range b.subConns.Values() {
sd := v.(*scData)
if sd.rawConnectivityState == connectivity.Idle { if sd.rawConnectivityState == connectivity.Idle {
sd.subConn.Connect() sd.subConn.Connect()
} }
@ -927,6 +902,5 @@ func (al *addressList) hasNext() bool {
// fields that are meaningful to the SubConn. // fields that are meaningful to the SubConn.
func equalAddressIgnoringBalAttributes(a, b *resolver.Address) bool { func equalAddressIgnoringBalAttributes(a, b *resolver.Address) bool {
return a.Addr == b.Addr && a.ServerName == b.ServerName && return a.Addr == b.Addr && a.ServerName == b.ServerName &&
a.Attributes.Equal(b.Attributes) && a.Attributes.Equal(b.Attributes)
a.Metadata == b.Metadata
} }

View File

@ -41,6 +41,7 @@ import (
"google.golang.org/grpc/internal/testutils" "google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/pickfirst" "google.golang.org/grpc/internal/testutils/pickfirst"
"google.golang.org/grpc/internal/testutils/stats" "google.golang.org/grpc/internal/testutils/stats"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual" "google.golang.org/grpc/resolver/manual"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -191,7 +192,7 @@ func (s) TestPickFirstLeaf_SimpleResolverUpdate_FirstServerReady(t *testing.T) {
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -233,7 +234,7 @@ func (s) TestPickFirstLeaf_SimpleResolverUpdate_FirstServerUnReady(t *testing.T)
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -278,7 +279,7 @@ func (s) TestPickFirstLeaf_SimpleResolverUpdate_DuplicateAddrs(t *testing.T) {
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -350,7 +351,7 @@ func (s) TestPickFirstLeaf_ResolverUpdates_DisjointLists(t *testing.T) {
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -412,7 +413,7 @@ func (s) TestPickFirstLeaf_ResolverUpdates_ActiveBackendInUpdatedList(t *testing
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -474,7 +475,7 @@ func (s) TestPickFirstLeaf_ResolverUpdates_InActiveBackendInUpdatedList(t *testi
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -535,7 +536,7 @@ func (s) TestPickFirstLeaf_ResolverUpdates_IdenticalLists(t *testing.T) {
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -608,7 +609,7 @@ func (s) TestPickFirstLeaf_StopConnectedServer_FirstServerRestart(t *testing.T)
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -677,7 +678,7 @@ func (s) TestPickFirstLeaf_StopConnectedServer_SecondServerRestart(t *testing.T)
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -746,7 +747,7 @@ func (s) TestPickFirstLeaf_StopConnectedServer_SecondServerToFirst(t *testing.T)
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -813,7 +814,7 @@ func (s) TestPickFirstLeaf_StopConnectedServer_FirstServerToSecond(t *testing.T)
connectivity.Connecting, connectivity.Connecting,
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantConnStateTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -866,7 +867,7 @@ func (s) TestPickFirstLeaf_EmptyAddressList(t *testing.T) {
connectivity.Ready, connectivity.Ready,
} }
if diff := cmp.Diff(wantTransitions, stateSubscriber.transitions); diff != "" { if diff := cmp.Diff(wantTransitions, stateSubscriber.transitions()); diff != "" {
t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff) t.Errorf("ClientConn states mismatch (-want +got):\n%s", diff)
} }
} }
@ -1240,14 +1241,14 @@ func (s) TestPickFirstLeaf_HealthListenerEnabled(t *testing.T) {
defer cancel() defer cancel()
bf := stub.BalancerFuncs{ bf := stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = balancer.Get(pickfirstleaf.Name).Build(bd.ClientConn, bd.BuildOptions) bd.ChildBalancer = balancer.Get(pickfirstleaf.Name).Build(bd.ClientConn, bd.BuildOptions)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
ccs.ResolverState = pickfirstleaf.EnableHealthListener(ccs.ResolverState) ccs.ResolverState = pickfirstleaf.EnableHealthListener(ccs.ResolverState)
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
} }
@ -1288,15 +1289,15 @@ func (s) TestPickFirstLeaf_HealthListenerNotEnabled(t *testing.T) {
healthListenerCh: healthListenerCh, healthListenerCh: healthListenerCh,
subConnStateCh: make(chan balancer.SubConnState, 5), subConnStateCh: make(chan balancer.SubConnState, 5),
} }
bd.Data = balancer.Get(pickfirstleaf.Name).Build(ccw, bd.BuildOptions) bd.ChildBalancer = balancer.Get(pickfirstleaf.Name).Build(ccw, bd.BuildOptions)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
// Functions like a non-petiole policy by not configuring the use // Functions like a non-petiole policy by not configuring the use
// of health listeners. // of health listeners.
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
} }
@ -1344,14 +1345,14 @@ func (s) TestPickFirstLeaf_HealthUpdates(t *testing.T) {
healthListenerCh: healthListenerCh, healthListenerCh: healthListenerCh,
subConnStateCh: scConnectivityStateCh, subConnStateCh: scConnectivityStateCh,
} }
bd.Data = balancer.Get(pickfirstleaf.Name).Build(ccw, bd.BuildOptions) bd.ChildBalancer = balancer.Get(pickfirstleaf.Name).Build(ccw, bd.BuildOptions)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
ccs.ResolverState = pickfirstleaf.EnableHealthListener(ccs.ResolverState) ccs.ResolverState = pickfirstleaf.EnableHealthListener(ccs.ResolverState)
return bd.Data.(balancer.Balancer).UpdateClientConnState(ccs) return bd.ChildBalancer.UpdateClientConnState(ccs)
}, },
} }
@ -1424,6 +1425,85 @@ func (s) TestPickFirstLeaf_HealthUpdates(t *testing.T) {
testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
} }
// Tests the case where an address update received by the pick_first LB policy
// differs in metadata which should be ignored by the LB policy. In this case,
// the test verifies that new connections are not created when the address
// update only changes the metadata.
func (s) TestPickFirstLeaf_AddressUpdateWithMetadata(t *testing.T) {
dialer := testutils.NewBlockingDialer()
dopts := []grpc.DialOption{
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, pickfirstleaf.Name)),
grpc.WithContextDialer(dialer.DialContext),
}
cc, r, backends := setupPickFirstLeaf(t, 2, dopts...)
// Add a metadata to the addresses before pushing them to the pick_first LB
// policy through the manual resolver.
addrs := backends.resolverAddrs()
for i := range addrs {
addrs[i].Metadata = &metadata.MD{
"test-metadata-1": []string{fmt.Sprintf("%d", i)},
}
}
r.UpdateState(resolver.State{Addresses: addrs})
// Ensure that RPCs succeed to the expected backend.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil {
t.Fatal(err)
}
// Create holds for each backend. This will be used to verify the connection
// is not re-established.
holds := backends.holds(dialer)
// Add metadata to the addresses before pushing them to the pick_first LB
// policy through the manual resolver. Leave the order of the addresses
// unchanged.
for i := range addrs {
addrs[i].Metadata = &metadata.MD{
"test-metadata-2": []string{fmt.Sprintf("%d", i)},
}
}
r.UpdateState(resolver.State{Addresses: addrs})
// Ensure that no new connection is established.
for i := range holds {
sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
defer sCancel()
if holds[i].Wait(sCtx) {
t.Fatalf("Unexpected connection attempt to backend: %s", addrs[i])
}
}
if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[0]); err != nil {
t.Fatal(err)
}
// Add metadata to the addresses before pushing them to the pick_first LB
// policy through the manual resolver. Reverse of the order of addresses.
for i := range addrs {
addrs[i].Metadata = &metadata.MD{
"test-metadata-3": []string{fmt.Sprintf("%d", i)},
}
}
addrs[0], addrs[1] = addrs[1], addrs[0]
r.UpdateState(resolver.State{Addresses: addrs})
// Ensure that no new connection is established.
for i := range holds {
sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
defer sCancel()
if holds[i].Wait(sCtx) {
t.Fatalf("Unexpected connection attempt to backend: %s", addrs[i])
}
}
if err := pickfirst.CheckRPCsToBackend(ctx, cc, addrs[1]); err != nil {
t.Fatal(err)
}
}
// healthListenerCapturingCCWrapper is used to capture the health listener so // healthListenerCapturingCCWrapper is used to capture the health listener so
// that health updates can be mocked for testing. // that health updates can be mocked for testing.
type healthListenerCapturingCCWrapper struct { type healthListenerCapturingCCWrapper struct {
@ -1512,12 +1592,6 @@ func (b *stateStoringBalancer) Close() {
b.Balancer.Close() b.Balancer.Close()
} }
func (b *stateStoringBalancer) ExitIdle() {
if ib, ok := b.Balancer.(balancer.ExitIdler); ok {
ib.ExitIdle()
}
}
type stateStoringBalancerBuilder struct { type stateStoringBalancerBuilder struct {
balancer chan *stateStoringBalancer balancer chan *stateStoringBalancer
} }
@ -1606,11 +1680,23 @@ func (b *backendManager) holds(dialer *testutils.BlockingDialer) []*testutils.Ho
} }
type ccStateSubscriber struct { type ccStateSubscriber struct {
transitions []connectivity.State mu sync.Mutex
states []connectivity.State
}
// transitions returns all the states that ccStateSubscriber recorded.
// Without this a race condition occurs when the test compares the states
// and the subscriber at the same time receives a connectivity.Shutdown.
func (c *ccStateSubscriber) transitions() []connectivity.State {
c.mu.Lock()
defer c.mu.Unlock()
return c.states
} }
func (c *ccStateSubscriber) OnMessage(msg any) { func (c *ccStateSubscriber) OnMessage(msg any) {
c.transitions = append(c.transitions, msg.(connectivity.State)) c.mu.Lock()
defer c.mu.Unlock()
c.states = append(c.states, msg.(connectivity.State))
} }
// mockTimer returns a fake timeAfterFunc that will not trigger automatically. // mockTimer returns a fake timeAfterFunc that will not trigger automatically.

View File

@ -21,27 +21,21 @@ package ringhash
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
"google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/serviceconfig" "google.golang.org/grpc/internal/metadata"
iringhash "google.golang.org/grpc/internal/ringhash"
) )
// LBConfig is the balancer config for ring_hash balancer.
type LBConfig struct {
serviceconfig.LoadBalancingConfig `json:"-"`
MinRingSize uint64 `json:"minRingSize,omitempty"`
MaxRingSize uint64 `json:"maxRingSize,omitempty"`
}
const ( const (
defaultMinSize = 1024 defaultMinSize = 1024
defaultMaxSize = 4096 defaultMaxSize = 4096
ringHashSizeUpperBound = 8 * 1024 * 1024 // 8M ringHashSizeUpperBound = 8 * 1024 * 1024 // 8M
) )
func parseConfig(c json.RawMessage) (*LBConfig, error) { func parseConfig(c json.RawMessage) (*iringhash.LBConfig, error) {
var cfg LBConfig var cfg iringhash.LBConfig
if err := json.Unmarshal(c, &cfg); err != nil { if err := json.Unmarshal(c, &cfg); err != nil {
return nil, err return nil, err
} }
@ -66,5 +60,18 @@ func parseConfig(c json.RawMessage) (*LBConfig, error) {
if cfg.MaxRingSize > envconfig.RingHashCap { if cfg.MaxRingSize > envconfig.RingHashCap {
cfg.MaxRingSize = envconfig.RingHashCap cfg.MaxRingSize = envconfig.RingHashCap
} }
if !envconfig.RingHashSetRequestHashKey {
cfg.RequestHashHeader = ""
}
if cfg.RequestHashHeader != "" {
cfg.RequestHashHeader = strings.ToLower(cfg.RequestHashHeader)
// See rules in https://github.com/grpc/proposal/blob/master/A76-ring-hash-improvements.md#explicitly-setting-the-request-hash-key
if err := metadata.ValidateKey(cfg.RequestHashHeader); err != nil {
return nil, fmt.Errorf("invalid requestHashHeader %q: %v", cfg.RequestHashHeader, err)
}
if strings.HasSuffix(cfg.RequestHashHeader, "-bin") {
return nil, fmt.Errorf("invalid requestHashHeader %q: key must not end with \"-bin\"", cfg.RequestHashHeader)
}
}
return &cfg, nil return &cfg, nil
} }

View File

@ -0,0 +1,173 @@
/*
*
* Copyright 2021 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ringhash
import (
"encoding/json"
"testing"
"github.com/google/go-cmp/cmp"
"google.golang.org/grpc/internal/envconfig"
iringhash "google.golang.org/grpc/internal/ringhash"
"google.golang.org/grpc/internal/testutils"
)
func (s) TestParseConfig(t *testing.T) {
tests := []struct {
name string
js string
envConfigCap uint64
requestHeaderEnvVar bool
want *iringhash.LBConfig
wantErr bool
}{
{
name: "OK",
js: `{"minRingSize": 1, "maxRingSize": 2}`,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{MinRingSize: 1, MaxRingSize: 2},
},
{
name: "OK with default min",
js: `{"maxRingSize": 2000}`,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{MinRingSize: defaultMinSize, MaxRingSize: 2000},
},
{
name: "OK with default max",
js: `{"minRingSize": 2000}`,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{MinRingSize: 2000, MaxRingSize: defaultMaxSize},
},
{
name: "min greater than max",
js: `{"minRingSize": 10, "maxRingSize": 2}`,
requestHeaderEnvVar: true,
want: nil,
wantErr: true,
},
{
name: "min greater than max greater than global limit",
js: `{"minRingSize": 6000, "maxRingSize": 5000}`,
requestHeaderEnvVar: true,
want: nil,
wantErr: true,
},
{
name: "max greater than global limit",
js: `{"minRingSize": 1, "maxRingSize": 6000}`,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{MinRingSize: 1, MaxRingSize: 4096},
},
{
name: "min and max greater than global limit",
js: `{"minRingSize": 5000, "maxRingSize": 6000}`,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{MinRingSize: 4096, MaxRingSize: 4096},
},
{
name: "min and max less than raised global limit",
js: `{"minRingSize": 5000, "maxRingSize": 6000}`,
envConfigCap: 8000,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{MinRingSize: 5000, MaxRingSize: 6000},
},
{
name: "min and max greater than raised global limit",
js: `{"minRingSize": 10000, "maxRingSize": 10000}`,
envConfigCap: 8000,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{MinRingSize: 8000, MaxRingSize: 8000},
},
{
name: "min greater than upper bound",
js: `{"minRingSize": 8388610, "maxRingSize": 10}`,
requestHeaderEnvVar: true,
want: nil,
wantErr: true,
},
{
name: "max greater than upper bound",
js: `{"minRingSize": 10, "maxRingSize": 8388610}`,
requestHeaderEnvVar: true,
want: nil,
wantErr: true,
},
{
name: "request metadata key set",
js: `{"requestHashHeader": "x-foo"}`,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{
MinRingSize: defaultMinSize,
MaxRingSize: defaultMaxSize,
RequestHashHeader: "x-foo",
},
},
{
name: "request metadata key set with uppercase letters",
js: `{"requestHashHeader": "x-FOO"}`,
requestHeaderEnvVar: true,
want: &iringhash.LBConfig{
MinRingSize: defaultMinSize,
MaxRingSize: defaultMaxSize,
RequestHashHeader: "x-foo",
},
},
{
name: "invalid request hash header",
js: `{"requestHashHeader": "!invalid"}`,
requestHeaderEnvVar: true,
want: nil,
wantErr: true,
},
{
name: "binary request hash header",
js: `{"requestHashHeader": "header-with-bin"}`,
requestHeaderEnvVar: true,
want: nil,
wantErr: true,
},
{
name: "request hash header cleared when RingHashSetRequestHashKey env var is false",
js: `{"requestHashHeader": "x-foo"}`,
requestHeaderEnvVar: false,
want: &iringhash.LBConfig{
MinRingSize: defaultMinSize,
MaxRingSize: defaultMaxSize,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.envConfigCap != 0 {
testutils.SetEnvConfig(t, &envconfig.RingHashCap, tt.envConfigCap)
}
testutils.SetEnvConfig(t, &envconfig.RingHashSetRequestHashKey, tt.requestHeaderEnvVar)
got, err := parseConfig(json.RawMessage(tt.js))
if (err != nil) != tt.wantErr {
t.Errorf("parseConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if diff := cmp.Diff(got, tt.want); diff != "" {
t.Errorf("parseConfig() got unexpected output, diff (-got +want): %v", diff)
}
})
}
}

124
balancer/ringhash/picker.go Normal file
View File

@ -0,0 +1,124 @@
/*
*
* Copyright 2021 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ringhash
import (
"fmt"
"strings"
xxhash "github.com/cespare/xxhash/v2"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/connectivity"
iringhash "google.golang.org/grpc/internal/ringhash"
"google.golang.org/grpc/metadata"
)
type picker struct {
ring *ring
// endpointStates is a cache of endpoint states.
// The ringhash balancer stores endpoint states in a `resolver.EndpointMap`,
// with access guarded by `ringhashBalancer.mu`. The `endpointStates` cache
// in the picker helps avoid locking the ringhash balancer's mutex when
// reading the latest state at RPC time.
endpointStates map[string]endpointState // endpointState.hashKey -> endpointState
// requestHashHeader is the header key to look for the request hash. If it's
// empty, the request hash is expected to be set in the context via xDS.
// See gRFC A76.
requestHashHeader string
// hasEndpointInConnectingState is true if any of the endpoints is in
// CONNECTING.
hasEndpointInConnectingState bool
randUint64 func() uint64
}
func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
usingRandomHash := false
var requestHash uint64
if p.requestHashHeader == "" {
var ok bool
if requestHash, ok = iringhash.XDSRequestHash(info.Ctx); !ok {
return balancer.PickResult{}, fmt.Errorf("ringhash: expected xDS config selector to set the request hash")
}
} else {
md, ok := metadata.FromOutgoingContext(info.Ctx)
if !ok || len(md.Get(p.requestHashHeader)) == 0 {
requestHash = p.randUint64()
usingRandomHash = true
} else {
values := strings.Join(md.Get(p.requestHashHeader), ",")
requestHash = xxhash.Sum64String(values)
}
}
e := p.ring.pick(requestHash)
ringSize := len(p.ring.items)
if !usingRandomHash {
// Per gRFC A61, because of sticky-TF with PickFirst's auto reconnect on TF,
// we ignore all TF subchannels and find the first ring entry in READY,
// CONNECTING or IDLE. If that entry is in IDLE, we need to initiate a
// connection. The idlePicker returned by the LazyLB or the new Pickfirst
// should do this automatically.
for i := 0; i < ringSize; i++ {
index := (e.idx + i) % ringSize
es := p.endpointState(p.ring.items[index])
switch es.state.ConnectivityState {
case connectivity.Ready, connectivity.Connecting, connectivity.Idle:
return es.state.Picker.Pick(info)
case connectivity.TransientFailure:
default:
panic(fmt.Sprintf("Found child balancer in unknown state: %v", es.state.ConnectivityState))
}
}
} else {
// If the picker has generated a random hash, it will walk the ring from
// this hash, and pick the first READY endpoint. If no endpoint is
// currently in CONNECTING state, it will trigger a connection attempt
// on at most one endpoint that is in IDLE state along the way. - A76
requestedConnection := p.hasEndpointInConnectingState
for i := 0; i < ringSize; i++ {
index := (e.idx + i) % ringSize
es := p.endpointState(p.ring.items[index])
if es.state.ConnectivityState == connectivity.Ready {
return es.state.Picker.Pick(info)
}
if !requestedConnection && es.state.ConnectivityState == connectivity.Idle {
requestedConnection = true
// If the SubChannel is in idle state, initiate a connection but
// continue to check other pickers to see if there is one in
// ready state.
es.balancer.ExitIdle()
}
}
if requestedConnection {
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
}
}
// All children are in transient failure. Return the first failure.
return p.endpointState(e).state.Picker.Pick(info)
}
func (p *picker) endpointState(e *ringEntry) endpointState {
return p.endpointStates[e.hashKey]
}

View File

@ -0,0 +1,311 @@
/*
*
* Copyright 2021 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ringhash
import (
"context"
"errors"
"fmt"
"math"
"testing"
"time"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/connectivity"
iringhash "google.golang.org/grpc/internal/ringhash"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/metadata"
)
var (
testSubConns []*testutils.TestSubConn
errPicker = errors.New("picker in TransientFailure")
)
func init() {
for i := 0; i < 8; i++ {
testSubConns = append(testSubConns, testutils.NewTestSubConn(fmt.Sprint(i)))
}
}
// fakeChildPicker is used to mock pickers from child pickfirst balancers.
type fakeChildPicker struct {
connectivityState connectivity.State
subConn *testutils.TestSubConn
tfError error
}
func (p *fakeChildPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
switch p.connectivityState {
case connectivity.Idle:
p.subConn.Connect()
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
case connectivity.Connecting:
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
case connectivity.Ready:
return balancer.PickResult{SubConn: p.subConn}, nil
default:
return balancer.PickResult{}, p.tfError
}
}
type fakeExitIdler struct {
sc *testutils.TestSubConn
}
func (ei *fakeExitIdler) ExitIdle() {
ei.sc.Connect()
}
func testRingAndEndpointStates(states []connectivity.State) (*ring, map[string]endpointState) {
var items []*ringEntry
epStates := map[string]endpointState{}
for i, st := range states {
testSC := testSubConns[i]
items = append(items, &ringEntry{
idx: i,
hash: math.MaxUint64 / uint64(len(states)) * uint64(i),
hashKey: testSC.String(),
})
epState := endpointState{
state: balancer.State{
ConnectivityState: st,
Picker: &fakeChildPicker{
connectivityState: st,
tfError: fmt.Errorf("%d: %w", i, errPicker),
subConn: testSC,
},
},
balancer: &fakeExitIdler{
sc: testSC,
},
}
epStates[testSC.String()] = epState
}
return &ring{items: items}, epStates
}
func (s) TestPickerPickFirstTwo(t *testing.T) {
tests := []struct {
name string
connectivityStates []connectivity.State
wantSC balancer.SubConn
wantErr error
wantSCToConnect balancer.SubConn
}{
{
name: "picked is Ready",
connectivityStates: []connectivity.State{connectivity.Ready, connectivity.Idle},
wantSC: testSubConns[0],
},
{
name: "picked is connecting, queue",
connectivityStates: []connectivity.State{connectivity.Connecting, connectivity.Idle},
wantErr: balancer.ErrNoSubConnAvailable,
},
{
name: "picked is Idle, connect and queue",
connectivityStates: []connectivity.State{connectivity.Idle, connectivity.Idle},
wantErr: balancer.ErrNoSubConnAvailable,
wantSCToConnect: testSubConns[0],
},
{
name: "picked is TransientFailure, next is ready, return",
connectivityStates: []connectivity.State{connectivity.TransientFailure, connectivity.Ready},
wantSC: testSubConns[1],
},
{
name: "picked is TransientFailure, next is connecting, queue",
connectivityStates: []connectivity.State{connectivity.TransientFailure, connectivity.Connecting},
wantErr: balancer.ErrNoSubConnAvailable,
},
{
name: "picked is TransientFailure, next is Idle, connect and queue",
connectivityStates: []connectivity.State{connectivity.TransientFailure, connectivity.Idle},
wantErr: balancer.ErrNoSubConnAvailable,
wantSCToConnect: testSubConns[1],
},
{
name: "all are in TransientFailure, return picked failure",
connectivityStates: []connectivity.State{connectivity.TransientFailure, connectivity.TransientFailure},
wantErr: errPicker,
},
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ring, epStates := testRingAndEndpointStates(tt.connectivityStates)
p := &picker{
ring: ring,
endpointStates: epStates,
}
got, err := p.Pick(balancer.PickInfo{
Ctx: iringhash.SetXDSRequestHash(ctx, 0), // always pick the first endpoint on the ring.
})
if (err != nil || tt.wantErr != nil) && !errors.Is(err, tt.wantErr) {
t.Errorf("Pick() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got.SubConn != tt.wantSC {
t.Errorf("Pick() got = %v, want picked SubConn: %v", got, tt.wantSC)
}
if sc := tt.wantSCToConnect; sc != nil {
select {
case <-sc.(*testutils.TestSubConn).ConnectCh:
case <-time.After(defaultTestShortTimeout):
t.Errorf("timeout waiting for Connect() from SubConn %v", sc)
}
}
})
}
}
func (s) TestPickerNoRequestHash(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
ring, epStates := testRingAndEndpointStates([]connectivity.State{connectivity.Ready})
p := &picker{
ring: ring,
endpointStates: epStates,
}
if _, err := p.Pick(balancer.PickInfo{Ctx: ctx}); err == nil {
t.Errorf("Pick() should have failed with no request hash")
}
}
func (s) TestPickerRequestHashKey(t *testing.T) {
tests := []struct {
name string
headerValues []string
expectedPick int
}{
{
name: "header not set",
expectedPick: 0, // Random hash set to 0, which is within (MaxUint64 / 3 * 2, 0]
},
{
name: "header empty",
headerValues: []string{""},
expectedPick: 0, // xxhash.Sum64String("value1,value2") is within (MaxUint64 / 3 * 2, 0]
},
{
name: "header set to one value",
headerValues: []string{"some-value"},
expectedPick: 1, // xxhash.Sum64String("some-value") is within (0, MaxUint64 / 3]
},
{
name: "header set to multiple values",
headerValues: []string{"value1", "value2"},
expectedPick: 2, // xxhash.Sum64String("value1,value2") is within (MaxUint64 / 3, MaxUint64 / 3 * 2]
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
ring, epStates := testRingAndEndpointStates(
[]connectivity.State{
connectivity.Ready,
connectivity.Ready,
connectivity.Ready,
})
headerName := "some-header"
p := &picker{
ring: ring,
endpointStates: epStates,
requestHashHeader: headerName,
randUint64: func() uint64 { return 0 },
}
for _, v := range tt.headerValues {
ctx = metadata.AppendToOutgoingContext(ctx, headerName, v)
}
if res, err := p.Pick(balancer.PickInfo{Ctx: ctx}); err != nil {
t.Errorf("Pick() failed: %v", err)
} else if res.SubConn != testSubConns[tt.expectedPick] {
t.Errorf("Pick() got = %v, want SubConn: %v", res.SubConn, testSubConns[tt.expectedPick])
}
})
}
}
func (s) TestPickerRandomHash(t *testing.T) {
tests := []struct {
name string
hash uint64
connectivityStates []connectivity.State
wantSC balancer.SubConn
wantErr error
wantSCToConnect balancer.SubConn
hasEndpointInConnectingState bool
}{
{
name: "header not set, picked is Ready",
connectivityStates: []connectivity.State{connectivity.Ready, connectivity.Idle},
wantSC: testSubConns[0],
},
{
name: "header not set, picked is Idle, another is Ready. Connect and pick Ready",
connectivityStates: []connectivity.State{connectivity.Idle, connectivity.Ready},
wantSC: testSubConns[1],
wantSCToConnect: testSubConns[0],
},
{
name: "header not set, picked is Idle, there is at least one Connecting",
connectivityStates: []connectivity.State{connectivity.Connecting, connectivity.Idle},
wantErr: balancer.ErrNoSubConnAvailable,
hasEndpointInConnectingState: true,
},
{
name: "header not set, all Idle or TransientFailure, connect",
connectivityStates: []connectivity.State{connectivity.TransientFailure, connectivity.Idle},
wantErr: balancer.ErrNoSubConnAvailable,
wantSCToConnect: testSubConns[1],
},
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ring, epStates := testRingAndEndpointStates(tt.connectivityStates)
p := &picker{
ring: ring,
endpointStates: epStates,
requestHashHeader: "some-header",
hasEndpointInConnectingState: tt.hasEndpointInConnectingState,
randUint64: func() uint64 { return 0 }, // always return the first endpoint on the ring.
}
if got, err := p.Pick(balancer.PickInfo{Ctx: ctx}); err != tt.wantErr {
t.Errorf("Pick() error = %v, wantErr %v", err, tt.wantErr)
return
} else if got.SubConn != tt.wantSC {
t.Errorf("Pick() got = %v, want picked SubConn: %v", got, tt.wantSC)
}
if sc := tt.wantSCToConnect; sc != nil {
select {
case <-sc.(*testutils.TestSubConn).ConnectCh:
case <-time.After(defaultTestShortTimeout):
t.Errorf("timeout waiting for Connect() from SubConn %v", sc)
}
}
})
}
}

View File

@ -33,23 +33,23 @@ type ring struct {
} }
type endpointInfo struct { type endpointInfo struct {
firstAddr string hashKey string
scaledWeight float64 scaledWeight float64
originalWeight uint32 originalWeight uint32
} }
type ringEntry struct { type ringEntry struct {
idx int idx int
hash uint64 hash uint64
firstAddr string hashKey string
weight uint32 weight uint32
} }
// newRing creates a ring from the endpoints stored in the EndpointMap. The ring // newRing creates a ring from the endpoints stored in the EndpointMap. The ring
// size is limited by the passed in max/min. // size is limited by the passed in max/min.
// //
// ring entries will be created for each endpoint, and endpoints with high // ring entries will be created for each endpoint, and endpoints with high
// weight (specified by the address) may have multiple entries. // weight (specified by the endpoint) may have multiple entries.
// //
// For example, for endpoints with weights {a:3, b:3, c:4}, a generated ring of // For example, for endpoints with weights {a:3, b:3, c:4}, a generated ring of
// size 10 could be: // size 10 could be:
@ -68,7 +68,7 @@ type ringEntry struct {
// and first item with hash >= given hash will be returned. // and first item with hash >= given hash will be returned.
// //
// Must be called with a non-empty endpoints map. // Must be called with a non-empty endpoints map.
func newRing(endpoints *resolver.EndpointMap, minRingSize, maxRingSize uint64, logger *grpclog.PrefixLogger) *ring { func newRing(endpoints *resolver.EndpointMap[*endpointState], minRingSize, maxRingSize uint64, logger *grpclog.PrefixLogger) *ring {
if logger.V(2) { if logger.V(2) {
logger.Infof("newRing: number of endpoints is %d, minRingSize is %d, maxRingSize is %d", endpoints.Len(), minRingSize, maxRingSize) logger.Infof("newRing: number of endpoints is %d, minRingSize is %d, maxRingSize is %d", endpoints.Len(), minRingSize, maxRingSize)
} }
@ -109,8 +109,8 @@ func newRing(endpoints *resolver.EndpointMap, minRingSize, maxRingSize uint64, l
// updates. // updates.
idx := 0 idx := 0
for currentHashes < targetHashes { for currentHashes < targetHashes {
h := xxhash.Sum64String(epInfo.firstAddr + "_" + strconv.Itoa(idx)) h := xxhash.Sum64String(epInfo.hashKey + "_" + strconv.Itoa(idx))
items = append(items, &ringEntry{hash: h, firstAddr: epInfo.firstAddr, weight: epInfo.originalWeight}) items = append(items, &ringEntry{hash: h, hashKey: epInfo.hashKey, weight: epInfo.originalWeight})
idx++ idx++
currentHashes++ currentHashes++
} }
@ -136,25 +136,24 @@ func newRing(endpoints *resolver.EndpointMap, minRingSize, maxRingSize uint64, l
// The endpoints are sorted in ascending order to ensure consistent results. // The endpoints are sorted in ascending order to ensure consistent results.
// //
// Must be called with a non-empty endpoints map. // Must be called with a non-empty endpoints map.
func normalizeWeights(endpoints *resolver.EndpointMap) ([]endpointInfo, float64) { func normalizeWeights(endpoints *resolver.EndpointMap[*endpointState]) ([]endpointInfo, float64) {
var weightSum uint32 var weightSum uint32
// Since attributes are explicitly ignored in the EndpointMap key, we need // Since attributes are explicitly ignored in the EndpointMap key, we need
// to iterate over the values to get the weights. // to iterate over the values to get the weights.
endpointVals := endpoints.Values() endpointVals := endpoints.Values()
for _, a := range endpointVals { for _, epState := range endpointVals {
weightSum += a.(*endpointState).weight weightSum += epState.weight
} }
ret := make([]endpointInfo, 0, endpoints.Len()) ret := make([]endpointInfo, 0, endpoints.Len())
min := 1.0 min := 1.0
for _, a := range endpointVals { for _, epState := range endpointVals {
epState := a.(*endpointState)
// (*endpointState).weight is set to 1 if the weight attribute is not // (*endpointState).weight is set to 1 if the weight attribute is not
// found on the endpoint. And since this function is guaranteed to be // found on the endpoint. And since this function is guaranteed to be
// called with a non-empty endpoints map, weightSum is guaranteed to be // called with a non-empty endpoints map, weightSum is guaranteed to be
// non-zero. So, we need not worry about divide by zero error here. // non-zero. So, we need not worry about divide by zero error here.
nw := float64(epState.weight) / float64(weightSum) nw := float64(epState.weight) / float64(weightSum)
ret = append(ret, endpointInfo{ ret = append(ret, endpointInfo{
firstAddr: epState.firstAddr, hashKey: epState.hashKey,
scaledWeight: nw, scaledWeight: nw,
originalWeight: epState.weight, originalWeight: epState.weight,
}) })
@ -167,7 +166,7 @@ func normalizeWeights(endpoints *resolver.EndpointMap) ([]endpointInfo, float64)
// where an endpoint is added and then removed, the RPCs will still pick the // where an endpoint is added and then removed, the RPCs will still pick the
// same old endpoint. // same old endpoint.
sort.Slice(ret, func(i, j int) bool { sort.Slice(ret, func(i, j int) bool {
return ret[i].firstAddr < ret[j].firstAddr return ret[i].hashKey < ret[j].hashKey
}) })
return ret, min return ret, min
} }

View File

@ -24,12 +24,12 @@ import (
"testing" "testing"
xxhash "github.com/cespare/xxhash/v2" xxhash "github.com/cespare/xxhash/v2"
"google.golang.org/grpc/balancer/weightedroundrobin" "google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
) )
var testEndpoints []resolver.Endpoint var testEndpoints []resolver.Endpoint
var testEndpointStateMap *resolver.EndpointMap var testEndpointStateMap *resolver.EndpointMap[*endpointState]
func init() { func init() {
testEndpoints = []resolver.Endpoint{ testEndpoints = []resolver.Endpoint{
@ -37,15 +37,15 @@ func init() {
testEndpoint("b", 3), testEndpoint("b", 3),
testEndpoint("c", 4), testEndpoint("c", 4),
} }
testEndpointStateMap = resolver.NewEndpointMap() testEndpointStateMap = resolver.NewEndpointMap[*endpointState]()
testEndpointStateMap.Set(testEndpoints[0], &endpointState{firstAddr: "a", weight: 3}) testEndpointStateMap.Set(testEndpoints[0], &endpointState{hashKey: "a", weight: 3})
testEndpointStateMap.Set(testEndpoints[1], &endpointState{firstAddr: "b", weight: 3}) testEndpointStateMap.Set(testEndpoints[1], &endpointState{hashKey: "b", weight: 3})
testEndpointStateMap.Set(testEndpoints[2], &endpointState{firstAddr: "c", weight: 4}) testEndpointStateMap.Set(testEndpoints[2], &endpointState{hashKey: "c", weight: 4})
} }
func testEndpoint(addr string, weight uint32) resolver.Endpoint { func testEndpoint(addr string, endpointWeight uint32) resolver.Endpoint {
ep := resolver.Endpoint{Addresses: []resolver.Address{{Addr: addr}}} ep := resolver.Endpoint{Addresses: []resolver.Address{{Addr: addr}}}
return weightedroundrobin.SetAddrInfoInEndpoint(ep, weightedroundrobin.AddrInfo{Weight: weight}) return weight.Set(ep, weight.EndpointInfo{Weight: endpointWeight})
} }
func (s) TestRingNew(t *testing.T) { func (s) TestRingNew(t *testing.T) {
@ -61,7 +61,7 @@ func (s) TestRingNew(t *testing.T) {
for _, e := range testEndpoints { for _, e := range testEndpoints {
var count int var count int
for _, ii := range r.items { for _, ii := range r.items {
if ii.firstAddr == e.Addresses[0].Addr { if ii.hashKey == hashKey(e) {
count++ count++
} }
} }

View File

@ -16,13 +16,23 @@
* *
*/ */
// Package ringhash implements the ringhash balancer. // Package ringhash implements the ringhash balancer. See the following
// gRFCs for details:
// - https://github.com/grpc/proposal/blob/master/A42-xds-ring-hash-lb-policy.md
// - https://github.com/grpc/proposal/blob/master/A61-IPv4-IPv6-dualstack-backends.md#ring-hash
// - https://github.com/grpc/proposal/blob/master/A76-ring-hash-improvements.md
//
// # Experimental
//
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
// later release.
package ringhash package ringhash
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/rand/v2"
"sort" "sort"
"sync" "sync"
@ -31,11 +41,13 @@ import (
"google.golang.org/grpc/balancer/endpointsharding" "google.golang.org/grpc/balancer/endpointsharding"
"google.golang.org/grpc/balancer/lazy" "google.golang.org/grpc/balancer/lazy"
"google.golang.org/grpc/balancer/pickfirst/pickfirstleaf" "google.golang.org/grpc/balancer/pickfirst/pickfirstleaf"
"google.golang.org/grpc/balancer/weightedroundrobin"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/internal/pretty"
iringhash "google.golang.org/grpc/internal/ringhash"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/ringhash"
"google.golang.org/grpc/serviceconfig" "google.golang.org/grpc/serviceconfig"
) )
@ -55,7 +67,7 @@ type bb struct{}
func (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { func (bb) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
b := &ringhashBalancer{ b := &ringhashBalancer{
ClientConn: cc, ClientConn: cc,
endpointStates: resolver.NewEndpointMap(), endpointStates: resolver.NewEndpointMap[*endpointState](),
} }
esOpts := endpointsharding.Options{DisableAutoReconnect: true} esOpts := endpointsharding.Options{DisableAutoReconnect: true}
b.child = endpointsharding.NewBalancer(b, opts, lazyPickFirstBuilder, esOpts) b.child = endpointsharding.NewBalancer(b, opts, lazyPickFirstBuilder, esOpts)
@ -83,10 +95,10 @@ type ringhashBalancer struct {
child balancer.Balancer child balancer.Balancer
mu sync.Mutex mu sync.Mutex
config *LBConfig config *iringhash.LBConfig
inhibitChildUpdates bool inhibitChildUpdates bool
shouldRegenerateRing bool shouldRegenerateRing bool
endpointStates *resolver.EndpointMap // Map from endpoint -> *endpointState endpointStates *resolver.EndpointMap[*endpointState]
// ring is always in sync with endpoints. When endpoints change, a new ring // ring is always in sync with endpoints. When endpoints change, a new ring
// is generated. Note that address weights updates also regenerates the // is generated. Note that address weights updates also regenerates the
@ -94,6 +106,18 @@ type ringhashBalancer struct {
ring *ring ring *ring
} }
// hashKey returns the hash key to use for an endpoint. Per gRFC A61, each entry
// in the ring is a hash of the endpoint's hash key concatenated with a
// per-entry unique suffix.
func hashKey(endpoint resolver.Endpoint) string {
if hk := ringhash.HashKey(endpoint); hk != "" {
return hk
}
// If no hash key is set, use the endpoint's first address as the hash key.
// This is the default behavior when no hash key is set.
return endpoint.Addresses[0].Addr
}
// UpdateState intercepts child balancer state updates. It updates the // UpdateState intercepts child balancer state updates. It updates the
// per-endpoint state stored in the ring, and also the aggregated state based on // per-endpoint state stored in the ring, and also the aggregated state based on
// the child picker. It also reconciles the endpoint list. It sets // the child picker. It also reconciles the endpoint list. It sets
@ -108,37 +132,35 @@ func (b *ringhashBalancer) UpdateState(state balancer.State) {
defer b.mu.Unlock() defer b.mu.Unlock()
childStates := endpointsharding.ChildStatesFromPicker(state.Picker) childStates := endpointsharding.ChildStatesFromPicker(state.Picker)
// endpointsSet is the set converted from endpoints, used for quick lookup. // endpointsSet is the set converted from endpoints, used for quick lookup.
endpointsSet := resolver.NewEndpointMap() endpointsSet := resolver.NewEndpointMap[bool]()
for _, childState := range childStates { for _, childState := range childStates {
endpoint := childState.Endpoint endpoint := childState.Endpoint
endpointsSet.Set(endpoint, true) endpointsSet.Set(endpoint, true)
newWeight := getWeightAttribute(endpoint) newWeight := getWeightAttribute(endpoint)
if val, ok := b.endpointStates.Get(endpoint); !ok { hk := hashKey(endpoint)
es, ok := b.endpointStates.Get(endpoint)
if !ok {
es := &endpointState{ es := &endpointState{
balancer: childState.Balancer, balancer: childState.Balancer,
weight: newWeight, hashKey: hk,
firstAddr: endpoint.Addresses[0].Addr, weight: newWeight,
state: childState.State, state: childState.State,
} }
b.endpointStates.Set(endpoint, es) b.endpointStates.Set(endpoint, es)
b.shouldRegenerateRing = true b.shouldRegenerateRing = true
} else { } else {
// We have seen this endpoint before and created a `endpointState` // We have seen this endpoint before and created a `endpointState`
// object for it. If the weight or the first address of the endpoint // object for it. If the weight or the hash key of the endpoint has
// has changed, update the endpoint state map with the new weight. // changed, update the endpoint state map with the new weight or
// This will be used when a new ring is created. // hash key. This will be used when a new ring is created.
es := val.(*endpointState)
if oldWeight := es.weight; oldWeight != newWeight { if oldWeight := es.weight; oldWeight != newWeight {
b.shouldRegenerateRing = true b.shouldRegenerateRing = true
es.weight = newWeight es.weight = newWeight
} }
if es.firstAddr != endpoint.Addresses[0].Addr { if es.hashKey != hk {
// If the order of the addresses for a given endpoint change,
// that will change the position of the endpoint in the ring.
// -A61
b.shouldRegenerateRing = true b.shouldRegenerateRing = true
es.firstAddr = endpoint.Addresses[0].Addr es.hashKey = hk
} }
es.state = childState.State es.state = childState.State
} }
@ -161,7 +183,7 @@ func (b *ringhashBalancer) UpdateClientConnState(ccs balancer.ClientConnState) e
b.logger.Infof("Received update from resolver, balancer config: %+v", pretty.ToJSON(ccs.BalancerConfig)) b.logger.Infof("Received update from resolver, balancer config: %+v", pretty.ToJSON(ccs.BalancerConfig))
} }
newConfig, ok := ccs.BalancerConfig.(*LBConfig) newConfig, ok := ccs.BalancerConfig.(*iringhash.LBConfig)
if !ok { if !ok {
return fmt.Errorf("unexpected balancer config with type: %T", ccs.BalancerConfig) return fmt.Errorf("unexpected balancer config with type: %T", ccs.BalancerConfig)
} }
@ -240,13 +262,13 @@ func (b *ringhashBalancer) updatePickerLocked() {
// ensure `ExitIdle` is called on the same child, preventing unnecessary // ensure `ExitIdle` is called on the same child, preventing unnecessary
// connections. // connections.
var endpointStates = make([]*endpointState, b.endpointStates.Len()) var endpointStates = make([]*endpointState, b.endpointStates.Len())
for i, val := range b.endpointStates.Values() { for i, s := range b.endpointStates.Values() {
endpointStates[i] = val.(*endpointState) endpointStates[i] = s
} }
sort.Slice(endpointStates, func(i, j int) bool { sort.Slice(endpointStates, func(i, j int) bool {
return endpointStates[i].firstAddr < endpointStates[j].firstAddr return endpointStates[i].hashKey < endpointStates[j].hashKey
}) })
var idleBalancer balancer.ExitIdler var idleBalancer endpointsharding.ExitIdler
for _, es := range endpointStates { for _, es := range endpointStates {
connState := es.state.ConnectivityState connState := es.state.ConnectivityState
if connState == connectivity.Connecting { if connState == connectivity.Connecting {
@ -278,7 +300,6 @@ func (b *ringhashBalancer) updatePickerLocked() {
} else { } else {
newPicker = b.newPickerLocked() newPicker = b.newPickerLocked()
} }
b.logger.Infof("Pushing new state %v and picker %p", state, newPicker)
b.ClientConn.UpdateState(balancer.State{ b.ClientConn.UpdateState(balancer.State{
ConnectivityState: state, ConnectivityState: state,
Picker: newPicker, Picker: newPicker,
@ -299,12 +320,23 @@ func (b *ringhashBalancer) ExitIdle() {
// over to avoid locking the mutex at RPC time. The picker should be // over to avoid locking the mutex at RPC time. The picker should be
// re-generated every time an endpoint state is updated. // re-generated every time an endpoint state is updated.
func (b *ringhashBalancer) newPickerLocked() *picker { func (b *ringhashBalancer) newPickerLocked() *picker {
states := make(map[string]balancer.State) states := make(map[string]endpointState)
for _, val := range b.endpointStates.Values() { hasEndpointConnecting := false
epState := val.(*endpointState) for _, epState := range b.endpointStates.Values() {
states[epState.firstAddr] = epState.state // Copy the endpoint state to avoid races, since ring hash
// mutates the state, weight and hash key in place.
states[epState.hashKey] = *epState
if epState.state.ConnectivityState == connectivity.Connecting {
hasEndpointConnecting = true
}
}
return &picker{
ring: b.ring,
endpointStates: states,
requestHashHeader: b.config.RequestHashHeader,
hasEndpointInConnectingState: hasEndpointConnecting,
randUint64: rand.Uint64,
} }
return &picker{ring: b.ring, logger: b.logger, endpointStates: states}
} }
// aggregatedStateLocked returns the aggregated child balancers state // aggregatedStateLocked returns the aggregated child balancers state
@ -324,8 +356,7 @@ func (b *ringhashBalancer) newPickerLocked() *picker {
// failure to failover to the lower priority. // failure to failover to the lower priority.
func (b *ringhashBalancer) aggregatedStateLocked() connectivity.State { func (b *ringhashBalancer) aggregatedStateLocked() connectivity.State {
var nums [5]int var nums [5]int
for _, val := range b.endpointStates.Values() { for _, es := range b.endpointStates.Values() {
es := val.(*endpointState)
nums[es.state.ConnectivityState]++ nums[es.state.ConnectivityState]++
} }
@ -348,14 +379,13 @@ func (b *ringhashBalancer) aggregatedStateLocked() connectivity.State {
} }
// getWeightAttribute is a convenience function which returns the value of the // getWeightAttribute is a convenience function which returns the value of the
// weight attribute stored in the BalancerAttributes field of addr, using the // weight endpoint Attribute.
// weightedroundrobin package.
// //
// When used in the xDS context, the weight attribute is guaranteed to be // When used in the xDS context, the weight attribute is guaranteed to be
// non-zero. But, when used in a non-xDS context, the weight attribute could be // non-zero. But, when used in a non-xDS context, the weight attribute could be
// unset. A Default of 1 is used in the latter case. // unset. A Default of 1 is used in the latter case.
func getWeightAttribute(e resolver.Endpoint) uint32 { func getWeightAttribute(e resolver.Endpoint) uint32 {
w := weightedroundrobin.AddrInfoFromEndpoint(e).Weight w := weight.FromEndpoint(e).Weight
if w == 0 { if w == 0 {
return 1 return 1
} }
@ -363,12 +393,13 @@ func getWeightAttribute(e resolver.Endpoint) uint32 {
} }
type endpointState struct { type endpointState struct {
// firstAddr is the first address in the endpoint. Per gRFC A61, each entry // hashKey is the hash key of the endpoint. Per gRFC A61, each entry in the
// in the ring is an endpoint, positioned based on the hash of the // ring is an endpoint, positioned based on the hash of the endpoint's first
// endpoint's first address. // address by default. Per gRFC A76, the hash key of an endpoint may be
firstAddr string // overridden, for example based on EDS endpoint metadata.
weight uint32 hashKey string
balancer balancer.ExitIdler weight uint32
balancer endpointsharding.ExitIdler
// state is updated by the balancer while receiving resolver updates from // state is updated by the balancer while receiving resolver updates from
// the channel and picker updates from its children. Access to it is guarded // the channel and picker updates from its children. Access to it is guarded

View File

@ -26,6 +26,8 @@ import (
rand "math/rand/v2" rand "math/rand/v2"
"net" "net"
"slices" "slices"
"strconv"
"sync"
"testing" "testing"
"time" "time"
@ -40,6 +42,7 @@ import (
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpctest" "google.golang.org/grpc/internal/grpctest"
iringhash "google.golang.org/grpc/internal/ringhash"
"google.golang.org/grpc/internal/stubserver" "google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/internal/testutils" "google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/xds/e2e" "google.golang.org/grpc/internal/testutils/xds/e2e"
@ -78,6 +81,11 @@ const (
errorTolerance = .05 // For tests that rely on statistical significance. errorTolerance = .05 // For tests that rely on statistical significance.
virtualHostName = "test.server" virtualHostName = "test.server"
// minRingSize is the minimum ring size to use when testing randomly a
// backend for each request. It lowers the skew that may occur from
// an imbalanced ring.
minRingSize = 10000
) )
// fastConnectParams disables connection attempts backoffs and lowers delays. // fastConnectParams disables connection attempts backoffs and lowers delays.
@ -123,9 +131,10 @@ func (s) TestRingHash_ReconnectToMoveOutOfTransientFailure(t *testing.T) {
defer cc.Close() defer cc.Close()
// Push the address of the test backend through the manual resolver. // Push the address of the test backend through the manual resolver.
r.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: lis.Addr().String()}}}) r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: lis.Addr().String()}}})
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
ctx = iringhash.SetXDSRequestHash(ctx, 0)
defer cancel() defer cancel()
client := testgrpc.NewTestServiceClient(cc) client := testgrpc.NewTestServiceClient(cc)
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
@ -469,7 +478,7 @@ func (s) TestRingHash_AggregateClusterFallBackFromRingHashToLogicalDnsAtStartup(
} }
dnsR := replaceDNSResolver(t) dnsR := replaceDNSResolver(t)
dnsR.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: backends[0]}}}) dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: backends[0]}}})
if err := xdsServer.Update(ctx, updateOpts); err != nil { if err := xdsServer.Update(ctx, updateOpts); err != nil {
t.Fatalf("Failed to update xDS resources: %v", err) t.Fatalf("Failed to update xDS resources: %v", err)
@ -547,7 +556,7 @@ func (s) TestRingHash_AggregateClusterFallBackFromRingHashToLogicalDnsAtStartupN
} }
dnsR := replaceDNSResolver(t) dnsR := replaceDNSResolver(t)
dnsR.InitialState(resolver.State{Addresses: []resolver.Address{{Addr: backends[0]}}}) dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: backends[0]}}})
if err := xdsServer.Update(ctx, updateOpts); err != nil { if err := xdsServer.Update(ctx, updateOpts); err != nil {
t.Fatalf("Failed to update xDS resources: %v", err) t.Fatalf("Failed to update xDS resources: %v", err)
@ -841,12 +850,8 @@ func computeIdealNumberOfRPCs(t *testing.T, p, errorTolerance float64) int {
// minimum ring size to ensure that the ring is large enough to distribute // minimum ring size to ensure that the ring is large enough to distribute
// requests more uniformly across endpoints when a random hash is used. // requests more uniformly across endpoints when a random hash is used.
func setRingHashLBPolicyWithHighMinRingSize(t *testing.T, cluster *v3clusterpb.Cluster) { func setRingHashLBPolicyWithHighMinRingSize(t *testing.T, cluster *v3clusterpb.Cluster) {
const minRingSize = 100000 testutils.SetEnvConfig(t, &envconfig.RingHashCap, minRingSize)
oldVal := envconfig.RingHashCap
envconfig.RingHashCap = minRingSize
t.Cleanup(func() {
envconfig.RingHashCap = oldVal
})
// Increasing min ring size for random distribution. // Increasing min ring size for random distribution.
config := testutils.MarshalAny(t, &v3ringhashpb.RingHash{ config := testutils.MarshalAny(t, &v3ringhashpb.RingHash{
HashFunction: v3ringhashpb.RingHash_XX_HASH, HashFunction: v3ringhashpb.RingHash_XX_HASH,
@ -1420,7 +1425,7 @@ func (s) TestRingHash_ContinuesConnectingWithoutPicks(t *testing.T) {
backend := stubserver.StartTestService(t, &stubserver.StubServer{ backend := stubserver.StartTestService(t, &stubserver.StubServer{
// We expect the server EmptyCall to not be call here because the // We expect the server EmptyCall to not be call here because the
// aggregated channel state is never READY when the call is pending. // aggregated channel state is never READY when the call is pending.
EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) { EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
t.Errorf("EmptyCall() should not have been called") t.Errorf("EmptyCall() should not have been called")
return &testpb.Empty{}, nil return &testpb.Empty{}, nil
}, },
@ -1595,7 +1600,7 @@ func (s) TestRingHash_ReattemptWhenGoingFromTransientFailureToIdle(t *testing.T)
// Tests that when all backends are down and then up, we may pick a TF backend // Tests that when all backends are down and then up, we may pick a TF backend
// and we will then jump to ready backend. // and we will then jump to ready backend.
func (s) TestRingHash_TransientFailureSkipToAvailableReady(t *testing.T) { func (s) TestRingHash_TransientFailureSkipToAvailableReady(t *testing.T) {
emptyCallF := func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { emptyCallF := func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
return &testpb.Empty{}, nil return &testpb.Empty{}, nil
} }
lis, err := testutils.LocalTCPListener() lis, err := testutils.LocalTCPListener()
@ -1717,7 +1722,7 @@ func (s) TestRingHash_ReattemptWhenAllEndpointsUnreachable(t *testing.T) {
restartableListener := testutils.NewRestartableListener(lis) restartableListener := testutils.NewRestartableListener(lis)
restartableServer := stubserver.StartTestService(t, &stubserver.StubServer{ restartableServer := stubserver.StartTestService(t, &stubserver.StubServer{
Listener: restartableListener, Listener: restartableListener,
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
return &testpb.Empty{}, nil return &testpb.Empty{}, nil
}, },
}) })
@ -1783,7 +1788,7 @@ func (s) TestRingHash_SwitchToLowerPriorityAndThenBack(t *testing.T) {
restartableListener := testutils.NewRestartableListener(lis) restartableListener := testutils.NewRestartableListener(lis)
restartableServer := stubserver.StartTestService(t, &stubserver.StubServer{ restartableServer := stubserver.StartTestService(t, &stubserver.StubServer{
Listener: restartableListener, Listener: restartableListener,
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
return &testpb.Empty{}, nil return &testpb.Empty{}, nil
}, },
}) })
@ -2542,3 +2547,391 @@ func (s) TestRingHash_RecoverWhenResolverRemovesEndpoint(t *testing.T) {
// Wait for channel to become READY without any pending RPC. // Wait for channel to become READY without any pending RPC.
testutils.AwaitState(ctx, t, conn, connectivity.Ready) testutils.AwaitState(ctx, t, conn, connectivity.Ready)
} }
// Tests that RPCs are routed according to endpoint hash key rather than
// endpoint first address if it is set in EDS endpoint metadata.
func (s) TestRingHash_EndpointHashKey(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.XDSEndpointHashKeyBackwardCompat, false)
backends := backendAddrs(startTestServiceBackends(t, 4))
const clusterName = "cluster"
var backendOpts []e2e.BackendOptions
for i, addr := range backends {
var ports []uint32
ports = append(ports, testutils.ParsePort(t, addr))
backendOpts = append(backendOpts, e2e.BackendOptions{
Ports: ports,
Metadata: map[string]any{"hash_key": strconv.Itoa(i)},
})
}
endpoints := e2e.EndpointResourceWithOptions(e2e.EndpointOptions{
ClusterName: clusterName,
Host: "localhost",
Localities: []e2e.LocalityOptions{{
Backends: backendOpts,
Weight: 1,
}},
})
cluster := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
ClusterName: clusterName,
ServiceName: clusterName,
Policy: e2e.LoadBalancingPolicyRingHash,
})
route := headerHashRoute("new_route", virtualHostName, clusterName, "address_hash")
listener := e2e.DefaultClientListener(virtualHostName, route.Name)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
xdsServer, nodeID, xdsResolver := setupManagementServerAndResolver(t)
if err := xdsServer.Update(ctx, xdsUpdateOpts(nodeID, endpoints, cluster, route, listener)); err != nil {
t.Fatalf("Failed to update xDS resources: %v", err)
}
opts := []grpc.DialOption{
grpc.WithResolvers(xdsResolver),
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
conn, err := grpc.NewClient("xds:///test.server", opts...)
if err != nil {
t.Fatalf("Failed to create client: %s", err)
}
defer conn.Close()
client := testgrpc.NewTestServiceClient(conn)
// Make sure RPCs are routed to backends according to the endpoint metadata
// rather than their address. Note each type of RPC contains a header value
// that will always be hashed to a specific backend as the header value
// matches the endpoint metadata hash key.
for i, backend := range backends {
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs("address_hash", strconv.Itoa(i)+"_0"))
numRPCs := 10
reqPerBackend := checkRPCSendOK(ctx, t, client, numRPCs)
if reqPerBackend[backend] != numRPCs {
t.Errorf("Got RPC routed to addresses %v, want all RPCs routed to %v", reqPerBackend, backend)
}
}
// Update the endpoints to swap the metadata hash key.
for i := range backendOpts {
backendOpts[i].Metadata = map[string]any{"hash_key": strconv.Itoa(len(backends) - i - 1)}
}
endpoints = e2e.EndpointResourceWithOptions(e2e.EndpointOptions{
ClusterName: clusterName,
Host: "localhost",
Localities: []e2e.LocalityOptions{{
Backends: backendOpts,
Weight: 1,
}},
})
if err := xdsServer.Update(ctx, xdsUpdateOpts(nodeID, endpoints, cluster, route, listener)); err != nil {
t.Fatalf("Failed to update xDS resources: %v", err)
}
// Wait for the resolver update to make it to the balancer. This RPC should
// be routed to backend 3 with the reverse numbering of the hash_key
// attribute delivered above.
for {
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs("address_hash", "0_0"))
var remote peer.Peer
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(&remote)); err != nil {
t.Fatalf("Unexpected RPC error waiting for EDS update propagation: %s", err)
}
if remote.Addr.String() == backends[3] {
break
}
}
// Now that the balancer has the new endpoint attributes, make sure RPCs are
// routed to backends according to the new endpoint metadata.
for i, backend := range backends {
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs("address_hash", strconv.Itoa(len(backends)-i-1)+"_0"))
numRPCs := 10
reqPerBackend := checkRPCSendOK(ctx, t, client, numRPCs)
if reqPerBackend[backend] != numRPCs {
t.Errorf("Got RPC routed to addresses %v, want all RPCs routed to %v", reqPerBackend, backend)
}
}
}
// Tests that when a request hash key is set in the balancer configuration via
// service config, this header is used to route to a specific backend.
func (s) TestRingHash_RequestHashKey(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.RingHashSetRequestHashKey, true)
backends := backendAddrs(startTestServiceBackends(t, 4))
// Create a clientConn with a manual resolver (which is used to push the
// address of the test backend), and a default service config pointing to
// the use of the ring_hash_experimental LB policy with an explicit hash
// header.
const ringHashServiceConfig = `{"loadBalancingConfig": [{"ring_hash_experimental":{"requestHashHeader":"address_hash"}}]}`
r := manual.NewBuilderWithScheme("whatever")
dopts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithResolvers(r),
grpc.WithDefaultServiceConfig(ringHashServiceConfig),
grpc.WithConnectParams(fastConnectParams),
}
cc, err := grpc.NewClient(r.Scheme()+":///test.server", dopts...)
if err != nil {
t.Fatalf("Failed to dial local test server: %v", err)
}
defer cc.Close()
var endpoints []resolver.Endpoint
for _, backend := range backends {
endpoints = append(endpoints, resolver.Endpoint{
Addresses: []resolver.Address{{Addr: backend}},
})
}
r.UpdateState(resolver.State{
Endpoints: endpoints,
})
client := testgrpc.NewTestServiceClient(cc)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
// Note each type of RPC contains a header value that will always be hashed
// to a specific backend as the header value matches the value used to
// create the entry in the ring.
for _, backend := range backends {
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs("address_hash", backend+"_0"))
numRPCs := 10
reqPerBackend := checkRPCSendOK(ctx, t, client, numRPCs)
if reqPerBackend[backend] != numRPCs {
t.Errorf("Got RPC routed to addresses %v, want all RPCs routed to %v", reqPerBackend, backend)
}
}
const ringHashServiceConfigUpdate = `{"loadBalancingConfig": [{"ring_hash_experimental":{"requestHashHeader":"other_header"}}]}`
r.UpdateState(resolver.State{
Endpoints: endpoints,
ServiceConfig: (&testutils.ResolverClientConn{}).ParseServiceConfig(ringHashServiceConfigUpdate),
})
// Make sure that requests with the new hash are sent to the right backend.
for _, backend := range backends {
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs("other_header", backend+"_0"))
numRPCs := 10
reqPerBackend := checkRPCSendOK(ctx, t, client, numRPCs)
if reqPerBackend[backend] != numRPCs {
t.Errorf("Got RPC routed to addresses %v, want all RPCs routed to %v", reqPerBackend, backend)
}
}
}
func highRingSizeServiceConfig(t *testing.T) string {
t.Helper()
testutils.SetEnvConfig(t, &envconfig.RingHashCap, minRingSize)
return fmt.Sprintf(`{
"loadBalancingConfig": [{"ring_hash_experimental":{
"requestHashHeader": "address_hash",
"minRingSize": %d,
"maxRingSize": %d
}
}]}`, minRingSize, minRingSize)
}
// Tests that when a request hash key is set in the balancer configuration via
// service config, and the header is not set in the outgoing request, then it
// is sent to a random backend.
func (s) TestRingHash_RequestHashKeyRandom(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.RingHashSetRequestHashKey, true)
backends := backendAddrs(startTestServiceBackends(t, 4))
// Create a clientConn with a manual resolver (which is used to push the
// address of the test backend), and a default service config pointing to
// the use of the ring_hash_experimental LB policy with an explicit hash
// header.
ringHashServiceConfig := highRingSizeServiceConfig(t)
r := manual.NewBuilderWithScheme("whatever")
dopts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithResolvers(r),
grpc.WithDefaultServiceConfig(ringHashServiceConfig),
grpc.WithConnectParams(fastConnectParams),
}
cc, err := grpc.NewClient(r.Scheme()+":///test.server", dopts...)
if err != nil {
t.Fatalf("Failed to dial local test server: %v", err)
}
defer cc.Close()
var endpoints []resolver.Endpoint
for _, backend := range backends {
endpoints = append(endpoints, resolver.Endpoint{
Addresses: []resolver.Address{{Addr: backend}},
})
}
r.UpdateState(resolver.State{
Endpoints: endpoints,
})
client := testgrpc.NewTestServiceClient(cc)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
// Due to the way that ring hash lazily establishes connections when using a
// random hash, request distribution is skewed towards the order in which we
// connected. The test send RPCs until we are connected to all backends, so
// we can later assert that the distribution is uniform.
seen := make(map[string]bool)
for len(seen) != 4 {
var remote peer.Peer
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(&remote)); err != nil {
t.Fatalf("rpc EmptyCall() failed: %v", err)
}
seen[remote.String()] = true
}
// Make sure that requests with the old hash are sent to random backends.
const want = 1.0 / 4
numRPCs := computeIdealNumberOfRPCs(t, want, errorTolerance)
gotPerBackend := checkRPCSendOK(ctx, t, client, numRPCs)
for _, backend := range backends {
got := float64(gotPerBackend[backend]) / float64(numRPCs)
if !cmp.Equal(got, want, cmpopts.EquateApprox(0, errorTolerance)) {
t.Errorf("Fraction of RPCs to backend %s: got %v, want %v (margin: +-%v)", backend, got, want, errorTolerance)
}
}
}
// Tests that when a request hash key is set in the balancer configuration via
// service config, and the header is not set in the outgoing request (random
// behavior), then each RPC wakes up at most one SubChannel, and, if there are
// SubChannels in Ready state, RPCs are routed to them.
func (s) TestRingHash_RequestHashKeyConnecting(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.RingHashSetRequestHashKey, true)
backends := backendAddrs(startTestServiceBackends(t, 20))
// Create a clientConn with a manual resolver (which is used to push the
// address of the test backend), and a default service config pointing to
// the use of the ring_hash_experimental LB policy with an explicit hash
// header. Use a blocking dialer to control connection attempts.
const ringHashServiceConfig = `{"loadBalancingConfig": [
{"ring_hash_experimental":{"requestHashHeader":"address_hash"}}
]}`
r := manual.NewBuilderWithScheme("whatever")
blockingDialer := testutils.NewBlockingDialer()
dopts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithResolvers(r),
grpc.WithDefaultServiceConfig(ringHashServiceConfig),
grpc.WithConnectParams(fastConnectParams),
grpc.WithContextDialer(blockingDialer.DialContext),
}
cc, err := grpc.NewClient(r.Scheme()+":///test.server", dopts...)
if err != nil {
t.Fatalf("Failed to dial local test server: %v", err)
}
defer cc.Close()
var endpoints []resolver.Endpoint
for _, backend := range backends {
endpoints = append(endpoints, resolver.Endpoint{
Addresses: []resolver.Address{{Addr: backend}},
})
}
r.UpdateState(resolver.State{
Endpoints: endpoints,
})
client := testgrpc.NewTestServiceClient(cc)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
// Intercept all connection attempts to the backends.
var holds []*testutils.Hold
for i := 0; i < len(backends); i++ {
holds = append(holds, blockingDialer.Hold(backends[i]))
}
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
// Send 1 RPC and make sure this triggers at most 1 connection attempt.
_, err := client.EmptyCall(ctx, &testpb.Empty{})
if err != nil {
t.Errorf("EmptyCall(): got %v, want success", err)
}
wg.Done()
}()
// Wait for at least one connection attempt.
nConn := 0
for nConn == 0 {
if ctx.Err() != nil {
t.Fatal("Test timed out waiting for a connection attempt")
}
time.Sleep(1 * time.Millisecond)
for _, hold := range holds {
if hold.IsStarted() {
nConn++
}
}
}
if wantMaxConn := 1; nConn > wantMaxConn {
t.Fatalf("Got %d connection attempts, want at most %d", nConn, wantMaxConn)
}
// Do a second RPC. Since there should already be a SubChannel in
// Connecting state, this should not trigger a connection attempt.
wg.Add(1)
go func() {
_, err := client.EmptyCall(ctx, &testpb.Empty{})
if err != nil {
t.Errorf("EmptyCall(): got %v, want success", err)
}
wg.Done()
}()
// Give extra time for more connections to be attempted.
time.Sleep(defaultTestShortTimeout)
var firstConnectedBackend string
nConn = 0
for i, hold := range holds {
if hold.IsStarted() {
// Unblock the connection attempt. The SubChannel (and hence the
// channel) should transition to Ready. RPCs should succeed and
// be routed to this backend.
hold.Resume()
holds[i] = nil
firstConnectedBackend = backends[i]
nConn++
}
}
if wantMaxConn := 1; nConn > wantMaxConn {
t.Fatalf("Got %d connection attempts, want at most %d", nConn, wantMaxConn)
}
testutils.AwaitState(ctx, t, cc, connectivity.Ready)
wg.Wait() // Make sure we're done with the 2 previous RPCs.
// Now send RPCs until we have at least one more connection attempt, that
// is, the random hash did not land on the same backend on every pick (the
// chances are low, but we don't want this to be flaky). Make sure no RPC
// fails and that we route all of them to the only subchannel in ready
// state.
nConn = 0
for nConn == 0 {
p := peer.Peer{}
_, err = client.EmptyCall(ctx, &testpb.Empty{}, grpc.Peer(&p))
if status.Code(err) == codes.DeadlineExceeded {
t.Fatal("EmptyCall(): test timed out while waiting for more connection attempts")
}
if err != nil {
t.Fatalf("EmptyCall(): got %v, want success", err)
}
if p.Addr.String() != firstConnectedBackend {
t.Errorf("RPC sent to backend %q, want %q", p.Addr.String(), firstConnectedBackend)
}
for _, hold := range holds {
if hold != nil && hold.IsStarted() {
nConn++
}
}
}
}

View File

@ -25,12 +25,12 @@ import (
"time" "time"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/weightedroundrobin"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/internal/balancer/weight"
"google.golang.org/grpc/internal/grpctest" "google.golang.org/grpc/internal/grpctest"
iringhash "google.golang.org/grpc/internal/ringhash"
"google.golang.org/grpc/internal/testutils" "google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/xds/internal"
) )
const ( const (
@ -42,7 +42,7 @@ const (
var ( var (
testBackendAddrStrs []string testBackendAddrStrs []string
testConfig = &LBConfig{MinRingSize: 1, MaxRingSize: 10} testConfig = &iringhash.LBConfig{MinRingSize: 1, MaxRingSize: 10}
) )
func init() { func init() {
@ -83,7 +83,7 @@ func setupTest(t *testing.T, endpoints []resolver.Endpoint) (*testutils.Balancer
t.Errorf("Number of child balancers = %d, want = %d", got, want) t.Errorf("Number of child balancers = %d, want = %d", got, want)
} }
for firstAddr, bs := range ringHashPicker.endpointStates { for firstAddr, bs := range ringHashPicker.endpointStates {
if got, want := bs.ConnectivityState, connectivity.Idle; got != want { if got, want := bs.state.ConnectivityState, connectivity.Idle; got != want {
t.Errorf("Child balancer connectivity state for address %q = %v, want = %v", firstAddr, got, want) t.Errorf("Child balancer connectivity state for address %q = %v, want = %v", firstAddr, got, want)
} }
} }
@ -114,8 +114,11 @@ func (s) TestUpdateClientConnState_NewRingSize(t *testing.T) {
} }
if err := b.UpdateClientConnState(balancer.ClientConnState{ if err := b.UpdateClientConnState(balancer.ClientConnState{
ResolverState: resolver.State{Endpoints: endpoints}, ResolverState: resolver.State{Endpoints: endpoints},
BalancerConfig: &LBConfig{MinRingSize: uint64(newMinRingSize), MaxRingSize: uint64(newMaxRingSize)}, BalancerConfig: &iringhash.LBConfig{
MinRingSize: uint64(newMinRingSize),
MaxRingSize: uint64(newMaxRingSize),
},
}); err != nil { }); err != nil {
t.Fatalf("UpdateClientConnState returned err: %v", err) t.Fatalf("UpdateClientConnState returned err: %v", err)
} }
@ -144,7 +147,7 @@ func (s) TestOneEndpoint(t *testing.T) {
// only Endpoint which has a single address. // only Endpoint which has a single address.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel() defer cancel()
if _, err := p0.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}); err != balancer.ErrNoSubConnAvailable { if _, err := p0.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)}); err != balancer.ErrNoSubConnAvailable {
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable) t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
} }
var sc0 *testutils.TestSubConn var sc0 *testutils.TestSubConn
@ -172,7 +175,7 @@ func (s) TestOneEndpoint(t *testing.T) {
// Test pick with one backend. // Test pick with one backend.
p1 := <-cc.NewPickerCh p1 := <-cc.NewPickerCh
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
gotSCSt, _ := p1.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}) gotSCSt, _ := p1.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)})
if gotSCSt.SubConn != sc0 { if gotSCSt.SubConn != sc0 {
t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0) t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0)
} }
@ -205,7 +208,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
// SubConn. // SubConn.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel() defer cancel()
if _, err := p0.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}); err != balancer.ErrNoSubConnAvailable { if _, err := p0.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)}); err != balancer.ErrNoSubConnAvailable {
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable) t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
} }
@ -216,7 +219,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
t.Fatalf("Timed out waiting for SubConn creation.") t.Fatalf("Timed out waiting for SubConn creation.")
case subConns[1] = <-cc.NewSubConnCh: case subConns[1] = <-cc.NewSubConnCh:
} }
if got, want := subConns[1].Addresses[0].Addr, ring.items[1].firstAddr; got != want { if got, want := subConns[1].Addresses[0].Addr, ring.items[1].hashKey; got != want {
t.Fatalf("SubConn.Address = %v, want = %v", got, want) t.Fatalf("SubConn.Address = %v, want = %v", got, want)
} }
select { select {
@ -224,7 +227,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
case <-time.After(defaultTestTimeout): case <-time.After(defaultTestTimeout):
t.Errorf("timeout waiting for Connect() from SubConn %v", subConns[1]) t.Errorf("timeout waiting for Connect() from SubConn %v", subConns[1])
} }
delete(remainingAddrs, ring.items[1].firstAddr) delete(remainingAddrs, ring.items[1].hashKey)
// Turn down the subConn in use. // Turn down the subConn in use.
subConns[1].UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) subConns[1].UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
@ -248,9 +251,9 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
case <-time.After(defaultTestTimeout): case <-time.After(defaultTestTimeout):
t.Errorf("timeout waiting for Connect() from SubConn %v", subConns[1]) t.Errorf("timeout waiting for Connect() from SubConn %v", subConns[1])
} }
if scAddr == ring.items[0].firstAddr { if scAddr == ring.items[0].hashKey {
subConns[0] = sc subConns[0] = sc
} else if scAddr == ring.items[2].firstAddr { } else if scAddr == ring.items[2].hashKey {
subConns[2] = sc subConns[2] = sc
} }
@ -273,9 +276,9 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
case <-time.After(defaultTestTimeout): case <-time.After(defaultTestTimeout):
t.Errorf("timeout waiting for Connect() from SubConn %v", subConns[1]) t.Errorf("timeout waiting for Connect() from SubConn %v", subConns[1])
} }
if scAddr == ring.items[0].firstAddr { if scAddr == ring.items[0].hashKey {
subConns[0] = sc subConns[0] = sc
} else if scAddr == ring.items[2].firstAddr { } else if scAddr == ring.items[2].hashKey {
subConns[2] = sc subConns[2] = sc
} }
sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) sc.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
@ -292,7 +295,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
} }
p1 := <-cc.NewPickerCh p1 := <-cc.NewPickerCh
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
gotSCSt, _ := p1.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}) gotSCSt, _ := p1.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)})
if gotSCSt.SubConn != subConns[0] { if gotSCSt.SubConn != subConns[0] {
t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, subConns[0]) t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, subConns[0])
} }
@ -305,7 +308,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
subConns[2].UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) subConns[2].UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
p2 := <-cc.NewPickerCh p2 := <-cc.NewPickerCh
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
gotSCSt, _ := p2.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}) gotSCSt, _ := p2.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)})
if gotSCSt.SubConn != subConns[2] { if gotSCSt.SubConn != subConns[2] {
t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, subConns[2]) t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, subConns[2])
} }
@ -318,7 +321,7 @@ func (s) TestThreeSubConnsAffinity(t *testing.T) {
subConns[1].UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) subConns[1].UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
p3 := <-cc.NewPickerCh p3 := <-cc.NewPickerCh
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
gotSCSt, _ := p3.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}) gotSCSt, _ := p3.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)})
if gotSCSt.SubConn != subConns[1] { if gotSCSt.SubConn != subConns[1] {
t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, subConns[1]) t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, subConns[1])
} }
@ -346,7 +349,7 @@ func (s) TestThreeBackendsAffinityMultiple(t *testing.T) {
// SubConn. // SubConn.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel() defer cancel()
if _, err := p0.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}); err != balancer.ErrNoSubConnAvailable { if _, err := p0.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)}); err != balancer.ErrNoSubConnAvailable {
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable) t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
} }
// The picked SubConn should be the second in the ring. // The picked SubConn should be the second in the ring.
@ -356,7 +359,7 @@ func (s) TestThreeBackendsAffinityMultiple(t *testing.T) {
t.Fatalf("Timed out waiting for SubConn creation.") t.Fatalf("Timed out waiting for SubConn creation.")
case sc0 = <-cc.NewSubConnCh: case sc0 = <-cc.NewSubConnCh:
} }
if got, want := sc0.Addresses[0].Addr, ring0.items[1].firstAddr; got != want { if got, want := sc0.Addresses[0].Addr, ring0.items[1].hashKey; got != want {
t.Fatalf("SubConn.Address = %v, want = %v", got, want) t.Fatalf("SubConn.Address = %v, want = %v", got, want)
} }
select { select {
@ -375,7 +378,7 @@ func (s) TestThreeBackendsAffinityMultiple(t *testing.T) {
// First hash should always pick sc0. // First hash should always pick sc0.
p1 := <-cc.NewPickerCh p1 := <-cc.NewPickerCh
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
gotSCSt, _ := p1.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}) gotSCSt, _ := p1.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)})
if gotSCSt.SubConn != sc0 { if gotSCSt.SubConn != sc0 {
t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0) t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0)
} }
@ -384,7 +387,7 @@ func (s) TestThreeBackendsAffinityMultiple(t *testing.T) {
secondHash := ring0.items[1].hash secondHash := ring0.items[1].hash
// secondHash+1 will pick the third SubConn from the ring. // secondHash+1 will pick the third SubConn from the ring.
testHash2 := secondHash + 1 testHash2 := secondHash + 1
if _, err := p0.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash2)}); err != balancer.ErrNoSubConnAvailable { if _, err := p0.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash2)}); err != balancer.ErrNoSubConnAvailable {
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable) t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
} }
var sc1 *testutils.TestSubConn var sc1 *testutils.TestSubConn
@ -393,7 +396,7 @@ func (s) TestThreeBackendsAffinityMultiple(t *testing.T) {
t.Fatalf("Timed out waiting for SubConn creation.") t.Fatalf("Timed out waiting for SubConn creation.")
case sc1 = <-cc.NewSubConnCh: case sc1 = <-cc.NewSubConnCh:
} }
if got, want := sc1.Addresses[0].Addr, ring0.items[2].firstAddr; got != want { if got, want := sc1.Addresses[0].Addr, ring0.items[2].hashKey; got != want {
t.Fatalf("SubConn.Address = %v, want = %v", got, want) t.Fatalf("SubConn.Address = %v, want = %v", got, want)
} }
select { select {
@ -407,14 +410,14 @@ func (s) TestThreeBackendsAffinityMultiple(t *testing.T) {
// With the new generated picker, hash2 always picks sc1. // With the new generated picker, hash2 always picks sc1.
p2 := <-cc.NewPickerCh p2 := <-cc.NewPickerCh
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
gotSCSt, _ := p2.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash2)}) gotSCSt, _ := p2.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash2)})
if gotSCSt.SubConn != sc1 { if gotSCSt.SubConn != sc1 {
t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc1) t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc1)
} }
} }
// But the first hash still picks sc0. // But the first hash still picks sc0.
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
gotSCSt, _ := p2.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, testHash)}) gotSCSt, _ := p2.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, testHash)})
if gotSCSt.SubConn != sc0 { if gotSCSt.SubConn != sc0 {
t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0) t.Fatalf("picker.Pick, got %v, want SubConn=%v", gotSCSt, sc0)
} }
@ -479,9 +482,7 @@ func (s) TestAddrWeightChange(t *testing.T) {
if err := b.UpdateClientConnState(balancer.ClientConnState{ if err := b.UpdateClientConnState(balancer.ClientConnState{
ResolverState: resolver.State{Endpoints: []resolver.Endpoint{ ResolverState: resolver.State{Endpoints: []resolver.Endpoint{
endpoints[0], endpoints[0],
weightedroundrobin.SetAddrInfoInEndpoint( weight.Set(endpoints[1], weight.EndpointInfo{Weight: 2}),
endpoints[1],
weightedroundrobin.AddrInfo{Weight: 2}),
}}, }},
BalancerConfig: testConfig, BalancerConfig: testConfig,
}); err != nil { }); err != nil {
@ -506,14 +507,14 @@ func (s) TestAddrWeightChange(t *testing.T) {
t.Fatalf("new picker after changing address weight has %d entries, want 3", len(p3.(*picker).ring.items)) t.Fatalf("new picker after changing address weight has %d entries, want 3", len(p3.(*picker).ring.items))
} }
for _, i := range p3.(*picker).ring.items { for _, i := range p3.(*picker).ring.items {
if i.firstAddr == testBackendAddrStrs[0] { if i.hashKey == testBackendAddrStrs[0] {
if i.weight != 1 { if i.weight != 1 {
t.Fatalf("new picker after changing address weight has weight %d for %v, want 1", i.weight, i.firstAddr) t.Fatalf("new picker after changing address weight has weight %d for %v, want 1", i.weight, i.hashKey)
} }
} }
if i.firstAddr == testBackendAddrStrs[1] { if i.hashKey == testBackendAddrStrs[1] {
if i.weight != 2 { if i.weight != 2 {
t.Fatalf("new picker after changing address weight has weight %d for %v, want 2", i.weight, i.firstAddr) t.Fatalf("new picker after changing address weight has weight %d for %v, want 2", i.weight, i.hashKey)
} }
} }
} }
@ -534,6 +535,7 @@ func (s) TestAutoConnectEndpointOnTransientFailure(t *testing.T) {
// ringhash won't tell SCs to connect until there is an RPC, so simulate // ringhash won't tell SCs to connect until there is an RPC, so simulate
// one now. // one now.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
ctx = iringhash.SetXDSRequestHash(ctx, 0)
defer cancel() defer cancel()
p0.Pick(balancer.PickInfo{Ctx: ctx}) p0.Pick(balancer.PickInfo{Ctx: ctx})
@ -658,7 +660,7 @@ func (s) TestAggregatedConnectivityState(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
bal := &ringhashBalancer{endpointStates: resolver.NewEndpointMap()} bal := &ringhashBalancer{endpointStates: resolver.NewEndpointMap[*endpointState]()}
for i, cs := range tt.endpointStates { for i, cs := range tt.endpointStates {
es := &endpointState{ es := &endpointState{
state: balancer.State{ConnectivityState: cs}, state: balancer.State{ConnectivityState: cs},
@ -673,16 +675,34 @@ func (s) TestAggregatedConnectivityState(t *testing.T) {
} }
} }
type testKeyType string
const testKey testKeyType = "grpc.lb.ringhash.testKey"
type testAttribute struct {
content string
}
func setTestAttrAddr(addr resolver.Address, content string) resolver.Address {
addr.BalancerAttributes = addr.BalancerAttributes.WithValue(testKey, testAttribute{content})
return addr
}
func setTestAttrEndpoint(endpoint resolver.Endpoint, content string) resolver.Endpoint {
endpoint.Attributes = endpoint.Attributes.WithValue(testKey, testAttribute{content})
return endpoint
}
// TestAddrBalancerAttributesChange tests the case where the ringhash balancer // TestAddrBalancerAttributesChange tests the case where the ringhash balancer
// receives a ClientConnUpdate with the same config and addresses as received in // receives a ClientConnUpdate with the same config and addresses as received in
// the previous update. Although the `BalancerAttributes` and endpoint // the previous update. Although the `BalancerAttributes` and endpoint
// attributes contents are the same, the pointers are different. This test // attributes contents are the same, the pointers are different. This test
// verifies that subConns are not recreated in this scenario. // verifies that subConns are not recreated in this scenario.
func (s) TestAddrBalancerAttributesChange(t *testing.T) { func (s) TestAddrBalancerAttributesChange(t *testing.T) {
locality := internal.LocalityID{Region: "americas"} content := "test"
addrs1 := []resolver.Address{internal.SetLocalityID(resolver.Address{Addr: testBackendAddrStrs[0]}, locality)} addrs1 := []resolver.Address{setTestAttrAddr(resolver.Address{Addr: testBackendAddrStrs[0]}, content)}
wantEndpoints1 := []resolver.Endpoint{ wantEndpoints1 := []resolver.Endpoint{
internal.SetLocalityIDInEndpoint(resolver.Endpoint{Addresses: addrs1}, locality), setTestAttrEndpoint(resolver.Endpoint{Addresses: addrs1}, "content"),
} }
cc, b, p0 := setupTest(t, wantEndpoints1) cc, b, p0 := setupTest(t, wantEndpoints1)
ring0 := p0.(*picker).ring ring0 := p0.(*picker).ring
@ -692,7 +712,7 @@ func (s) TestAddrBalancerAttributesChange(t *testing.T) {
// only Endpoint which has a single address. // only Endpoint which has a single address.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel() defer cancel()
if _, err := p0.Pick(balancer.PickInfo{Ctx: SetRequestHash(ctx, firstHash)}); err != balancer.ErrNoSubConnAvailable { if _, err := p0.Pick(balancer.PickInfo{Ctx: iringhash.SetXDSRequestHash(ctx, firstHash)}); err != balancer.ErrNoSubConnAvailable {
t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable) t.Fatalf("first pick returned err %v, want %v", err, balancer.ErrNoSubConnAvailable)
} }
select { select {
@ -701,10 +721,8 @@ func (s) TestAddrBalancerAttributesChange(t *testing.T) {
case <-cc.NewSubConnCh: case <-cc.NewSubConnCh:
} }
addrs2 := []resolver.Address{internal.SetLocalityID(resolver.Address{Addr: testBackendAddrStrs[0]}, locality)} addrs2 := []resolver.Address{setTestAttrAddr(resolver.Address{Addr: testBackendAddrStrs[0]}, content)}
wantEndpoints2 := []resolver.Endpoint{ wantEndpoints2 := []resolver.Endpoint{setTestAttrEndpoint(resolver.Endpoint{Addresses: addrs2}, content)}
internal.SetLocalityIDInEndpoint(resolver.Endpoint{Addresses: addrs2}, locality),
}
if err := b.UpdateClientConnState(balancer.ClientConnState{ if err := b.UpdateClientConnState(balancer.ClientConnState{
ResolverState: resolver.State{Endpoints: wantEndpoints2}, ResolverState: resolver.State{Endpoints: wantEndpoints2},
BalancerConfig: testConfig, BalancerConfig: testConfig,

View File

@ -148,7 +148,6 @@ func (rlsBB) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.
Logger: lb.logger, Logger: lb.logger,
SubBalancerCloseTimeout: time.Duration(0), // Disable caching of removed child policies SubBalancerCloseTimeout: time.Duration(0), // Disable caching of removed child policies
}) })
lb.bg.Start()
go lb.run() go lb.run()
return lb return lb
} }

View File

@ -689,19 +689,17 @@ func (s) TestPickerUpdateOnDataCacheSizeDecrease(t *testing.T) {
stub.Register(topLevelBalancerName, stub.BalancerFuncs{ stub.Register(topLevelBalancerName, stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
ccWrapper = &testCCWrapper{ClientConn: bd.ClientConn} ccWrapper = &testCCWrapper{ClientConn: bd.ClientConn}
bd.Data = balancer.Get(Name).Build(ccWrapper, bd.BuildOptions) bd.ChildBalancer = balancer.Get(Name).Build(ccWrapper, bd.BuildOptions)
}, },
ParseConfig: func(sc json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { ParseConfig: func(sc json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
parser := balancer.Get(Name).(balancer.ConfigParser) parser := balancer.Get(Name).(balancer.ConfigParser)
return parser.ParseConfig(sc) return parser.ParseConfig(sc)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
bal := bd.Data.(balancer.Balancer) return bd.ChildBalancer.UpdateClientConnState(ccs)
return bal.UpdateClientConnState(ccs)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bal := bd.Data.(balancer.Balancer) bd.ChildBalancer.Close()
bal.Close()
}, },
}) })
@ -1070,19 +1068,17 @@ func (s) TestUpdateStatePauses(t *testing.T) {
stub.Register(topLevelBalancerName, stub.BalancerFuncs{ stub.Register(topLevelBalancerName, stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
ccWrapper = &testCCWrapper{ClientConn: bd.ClientConn} ccWrapper = &testCCWrapper{ClientConn: bd.ClientConn}
bd.Data = balancer.Get(Name).Build(ccWrapper, bd.BuildOptions) bd.ChildBalancer = balancer.Get(Name).Build(ccWrapper, bd.BuildOptions)
}, },
ParseConfig: func(sc json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { ParseConfig: func(sc json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
parser := balancer.Get(Name).(balancer.ConfigParser) parser := balancer.Get(Name).(balancer.ConfigParser)
return parser.ParseConfig(sc) return parser.ParseConfig(sc)
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
bal := bd.Data.(balancer.Balancer) return bd.ChildBalancer.UpdateClientConnState(ccs)
return bal.UpdateClientConnState(ccs)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bal := bd.Data.(balancer.Balancer) bd.ChildBalancer.Close()
bal.Close()
}, },
}) })
@ -1098,10 +1094,10 @@ func (s) TestUpdateStatePauses(t *testing.T) {
} }
stub.Register(childPolicyName, stub.BalancerFuncs{ stub.Register(childPolicyName, stub.BalancerFuncs{
Init: func(bd *stub.BalancerData) { Init: func(bd *stub.BalancerData) {
bd.Data = balancer.Get(pickfirst.Name).Build(bd.ClientConn, bd.BuildOptions) bd.ChildBalancer = balancer.Get(pickfirst.Name).Build(bd.ClientConn, bd.BuildOptions)
}, },
Close: func(bd *stub.BalancerData) { Close: func(bd *stub.BalancerData) {
bd.Data.(balancer.Balancer).Close() bd.ChildBalancer.Close()
}, },
ParseConfig: func(sc json.RawMessage) (serviceconfig.LoadBalancingConfig, error) { ParseConfig: func(sc json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
cfg := &childPolicyConfig{} cfg := &childPolicyConfig{}
@ -1111,7 +1107,7 @@ func (s) TestUpdateStatePauses(t *testing.T) {
return cfg, nil return cfg, nil
}, },
UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
bal := bd.Data.(balancer.Balancer) bal := bd.ChildBalancer
bd.ClientConn.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &testutils.TestConstPicker{Err: balancer.ErrNoSubConnAvailable}}) bd.ClientConn.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &testutils.TestConstPicker{Err: balancer.ErrNoSubConnAvailable}})
bd.ClientConn.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: &testutils.TestConstPicker{Err: balancer.ErrNoSubConnAvailable}}) bd.ClientConn.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: &testutils.TestConstPicker{Err: balancer.ErrNoSubConnAvailable}})

View File

@ -220,27 +220,43 @@ func parseRLSProto(rlsProto *rlspb.RouteLookupConfig) (*lbConfig, error) {
// Validations performed here: // Validations performed here:
// - if `max_age` > 5m, it should be set to 5 minutes // - if `max_age` > 5m, it should be set to 5 minutes
// only if stale age is not set
// - if `stale_age` > `max_age`, ignore it // - if `stale_age` > `max_age`, ignore it
// - if `stale_age` is set, then `max_age` must also be set // - if `stale_age` is set, then `max_age` must also be set
maxAgeSet := false
maxAge, err := convertDuration(rlsProto.GetMaxAge()) maxAge, err := convertDuration(rlsProto.GetMaxAge())
if err != nil { if err != nil {
return nil, fmt.Errorf("rls: failed to parse max_age in route lookup config %+v: %v", rlsProto, err) return nil, fmt.Errorf("rls: failed to parse max_age in route lookup config %+v: %v", rlsProto, err)
} }
if maxAge == 0 {
maxAge = maxMaxAge
} else {
maxAgeSet = true
}
staleAgeSet := false
staleAge, err := convertDuration(rlsProto.GetStaleAge()) staleAge, err := convertDuration(rlsProto.GetStaleAge())
if err != nil { if err != nil {
return nil, fmt.Errorf("rls: failed to parse staleAge in route lookup config %+v: %v", rlsProto, err) return nil, fmt.Errorf("rls: failed to parse staleAge in route lookup config %+v: %v", rlsProto, err)
} }
if staleAge != 0 && maxAge == 0 { if staleAge == 0 {
staleAge = maxMaxAge
} else {
staleAgeSet = true
}
if staleAgeSet && !maxAgeSet {
return nil, fmt.Errorf("rls: stale_age is set, but max_age is not in route lookup config %+v", rlsProto) return nil, fmt.Errorf("rls: stale_age is set, but max_age is not in route lookup config %+v", rlsProto)
} }
if staleAge >= maxAge { if staleAge > maxMaxAge {
logger.Infof("rls: stale_age %v is not less than max_age %v, ignoring it", staleAge, maxAge) staleAge = maxMaxAge
staleAge = 0
} }
if maxAge == 0 || maxAge > maxMaxAge { if !staleAgeSet && maxAge > maxMaxAge {
logger.Infof("rls: max_age in route lookup config is %v, using %v", maxAge, maxMaxAge)
maxAge = maxMaxAge maxAge = maxMaxAge
} }
if staleAge > maxAge {
staleAge = maxAge
}
// `cache_size_bytes` field must have a value greater than 0, and if its // `cache_size_bytes` field must have a value greater than 0, and if its
// value is greater than 5M, we cap it at 5M // value is greater than 5M, we cap it at 5M

View File

@ -60,8 +60,8 @@ func (s) TestParseConfig(t *testing.T) {
// - A top-level unknown field should not fail. // - A top-level unknown field should not fail.
// - An unknown field in routeLookupConfig proto should not fail. // - An unknown field in routeLookupConfig proto should not fail.
// - lookupServiceTimeout is set to its default value, since it is not specified in the input. // - lookupServiceTimeout is set to its default value, since it is not specified in the input.
// - maxAge is set to maxMaxAge since the value is too large in the input. // - maxAge is clamped to maxMaxAge if staleAge is not set.
// - staleAge is ignore because it is higher than maxAge in the input. // - staleAge is ignored because it is higher than maxAge in the input.
// - cacheSizeBytes is greater than the hard upper limit of 5MB // - cacheSizeBytes is greater than the hard upper limit of 5MB
desc: "with transformations 1", desc: "with transformations 1",
input: []byte(`{ input: []byte(`{
@ -87,9 +87,9 @@ func (s) TestParseConfig(t *testing.T) {
}`), }`),
wantCfg: &lbConfig{ wantCfg: &lbConfig{
lookupService: ":///target", lookupService: ":///target",
lookupServiceTimeout: 10 * time.Second, // This is the default value. lookupServiceTimeout: 10 * time.Second, // This is the default value.
maxAge: 5 * time.Minute, // This is max maxAge. maxAge: 500 * time.Second, // Max age is not clamped when stale age is set.
staleAge: time.Duration(0), // StaleAge is ignore because it was higher than maxAge. staleAge: 300 * time.Second, // StaleAge is clamped because it was higher than maxMaxAge.
cacheSizeBytes: maxCacheSize, cacheSizeBytes: maxCacheSize,
defaultTarget: "passthrough:///default", defaultTarget: "passthrough:///default",
childPolicyName: "grpclb", childPolicyName: "grpclb",
@ -100,6 +100,69 @@ func (s) TestParseConfig(t *testing.T) {
}, },
}, },
}, },
{
desc: "maxAge not clamped when staleAge is set",
input: []byte(`{
"routeLookupConfig": {
"grpcKeybuilders": [{
"names": [{"service": "service", "method": "method"}],
"headers": [{"key": "k1", "names": ["v1"]}]
}],
"lookupService": ":///target",
"maxAge" : "500s",
"staleAge": "200s",
"cacheSizeBytes": 100000000
},
"childPolicy": [
{"grpclb": {"childPolicy": [{"pickfirst": {}}]}}
],
"childPolicyConfigTargetFieldName": "serviceName"
}`),
wantCfg: &lbConfig{
lookupService: ":///target",
lookupServiceTimeout: 10 * time.Second, // This is the default value.
maxAge: 500 * time.Second, // Max age is not clamped when stale age is set.
staleAge: 200 * time.Second, // This is stale age within maxMaxAge.
cacheSizeBytes: maxCacheSize,
childPolicyName: "grpclb",
childPolicyTargetField: "serviceName",
childPolicyConfig: map[string]json.RawMessage{
"childPolicy": json.RawMessage(`[{"pickfirst": {}}]`),
"serviceName": json.RawMessage(childPolicyTargetFieldVal),
},
},
},
{
desc: "maxAge clamped when staleAge is not set",
input: []byte(`{
"routeLookupConfig": {
"grpcKeybuilders": [{
"names": [{"service": "service", "method": "method"}],
"headers": [{"key": "k1", "names": ["v1"]}]
}],
"lookupService": ":///target",
"maxAge" : "500s",
"cacheSizeBytes": 100000000
},
"childPolicy": [
{"grpclb": {"childPolicy": [{"pickfirst": {}}]}}
],
"childPolicyConfigTargetFieldName": "serviceName"
}`),
wantCfg: &lbConfig{
lookupService: ":///target",
lookupServiceTimeout: 10 * time.Second, // This is the default value.
maxAge: 300 * time.Second, // Max age is clamped when stale age is not set.
staleAge: 300 * time.Second,
cacheSizeBytes: maxCacheSize,
childPolicyName: "grpclb",
childPolicyTargetField: "serviceName",
childPolicyConfig: map[string]json.RawMessage{
"childPolicy": json.RawMessage(`[{"pickfirst": {}}]`),
"serviceName": json.RawMessage(childPolicyTargetFieldVal),
},
},
},
{ {
desc: "without transformations", desc: "without transformations",
input: []byte(`{ input: []byte(`{

View File

@ -92,12 +92,7 @@ func newControlChannel(rlsServerName, serviceConfig string, rpcTimeout time.Dura
ctrlCh.cc.Connect() ctrlCh.cc.Connect()
ctrlCh.client = rlsgrpc.NewRouteLookupServiceClient(ctrlCh.cc) ctrlCh.client = rlsgrpc.NewRouteLookupServiceClient(ctrlCh.cc)
ctrlCh.logger.Infof("Control channel created to RLS server at: %v", rlsServerName) ctrlCh.logger.Infof("Control channel created to RLS server at: %v", rlsServerName)
start := make(chan struct{}) go ctrlCh.monitorConnectivityState()
go func() {
close(start)
ctrlCh.monitorConnectivityState()
}()
<-start
return ctrlCh, nil return ctrlCh, nil
} }

View File

@ -70,10 +70,3 @@ func (b *rrBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error {
ResolverState: pickfirstleaf.EnableHealthListener(ccs.ResolverState), ResolverState: pickfirstleaf.EnableHealthListener(ccs.ResolverState),
}) })
} }
func (b *rrBalancer) ExitIdle() {
// Should always be ok, as child is endpoint sharding.
if ei, ok := b.Balancer.(balancer.ExitIdler); ok {
ei.ExitIdle()
}
}

View File

@ -16,6 +16,15 @@
* *
*/ */
// Package weightedroundrobin provides an implementation of the weighted round
// robin LB policy, as defined in [gRFC A58].
//
// # Experimental
//
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
// later release.
//
// [gRFC A58]: https://github.com/grpc/proposal/blob/master/A58-client-side-weighted-round-robin-lb-policy.md
package weightedroundrobin package weightedroundrobin
import ( import (
@ -95,8 +104,8 @@ func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Ba
ClientConn: cc, ClientConn: cc,
target: bOpts.Target.String(), target: bOpts.Target.String(),
metricsRecorder: cc.MetricsRecorder(), metricsRecorder: cc.MetricsRecorder(),
addressWeights: resolver.NewAddressMap(), addressWeights: resolver.NewAddressMapV2[*endpointWeight](),
endpointToWeight: resolver.NewEndpointMap(), endpointToWeight: resolver.NewEndpointMap[*endpointWeight](),
scToWeight: make(map[balancer.SubConn]*endpointWeight), scToWeight: make(map[balancer.SubConn]*endpointWeight),
} }
@ -146,17 +155,15 @@ func (bb) Name() string {
// //
// Caller must hold b.mu. // Caller must hold b.mu.
func (b *wrrBalancer) updateEndpointsLocked(endpoints []resolver.Endpoint) { func (b *wrrBalancer) updateEndpointsLocked(endpoints []resolver.Endpoint) {
endpointSet := resolver.NewEndpointMap() endpointSet := resolver.NewEndpointMap[*endpointWeight]()
addressSet := resolver.NewAddressMap() addressSet := resolver.NewAddressMapV2[*endpointWeight]()
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
endpointSet.Set(endpoint, nil) endpointSet.Set(endpoint, nil)
for _, addr := range endpoint.Addresses { for _, addr := range endpoint.Addresses {
addressSet.Set(addr, nil) addressSet.Set(addr, nil)
} }
var ew *endpointWeight ew, ok := b.endpointToWeight.Get(endpoint)
if ewi, ok := b.endpointToWeight.Get(endpoint); ok { if !ok {
ew = ewi.(*endpointWeight)
} else {
ew = &endpointWeight{ ew = &endpointWeight{
logger: b.logger, logger: b.logger,
connectivityState: connectivity.Connecting, connectivityState: connectivity.Connecting,
@ -205,8 +212,8 @@ type wrrBalancer struct {
cfg *lbConfig // active config cfg *lbConfig // active config
locality string locality string
stopPicker *grpcsync.Event stopPicker *grpcsync.Event
addressWeights *resolver.AddressMap // addr -> endpointWeight addressWeights *resolver.AddressMapV2[*endpointWeight]
endpointToWeight *resolver.EndpointMap // endpoint -> endpointWeight endpointToWeight *resolver.EndpointMap[*endpointWeight]
scToWeight map[balancer.SubConn]*endpointWeight scToWeight map[balancer.SubConn]*endpointWeight
} }
@ -251,13 +258,12 @@ func (b *wrrBalancer) UpdateState(state balancer.State) {
for _, childState := range childStates { for _, childState := range childStates {
if childState.State.ConnectivityState == connectivity.Ready { if childState.State.ConnectivityState == connectivity.Ready {
ewv, ok := b.endpointToWeight.Get(childState.Endpoint) ew, ok := b.endpointToWeight.Get(childState.Endpoint)
if !ok { if !ok {
// Should never happen, simply continue and ignore this endpoint // Should never happen, simply continue and ignore this endpoint
// for READY pickers. // for READY pickers.
continue continue
} }
ew := ewv.(*endpointWeight)
readyPickersWeight = append(readyPickersWeight, pickerWeightedEndpoint{ readyPickersWeight = append(readyPickersWeight, pickerWeightedEndpoint{
picker: childState.State.Picker, picker: childState.State.Picker,
weightedEndpoint: ew, weightedEndpoint: ew,
@ -320,7 +326,7 @@ func (b *wrrBalancer) NewSubConn(addrs []resolver.Address, opts balancer.NewSubC
if err != nil { if err != nil {
return nil, err return nil, err
} }
b.scToWeight[sc] = ewi.(*endpointWeight) b.scToWeight[sc] = ewi
return sc, nil return sc, nil
} }
@ -389,8 +395,7 @@ func (b *wrrBalancer) Close() {
b.mu.Unlock() b.mu.Unlock()
// Ensure any lingering OOB watchers are stopped. // Ensure any lingering OOB watchers are stopped.
for _, ewv := range b.endpointToWeight.Values() { for _, ew := range b.endpointToWeight.Values() {
ew := ewv.(*endpointWeight)
if ew.stopORCAListener != nil { if ew.stopORCAListener != nil {
ew.stopORCAListener() ew.stopORCAListener()
} }
@ -399,9 +404,7 @@ func (b *wrrBalancer) Close() {
} }
func (b *wrrBalancer) ExitIdle() { func (b *wrrBalancer) ExitIdle() {
if ei, ok := b.child.(balancer.ExitIdler); ok { // Should always be ok, as child is endpoint sharding. b.child.ExitIdle()
ei.ExitIdle()
}
} }
// picker is the WRR policy's picker. It uses live-updating backend weights to // picker is the WRR policy's picker. It uses live-updating backend weights to
@ -492,7 +495,6 @@ func (p *picker) start(stopPicker *grpcsync.Event) {
// that listener. // that listener.
type endpointWeight struct { type endpointWeight struct {
// The following fields are immutable. // The following fields are immutable.
balancer.SubConn
logger *grpclog.PrefixLogger logger *grpclog.PrefixLogger
target string target string
metricsRecorder estats.MetricsRecorder metricsRecorder estats.MetricsRecorder
@ -522,7 +524,7 @@ type endpointWeight struct {
func (w *endpointWeight) OnLoadReport(load *v3orcapb.OrcaLoadReport) { func (w *endpointWeight) OnLoadReport(load *v3orcapb.OrcaLoadReport) {
if w.logger.V(2) { if w.logger.V(2) {
w.logger.Infof("Received load report for subchannel %v: %v", w.SubConn, load) w.logger.Infof("Received load report for subchannel %v: %v", w.pickedSC, load)
} }
// Update weights of this endpoint according to the reported load. // Update weights of this endpoint according to the reported load.
utilization := load.ApplicationUtilization utilization := load.ApplicationUtilization
@ -531,7 +533,7 @@ func (w *endpointWeight) OnLoadReport(load *v3orcapb.OrcaLoadReport) {
} }
if utilization == 0 || load.RpsFractional == 0 { if utilization == 0 || load.RpsFractional == 0 {
if w.logger.V(2) { if w.logger.V(2) {
w.logger.Infof("Ignoring empty load report for subchannel %v", w.SubConn) w.logger.Infof("Ignoring empty load report for subchannel %v", w.pickedSC)
} }
return return
} }
@ -542,7 +544,7 @@ func (w *endpointWeight) OnLoadReport(load *v3orcapb.OrcaLoadReport) {
errorRate := load.Eps / load.RpsFractional errorRate := load.Eps / load.RpsFractional
w.weightVal = load.RpsFractional / (utilization + errorRate*w.cfg.ErrorUtilizationPenalty) w.weightVal = load.RpsFractional / (utilization + errorRate*w.cfg.ErrorUtilizationPenalty)
if w.logger.V(2) { if w.logger.V(2) {
w.logger.Infof("New weight for subchannel %v: %v", w.SubConn, w.weightVal) w.logger.Infof("New weight for subchannel %v: %v", w.pickedSC, w.weightVal)
} }
w.lastUpdated = internal.TimeNow() w.lastUpdated = internal.TimeNow()

View File

@ -115,7 +115,7 @@ func startServer(t *testing.T, r reportType) *testServer {
cmr := orca.NewServerMetricsRecorder().(orca.CallMetricsRecorder) cmr := orca.NewServerMetricsRecorder().(orca.CallMetricsRecorder)
ss := &stubserver.StubServer{ ss := &stubserver.StubServer{
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
if r := orca.CallMetricsRecorderFromContext(ctx); r != nil { if r := orca.CallMetricsRecorderFromContext(ctx); r != nil {
// Copy metrics from what the test set in cmr into r. // Copy metrics from what the test set in cmr into r.
sm := cmr.(orca.ServerMetricsProvider).ServerMetrics() sm := cmr.(orca.ServerMetricsProvider).ServerMetrics()
@ -449,7 +449,7 @@ func (s) TestBalancer_TwoAddresses_OOBThenPerCall(t *testing.T) {
// Update to per-call weights. // Update to per-call weights.
c := svcConfig(t, perCallConfig) c := svcConfig(t, perCallConfig)
parsedCfg := srv1.R.CC.ParseServiceConfig(c) parsedCfg := srv1.R.CC().ParseServiceConfig(c)
if parsedCfg.Err != nil { if parsedCfg.Err != nil {
panic(fmt.Sprintf("Error parsing config %q: %v", c, parsedCfg.Err)) panic(fmt.Sprintf("Error parsing config %q: %v", c, parsedCfg.Err))
} }
@ -563,7 +563,7 @@ func (s) TestBalancer_TwoAddresses_ErrorPenalty(t *testing.T) {
newCfg := oobConfig newCfg := oobConfig
newCfg.ErrorUtilizationPenalty = float64p(0.9) newCfg.ErrorUtilizationPenalty = float64p(0.9)
c := svcConfig(t, newCfg) c := svcConfig(t, newCfg)
parsedCfg := srv1.R.CC.ParseServiceConfig(c) parsedCfg := srv1.R.CC().ParseServiceConfig(c)
if parsedCfg.Err != nil { if parsedCfg.Err != nil {
panic(fmt.Sprintf("Error parsing config %q: %v", c, parsedCfg.Err)) panic(fmt.Sprintf("Error parsing config %q: %v", c, parsedCfg.Err))
} }

View File

@ -1,85 +0,0 @@
/*
*
* Copyright 2019 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package weightedroundrobin provides an implementation of the weighted round
// robin LB policy, as defined in [gRFC A58].
//
// # Experimental
//
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
// later release.
//
// [gRFC A58]: https://github.com/grpc/proposal/blob/master/A58-client-side-weighted-round-robin-lb-policy.md
package weightedroundrobin
import (
"fmt"
"google.golang.org/grpc/resolver"
)
// attributeKey is the type used as the key to store AddrInfo in the
// BalancerAttributes field of resolver.Address or Attributes field of
// resolver.Endpoint.
type attributeKey struct{}
// AddrInfo will be stored in the BalancerAttributes field of Address in order
// to use weighted roundrobin balancer.
type AddrInfo struct {
Weight uint32
}
// Equal allows the values to be compared by Attributes.Equal.
func (a AddrInfo) Equal(o any) bool {
oa, ok := o.(AddrInfo)
return ok && oa.Weight == a.Weight
}
// SetAddrInfo returns a copy of addr in which the BalancerAttributes field is
// updated with addrInfo.
func SetAddrInfo(addr resolver.Address, addrInfo AddrInfo) resolver.Address {
addr.BalancerAttributes = addr.BalancerAttributes.WithValue(attributeKey{}, addrInfo)
return addr
}
// SetAddrInfoInEndpoint returns a copy of endpoint in which the Attributes
// field is updated with addrInfo.
func SetAddrInfoInEndpoint(endpoint resolver.Endpoint, addrInfo AddrInfo) resolver.Endpoint {
endpoint.Attributes = endpoint.Attributes.WithValue(attributeKey{}, addrInfo)
return endpoint
}
// GetAddrInfo returns the AddrInfo stored in the BalancerAttributes field of
// addr.
func GetAddrInfo(addr resolver.Address) AddrInfo {
v := addr.BalancerAttributes.Value(attributeKey{})
ai, _ := v.(AddrInfo)
return ai
}
// AddrInfoFromEndpoint returns the AddrInfo stored in the Attributes field of
// endpoint.
func AddrInfoFromEndpoint(endpoint resolver.Endpoint) AddrInfo {
v := endpoint.Attributes.Value(attributeKey{})
ai, _ := v.(AddrInfo)
return ai
}
func (a AddrInfo) String() string {
return fmt.Sprintf("Weight: %d", a.Weight)
}

View File

@ -1,82 +0,0 @@
/*
*
* Copyright 2020 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package weightedroundrobin
import (
"testing"
"github.com/google/go-cmp/cmp"
"google.golang.org/grpc/attributes"
"google.golang.org/grpc/resolver"
)
func TestAddrInfoToAndFromAttributes(t *testing.T) {
tests := []struct {
desc string
inputAddrInfo AddrInfo
inputAttributes *attributes.Attributes
wantAddrInfo AddrInfo
}{
{
desc: "empty attributes",
inputAddrInfo: AddrInfo{Weight: 100},
inputAttributes: nil,
wantAddrInfo: AddrInfo{Weight: 100},
},
{
desc: "non-empty attributes",
inputAddrInfo: AddrInfo{Weight: 100},
inputAttributes: attributes.New("foo", "bar"),
wantAddrInfo: AddrInfo{Weight: 100},
},
{
desc: "addrInfo not present in empty attributes",
inputAddrInfo: AddrInfo{},
inputAttributes: nil,
wantAddrInfo: AddrInfo{},
},
{
desc: "addrInfo not present in non-empty attributes",
inputAddrInfo: AddrInfo{},
inputAttributes: attributes.New("foo", "bar"),
wantAddrInfo: AddrInfo{},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
addr := resolver.Address{Attributes: test.inputAttributes}
addr = SetAddrInfo(addr, test.inputAddrInfo)
gotAddrInfo := GetAddrInfo(addr)
if !cmp.Equal(gotAddrInfo, test.wantAddrInfo) {
t.Errorf("gotAddrInfo: %v, wantAddrInfo: %v", gotAddrInfo, test.wantAddrInfo)
}
})
}
}
func TestGetAddInfoEmpty(t *testing.T) {
addr := resolver.Address{}
gotAddrInfo := GetAddrInfo(addr)
wantAddrInfo := AddrInfo{}
if !cmp.Equal(gotAddrInfo, wantAddrInfo) {
t.Errorf("gotAddrInfo: %v, wantAddrInfo: %v", gotAddrInfo, wantAddrInfo)
}
}

View File

@ -62,7 +62,6 @@ func (bb) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Ba
Logger: b.logger, Logger: b.logger,
SubBalancerCloseTimeout: time.Duration(0), // Disable caching of removed child policies SubBalancerCloseTimeout: time.Duration(0), // Disable caching of removed child policies
}) })
b.bg.Start()
b.logger.Infof("Created") b.logger.Infof("Created")
return b return b
} }

View File

@ -28,6 +28,7 @@ import (
"time" "time"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/attributes" "google.golang.org/grpc/attributes"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
@ -620,6 +621,15 @@ func (s) TestWeightedTarget_TwoSubBalancers_MoreBackends(t *testing.T) {
sc3 := scs["cluster_2"][0].sc.(*testutils.TestSubConn) sc3 := scs["cluster_2"][0].sc.(*testutils.TestSubConn)
sc4 := scs["cluster_2"][1].sc.(*testutils.TestSubConn) sc4 := scs["cluster_2"][1].sc.(*testutils.TestSubConn)
// Due to connection order randomization in RR, and the assumed order in the
// remainder of this test, adjust the scs according to the addrs if needed.
if sc1.Addresses[0].Addr != addr1.Addr {
sc1, sc2 = sc2, sc1
}
if sc3.Addresses[0].Addr != addr3.Addr {
sc3, sc4 = sc4, sc3
}
// The CONNECTING picker should be sent by all leaf pickfirst policies on // The CONNECTING picker should be sent by all leaf pickfirst policies on
// receiving the first resolver update. // receiving the first resolver update.
<-cc.NewPickerCh <-cc.NewPickerCh
@ -670,6 +680,7 @@ func (s) TestWeightedTarget_TwoSubBalancers_MoreBackends(t *testing.T) {
}); err != nil { }); err != nil {
t.Fatalf("failed to update ClientConn state: %v", err) t.Fatalf("failed to update ClientConn state: %v", err)
} }
scShutdown := <-cc.ShutdownSubConnCh scShutdown := <-cc.ShutdownSubConnCh
if scShutdown != sc3 { if scShutdown != sc3 {
t.Fatalf("ShutdownSubConn, want %v, got %v", sc3, scShutdown) t.Fatalf("ShutdownSubConn, want %v, got %v", sc3, scShutdown)
@ -1311,14 +1322,32 @@ func verifySubConnAddrs(t *testing.T, scs map[string][]subConnWithAddr, wantSubC
t.Fatalf("got new subConns %+v, want %v", scs, wantSubConnAddrs) t.Fatalf("got new subConns %+v, want %v", scs, wantSubConnAddrs)
} }
wantAddrs := wantSubConnAddrs[cfg] wantAddrs := wantSubConnAddrs[cfg]
for i, scWithAddr := range scsWithAddr { if diff := cmp.Diff(addressesToAddrs(wantAddrs), scwasToAddrs(scsWithAddr),
if diff := cmp.Diff(wantAddrs[i].Addr, scWithAddr.addr.Addr); diff != "" { cmpopts.SortSlices(func(a, b string) bool {
t.Fatalf("got unexpected new subconn addrs: %v", diff) return a < b
} }),
); diff != "" {
t.Fatalf("got unexpected new subconn addrs: %v", diff)
} }
} }
} }
func scwasToAddrs(ss []subConnWithAddr) []string {
ret := make([]string, len(ss))
for i, s := range ss {
ret[i] = s.addr.Addr
}
return ret
}
func addressesToAddrs(as []resolver.Address) []string {
ret := make([]string, len(as))
for i, a := range as {
ret[i] = a.Addr
}
return ret
}
const initIdleBalancerName = "test-init-Idle-balancer" const initIdleBalancerName = "test-init-Idle-balancer"
var errTestInitIdle = fmt.Errorf("init Idle balancer error 0") var errTestInitIdle = fmt.Errorf("init Idle balancer error 0")

View File

@ -70,10 +70,11 @@ func (s) TestBalancer_StateListenerBeforeConnect(t *testing.T) {
stub.Register(t.Name(), bf) stub.Register(t.Name(), bf)
svcCfg := fmt.Sprintf(`{ "loadBalancingConfig": [{%q: {}}] }`, t.Name()) svcCfg := fmt.Sprintf(`{ "loadBalancingConfig": [{%q: {}}] }`, t.Name())
cc, err := Dial("fake", WithTransportCredentials(insecure.NewCredentials()), WithDefaultServiceConfig(svcCfg)) cc, err := NewClient("passthrough:///test.server", WithTransportCredentials(insecure.NewCredentials()), WithDefaultServiceConfig(svcCfg))
if err != nil { if err != nil {
t.Fatal("Error dialing:", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
cc.Connect()
started.Fire() started.Fire()
// Wait for the LB policy to call NewSubConn and cc.Close. // Wait for the LB policy to call NewSubConn and cc.Close.

View File

@ -404,7 +404,7 @@ func makeClients(bf stats.Features) ([]testgrpc.BenchmarkServiceClient, func())
conns := make([]*grpc.ClientConn, bf.Connections) conns := make([]*grpc.ClientConn, bf.Connections)
clients := make([]testgrpc.BenchmarkServiceClient, bf.Connections) clients := make([]testgrpc.BenchmarkServiceClient, bf.Connections)
for cn := 0; cn < bf.Connections; cn++ { for cn := 0; cn < bf.Connections; cn++ {
conns[cn] = benchmark.NewClientConn("" /* target not used */, opts...) conns[cn] = benchmark.NewClientConn("passthrough://" /* target not used */, opts...)
clients[cn] = testgrpc.NewBenchmarkServiceClient(conns[cn]) clients[cn] = testgrpc.NewBenchmarkServiceClient(conns[cn])
} }

View File

@ -340,10 +340,10 @@ func NewClientConn(addr string, opts ...grpc.DialOption) *grpc.ClientConn {
} }
// NewClientConnWithContext creates a gRPC client connection to addr using ctx. // NewClientConnWithContext creates a gRPC client connection to addr using ctx.
func NewClientConnWithContext(ctx context.Context, addr string, opts ...grpc.DialOption) *grpc.ClientConn { func NewClientConnWithContext(_ context.Context, addr string, opts ...grpc.DialOption) *grpc.ClientConn {
conn, err := grpc.DialContext(ctx, addr, opts...) conn, err := grpc.NewClient(addr, opts...)
if err != nil { if err != nil {
logger.Fatalf("NewClientConn(%q) failed to create a ClientConn: %v", addr, err) logger.Fatalf("grpc.NewClient(%q) = %v", addr, err)
} }
return conn return conn
} }

View File

@ -48,15 +48,15 @@ const (
) )
// The following String() function was generated by stringer. // The following String() function was generated by stringer.
const _Code_name = "OKCanceledUnknownInvalidArgumentDeadlineExceededNotFoundAlreadyExistsPermissionDeniedResourceExhaustedFailedPreconditionAbortedOutOfRangeUnimplementedInternalUnavailableDataLossUnauthenticated" const codeName = "OKCanceledUnknownInvalidArgumentDeadlineExceededNotFoundAlreadyExistsPermissionDeniedResourceExhaustedFailedPreconditionAbortedOutOfRangeUnimplementedInternalUnavailableDataLossUnauthenticated"
var _Code_index = [...]uint8{0, 2, 10, 17, 32, 48, 56, 69, 85, 102, 120, 127, 137, 150, 158, 169, 177, 192} var codeIndex = [...]uint8{0, 2, 10, 17, 32, 48, 56, 69, 85, 102, 120, 127, 137, 150, 158, 169, 177, 192}
func (i codeBench) String() string { func (i codeBench) String() string {
if i >= codeBench(len(_Code_index)-1) { if i >= codeBench(len(codeIndex)-1) {
return "Code(" + strconv.FormatInt(int64(i), 10) + ")" return "Code(" + strconv.FormatInt(int64(i), 10) + ")"
} }
return _Code_name[_Code_index[i]:_Code_index[i+1]] return codeName[codeIndex[i]:codeIndex[i+1]]
} }
var nameMap = map[codeBench]string{ var nameMap = map[codeBench]string{

View File

@ -1072,7 +1072,7 @@ func (s) TestCanceledStatus(t *testing.T) {
const statusMsgWant = "server returned Canceled" const statusMsgWant = "server returned Canceled"
ss := &stubserver.StubServer{ ss := &stubserver.StubServer{
UnaryCallF: func(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { UnaryCallF: func(ctx context.Context, _ *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
grpc.SetTrailer(ctx, metadata.Pairs("key", "value")) grpc.SetTrailer(ctx, metadata.Pairs("key", "value"))
return nil, status.Error(codes.Canceled, statusMsgWant) return nil, status.Error(codes.Canceled, statusMsgWant)
}, },

View File

@ -18,7 +18,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.4 // protoc-gen-go v1.36.6
// protoc v5.27.1 // protoc v5.27.1
// source: grpc/binlog/v1/binarylog.proto // source: grpc/binlog/v1/binarylog.proto
@ -858,133 +858,68 @@ func (x *Address) GetIpPort() uint32 {
var File_grpc_binlog_v1_binarylog_proto protoreflect.FileDescriptor var File_grpc_binlog_v1_binarylog_proto protoreflect.FileDescriptor
var file_grpc_binlog_v1_binarylog_proto_rawDesc = string([]byte{ const file_grpc_binlog_v1_binarylog_proto_rawDesc = "" +
0x0a, 0x1e, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x2f, 0x76, 0x31, "\n" +
0x2f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, "\x1egrpc/binlog/v1/binarylog.proto\x12\x11grpc.binarylog.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xbb\a\n" +
0x12, 0x11, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, "\fGrpcLogEntry\x128\n" +
0x2e, 0x76, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, "\ttimestamp\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\x12\x17\n" +
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, "\acall_id\x18\x02 \x01(\x04R\x06callId\x125\n" +
0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, "\x17sequence_id_within_call\x18\x03 \x01(\x04R\x14sequenceIdWithinCall\x12=\n" +
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, "\x04type\x18\x04 \x01(\x0e2).grpc.binarylog.v1.GrpcLogEntry.EventTypeR\x04type\x12>\n" +
0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb, 0x07, 0x0a, 0x0c, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, "\x06logger\x18\x05 \x01(\x0e2&.grpc.binarylog.v1.GrpcLogEntry.LoggerR\x06logger\x12F\n" +
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, "\rclient_header\x18\x06 \x01(\v2\x1f.grpc.binarylog.v1.ClientHeaderH\x00R\fclientHeader\x12F\n" +
0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, "\rserver_header\x18\a \x01(\v2\x1f.grpc.binarylog.v1.ServerHeaderH\x00R\fserverHeader\x126\n" +
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, "\amessage\x18\b \x01(\v2\x1a.grpc.binarylog.v1.MessageH\x00R\amessage\x126\n" +
0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, "\atrailer\x18\t \x01(\v2\x1a.grpc.binarylog.v1.TrailerH\x00R\atrailer\x12+\n" +
0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, "\x11payload_truncated\x18\n" +
0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x35, 0x0a, 0x17, 0x73, 0x65, 0x71, 0x75, " \x01(\bR\x10payloadTruncated\x12.\n" +
0x65, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x5f, 0x63, "\x04peer\x18\v \x01(\v2\x1a.grpc.binarylog.v1.AddressR\x04peer\"\xf5\x01\n" +
0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x73, 0x65, 0x71, 0x75, 0x65, "\tEventType\x12\x16\n" +
0x6e, 0x63, 0x65, 0x49, 0x64, 0x57, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x12, "\x12EVENT_TYPE_UNKNOWN\x10\x00\x12\x1c\n" +
0x3d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, "\x18EVENT_TYPE_CLIENT_HEADER\x10\x01\x12\x1c\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, "\x18EVENT_TYPE_SERVER_HEADER\x10\x02\x12\x1d\n" +
0x31, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x45, "\x19EVENT_TYPE_CLIENT_MESSAGE\x10\x03\x12\x1d\n" +
0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3e, "\x19EVENT_TYPE_SERVER_MESSAGE\x10\x04\x12 \n" +
0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, "\x1cEVENT_TYPE_CLIENT_HALF_CLOSE\x10\x05\x12\x1d\n" +
0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, "\x19EVENT_TYPE_SERVER_TRAILER\x10\x06\x12\x15\n" +
0x76, 0x31, 0x2e, 0x47, 0x72, 0x70, 0x63, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, "\x11EVENT_TYPE_CANCEL\x10\a\"B\n" +
0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x12, 0x46, "\x06Logger\x12\x12\n" +
0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, "\x0eLOGGER_UNKNOWN\x10\x00\x12\x11\n" +
0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, "\rLOGGER_CLIENT\x10\x01\x12\x11\n" +
0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, "\rLOGGER_SERVER\x10\x02B\t\n" +
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, "\apayload\"\xbb\x01\n" +
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, "\fClientHeader\x127\n" +
0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, "\bmetadata\x18\x01 \x01(\v2\x1b.grpc.binarylog.v1.MetadataR\bmetadata\x12\x1f\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, "\vmethod_name\x18\x02 \x01(\tR\n" +
0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, "methodName\x12\x1c\n" +
0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x36, "\tauthority\x18\x03 \x01(\tR\tauthority\x123\n" +
0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, "\atimeout\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\atimeout\"G\n" +
0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, "\fServerHeader\x127\n" +
0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x6d, "\bmetadata\x18\x01 \x01(\v2\x1b.grpc.binarylog.v1.MetadataR\bmetadata\"\xb1\x01\n" +
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, "\aTrailer\x127\n" +
0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, "\bmetadata\x18\x01 \x01(\v2\x1b.grpc.binarylog.v1.MetadataR\bmetadata\x12\x1f\n" +
0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x69, "\vstatus_code\x18\x02 \x01(\rR\n" +
0x6c, 0x65, 0x72, 0x48, 0x00, 0x52, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x2b, "statusCode\x12%\n" +
0x0a, 0x11, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, "\x0estatus_message\x18\x03 \x01(\tR\rstatusMessage\x12%\n" +
0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, "\x0estatus_details\x18\x04 \x01(\fR\rstatusDetails\"5\n" +
0x61, 0x64, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x70, "\aMessage\x12\x16\n" +
0x65, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, "\x06length\x18\x01 \x01(\rR\x06length\x12\x12\n" +
0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, "\x04data\x18\x02 \x01(\fR\x04data\"B\n" +
0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x22, 0xf5, 0x01, 0x0a, 0x09, "\bMetadata\x126\n" +
0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x56, 0x45, "\x05entry\x18\x01 \x03(\v2 .grpc.binarylog.v1.MetadataEntryR\x05entry\"7\n" +
0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, "\rMetadataEntry\x12\x10\n" +
0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x01, 0x12, "\x05value\x18\x02 \x01(\fR\x05value\"\xb8\x01\n" +
0x1c, 0x0a, 0x18, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, "\aAddress\x123\n" +
0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, 0x1d, 0x0a, "\x04type\x18\x01 \x01(\x0e2\x1f.grpc.binarylog.v1.Address.TypeR\x04type\x12\x18\n" +
0x19, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x49, 0x45, "\aaddress\x18\x02 \x01(\tR\aaddress\x12\x17\n" +
0x4e, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x1d, 0x0a, 0x19, "\aip_port\x18\x03 \x01(\rR\x06ipPort\"E\n" +
0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, "\x04Type\x12\x10\n" +
0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x20, 0x0a, 0x1c, 0x45, "\fTYPE_UNKNOWN\x10\x00\x12\r\n" +
0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, "\tTYPE_IPV4\x10\x01\x12\r\n" +
0x5f, 0x48, 0x41, 0x4c, 0x46, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x05, 0x12, 0x1d, 0x0a, "\tTYPE_IPV6\x10\x02\x12\r\n" +
0x19, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, "\tTYPE_UNIX\x10\x03B\\\n" +
0x45, 0x52, 0x5f, 0x54, 0x52, 0x41, 0x49, 0x4c, 0x45, 0x52, 0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, "\x14io.grpc.binarylog.v1B\x0eBinaryLogProtoP\x01Z2google.golang.org/grpc/binarylog/grpc_binarylog_v1b\x06proto3"
0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45,
0x4c, 0x10, 0x07, 0x22, 0x42, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x12, 0x12, 0x0a,
0x0e, 0x4c, 0x4f, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
0x00, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x4f, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45,
0x4e, 0x54, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x4f, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x53,
0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,
0x61, 0x64, 0x22, 0xbb, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e,
0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b,
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a,
0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x33, 0x0a, 0x07, 0x74,
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
0x22, 0x47, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79,
0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xb1, 0x01, 0x0a, 0x07, 0x54, 0x72,
0x61, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62,
0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f,
0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12,
0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x35, 0x0a,
0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67,
0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
0x64, 0x61, 0x74, 0x61, 0x22, 0x42, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0x12, 0x36, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x20, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67,
0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x37, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x22, 0xb8, 0x01, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x33, 0x0a,
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x67, 0x72,
0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x76, 0x31, 0x2e,
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79,
0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07,
0x69, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x69,
0x70, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x45, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a,
0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12,
0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x50, 0x56, 0x34, 0x10, 0x01, 0x12, 0x0d,
0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x50, 0x56, 0x36, 0x10, 0x02, 0x12, 0x0d, 0x0a,
0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x58, 0x10, 0x03, 0x42, 0x5c, 0x0a, 0x14,
0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f,
0x67, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x4c, 0x6f, 0x67, 0x50,
0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x32, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67,
0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x62,
0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x62, 0x69,
0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x5f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
})
var ( var (
file_grpc_binlog_v1_binarylog_proto_rawDescOnce sync.Once file_grpc_binlog_v1_binarylog_proto_rawDescOnce sync.Once

View File

@ -21,7 +21,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.4 // protoc-gen-go v1.36.6
// protoc v5.27.1 // protoc v5.27.1
// source: grpc/channelz/v1/channelz.proto // source: grpc/channelz/v1/channelz.proto
@ -2964,518 +2964,245 @@ func (x *Security_OtherSecurity) GetValue() *anypb.Any {
var File_grpc_channelz_v1_channelz_proto protoreflect.FileDescriptor var File_grpc_channelz_v1_channelz_proto protoreflect.FileDescriptor
var file_grpc_channelz_v1_channelz_proto_rawDesc = string([]byte{ const file_grpc_channelz_v1_channelz_proto_rawDesc = "" +
0x0a, 0x1f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2f, "\n" +
0x76, 0x31, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x70, 0x72, 0x6f, 0x74, "\x1fgrpc/channelz/v1/channelz.proto\x12\x10grpc.channelz.v1\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\xaf\x02\n" +
0x6f, 0x12, 0x10, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, "\aChannel\x12.\n" +
0x2e, 0x76, 0x31, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, "\x03ref\x18\x01 \x01(\v2\x1c.grpc.channelz.v1.ChannelRefR\x03ref\x121\n" +
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, "\x04data\x18\x02 \x01(\v2\x1d.grpc.channelz.v1.ChannelDataR\x04data\x12=\n" +
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, "\vchannel_ref\x18\x03 \x03(\v2\x1c.grpc.channelz.v1.ChannelRefR\n" +
0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, "channelRef\x12F\n" +
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, "\x0esubchannel_ref\x18\x04 \x03(\v2\x1f.grpc.channelz.v1.SubchannelRefR\rsubchannelRef\x12:\n" +
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, "\n" +
0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, "socket_ref\x18\x05 \x03(\v2\x1b.grpc.channelz.v1.SocketRefR\tsocketRef\"\xb5\x02\n" +
0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, "\n" +
0xaf, 0x02, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x2e, 0x0a, 0x03, 0x72, "Subchannel\x121\n" +
0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, "\x03ref\x18\x01 \x01(\v2\x1f.grpc.channelz.v1.SubchannelRefR\x03ref\x121\n" +
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, "\x04data\x18\x02 \x01(\v2\x1d.grpc.channelz.v1.ChannelDataR\x04data\x12=\n" +
0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x31, 0x0a, 0x04, 0x64, "\vchannel_ref\x18\x03 \x03(\v2\x1c.grpc.channelz.v1.ChannelRefR\n" +
0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x72, 0x70, 0x63, "channelRef\x12F\n" +
0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, "\x0esubchannel_ref\x18\x04 \x03(\v2\x1f.grpc.channelz.v1.SubchannelRefR\rsubchannelRef\x12:\n" +
0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3d, "\n" +
0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, "socket_ref\x18\x05 \x03(\v2\x1b.grpc.channelz.v1.SocketRefR\tsocketRef\"\xc2\x01\n" +
0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, "\x18ChannelConnectivityState\x12F\n" +
0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, "\x05state\x18\x01 \x01(\x0e20.grpc.channelz.v1.ChannelConnectivityState.StateR\x05state\"^\n" +
0x66, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x46, 0x0a, "\x05State\x12\v\n" +
0x0e, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x18, "\aUNKNOWN\x10\x00\x12\b\n" +
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, "\x04IDLE\x10\x01\x12\x0e\n" +
0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, "\n" +
0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, "CONNECTING\x10\x02\x12\t\n" +
0x65, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x3a, 0x0a, 0x0a, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, "\x05READY\x10\x03\x12\x15\n" +
0x72, 0x65, 0x66, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, "\x11TRANSIENT_FAILURE\x10\x04\x12\f\n" +
0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x63, "\bSHUTDOWN\x10\x05\"\xe9\x02\n" +
0x6b, 0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x09, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, "\vChannelData\x12@\n" +
0x66, 0x22, 0xb5, 0x02, 0x0a, 0x0a, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, "\x05state\x18\x01 \x01(\v2*.grpc.channelz.v1.ChannelConnectivityStateR\x05state\x12\x16\n" +
0x12, 0x31, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, "\x06target\x18\x02 \x01(\tR\x06target\x124\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, "\x05trace\x18\x03 \x01(\v2\x1e.grpc.channelz.v1.ChannelTraceR\x05trace\x12#\n" +
0x2e, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x03, "\rcalls_started\x18\x04 \x01(\x03R\fcallsStarted\x12'\n" +
0x72, 0x65, 0x66, 0x12, 0x31, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, "\x0fcalls_succeeded\x18\x05 \x01(\x03R\x0ecallsSucceeded\x12!\n" +
0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, "\fcalls_failed\x18\x06 \x01(\x03R\vcallsFailed\x12Y\n" +
0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, "\x1blast_call_started_timestamp\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\x18lastCallStartedTimestamp\"\x98\x03\n" +
0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3d, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, "\x11ChannelTraceEvent\x12 \n" +
0x6c, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, "\vdescription\x18\x01 \x01(\tR\vdescription\x12H\n" +
0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, "\bseverity\x18\x02 \x01(\x0e2,.grpc.channelz.v1.ChannelTraceEvent.SeverityR\bseverity\x128\n" +
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, "\ttimestamp\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\x12?\n" +
0x65, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x46, 0x0a, 0x0e, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, "\vchannel_ref\x18\x04 \x01(\v2\x1c.grpc.channelz.v1.ChannelRefH\x00R\n" +
0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, "channelRef\x12H\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, "\x0esubchannel_ref\x18\x05 \x01(\v2\x1f.grpc.channelz.v1.SubchannelRefH\x00R\rsubchannelRef\"E\n" +
0x2e, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x0d, "\bSeverity\x12\x0e\n" +
0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x3a, 0x0a, "\n" +
0x0a, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x05, 0x20, 0x03, 0x28, "CT_UNKNOWN\x10\x00\x12\v\n" +
0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, "\aCT_INFO\x10\x01\x12\x0e\n" +
0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x09, "\n" +
0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x66, 0x22, 0xc2, 0x01, 0x0a, 0x18, 0x43, 0x68, "CT_WARNING\x10\x02\x12\f\n" +
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, "\bCT_ERROR\x10\x03B\v\n" +
0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, "\tchild_ref\"\xc2\x01\n" +
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, "\fChannelTrace\x12*\n" +
0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, "\x11num_events_logged\x18\x01 \x01(\x03R\x0fnumEventsLogged\x12I\n" +
0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x53, 0x74, 0x61, 0x74, "\x12creation_timestamp\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x11creationTimestamp\x12;\n" +
0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x5e, "\x06events\x18\x03 \x03(\v2#.grpc.channelz.v1.ChannelTraceEventR\x06events\"c\n" +
0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, "\n" +
0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x0e, "ChannelRef\x12\x1d\n" +
0x0a, 0x0a, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x09, "\n" +
0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x52, 0x41, "channel_id\x18\x01 \x01(\x03R\tchannelId\x12\x12\n" +
0x4e, 0x53, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x04, "\x04name\x18\x02 \x01(\tR\x04nameJ\x04\b\x03\x10\x04J\x04\b\x04\x10\x05J\x04\b\x05\x10\x06J\x04\b\x06\x10\aJ\x04\b\a\x10\bJ\x04\b\b\x10\t\"l\n" +
0x12, 0x0c, 0x0a, 0x08, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x05, 0x22, 0xe9, "\rSubchannelRef\x12#\n" +
0x02, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, "\rsubchannel_id\x18\a \x01(\x03R\fsubchannelId\x12\x12\n" +
0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, "\x04name\x18\b \x01(\tR\x04nameJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03J\x04\b\x03\x10\x04J\x04\b\x04\x10\x05J\x04\b\x05\x10\x06J\x04\b\x06\x10\a\"`\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, "\tSocketRef\x12\x1b\n" +
0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, "\tsocket_id\x18\x03 \x01(\x03R\bsocketId\x12\x12\n" +
0x76, 0x69, 0x74, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, "\x04name\x18\x04 \x01(\tR\x04nameJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03J\x04\b\x05\x10\x06J\x04\b\x06\x10\aJ\x04\b\a\x10\bJ\x04\b\b\x10\t\"`\n" +
0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, "\tServerRef\x12\x1b\n" +
0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x34, 0x0a, 0x05, 0x74, 0x72, 0x61, 0x63, "\tserver_id\x18\x05 \x01(\x03R\bserverId\x12\x12\n" +
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, "\x04name\x18\x06 \x01(\tR\x04nameJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03J\x04\b\x03\x10\x04J\x04\b\x04\x10\x05J\x04\b\a\x10\bJ\x04\b\b\x10\t\"\xab\x01\n" +
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, "\x06Server\x12-\n" +
0x65, 0x6c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x12, 0x23, "\x03ref\x18\x01 \x01(\v2\x1b.grpc.channelz.v1.ServerRefR\x03ref\x120\n" +
0x0a, 0x0d, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, "\x04data\x18\x02 \x01(\v2\x1c.grpc.channelz.v1.ServerDataR\x04data\x12@\n" +
0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x53, 0x74, 0x61, 0x72, "\rlisten_socket\x18\x03 \x03(\v2\x1b.grpc.channelz.v1.SocketRefR\flistenSocket\"\x8e\x02\n" +
0x74, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x73, 0x75, 0x63, "\n" +
0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, 0x61, "ServerData\x124\n" +
0x6c, 0x6c, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, "\x05trace\x18\x01 \x01(\v2\x1e.grpc.channelz.v1.ChannelTraceR\x05trace\x12#\n" +
0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, "\rcalls_started\x18\x02 \x01(\x03R\fcallsStarted\x12'\n" +
0x28, 0x03, 0x52, 0x0b, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, "\x0fcalls_succeeded\x18\x03 \x01(\x03R\x0ecallsSucceeded\x12!\n" +
0x59, 0x0a, 0x1b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x73, 0x74, 0x61, "\fcalls_failed\x18\x04 \x01(\x03R\vcallsFailed\x12Y\n" +
0x72, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, "\x1blast_call_started_timestamp\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\x18lastCallStartedTimestamp\"\xa6\x02\n" +
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, "\x06Socket\x12-\n" +
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, "\x03ref\x18\x01 \x01(\v2\x1b.grpc.channelz.v1.SocketRefR\x03ref\x120\n" +
0x52, 0x18, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, "\x04data\x18\x02 \x01(\v2\x1c.grpc.channelz.v1.SocketDataR\x04data\x12/\n" +
0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x98, 0x03, 0x0a, 0x11, 0x43, "\x05local\x18\x03 \x01(\v2\x19.grpc.channelz.v1.AddressR\x05local\x121\n" +
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, "\x06remote\x18\x04 \x01(\v2\x19.grpc.channelz.v1.AddressR\x06remote\x126\n" +
0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, "\bsecurity\x18\x05 \x01(\v2\x1a.grpc.channelz.v1.SecurityR\bsecurity\x12\x1f\n" +
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, "\vremote_name\x18\x06 \x01(\tR\n" +
0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, "remoteName\"\x83\a\n" +
0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, "\n" +
0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x54, "SocketData\x12'\n" +
0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, "\x0fstreams_started\x18\x01 \x01(\x03R\x0estreamsStarted\x12+\n" +
0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x38, 0x0a, 0x09, "\x11streams_succeeded\x18\x02 \x01(\x03R\x10streamsSucceeded\x12%\n" +
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, "\x0estreams_failed\x18\x03 \x01(\x03R\rstreamsFailed\x12#\n" +
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, "\rmessages_sent\x18\x04 \x01(\x03R\fmessagesSent\x12+\n" +
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, "\x11messages_received\x18\x05 \x01(\x03R\x10messagesReceived\x12(\n" +
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3f, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, "\x10keep_alives_sent\x18\x06 \x01(\x03R\x0ekeepAlivesSent\x12h\n" +
0x6c, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, "#last_local_stream_created_timestamp\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\x1flastLocalStreamCreatedTimestamp\x12j\n" +
0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, "$last_remote_stream_created_timestamp\x18\b \x01(\v2\x1a.google.protobuf.TimestampR lastRemoteStreamCreatedTimestamp\x12Y\n" +
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x61, "\x1blast_message_sent_timestamp\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\x18lastMessageSentTimestamp\x12a\n" +
0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x48, 0x0a, 0x0e, 0x73, 0x75, 0x62, 0x63, 0x68, "\x1flast_message_received_timestamp\x18\n" +
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, " \x01(\v2\x1a.google.protobuf.TimestampR\x1clastMessageReceivedTimestamp\x12V\n" +
0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, "\x19local_flow_control_window\x18\v \x01(\v2\x1b.google.protobuf.Int64ValueR\x16localFlowControlWindow\x12X\n" +
0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, "\x1aremote_flow_control_window\x18\f \x01(\v2\x1b.google.protobuf.Int64ValueR\x17remoteFlowControlWindow\x126\n" +
0x48, 0x00, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, "\x06option\x18\r \x03(\v2\x1e.grpc.channelz.v1.SocketOptionR\x06option\"\xb8\x03\n" +
0x66, 0x22, 0x45, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0e, 0x0a, "\aAddress\x12M\n" +
0x0a, 0x43, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, "\rtcpip_address\x18\x01 \x01(\v2&.grpc.channelz.v1.Address.TcpIpAddressH\x00R\ftcpipAddress\x12G\n" +
0x07, 0x43, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x54, "\vuds_address\x18\x02 \x01(\v2$.grpc.channelz.v1.Address.UdsAddressH\x00R\n" +
0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x54, "udsAddress\x12M\n" +
0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, 0x0b, 0x0a, 0x09, 0x63, 0x68, 0x69, 0x6c, "\rother_address\x18\x03 \x01(\v2&.grpc.channelz.v1.Address.OtherAddressH\x00R\fotherAddress\x1aA\n" +
0x64, 0x5f, 0x72, 0x65, 0x66, 0x22, 0xc2, 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, "\fTcpIpAddress\x12\x1d\n" +
0x6c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x75, 0x6d, 0x5f, 0x65, 0x76, "\n" +
0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, "ip_address\x18\x01 \x01(\fR\tipAddress\x12\x12\n" +
0x03, 0x52, 0x0f, 0x6e, 0x75, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4c, 0x6f, 0x67, 0x67, "\x04port\x18\x02 \x01(\x05R\x04port\x1a(\n" +
0x65, 0x64, 0x12, 0x49, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, "\n" +
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, "UdsAddress\x12\x1a\n" +
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, "\bfilename\x18\x01 \x01(\tR\bfilename\x1aN\n" +
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, "\fOtherAddress\x12\x12\n" +
0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3b, 0x0a, "\x04name\x18\x01 \x01(\tR\x04name\x12*\n" +
0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, "\x05value\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x05valueB\t\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, "\aaddress\"\x96\x03\n" +
0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, "\bSecurity\x122\n" +
0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x63, 0x0a, 0x0a, 0x43, 0x68, "\x03tls\x18\x01 \x01(\v2\x1e.grpc.channelz.v1.Security.TlsH\x00R\x03tls\x12@\n" +
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, "\x05other\x18\x02 \x01(\v2(.grpc.channelz.v1.Security.OtherSecurityH\x00R\x05other\x1a\xb9\x01\n" +
0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x68, "\x03Tls\x12%\n" +
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, "\rstandard_name\x18\x01 \x01(\tH\x00R\fstandardName\x12\x1f\n" +
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, "\n" +
0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, "other_name\x18\x02 \x01(\tH\x00R\totherName\x12+\n" +
0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, "\x11local_certificate\x18\x03 \x01(\fR\x10localCertificate\x12-\n" +
0x6c, 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x66, "\x12remote_certificate\x18\x04 \x01(\fR\x11remoteCertificateB\x0e\n" +
0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, "\fcipher_suite\x1aO\n" +
0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, "\rOtherSecurity\x12\x12\n" +
0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, "\x04name\x18\x01 \x01(\tR\x04name\x12*\n" +
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, "\x05value\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x05valueB\a\n" +
0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, "\x05model\"n\n" +
0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0x60, 0x0a, "\fSocketOption\x12\x12\n" +
0x09, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" +
0x63, 0x6b, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, "\x05value\x18\x02 \x01(\tR\x05value\x124\n" +
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, "\n" +
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x01, 0x10, "additional\x18\x03 \x01(\v2\x14.google.protobuf.AnyR\n" +
0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, "additional\"L\n" +
0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, "\x13SocketOptionTimeout\x125\n" +
0x60, 0x0a, 0x09, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, "\bduration\x18\x01 \x01(\v2\x19.google.protobuf.DurationR\bduration\"c\n" +
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, "\x12SocketOptionLinger\x12\x16\n" +
0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, "\x06active\x18\x01 \x01(\bR\x06active\x125\n" +
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, "\bduration\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\bduration\"\xb2\b\n" +
0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, "\x13SocketOptionTcpInfo\x12\x1d\n" +
0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, "\n" +
0x09, 0x22, 0xab, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x03, "tcpi_state\x18\x01 \x01(\rR\ttcpiState\x12\"\n" +
0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, "\rtcpi_ca_state\x18\x02 \x01(\rR\vtcpiCaState\x12)\n" +
0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, "\x10tcpi_retransmits\x18\x03 \x01(\rR\x0ftcpiRetransmits\x12\x1f\n" +
0x76, 0x65, 0x72, 0x52, 0x65, 0x66, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x30, 0x0a, 0x04, 0x64, "\vtcpi_probes\x18\x04 \x01(\rR\n" +
0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x70, 0x63, "tcpiProbes\x12!\n" +
0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, "\ftcpi_backoff\x18\x05 \x01(\rR\vtcpiBackoff\x12!\n" +
0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, "\ftcpi_options\x18\x06 \x01(\rR\vtcpiOptions\x12&\n" +
0x0d, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x03, "\x0ftcpi_snd_wscale\x18\a \x01(\rR\rtcpiSndWscale\x12&\n" +
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, "\x0ftcpi_rcv_wscale\x18\b \x01(\rR\rtcpiRcvWscale\x12\x19\n" +
0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, "\btcpi_rto\x18\t \x01(\rR\atcpiRto\x12\x19\n" +
0x66, 0x52, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, "\btcpi_ato\x18\n" +
0x8e, 0x02, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, " \x01(\rR\atcpiAto\x12 \n" +
0x0a, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, "\ftcpi_snd_mss\x18\v \x01(\rR\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, "tcpiSndMss\x12 \n" +
0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x74, "\ftcpi_rcv_mss\x18\f \x01(\rR\n" +
0x72, 0x61, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x73, 0x74, "tcpiRcvMss\x12!\n" +
0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x61, 0x6c, "\ftcpi_unacked\x18\r \x01(\rR\vtcpiUnacked\x12\x1f\n" +
0x6c, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x61, 0x6c, "\vtcpi_sacked\x18\x0e \x01(\rR\n" +
0x6c, 0x73, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, "tcpiSacked\x12\x1b\n" +
0x28, 0x03, 0x52, 0x0e, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, "\ttcpi_lost\x18\x0f \x01(\rR\btcpiLost\x12!\n" +
0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x5f, 0x66, 0x61, 0x69, 0x6c, "\ftcpi_retrans\x18\x10 \x01(\rR\vtcpiRetrans\x12!\n" +
0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x46, "\ftcpi_fackets\x18\x11 \x01(\rR\vtcpiFackets\x12-\n" +
0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x59, 0x0a, 0x1b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x61, "\x13tcpi_last_data_sent\x18\x12 \x01(\rR\x10tcpiLastDataSent\x12+\n" +
0x6c, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, "\x12tcpi_last_ack_sent\x18\x13 \x01(\rR\x0ftcpiLastAckSent\x12-\n" +
0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, "\x13tcpi_last_data_recv\x18\x14 \x01(\rR\x10tcpiLastDataRecv\x12+\n" +
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, "\x12tcpi_last_ack_recv\x18\x15 \x01(\rR\x0ftcpiLastAckRecv\x12\x1b\n" +
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x18, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x61, 0x6c, 0x6c, "\ttcpi_pmtu\x18\x16 \x01(\rR\btcpiPmtu\x12*\n" +
0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, "\x11tcpi_rcv_ssthresh\x18\x17 \x01(\rR\x0ftcpiRcvSsthresh\x12\x19\n" +
0x22, 0xa6, 0x02, 0x0a, 0x06, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x2d, 0x0a, 0x03, 0x72, "\btcpi_rtt\x18\x18 \x01(\rR\atcpiRtt\x12\x1f\n" +
0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, "\vtcpi_rttvar\x18\x19 \x01(\rR\n" +
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x63, 0x6b, "tcpiRttvar\x12*\n" +
0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x30, 0x0a, 0x04, 0x64, 0x61, "\x11tcpi_snd_ssthresh\x18\x1a \x01(\rR\x0ftcpiSndSsthresh\x12\"\n" +
0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, "\rtcpi_snd_cwnd\x18\x1b \x01(\rR\vtcpiSndCwnd\x12\x1f\n" +
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x63, 0x6b, "\vtcpi_advmss\x18\x1c \x01(\rR\n" +
0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x05, "tcpiAdvmss\x12'\n" +
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x72, "\x0ftcpi_reordering\x18\x1d \x01(\rR\x0etcpiReordering\"b\n" +
0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x41, "\x15GetTopChannelsRequest\x12(\n" +
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x12, 0x31, 0x0a, "\x10start_channel_id\x18\x01 \x01(\x03R\x0estartChannelId\x12\x1f\n" +
0x06, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, "\vmax_results\x18\x02 \x01(\x03R\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, "maxResults\"_\n" +
0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, "\x16GetTopChannelsResponse\x123\n" +
0x12, 0x36, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, "\achannel\x18\x01 \x03(\v2\x19.grpc.channelz.v1.ChannelR\achannel\x12\x10\n" +
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, "\x03end\x18\x02 \x01(\bR\x03end\"\\\n" +
0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, "\x11GetServersRequest\x12&\n" +
0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, "\x0fstart_server_id\x18\x01 \x01(\x03R\rstartServerId\x12\x1f\n" +
0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, "\vmax_results\x18\x02 \x01(\x03R\n" +
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x83, 0x07, 0x0a, 0x0a, 0x53, 0x6f, "maxResults\"X\n" +
0x63, 0x6b, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, "\x12GetServersResponse\x120\n" +
0x61, 0x6d, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, "\x06server\x18\x01 \x03(\v2\x18.grpc.channelz.v1.ServerR\x06server\x12\x10\n" +
0x03, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, "\x03end\x18\x02 \x01(\bR\x03end\"/\n" +
0x64, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x5f, 0x73, 0x75, 0x63, "\x10GetServerRequest\x12\x1b\n" +
0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x73, 0x74, "\tserver_id\x18\x01 \x01(\x03R\bserverId\"E\n" +
0x72, 0x65, 0x61, 0x6d, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x12, 0x25, "\x11GetServerResponse\x120\n" +
0x0a, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, "\x06server\x18\x01 \x01(\v2\x18.grpc.channelz.v1.ServerR\x06server\"\x7f\n" +
0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x46, "\x17GetServerSocketsRequest\x12\x1b\n" +
0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, "\tserver_id\x18\x01 \x01(\x03R\bserverId\x12&\n" +
0x73, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6d, 0x65, "\x0fstart_socket_id\x18\x02 \x01(\x03R\rstartSocketId\x12\x1f\n" +
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x65, "\vmax_results\x18\x03 \x01(\x03R\n" +
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x18, "maxResults\"h\n" +
0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, "\x18GetServerSocketsResponse\x12:\n" +
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x5f, "\n" +
0x61, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, "socket_ref\x18\x01 \x03(\v2\x1b.grpc.channelz.v1.SocketRefR\tsocketRef\x12\x10\n" +
0x03, 0x52, 0x0e, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x53, 0x65, 0x6e, "\x03end\x18\x02 \x01(\bR\x03end\"2\n" +
0x74, 0x12, 0x68, 0x0a, 0x23, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, "\x11GetChannelRequest\x12\x1d\n" +
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, "\n" +
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, "channel_id\x18\x01 \x01(\x03R\tchannelId\"I\n" +
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, "\x12GetChannelResponse\x123\n" +
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x1f, 0x6c, 0x61, 0x73, 0x74, "\achannel\x18\x01 \x01(\v2\x19.grpc.channelz.v1.ChannelR\achannel\";\n" +
0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x72, 0x65, 0x61, 0x74, "\x14GetSubchannelRequest\x12#\n" +
0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x6a, 0x0a, 0x24, 0x6c, "\rsubchannel_id\x18\x01 \x01(\x03R\fsubchannelId\"U\n" +
0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, "\x15GetSubchannelResponse\x12<\n" +
0x6d, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, "\n" +
0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, "subchannel\x18\x01 \x01(\v2\x1c.grpc.channelz.v1.SubchannelR\n" +
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, "subchannel\"I\n" +
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x74, "\x10GetSocketRequest\x12\x1b\n" +
0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, "\tsocket_id\x18\x01 \x01(\x03R\bsocketId\x12\x18\n" +
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x59, 0x0a, 0x1b, 0x6c, 0x61, 0x73, 0x74, 0x5f, "\asummary\x18\x02 \x01(\bR\asummary\"E\n" +
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, "\x11GetSocketResponse\x120\n" +
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, "\x06socket\x18\x01 \x01(\v2\x18.grpc.channelz.v1.SocketR\x06socket2\x9a\x05\n" +
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, "\bChannelz\x12c\n" +
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x18, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x65, "\x0eGetTopChannels\x12'.grpc.channelz.v1.GetTopChannelsRequest\x1a(.grpc.channelz.v1.GetTopChannelsResponse\x12W\n" +
0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, "\n" +
0x6d, 0x70, 0x12, 0x61, 0x0a, 0x1f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, "GetServers\x12#.grpc.channelz.v1.GetServersRequest\x1a$.grpc.channelz.v1.GetServersResponse\x12T\n" +
0x67, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, "\tGetServer\x12\".grpc.channelz.v1.GetServerRequest\x1a#.grpc.channelz.v1.GetServerResponse\x12i\n" +
0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, "\x10GetServerSockets\x12).grpc.channelz.v1.GetServerSocketsRequest\x1a*.grpc.channelz.v1.GetServerSocketsResponse\x12W\n" +
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, "\n" +
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x1c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x65, 0x73, "GetChannel\x12#.grpc.channelz.v1.GetChannelRequest\x1a$.grpc.channelz.v1.GetChannelResponse\x12`\n" +
0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, "\rGetSubchannel\x12&.grpc.channelz.v1.GetSubchannelRequest\x1a'.grpc.channelz.v1.GetSubchannelResponse\x12T\n" +
0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x56, 0x0a, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x66, "\tGetSocket\x12\".grpc.channelz.v1.GetSocketRequest\x1a#.grpc.channelz.v1.GetSocketResponseBX\n" +
0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x77, 0x69, 0x6e, 0x64, "\x13io.grpc.channelz.v1B\rChannelzProtoP\x01Z0google.golang.org/grpc/channelz/grpc_channelz_v1b\x06proto3"
0x6f, 0x77, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x16, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x6c, 0x6f, 0x77,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x58, 0x0a,
0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x0c, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x17,
0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x46, 0x6c, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x36, 0x0a, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65,
0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22,
0xb8, 0x03, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x74,
0x63, 0x70, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x54, 0x63,
0x70, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x63,
0x70, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x47, 0x0a, 0x0b, 0x75, 0x64,
0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e,
0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x55, 0x64, 0x73, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0a, 0x75, 0x64, 0x73, 0x41, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x72, 0x70,
0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x1a, 0x41, 0x0a, 0x0c, 0x54, 0x63, 0x70, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
0x04, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x28, 0x0a, 0x0a, 0x55, 0x64, 0x73, 0x41, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x1a,
0x4e, 0x0a, 0x0c, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42,
0x09, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x96, 0x03, 0x0a, 0x08, 0x53,
0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x32, 0x0a, 0x03, 0x74, 0x6c, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
0x2e, 0x54, 0x6c, 0x73, 0x48, 0x00, 0x52, 0x03, 0x74, 0x6c, 0x73, 0x12, 0x40, 0x0a, 0x05, 0x6f,
0x74, 0x68, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x72, 0x70,
0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65,
0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2e, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x53, 0x65, 0x63, 0x75,
0x72, 0x69, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x1a, 0xb9, 0x01,
0x0a, 0x03, 0x54, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72,
0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c,
0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0a,
0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x48, 0x00, 0x52, 0x09, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a,
0x11, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x43,
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x63, 0x69, 0x70,
0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x4f, 0x0a, 0x0d, 0x4f, 0x74, 0x68,
0x65, 0x72, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6d, 0x6f,
0x64, 0x65, 0x6c, 0x22, 0x6e, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x34, 0x0a,
0x0a, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
0x6e, 0x61, 0x6c, 0x22, 0x4c, 0x0a, 0x13, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x22, 0x63, 0x0a, 0x12, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x4c, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12,
0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb2, 0x08, 0x0a, 0x13, 0x53, 0x6f, 0x63, 0x6b, 0x65,
0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x63, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d,
0x0a, 0x0a, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x09, 0x74, 0x63, 0x70, 0x69, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a,
0x0d, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x63, 0x61, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x43, 0x61, 0x53, 0x74, 0x61, 0x74,
0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x63, 0x70,
0x69, 0x52, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b,
0x74, 0x63, 0x70, 0x69, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x0a, 0x74, 0x63, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x12, 0x21, 0x0a,
0x0c, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x05, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66,
0x12, 0x21, 0x0a, 0x0c, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x4f, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x73, 0x6e, 0x64, 0x5f,
0x77, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x63,
0x70, 0x69, 0x53, 0x6e, 0x64, 0x57, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x74,
0x63, 0x70, 0x69, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x77, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x08,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x63, 0x70, 0x69, 0x52, 0x63, 0x76, 0x57, 0x73, 0x63,
0x61, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x72, 0x74, 0x6f, 0x18,
0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x63, 0x70, 0x69, 0x52, 0x74, 0x6f, 0x12, 0x19,
0x0a, 0x08, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x61, 0x74, 0x6f, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x07, 0x74, 0x63, 0x70, 0x69, 0x41, 0x74, 0x6f, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x63, 0x70,
0x69, 0x5f, 0x73, 0x6e, 0x64, 0x5f, 0x6d, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0a, 0x74, 0x63, 0x70, 0x69, 0x53, 0x6e, 0x64, 0x4d, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x74,
0x63, 0x70, 0x69, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x6d, 0x73, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x0a, 0x74, 0x63, 0x70, 0x69, 0x52, 0x63, 0x76, 0x4d, 0x73, 0x73, 0x12, 0x21, 0x0a,
0x0c, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x75, 0x6e, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x0d, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x55, 0x6e, 0x61, 0x63, 0x6b, 0x65, 0x64,
0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x73, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x18,
0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x63, 0x70, 0x69, 0x53, 0x61, 0x63, 0x6b, 0x65,
0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x6c, 0x6f, 0x73, 0x74, 0x18, 0x0f,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x74, 0x63, 0x70, 0x69, 0x4c, 0x6f, 0x73, 0x74, 0x12, 0x21,
0x0a, 0x0c, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x18, 0x10,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x52, 0x65, 0x74, 0x72, 0x61, 0x6e,
0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x66, 0x61, 0x63, 0x6b, 0x65, 0x74,
0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x46, 0x61, 0x63,
0x6b, 0x65, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x6c, 0x61, 0x73,
0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x10, 0x74, 0x63, 0x70, 0x69, 0x4c, 0x61, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x53,
0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x6c, 0x61, 0x73, 0x74,
0x5f, 0x61, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0f, 0x74, 0x63, 0x70, 0x69, 0x4c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x6b, 0x53, 0x65, 0x6e, 0x74,
0x12, 0x2d, 0x0a, 0x13, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, 0x61,
0x74, 0x61, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x74,
0x63, 0x70, 0x69, 0x4c, 0x61, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x63, 0x76, 0x12,
0x2b, 0x0a, 0x12, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x6b,
0x5f, 0x72, 0x65, 0x63, 0x76, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x63, 0x70,
0x69, 0x4c, 0x61, 0x73, 0x74, 0x41, 0x63, 0x6b, 0x52, 0x65, 0x63, 0x76, 0x12, 0x1b, 0x0a, 0x09,
0x74, 0x63, 0x70, 0x69, 0x5f, 0x70, 0x6d, 0x74, 0x75, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x08, 0x74, 0x63, 0x70, 0x69, 0x50, 0x6d, 0x74, 0x75, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x63, 0x70,
0x69, 0x5f, 0x72, 0x63, 0x76, 0x5f, 0x73, 0x73, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x18, 0x17,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x63, 0x70, 0x69, 0x52, 0x63, 0x76, 0x53, 0x73, 0x74,
0x68, 0x72, 0x65, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x72, 0x74,
0x74, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x63, 0x70, 0x69, 0x52, 0x74, 0x74,
0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x72, 0x74, 0x74, 0x76, 0x61, 0x72, 0x18,
0x19, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x63, 0x70, 0x69, 0x52, 0x74, 0x74, 0x76, 0x61,
0x72, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x73, 0x6e, 0x64, 0x5f, 0x73, 0x73,
0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x63,
0x70, 0x69, 0x53, 0x6e, 0x64, 0x53, 0x73, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x12, 0x22, 0x0a,
0x0d, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x73, 0x6e, 0x64, 0x5f, 0x63, 0x77, 0x6e, 0x64, 0x18, 0x1b,
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x53, 0x6e, 0x64, 0x43, 0x77, 0x6e,
0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x61, 0x64, 0x76, 0x6d, 0x73, 0x73,
0x18, 0x1c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x63, 0x70, 0x69, 0x41, 0x64, 0x76, 0x6d,
0x73, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x63, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x6f, 0x72, 0x64,
0x65, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x63, 0x70,
0x69, 0x52, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x62, 0x0a, 0x15, 0x47,
0x65, 0x74, 0x54, 0x6f, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e,
0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x1f,
0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22,
0x5f, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x72, 0x70,
0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x10,
0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x6e, 0x64,
0x22, 0x5c, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d,
0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1f, 0x0a,
0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01,
0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x58,
0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x2f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x22, 0x45, 0x0a, 0x11, 0x47, 0x65, 0x74,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30,
0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76,
0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x22, 0x7f, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x6f, 0x63,
0x6b, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72,
0x74, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x03, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x49, 0x64,
0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18,
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
0x73, 0x22, 0x68, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x6f,
0x63, 0x6b, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a,
0x0a, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x66, 0x52, 0x09,
0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x32, 0x0a, 0x11, 0x47,
0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x22,
0x49, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x3b, 0x0a, 0x14, 0x47, 0x65,
0x74, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x22, 0x55, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x75,
0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x3c, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x6c, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x49,
0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x49, 0x64, 0x12,
0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x45, 0x0a, 0x11, 0x47, 0x65, 0x74,
0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30,
0x0a, 0x06, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76,
0x31, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x06, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74,
0x32, 0x9a, 0x05, 0x0a, 0x08, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x12, 0x63, 0x0a,
0x0e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12,
0x27, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e,
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54,
0x6f, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x57, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
0x12, 0x23, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a,
0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76,
0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x09, 0x47,
0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67,
0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e,
0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x69, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x6f,
0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76,
0x65, 0x72, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x2a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a,
0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x6f, 0x63,
0x6b, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0a,
0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x23, 0x2e, 0x67, 0x72, 0x70,
0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e,
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x63,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x26, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27,
0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76,
0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x6f,
0x63, 0x6b, 0x65, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x63, 0x6b, 0x65,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53,
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x58, 0x0a,
0x13, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x7a, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x50, 0x72,
0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f,
0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7a, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x7a, 0x5f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var ( var (
file_grpc_channelz_v1_channelz_proto_rawDescOnce sync.Once file_grpc_channelz_v1_channelz_proto_rawDescOnce sync.Once

View File

@ -183,25 +183,25 @@ type ChannelzServer interface {
type UnimplementedChannelzServer struct{} type UnimplementedChannelzServer struct{}
func (UnimplementedChannelzServer) GetTopChannels(context.Context, *GetTopChannelsRequest) (*GetTopChannelsResponse, error) { func (UnimplementedChannelzServer) GetTopChannels(context.Context, *GetTopChannelsRequest) (*GetTopChannelsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetTopChannels not implemented") return nil, status.Error(codes.Unimplemented, "method GetTopChannels not implemented")
} }
func (UnimplementedChannelzServer) GetServers(context.Context, *GetServersRequest) (*GetServersResponse, error) { func (UnimplementedChannelzServer) GetServers(context.Context, *GetServersRequest) (*GetServersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetServers not implemented") return nil, status.Error(codes.Unimplemented, "method GetServers not implemented")
} }
func (UnimplementedChannelzServer) GetServer(context.Context, *GetServerRequest) (*GetServerResponse, error) { func (UnimplementedChannelzServer) GetServer(context.Context, *GetServerRequest) (*GetServerResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetServer not implemented") return nil, status.Error(codes.Unimplemented, "method GetServer not implemented")
} }
func (UnimplementedChannelzServer) GetServerSockets(context.Context, *GetServerSocketsRequest) (*GetServerSocketsResponse, error) { func (UnimplementedChannelzServer) GetServerSockets(context.Context, *GetServerSocketsRequest) (*GetServerSocketsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetServerSockets not implemented") return nil, status.Error(codes.Unimplemented, "method GetServerSockets not implemented")
} }
func (UnimplementedChannelzServer) GetChannel(context.Context, *GetChannelRequest) (*GetChannelResponse, error) { func (UnimplementedChannelzServer) GetChannel(context.Context, *GetChannelRequest) (*GetChannelResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetChannel not implemented") return nil, status.Error(codes.Unimplemented, "method GetChannel not implemented")
} }
func (UnimplementedChannelzServer) GetSubchannel(context.Context, *GetSubchannelRequest) (*GetSubchannelResponse, error) { func (UnimplementedChannelzServer) GetSubchannel(context.Context, *GetSubchannelRequest) (*GetSubchannelResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSubchannel not implemented") return nil, status.Error(codes.Unimplemented, "method GetSubchannel not implemented")
} }
func (UnimplementedChannelzServer) GetSocket(context.Context, *GetSocketRequest) (*GetSocketResponse, error) { func (UnimplementedChannelzServer) GetSocket(context.Context, *GetSocketRequest) (*GetSocketResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSocket not implemented") return nil, status.Error(codes.Unimplemented, "method GetSocket not implemented")
} }
func (UnimplementedChannelzServer) testEmbeddedByValue() {} func (UnimplementedChannelzServer) testEmbeddedByValue() {}

View File

@ -689,22 +689,31 @@ func (cc *ClientConn) Connect() {
cc.mu.Unlock() cc.mu.Unlock()
} }
// waitForResolvedAddrs blocks until the resolver has provided addresses or the // waitForResolvedAddrs blocks until the resolver provides addresses or the
// context expires. Returns nil unless the context expires first; otherwise // context expires, whichever happens first.
// returns a status error based on the context. //
func (cc *ClientConn) waitForResolvedAddrs(ctx context.Context) error { // Error is nil unless the context expires first; otherwise returns a status
// error based on the context.
//
// The returned boolean indicates whether it did block or not. If the
// resolution has already happened once before, it returns false without
// blocking. Otherwise, it wait for the resolution and return true if
// resolution has succeeded or return false along with error if resolution has
// failed.
func (cc *ClientConn) waitForResolvedAddrs(ctx context.Context) (bool, error) {
// This is on the RPC path, so we use a fast path to avoid the // This is on the RPC path, so we use a fast path to avoid the
// more-expensive "select" below after the resolver has returned once. // more-expensive "select" below after the resolver has returned once.
if cc.firstResolveEvent.HasFired() { if cc.firstResolveEvent.HasFired() {
return nil return false, nil
} }
internal.NewStreamWaitingForResolver()
select { select {
case <-cc.firstResolveEvent.Done(): case <-cc.firstResolveEvent.Done():
return nil return true, nil
case <-ctx.Done(): case <-ctx.Done():
return status.FromContextError(ctx.Err()).Err() return false, status.FromContextError(ctx.Err()).Err()
case <-cc.ctx.Done(): case <-cc.ctx.Done():
return ErrClientConnClosing return false, ErrClientConnClosing
} }
} }
@ -1231,8 +1240,7 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error)
// adjustParams updates parameters used to create transports upon // adjustParams updates parameters used to create transports upon
// receiving a GoAway. // receiving a GoAway.
func (ac *addrConn) adjustParams(r transport.GoAwayReason) { func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
switch r { if r == transport.GoAwayTooManyPings {
case transport.GoAwayTooManyPings:
v := 2 * ac.dopts.copts.KeepaliveParams.Time v := 2 * ac.dopts.copts.KeepaliveParams.Time
ac.cc.mu.Lock() ac.cc.mu.Lock()
if v > ac.cc.keepaliveParams.Time { if v > ac.cc.keepaliveParams.Time {

View File

@ -50,7 +50,10 @@ import (
const ( const (
defaultTestTimeout = 10 * time.Second defaultTestTimeout = 10 * time.Second
defaultTestShortTimeout = 10 * time.Millisecond
stateRecordingBalancerName = "state_recording_balancer" stateRecordingBalancerName = "state_recording_balancer"
grpclbServiceConfig = `{"loadBalancingConfig": [{"grpclb": {}}]}`
rrServiceConfig = `{"loadBalancingPolicy": [{"round_robin": {}}]}`
) )
var testBalancerBuilder = newStateRecordingBalancerBuilder() var testBalancerBuilder = newStateRecordingBalancerBuilder()
@ -60,7 +63,7 @@ func init() {
} }
func parseCfg(r *manual.Resolver, s string) *serviceconfig.ParseResult { func parseCfg(r *manual.Resolver, s string) *serviceconfig.ParseResult {
scpr := r.CC.ParseServiceConfig(s) scpr := r.CC().ParseServiceConfig(s)
if scpr.Err != nil { if scpr.Err != nil {
panic(fmt.Sprintf("Error parsing config %q: %v", s, scpr.Err)) panic(fmt.Sprintf("Error parsing config %q: %v", s, scpr.Err))
} }
@ -108,7 +111,7 @@ func (s) TestDialWithTimeout(t *testing.T) {
} }
} }
func (s) TestDialWithMultipleBackendsNotSendingServerPreface(t *testing.T) { func (s) TestNewClientWithMultipleBackendsNotSendingServerPreface(t *testing.T) {
lis1, err := net.Listen("tcp", "localhost:0") lis1, err := net.Listen("tcp", "localhost:0")
if err != nil { if err != nil {
t.Fatalf("Error while listening. Err: %v", err) t.Fatalf("Error while listening. Err: %v", err)
@ -146,10 +149,11 @@ func (s) TestDialWithMultipleBackendsNotSendingServerPreface(t *testing.T) {
r := manual.NewBuilderWithScheme("whatever") r := manual.NewBuilderWithScheme("whatever")
r.InitialState(resolver.State{Addresses: []resolver.Address{lis1Addr, lis2Addr}}) r.InitialState(resolver.State{Addresses: []resolver.Address{lis1Addr, lis2Addr}})
client, err := Dial(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r)) client, err := NewClient(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
if err != nil { if err != nil {
t.Fatalf("Dial failed. Err: %v", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
client.Connect()
defer client.Close() defer client.Close()
timeout := time.After(5 * time.Second) timeout := time.After(5 * time.Second)
select { select {
@ -313,9 +317,9 @@ func (s) TestCloseConnectionWhenServerPrefaceNotReceived(t *testing.T) {
break break
} }
}() }()
client, err := Dial(lis.Addr().String(), WithTransportCredentials(insecure.NewCredentials()), withMinConnectDeadline(func() time.Duration { return time.Millisecond * 500 })) client, err := NewClient(lis.Addr().String(), WithTransportCredentials(insecure.NewCredentials()), withMinConnectDeadline(func() time.Duration { return time.Millisecond * 500 }))
if err != nil { if err != nil {
t.Fatalf("Error while dialing. Err: %v", err) t.Fatalf("grpc.NewClient(%q) = %v", lis.Addr().String(), err)
} }
go stayConnected(client) go stayConnected(client)
@ -379,9 +383,9 @@ func (s) TestBackoffWhenNoServerPrefaceReceived(t *testing.T) {
Backoff: bc, Backoff: bc,
MinConnectTimeout: 1 * time.Second, MinConnectTimeout: 1 * time.Second,
} }
cc, err := Dial(lis.Addr().String(), WithTransportCredentials(insecure.NewCredentials()), WithConnectParams(cp)) cc, err := NewClient(lis.Addr().String(), WithTransportCredentials(insecure.NewCredentials()), WithConnectParams(cp))
if err != nil { if err != nil {
t.Fatalf("Unexpected error from Dial(%v) = %v", lis.Addr(), err) t.Fatalf("grpc.NewClient(%q) = %v", lis.Addr().String(), err)
} }
defer cc.Close() defer cc.Close()
go stayConnected(cc) go stayConnected(cc)
@ -420,7 +424,7 @@ func (s) TestWithTransportCredentialsTLS(t *testing.T) {
// When creating a transport configured with n addresses, only calculate the // When creating a transport configured with n addresses, only calculate the
// backoff once per "round" of attempts instead of once per address (n times // backoff once per "round" of attempts instead of once per address (n times
// per "round" of attempts) for old pickfirst and once per address for new pickfirst. // per "round" of attempts) for old pickfirst and once per address for new pickfirst.
func (s) TestDial_BackoffCountPerRetryGroup(t *testing.T) { func (s) TestNewClient_BackoffCountPerRetryGroup(t *testing.T) {
var attempts uint32 var attempts uint32
wantBackoffs := uint32(1) wantBackoffs := uint32(1)
if envconfig.NewPickFirstEnabled { if envconfig.NewPickFirstEnabled {
@ -437,9 +441,6 @@ func (s) TestDial_BackoffCountPerRetryGroup(t *testing.T) {
return 0 return 0
} }
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
lis1, err := net.Listen("tcp", "localhost:0") lis1, err := net.Listen("tcp", "localhost:0")
if err != nil { if err != nil {
t.Fatalf("Error while listening. Err: %v", err) t.Fatalf("Error while listening. Err: %v", err)
@ -482,7 +483,7 @@ func (s) TestDial_BackoffCountPerRetryGroup(t *testing.T) {
{Addr: lis1.Addr().String()}, {Addr: lis1.Addr().String()},
{Addr: lis2.Addr().String()}, {Addr: lis2.Addr().String()},
}}) }})
client, err := DialContext(ctx, "whatever:///this-gets-overwritten", client, err := NewClient("whatever:///this-gets-overwritten",
WithTransportCredentials(insecure.NewCredentials()), WithTransportCredentials(insecure.NewCredentials()),
WithResolvers(rb), WithResolvers(rb),
withMinConnectDeadline(getMinConnectTimeout)) withMinConnectDeadline(getMinConnectTimeout))
@ -490,7 +491,7 @@ func (s) TestDial_BackoffCountPerRetryGroup(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer client.Close() defer client.Close()
client.Connect()
timeout := time.After(15 * time.Second) timeout := time.After(15 * time.Second)
select { select {
@ -557,8 +558,8 @@ func (b *fakeBundleCreds) TransportCredentials() credentials.TransportCredential
func (s) TestCredentialsMisuse(t *testing.T) { func (s) TestCredentialsMisuse(t *testing.T) {
// Use of no transport creds and no creds bundle must fail. // Use of no transport creds and no creds bundle must fail.
if _, err := Dial("passthrough:///Non-Existent.Server:80"); err != errNoTransportSecurity { if _, err := NewClient("passthrough:///Non-Existent.Server:80"); err != errNoTransportSecurity {
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errNoTransportSecurity) t.Fatalf("grpc.NewClient() failed with error: %v, want: %v", err, errNoTransportSecurity)
} }
// Use of both transport creds and creds bundle must fail. // Use of both transport creds and creds bundle must fail.
@ -570,19 +571,19 @@ func (s) TestCredentialsMisuse(t *testing.T) {
WithTransportCredentials(creds), WithTransportCredentials(creds),
WithCredentialsBundle(&fakeBundleCreds{transportCreds: creds}), WithCredentialsBundle(&fakeBundleCreds{transportCreds: creds}),
} }
if _, err := Dial("passthrough:///Non-Existent.Server:80", dopts...); err != errTransportCredsAndBundle { if _, err := NewClient("passthrough:///Non-Existent.Server:80", dopts...); err != errTransportCredsAndBundle {
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errTransportCredsAndBundle) t.Fatalf("grpc.NewClient() failed with error: %v, want: %v", err, errTransportCredsAndBundle)
} }
// Use of perRPC creds requiring transport security over an insecure // Use of perRPC creds requiring transport security over an insecure
// transport must fail. // transport must fail.
if _, err := Dial("passthrough:///Non-Existent.Server:80", WithPerRPCCredentials(securePerRPCCredentials{}), WithTransportCredentials(insecure.NewCredentials())); err != errTransportCredentialsMissing { if _, err := NewClient("passthrough:///Non-Existent.Server:80", WithPerRPCCredentials(securePerRPCCredentials{}), WithTransportCredentials(insecure.NewCredentials())); err != errTransportCredentialsMissing {
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errTransportCredentialsMissing) t.Fatalf("grpc.NewClient() failed with error: %v, want: %v", err, errTransportCredentialsMissing)
} }
// Use of a creds bundle with nil transport credentials must fail. // Use of a creds bundle with nil transport credentials must fail.
if _, err := Dial("passthrough:///Non-Existent.Server:80", WithCredentialsBundle(&fakeBundleCreds{})); err != errNoTransportCredsInBundle { if _, err := NewClient("passthrough:///Non-Existent.Server:80", WithCredentialsBundle(&fakeBundleCreds{})); err != errNoTransportCredsInBundle {
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errTransportCredsAndBundle) t.Fatalf("grpc.NewClient() failed with error: %v, want: %v", err, errTransportCredsAndBundle)
} }
} }
@ -621,9 +622,9 @@ func (s) TestWithConnectParams(t *testing.T) {
func testBackoffConfigSet(t *testing.T, wantBackoff internalbackoff.Exponential, opts ...DialOption) { func testBackoffConfigSet(t *testing.T, wantBackoff internalbackoff.Exponential, opts ...DialOption) {
opts = append(opts, WithTransportCredentials(insecure.NewCredentials())) opts = append(opts, WithTransportCredentials(insecure.NewCredentials()))
conn, err := Dial("passthrough:///foo:80", opts...) conn, err := NewClient("passthrough:///foo:80", opts...)
if err != nil { if err != nil {
t.Fatalf("unexpected error dialing connection: %v", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
defer conn.Close() defer conn.Close()
@ -644,9 +645,9 @@ func testBackoffConfigSet(t *testing.T, wantBackoff internalbackoff.Exponential,
func (s) TestConnectParamsWithMinConnectTimeout(t *testing.T) { func (s) TestConnectParamsWithMinConnectTimeout(t *testing.T) {
// Default value specified for minConnectTimeout in the spec is 20 seconds. // Default value specified for minConnectTimeout in the spec is 20 seconds.
mct := 1 * time.Minute mct := 1 * time.Minute
conn, err := Dial("passthrough:///foo:80", WithTransportCredentials(insecure.NewCredentials()), WithConnectParams(ConnectParams{MinConnectTimeout: mct})) conn, err := NewClient("passthrough:///foo:80", WithTransportCredentials(insecure.NewCredentials()), WithConnectParams(ConnectParams{MinConnectTimeout: mct}))
if err != nil { if err != nil {
t.Fatalf("unexpected error dialing connection: %v", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
defer conn.Close() defer conn.Close()
@ -658,15 +659,15 @@ func (s) TestConnectParamsWithMinConnectTimeout(t *testing.T) {
func (s) TestResolverServiceConfigBeforeAddressNotPanic(t *testing.T) { func (s) TestResolverServiceConfigBeforeAddressNotPanic(t *testing.T) {
r := manual.NewBuilderWithScheme("whatever") r := manual.NewBuilderWithScheme("whatever")
cc, err := Dial(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r)) cc, err := NewClient(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
if err != nil { if err != nil {
t.Fatalf("failed to dial: %v", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
defer cc.Close() defer cc.Close()
cc.Connect()
// SwitchBalancer before NewAddress. There was no balancer created, this // SwitchBalancer before NewAddress. There was no balancer created, this
// makes sure we don't call close on nil balancerWrapper. // makes sure we don't call close on nil balancerWrapper.
r.UpdateState(resolver.State{ServiceConfig: parseCfg(r, `{"loadBalancingPolicy": "round_robin"}`)}) // This should not panic. r.UpdateState(resolver.State{ServiceConfig: r.CC().ParseServiceConfig(grpclbServiceConfig)}) // This should not panic.
time.Sleep(time.Second) // Sleep to make sure the service config is handled by ClientConn. time.Sleep(time.Second) // Sleep to make sure the service config is handled by ClientConn.
} }
@ -674,26 +675,26 @@ func (s) TestResolverServiceConfigBeforeAddressNotPanic(t *testing.T) {
func (s) TestResolverServiceConfigWhileClosingNotPanic(t *testing.T) { func (s) TestResolverServiceConfigWhileClosingNotPanic(t *testing.T) {
for i := 0; i < 10; i++ { // Run this multiple times to make sure it doesn't panic. for i := 0; i < 10; i++ { // Run this multiple times to make sure it doesn't panic.
r := manual.NewBuilderWithScheme(fmt.Sprintf("whatever-%d", i)) r := manual.NewBuilderWithScheme(fmt.Sprintf("whatever-%d", i))
cc, err := NewClient(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
cc, err := Dial(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
if err != nil { if err != nil {
t.Fatalf("failed to dial: %v", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
cc.Connect()
// Send a new service config while closing the ClientConn. // Send a new service config while closing the ClientConn.
go cc.Close() go cc.Close()
go r.UpdateState(resolver.State{ServiceConfig: parseCfg(r, `{"loadBalancingPolicy": "round_robin"}`)}) // This should not panic. go r.UpdateState(resolver.State{ServiceConfig: r.CC().ParseServiceConfig(rrServiceConfig)}) // This should not panic.
} }
} }
func (s) TestResolverEmptyUpdateNotPanic(t *testing.T) { func (s) TestResolverEmptyUpdateNotPanic(t *testing.T) {
r := manual.NewBuilderWithScheme("whatever") r := manual.NewBuilderWithScheme("whatever")
cc, err := Dial(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r)) cc, err := NewClient(r.Scheme()+":///test.server", WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r))
if err != nil { if err != nil {
t.Fatalf("failed to dial: %v", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
defer cc.Close() defer cc.Close()
cc.Connect()
// This make sure we don't create addrConn with empty address list. // This make sure we don't create addrConn with empty address list.
r.UpdateState(resolver.State{}) // This should not panic. r.UpdateState(resolver.State{}) // This should not panic.
@ -701,7 +702,7 @@ func (s) TestResolverEmptyUpdateNotPanic(t *testing.T) {
} }
func (s) TestClientUpdatesParamsAfterGoAway(t *testing.T) { func (s) TestClientUpdatesParamsAfterGoAway(t *testing.T) {
grpctest.TLogger.ExpectError("Client received GoAway with error code ENHANCE_YOUR_CALM and debug data equal to ASCII \"too_many_pings\"") grpctest.ExpectError("Client received GoAway with error code ENHANCE_YOUR_CALM and debug data equal to ASCII \"too_many_pings\"")
lis, err := net.Listen("tcp", "localhost:0") lis, err := net.Listen("tcp", "localhost:0")
if err != nil { if err != nil {
@ -746,7 +747,7 @@ func (s) TestClientUpdatesParamsAfterGoAway(t *testing.T) {
PermitWithoutStream: true, PermitWithoutStream: true,
})) }))
if err != nil { if err != nil {
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", addr, err) t.Fatalf("DialContext(%s) failed: %v, want: nil", addr, err)
} }
defer cc.Close() defer cc.Close()
connected.Fire() connected.Fire()
@ -769,12 +770,13 @@ func (s) TestClientUpdatesParamsAfterGoAway(t *testing.T) {
func (s) TestDisableServiceConfigOption(t *testing.T) { func (s) TestDisableServiceConfigOption(t *testing.T) {
r := manual.NewBuilderWithScheme("whatever") r := manual.NewBuilderWithScheme("whatever")
addr := r.Scheme() + ":///non.existent" addr := r.Scheme() + ":///non.existent"
cc, err := Dial(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDisableServiceConfig()) cc, err := NewClient(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDisableServiceConfig())
if err != nil { if err != nil {
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", addr, err) t.Fatalf("grpc.NewClient(%s) failed: %v, want: nil", addr, err)
} }
defer cc.Close() defer cc.Close()
r.UpdateState(resolver.State{ServiceConfig: parseCfg(r, `{ cc.Connect()
r.UpdateState(resolver.State{ServiceConfig: r.CC().ParseServiceConfig(`{
"methodConfig": [ "methodConfig": [
{ {
"name": [ "name": [
@ -795,8 +797,8 @@ func (s) TestDisableServiceConfigOption(t *testing.T) {
} }
func (s) TestMethodConfigDefaultService(t *testing.T) { func (s) TestMethodConfigDefaultService(t *testing.T) {
addr := "nonexist:///non.existent" addr := "passthrough:///non.existent"
cc, err := Dial(addr, WithTransportCredentials(insecure.NewCredentials()), WithDefaultServiceConfig(`{ cc, err := NewClient(addr, WithTransportCredentials(insecure.NewCredentials()), WithDefaultServiceConfig(`{
"methodConfig": [{ "methodConfig": [{
"name": [ "name": [
{ {
@ -807,8 +809,9 @@ func (s) TestMethodConfigDefaultService(t *testing.T) {
}] }]
}`)) }`))
if err != nil { if err != nil {
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", addr, err) t.Fatalf("grpc.NewClient(%s) failed: %v, want: nil", addr, err)
} }
cc.Connect()
defer cc.Close() defer cc.Close()
m := cc.GetMethodConfig("/foo/Bar") m := cc.GetMethodConfig("/foo/Bar")
@ -831,12 +834,12 @@ func (s) TestClientConnCanonicalTarget(t *testing.T) {
{ {
name: "canonical-target-not-specified", name: "canonical-target-not-specified",
addr: "no.scheme", addr: "no.scheme",
canonicalTargetWant: "passthrough:///no.scheme", canonicalTargetWant: "dns:///no.scheme",
}, },
{ {
name: "canonical-target-nonexistent", name: "canonical-target-nonexistent",
addr: "nonexist:///non.existent", addr: "nonexist:///non.existent",
canonicalTargetWant: "passthrough:///nonexist:///non.existent", canonicalTargetWant: "dns:///nonexist:///non.existent",
}, },
{ {
name: "canonical-target-add-colon-slash", name: "canonical-target-add-colon-slash",
@ -846,9 +849,9 @@ func (s) TestClientConnCanonicalTarget(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
cc, err := Dial(test.addr, WithTransportCredentials(insecure.NewCredentials())) cc, err := NewClient(test.addr, WithTransportCredentials(insecure.NewCredentials()))
if err != nil { if err != nil {
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", test.addr, err) t.Fatalf("grpc.NewClient(%s) failed: %v, want: nil", test.addr, err)
} }
defer cc.Close() defer cc.Close()
if cc.Target() != test.addr { if cc.Target() != test.addr {
@ -877,9 +880,9 @@ func (s) TestResetConnectBackoff(t *testing.T) {
dials <- struct{}{} dials <- struct{}{}
return nil, errors.New("failed to fake dial") return nil, errors.New("failed to fake dial")
} }
cc, err := Dial("any", WithTransportCredentials(insecure.NewCredentials()), WithDialer(dialer), withBackoff(backoffForever{})) cc, err := NewClient("passthrough:///", WithTransportCredentials(insecure.NewCredentials()), WithDialer(dialer), withBackoff(backoffForever{}))
if err != nil { if err != nil {
t.Fatalf("Dial() = _, %v; want _, nil", err) t.Fatalf("grpc.NewClient() failed with error: %v, want: nil", err)
} }
defer cc.Close() defer cc.Close()
go stayConnected(cc) go stayConnected(cc)
@ -906,18 +909,19 @@ func (s) TestResetConnectBackoff(t *testing.T) {
func (s) TestBackoffCancel(t *testing.T) { func (s) TestBackoffCancel(t *testing.T) {
dialStrCh := make(chan string) dialStrCh := make(chan string)
cc, err := Dial("any", WithTransportCredentials(insecure.NewCredentials()), WithDialer(func(t string, _ time.Duration) (net.Conn, error) { cc, err := NewClient("passthrough:///", WithTransportCredentials(insecure.NewCredentials()), WithDialer(func(t string, _ time.Duration) (net.Conn, error) {
dialStrCh <- t dialStrCh <- t
return nil, fmt.Errorf("test dialer, always error") return nil, fmt.Errorf("test dialer, always error")
})) }))
if err != nil { if err != nil {
t.Fatalf("Failed to create ClientConn: %v", err) t.Fatalf("grpc.NewClient() failed: %v", err)
} }
cc.Connect()
defer cc.Close() defer cc.Close()
select { select {
case <-time.After(defaultTestTimeout): case <-time.After(defaultTestTimeout):
t.Fatal("Timeout when waiting for custom dialer to be invoked during Dial") t.Fatal("Timeout when waiting for custom dialer to be invoked during Connect()")
case <-dialStrCh: case <-dialStrCh:
} }
} }
@ -972,9 +976,10 @@ func (s) TestUpdateAddresses_NoopIfCalledWithSameAddresses(t *testing.T) {
return return
} }
// nextStateNotifier() is updated after balancerBuilder.Build(), which is // nextStateNotifier() is updated after balancerBuilder.Build(), which
// called by grpc.Dial. It's safe to do it here because lis1.Accept blocks // is called by ClientConn.Connect in stayConnected. It's safe to do it
// until balancer is built to process the addresses. // here because lis1.Accept blocks until ClientConn.Connect is called
// and the balancer is built to process the addresses.
stateNotifications := testBalancerBuilder.nextStateNotifier() stateNotifications := testBalancerBuilder.nextStateNotifier()
// Wait for the transport to become ready. // Wait for the transport to become ready.
for { for {
@ -1158,17 +1163,18 @@ func verifyWaitForReadyEqualsTrue(cc *ClientConn) bool {
} }
func testInvalidDefaultServiceConfig(t *testing.T, r *manual.Resolver, addr, sc string) { func testInvalidDefaultServiceConfig(t *testing.T, r *manual.Resolver, addr, sc string) {
_, err := Dial(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDefaultServiceConfig(sc)) _, err := NewClient(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDefaultServiceConfig(sc))
if !strings.Contains(err.Error(), invalidDefaultServiceConfigErrPrefix) { if !strings.Contains(err.Error(), invalidDefaultServiceConfigErrPrefix) {
t.Fatalf("Dial got err: %v, want err contains: %v", err, invalidDefaultServiceConfigErrPrefix) t.Fatalf("grpc.NewClient() got err: %v, want err contains: %v", err, invalidDefaultServiceConfigErrPrefix)
} }
} }
func testDefaultServiceConfigWhenResolverServiceConfigDisabled(t *testing.T, r *manual.Resolver, addr string, js string) { func testDefaultServiceConfigWhenResolverServiceConfigDisabled(t *testing.T, r *manual.Resolver, addr string, js string) {
cc, err := Dial(addr, WithTransportCredentials(insecure.NewCredentials()), WithDisableServiceConfig(), WithResolvers(r), WithDefaultServiceConfig(js)) cc, err := NewClient(addr, WithTransportCredentials(insecure.NewCredentials()), WithDisableServiceConfig(), WithResolvers(r), WithDefaultServiceConfig(js))
if err != nil { if err != nil {
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", addr, err) t.Fatalf("grpc.NewClient(%s) failed: %v, want: nil", addr, err)
} }
cc.Connect()
defer cc.Close() defer cc.Close()
// Resolver service config gets ignored since resolver service config is disabled. // Resolver service config gets ignored since resolver service config is disabled.
r.UpdateState(resolver.State{ r.UpdateState(resolver.State{
@ -1181,10 +1187,11 @@ func testDefaultServiceConfigWhenResolverServiceConfigDisabled(t *testing.T, r *
} }
func testDefaultServiceConfigWhenResolverDoesNotReturnServiceConfig(t *testing.T, r *manual.Resolver, addr string, js string) { func testDefaultServiceConfigWhenResolverDoesNotReturnServiceConfig(t *testing.T, r *manual.Resolver, addr string, js string) {
cc, err := Dial(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDefaultServiceConfig(js)) cc, err := NewClient(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDefaultServiceConfig(js))
if err != nil { if err != nil {
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", addr, err) t.Fatalf("grpc.NewClient(%s) failed: %v, want: nil", addr, err)
} }
cc.Connect()
defer cc.Close() defer cc.Close()
r.UpdateState(resolver.State{ r.UpdateState(resolver.State{
Addresses: []resolver.Address{{Addr: addr}}, Addresses: []resolver.Address{{Addr: addr}},
@ -1195,10 +1202,11 @@ func testDefaultServiceConfigWhenResolverDoesNotReturnServiceConfig(t *testing.T
} }
func testDefaultServiceConfigWhenResolverReturnInvalidServiceConfig(t *testing.T, r *manual.Resolver, addr string, js string) { func testDefaultServiceConfigWhenResolverReturnInvalidServiceConfig(t *testing.T, r *manual.Resolver, addr string, js string) {
cc, err := Dial(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDefaultServiceConfig(js)) cc, err := NewClient(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDefaultServiceConfig(js))
if err != nil { if err != nil {
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", addr, err) t.Fatalf("grpc.NewClient(%s) failed: %v, want: nil", addr, err)
} }
cc.Connect()
defer cc.Close() defer cc.Close()
r.UpdateState(resolver.State{ r.UpdateState(resolver.State{
Addresses: []resolver.Address{{Addr: addr}}, Addresses: []resolver.Address{{Addr: addr}},

View File

@ -1,17 +1,18 @@
module google.golang.org/grpc/cmd/protoc-gen-go-grpc module google.golang.org/grpc/cmd/protoc-gen-go-grpc
go 1.22.0 go 1.23
require ( require (
google.golang.org/grpc v1.70.0 google.golang.org/grpc v1.70.0
google.golang.org/protobuf v1.36.4 google.golang.org/protobuf v1.36.6
) )
require ( require (
github.com/google/go-cmp v0.7.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
golang.org/x/net v0.34.0 // indirect golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.29.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
) )

View File

@ -4,8 +4,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
@ -20,15 +20,15 @@ go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=

View File

@ -97,7 +97,7 @@ func (serviceGenerateHelper) generateUnimplementedServerType(_ *protogen.Plugin,
nilArg = "nil," nilArg = "nil,"
} }
g.P("func (Unimplemented", serverType, ") ", serverSignature(g, method), "{") g.P("func (Unimplemented", serverType, ") ", serverSignature(g, method), "{")
g.P("return ", nilArg, statusPackage.Ident("Errorf"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`) g.P("return ", nilArg, statusPackage.Ident("Error"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`)
g.P("}") g.P("}")
} }
if *requireUnimplemented { if *requireUnimplemented {

View File

@ -133,10 +133,11 @@ func DefaultServerOptions() *ServerOptions {
// altsTC is the credentials required for authenticating a connection using ALTS. // altsTC is the credentials required for authenticating a connection using ALTS.
// It implements credentials.TransportCredentials interface. // It implements credentials.TransportCredentials interface.
type altsTC struct { type altsTC struct {
info *credentials.ProtocolInfo info *credentials.ProtocolInfo
side core.Side side core.Side
accounts []string accounts []string
hsAddress string hsAddress string
boundAccessToken string
} }
// NewClientCreds constructs a client-side ALTS TransportCredentials object. // NewClientCreds constructs a client-side ALTS TransportCredentials object.
@ -198,6 +199,7 @@ func (g *altsTC) ClientHandshake(ctx context.Context, addr string, rawConn net.C
MaxRpcVersion: maxRPCVersion, MaxRpcVersion: maxRPCVersion,
MinRpcVersion: minRPCVersion, MinRpcVersion: minRPCVersion,
} }
opts.BoundAccessToken = g.boundAccessToken
chs, err := handshaker.NewClientHandshaker(ctx, hsConn, rawConn, opts) chs, err := handshaker.NewClientHandshaker(ctx, hsConn, rawConn, opts)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -336,6 +336,28 @@ func (s) TestFullHandshake(t *testing.T) {
} }
} }
// TestHandshakeWithAccessToken performs an ALTS handshake between a test client and
// server, where both client and server offload to a local, fake handshaker
// service, and expects the StartClient request to include a bound access token.
func (s) TestHandshakeWithAccessToken(t *testing.T) {
// Start the fake handshaker service and the server.
var wait sync.WaitGroup
defer wait.Wait()
boundAccessToken := "fake-bound-access-token"
stopHandshaker, handshakerAddress := startFakeHandshakerServiceWithExpectedBoundAccessToken(t, &wait, boundAccessToken)
defer stopHandshaker()
stopServer, serverAddress := startServer(t, handshakerAddress)
defer stopServer()
// Ping the server, authenticating with ALTS and a bound access token.
establishAltsConnectionWithBoundAccessToken(t, handshakerAddress, serverAddress, boundAccessToken)
// Close open connections to the fake handshaker service.
if err := service.CloseForTesting(); err != nil {
t.Errorf("service.CloseForTesting() failed: %v", err)
}
}
// TestConcurrentHandshakes performs a several, concurrent ALTS handshakes // TestConcurrentHandshakes performs a several, concurrent ALTS handshakes
// between a test client and server, where both client and server offload to a // between a test client and server, where both client and server offload to a
// local, fake handshaker service. // local, fake handshaker service.
@ -385,7 +407,15 @@ func versions(minMajor, minMinor, maxMajor, maxMinor uint32) *altspb.RpcProtocol
} }
func establishAltsConnection(t *testing.T, handshakerAddress, serverAddress string) { func establishAltsConnection(t *testing.T, handshakerAddress, serverAddress string) {
establishAltsConnectionWithBoundAccessToken(t, handshakerAddress, serverAddress, "")
}
func establishAltsConnectionWithBoundAccessToken(t *testing.T, handshakerAddress, serverAddress, boundAccessToken string) {
clientCreds := NewClientCreds(&ClientOptions{HandshakerServiceAddress: handshakerAddress}) clientCreds := NewClientCreds(&ClientOptions{HandshakerServiceAddress: handshakerAddress})
if boundAccessToken != "" {
altsCreds := clientCreds.(*altsTC)
altsCreds.boundAccessToken = boundAccessToken
}
conn, err := grpc.NewClient(serverAddress, grpc.WithTransportCredentials(clientCreds)) conn, err := grpc.NewClient(serverAddress, grpc.WithTransportCredentials(clientCreds))
if err != nil { if err != nil {
t.Fatalf("grpc.NewClient(%v) failed: %v", serverAddress, err) t.Fatalf("grpc.NewClient(%v) failed: %v", serverAddress, err)
@ -429,12 +459,20 @@ func establishAltsConnection(t *testing.T, handshakerAddress, serverAddress stri
} }
func startFakeHandshakerService(t *testing.T, wait *sync.WaitGroup) (stop func(), address string) { func startFakeHandshakerService(t *testing.T, wait *sync.WaitGroup) (stop func(), address string) {
return startFakeHandshakerServiceWithExpectedBoundAccessToken(t, wait, "")
}
func startFakeHandshakerServiceWithExpectedBoundAccessToken(t *testing.T, wait *sync.WaitGroup, boundAccessToken string) (stop func(), address string) {
listener, err := testutils.LocalTCPListener() listener, err := testutils.LocalTCPListener()
if err != nil { if err != nil {
t.Fatalf("LocalTCPListener() failed: %v", err) t.Fatalf("LocalTCPListener() failed: %v", err)
} }
s := grpc.NewServer() s := grpc.NewServer()
altsgrpc.RegisterHandshakerServiceServer(s, &testutil.FakeHandshaker{}) hs := &testutil.FakeHandshaker{}
if boundAccessToken != "" {
hs.ExpectedBoundAccessToken = boundAccessToken
}
altsgrpc.RegisterHandshakerServiceServer(s, hs)
wait.Add(1) wait.Add(1)
go func() { go func() {
defer wait.Done() defer wait.Done()

View File

@ -54,11 +54,10 @@ func SliceForAppend(in []byte, n int) (head, tail []byte) {
func ParseFramedMsg(b []byte, maxLen uint32) ([]byte, []byte, error) { func ParseFramedMsg(b []byte, maxLen uint32) ([]byte, []byte, error) {
// If the size field is not complete, return the provided buffer as // If the size field is not complete, return the provided buffer as
// remaining buffer. // remaining buffer.
if len(b) < MsgLenFieldSize { length, sufficientBytes := parseMessageLength(b)
if !sufficientBytes {
return nil, b, nil return nil, b, nil
} }
msgLenField := b[:MsgLenFieldSize]
length := binary.LittleEndian.Uint32(msgLenField)
if length > maxLen { if length > maxLen {
return nil, nil, fmt.Errorf("received the frame length %d larger than the limit %d", length, maxLen) return nil, nil, fmt.Errorf("received the frame length %d larger than the limit %d", length, maxLen)
} }
@ -68,3 +67,14 @@ func ParseFramedMsg(b []byte, maxLen uint32) ([]byte, []byte, error) {
} }
return b[:MsgLenFieldSize+length], b[MsgLenFieldSize+length:], nil return b[:MsgLenFieldSize+length], b[MsgLenFieldSize+length:], nil
} }
// parseMessageLength returns the message length based on frame header. It also
// returns a boolean indicating if the buffer contains sufficient bytes to parse
// the length header. If there are insufficient bytes, (0, false) is returned.
func parseMessageLength(b []byte) (uint32, bool) {
if len(b) < MsgLenFieldSize {
return 0, false
}
msgLenField := b[:MsgLenFieldSize]
return binary.LittleEndian.Uint32(msgLenField), true
}

View File

@ -31,14 +31,18 @@ import (
// ALTSRecordCrypto is the interface for gRPC ALTS record protocol. // ALTSRecordCrypto is the interface for gRPC ALTS record protocol.
type ALTSRecordCrypto interface { type ALTSRecordCrypto interface {
// Encrypt encrypts the plaintext and computes the tag (if any) of dst // Encrypt encrypts the plaintext, computes the tag (if any) of dst and
// and plaintext. dst and plaintext may fully overlap or not at all. // plaintext, and appends the result to dst, returning the updated slice.
// dst and plaintext may fully overlap or not at all.
Encrypt(dst, plaintext []byte) ([]byte, error) Encrypt(dst, plaintext []byte) ([]byte, error)
// EncryptionOverhead returns the tag size (if any) in bytes. // EncryptionOverhead returns the tag size (if any) in bytes.
EncryptionOverhead() int EncryptionOverhead() int
// Decrypt decrypts ciphertext and verify the tag (if any). dst and // Decrypt decrypts ciphertext and verifies the tag (if any). If successful,
// ciphertext may alias exactly or not at all. To reuse ciphertext's // this function appends the resulting plaintext to dst, returning the
// storage for the decrypted output, use ciphertext[:0] as dst. // updated slice. dst and ciphertext may alias exactly or not at all. To
// reuse ciphertext's storage for the decrypted output, use ciphertext[:0]
// as dst. Even if the function fails, the contents of dst, up to its
// capacity, may be overwritten.
Decrypt(dst, ciphertext []byte) ([]byte, error) Decrypt(dst, ciphertext []byte) ([]byte, error)
} }
@ -63,6 +67,8 @@ const (
// The maximum write buffer size. This *must* be multiple of // The maximum write buffer size. This *must* be multiple of
// altsRecordDefaultLength. // altsRecordDefaultLength.
altsWriteBufferMaxSize = 512 * 1024 // 512KiB altsWriteBufferMaxSize = 512 * 1024 // 512KiB
// The initial buffer used to read from the network.
altsReadBufferInitialSize = 32 * 1024 // 32KiB
) )
var ( var (
@ -83,7 +89,7 @@ type conn struct {
net.Conn net.Conn
crypto ALTSRecordCrypto crypto ALTSRecordCrypto
// buf holds data that has been read from the connection and decrypted, // buf holds data that has been read from the connection and decrypted,
// but has not yet been returned by Read. // but has not yet been returned by Read. It is a sub-slice of protected.
buf []byte buf []byte
payloadLengthLimit int payloadLengthLimit int
// protected holds data read from the network but have not yet been // protected holds data read from the network but have not yet been
@ -111,21 +117,13 @@ func NewConn(c net.Conn, side core.Side, recordProtocol string, key []byte, prot
} }
overhead := MsgLenFieldSize + msgTypeFieldSize + crypto.EncryptionOverhead() overhead := MsgLenFieldSize + msgTypeFieldSize + crypto.EncryptionOverhead()
payloadLengthLimit := altsRecordDefaultLength - overhead payloadLengthLimit := altsRecordDefaultLength - overhead
var protectedBuf []byte // We pre-allocate protected to be of size 32KB during initialization.
if protected == nil { // We increase the size of the buffer by the required amount if it can't
// We pre-allocate protected to be of size // hold a complete encrypted record.
// 2*altsRecordDefaultLength-1 during initialization. We only protectedBuf := make([]byte, max(altsReadBufferInitialSize, len(protected)))
// read from the network into protected when protected does not // Copy additional data from hanshaker service.
// contain a complete frame, which is at most copy(protectedBuf, protected)
// altsRecordDefaultLength-1 (bytes). And we read at most protectedBuf = protectedBuf[:len(protected)]
// altsRecordDefaultLength (bytes) data into protected at one
// time. Therefore, 2*altsRecordDefaultLength-1 is large enough
// to buffer data read from the network.
protectedBuf = make([]byte, 0, 2*altsRecordDefaultLength-1)
} else {
protectedBuf = make([]byte, len(protected))
copy(protectedBuf, protected)
}
altsConn := &conn{ altsConn := &conn{
Conn: c, Conn: c,
@ -162,11 +160,26 @@ func (p *conn) Read(b []byte) (n int, err error) {
// Check whether a complete frame has been received yet. // Check whether a complete frame has been received yet.
for len(framedMsg) == 0 { for len(framedMsg) == 0 {
if len(p.protected) == cap(p.protected) { if len(p.protected) == cap(p.protected) {
tmp := make([]byte, len(p.protected), cap(p.protected)+altsRecordDefaultLength) // We can parse the length header to know exactly how large
copy(tmp, p.protected) // the buffer needs to be to hold the entire frame.
p.protected = tmp length, didParse := parseMessageLength(p.protected)
if !didParse {
// The protected buffer is initialized with a capacity of
// larger than 4B. It should always hold the message length
// header.
panic(fmt.Sprintf("protected buffer length shorter than expected: %d vs %d", len(p.protected), MsgLenFieldSize))
}
oldProtectedBuf := p.protected
// The new buffer must be able to hold the message length header
// and the entire message.
requiredCapacity := int(length) + MsgLenFieldSize
p.protected = make([]byte, requiredCapacity)
// Copy the contents of the old buffer and set the length of the
// new buffer to the number of bytes already read.
copy(p.protected, oldProtectedBuf)
p.protected = p.protected[:len(oldProtectedBuf)]
} }
n, err = p.Conn.Read(p.protected[len(p.protected):min(cap(p.protected), len(p.protected)+altsRecordDefaultLength)]) n, err = p.Conn.Read(p.protected[len(p.protected):cap(p.protected)])
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -185,6 +198,15 @@ func (p *conn) Read(b []byte) (n int, err error) {
} }
ciphertext := msg[msgTypeFieldSize:] ciphertext := msg[msgTypeFieldSize:]
// Decrypt directly into the buffer, avoiding a copy from p.buf if
// possible.
if len(b) >= len(ciphertext) {
dec, err := p.crypto.Decrypt(b[:0], ciphertext)
if err != nil {
return 0, err
}
return len(dec), nil
}
// Decrypt requires that if the dst and ciphertext alias, they // Decrypt requires that if the dst and ciphertext alias, they
// must alias exactly. Code here used to use msg[:0], but msg // must alias exactly. Code here used to use msg[:0], but msg
// starts MsgLenFieldSize+msgTypeFieldSize bytes earlier than // starts MsgLenFieldSize+msgTypeFieldSize bytes earlier than

View File

@ -26,6 +26,7 @@ import (
"math" "math"
"net" "net"
"reflect" "reflect"
"strings"
"testing" "testing"
core "google.golang.org/grpc/credentials/alts/internal" core "google.golang.org/grpc/credentials/alts/internal"
@ -188,6 +189,48 @@ func (s) TestLargeMsg(t *testing.T) {
} }
} }
// TestLargeRecord writes a very large ALTS record and verifies that the server
// receives it correctly. The large ALTS record should cause the reader to
// expand it's read buffer to hold the entire record and store the decrypted
// message until the receiver reads all of the bytes.
func (s) TestLargeRecord(t *testing.T) {
clientConn, serverConn := newConnPair(rekeyRecordProtocol, nil, nil)
msg := []byte(strings.Repeat("a", 2*altsReadBufferInitialSize))
// Increase the size of ALTS records written by the client.
clientConn.payloadLengthLimit = math.MaxInt32
if n, err := clientConn.Write(msg); n != len(msg) || err != nil {
t.Fatalf("Write() = %v, %v; want %v, <nil>", n, err, len(msg))
}
rcvMsg := make([]byte, len(msg))
if n, err := io.ReadFull(serverConn, rcvMsg); n != len(rcvMsg) || err != nil {
t.Fatalf("Read() = %v, %v; want %v, <nil>", n, err, len(rcvMsg))
}
if !reflect.DeepEqual(msg, rcvMsg) {
t.Fatalf("Write()/Server Read() = %v, want %v", rcvMsg, msg)
}
}
// BenchmarkLargeMessage measures the performance of ALTS conns for sending and
// receiving a large message.
func BenchmarkLargeMessage(b *testing.B) {
msgLen := 20 * 1024 * 1024 // 20 MiB
msg := make([]byte, msgLen)
rcvMsg := make([]byte, len(msg))
b.ResetTimer()
clientConn, serverConn := newConnPair(rekeyRecordProtocol, nil, nil)
for range b.N {
// Write 20 MiB 5 times to transfer a total of 100 MiB.
for range 5 {
if n, err := clientConn.Write(msg); n != len(msg) || err != nil {
b.Fatalf("Write() = %v, %v; want %v, <nil>", n, err, len(msg))
}
if n, err := io.ReadFull(serverConn, rcvMsg); n != len(rcvMsg) || err != nil {
b.Fatalf("Read() = %v, %v; want %v, <nil>", n, err, len(rcvMsg))
}
}
}
}
func testIncorrectMsgType(t *testing.T, rp string) { func testIncorrectMsgType(t *testing.T, rp string) {
// framedMsg is an empty ciphertext with correct framing but wrong // framedMsg is an empty ciphertext with correct framing but wrong
// message type. // message type.

View File

@ -88,6 +88,8 @@ type ClientHandshakerOptions struct {
TargetServiceAccounts []string TargetServiceAccounts []string
// RPCVersions specifies the gRPC versions accepted by the client. // RPCVersions specifies the gRPC versions accepted by the client.
RPCVersions *altspb.RpcProtocolVersions RPCVersions *altspb.RpcProtocolVersions
// BoundAccessToken is a bound access token to be sent to the server for authentication.
BoundAccessToken string
} }
// ServerHandshakerOptions contains the server handshaker options that can // ServerHandshakerOptions contains the server handshaker options that can
@ -195,7 +197,9 @@ func (h *altsHandshaker) ClientHandshake(ctx context.Context) (net.Conn, credent
}, },
}, },
} }
if h.clientOpts.BoundAccessToken != "" {
req.GetClientStart().AccessToken = h.clientOpts.BoundAccessToken
}
conn, result, err := h.doHandshake(req) conn, result, err := h.doHandshake(req)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -294,11 +298,11 @@ func (h *altsHandshaker) doHandshake(req *altspb.HandshakerReq) (net.Conn, *alts
func (h *altsHandshaker) accessHandshakerService(req *altspb.HandshakerReq) (*altspb.HandshakerResp, error) { func (h *altsHandshaker) accessHandshakerService(req *altspb.HandshakerReq) (*altspb.HandshakerResp, error) {
if err := h.stream.Send(req); err != nil { if err := h.stream.Send(req); err != nil {
return nil, err return nil, fmt.Errorf("failed to send ALTS handshaker request: %w", err)
} }
resp, err := h.stream.Recv() resp, err := h.stream.Recv()
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to receive ALTS handshaker response: %w", err)
} }
return resp, nil return resp, nil
} }
@ -308,6 +312,7 @@ func (h *altsHandshaker) accessHandshakerService(req *altspb.HandshakerReq) (*al
// whatever received from the network and send it to the handshaker service. // whatever received from the network and send it to the handshaker service.
func (h *altsHandshaker) processUntilDone(resp *altspb.HandshakerResp, extra []byte) (*altspb.HandshakerResult, []byte, error) { func (h *altsHandshaker) processUntilDone(resp *altspb.HandshakerResp, extra []byte) (*altspb.HandshakerResult, []byte, error) {
var lastWriteTime time.Time var lastWriteTime time.Time
buf := make([]byte, frameLimit)
for { for {
if len(resp.OutFrames) > 0 { if len(resp.OutFrames) > 0 {
lastWriteTime = time.Now() lastWriteTime = time.Now()
@ -318,7 +323,6 @@ func (h *altsHandshaker) processUntilDone(resp *altspb.HandshakerResp, extra []b
if resp.Result != nil { if resp.Result != nil {
return resp.Result, extra, nil return resp.Result, extra, nil
} }
buf := make([]byte, frameLimit)
n, err := h.conn.Read(buf) n, err := h.conn.Read(buf)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return nil, nil, err return nil, nil, err

View File

@ -22,9 +22,12 @@ package service
import ( import (
"sync" "sync"
"time"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/keepalive"
) )
var ( var (
@ -50,7 +53,17 @@ func Dial(hsAddress string) (*grpc.ClientConn, error) {
// Disable the service config to avoid unnecessary TXT record lookups that // Disable the service config to avoid unnecessary TXT record lookups that
// cause timeouts with some versions of systemd-resolved. // cause timeouts with some versions of systemd-resolved.
var err error var err error
hsConn, err = grpc.Dial(hsAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDisableServiceConfig()) opts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDisableServiceConfig(),
}
if envconfig.ALTSHandshakerKeepaliveParams {
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
Timeout: 10 * time.Second,
Time: 10 * time.Minute,
}))
}
hsConn, err = grpc.NewClient(hsAddress, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,7 +17,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.4 // protoc-gen-go v1.36.6
// protoc v5.27.1 // protoc v5.27.1
// source: grpc/gcp/altscontext.proto // source: grpc/gcp/altscontext.proto
@ -139,52 +139,21 @@ func (x *AltsContext) GetPeerAttributes() map[string]string {
var File_grpc_gcp_altscontext_proto protoreflect.FileDescriptor var File_grpc_gcp_altscontext_proto protoreflect.FileDescriptor
var file_grpc_gcp_altscontext_proto_rawDesc = string([]byte{ const file_grpc_gcp_altscontext_proto_rawDesc = "" +
0x0a, 0x1a, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x63, 0x70, 0x2f, 0x61, 0x6c, 0x74, 0x73, 0x63, "\n" +
0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x67, 0x72, "\x1agrpc/gcp/altscontext.proto\x12\bgrpc.gcp\x1a(grpc/gcp/transport_security_common.proto\"\xf1\x03\n" +
0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x1a, 0x28, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x63, 0x70, "\vAltsContext\x121\n" +
0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, "\x14application_protocol\x18\x01 \x01(\tR\x13applicationProtocol\x12'\n" +
0x69, 0x74, 0x79, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, "\x0frecord_protocol\x18\x02 \x01(\tR\x0erecordProtocol\x12>\n" +
0x22, 0xf1, 0x03, 0x0a, 0x0b, 0x41, 0x6c, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, "\x0esecurity_level\x18\x03 \x01(\x0e2\x17.grpc.gcp.SecurityLevelR\rsecurityLevel\x120\n" +
0x12, 0x31, 0x0a, 0x14, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, "\x14peer_service_account\x18\x04 \x01(\tR\x12peerServiceAccount\x122\n" +
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, "\x15local_service_account\x18\x05 \x01(\tR\x13localServiceAccount\x12I\n" +
0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, "\x11peer_rpc_versions\x18\x06 \x01(\v2\x1d.grpc.gcp.RpcProtocolVersionsR\x0fpeerRpcVersions\x12R\n" +
0x63, 0x6f, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x70, 0x72, "\x0fpeer_attributes\x18\a \x03(\v2).grpc.gcp.AltsContext.PeerAttributesEntryR\x0epeerAttributes\x1aA\n" +
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, "\x13PeerAttributesEntry\x12\x10\n" +
0x63, 0x6f, 0x72, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3e, 0x0a, 0x0e, "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01Bl\n" +
0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, "\x15io.grpc.alts.internalB\x10AltsContextProtoP\x01Z?google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcpb\x06proto3"
0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0d, 0x73,
0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x30, 0x0a, 0x14,
0x70, 0x65, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x70, 0x65, 0x65, 0x72,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32,
0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x72, 0x70, 0x63, 0x5f, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x52, 0x70, 0x63, 0x50, 0x72, 0x6f, 0x74,
0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0f, 0x70, 0x65,
0x65, 0x72, 0x52, 0x70, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x52, 0x0a,
0x0f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63,
0x70, 0x2e, 0x41, 0x6c, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x50, 0x65,
0x65, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
0x73, 0x1a, 0x41, 0x0a, 0x13, 0x50, 0x65, 0x65, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x3a, 0x02, 0x38, 0x01, 0x42, 0x6c, 0x0a, 0x15, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x61, 0x6c, 0x74, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x42, 0x10, 0x41,
0x6c, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50,
0x01, 0x5a, 0x3f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67,
0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x61, 0x6c, 0x74, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x67,
0x63, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var ( var (
file_grpc_gcp_altscontext_proto_rawDescOnce sync.Once file_grpc_gcp_altscontext_proto_rawDescOnce sync.Once

View File

@ -17,7 +17,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.4 // protoc-gen-go v1.36.6
// protoc v5.27.1 // protoc v5.27.1
// source: grpc/gcp/handshaker.proto // source: grpc/gcp/handshaker.proto
@ -331,9 +331,11 @@ type StartClientHandshakeReq struct {
// ALTS connections. The access token that should be used to authenticate to // ALTS connections. The access token that should be used to authenticate to
// the peer. The access token MUST be strongly bound to the ALTS credentials // the peer. The access token MUST be strongly bound to the ALTS credentials
// used to establish the connection that the token is sent over. // used to establish the connection that the token is sent over.
AccessToken string `protobuf:"bytes,11,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` AccessToken string `protobuf:"bytes,11,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
unknownFields protoimpl.UnknownFields // (Optional) Ordered transport protocol preferences supported by the client.
sizeCache protoimpl.SizeCache TransportProtocolPreferences *TransportProtocolPreferences `protobuf:"bytes,12,opt,name=transport_protocol_preferences,json=transportProtocolPreferences,proto3" json:"transport_protocol_preferences,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *StartClientHandshakeReq) Reset() { func (x *StartClientHandshakeReq) Reset() {
@ -443,6 +445,13 @@ func (x *StartClientHandshakeReq) GetAccessToken() string {
return "" return ""
} }
func (x *StartClientHandshakeReq) GetTransportProtocolPreferences() *TransportProtocolPreferences {
if x != nil {
return x.TransportProtocolPreferences
}
return nil
}
type ServerHandshakeParameters struct { type ServerHandshakeParameters struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// The record protocols supported by the server, e.g., // The record protocols supported by the server, e.g.,
@ -534,9 +543,11 @@ type StartServerHandshakeReq struct {
// (Optional) RPC protocol versions supported by the server. // (Optional) RPC protocol versions supported by the server.
RpcVersions *RpcProtocolVersions `protobuf:"bytes,6,opt,name=rpc_versions,json=rpcVersions,proto3" json:"rpc_versions,omitempty"` RpcVersions *RpcProtocolVersions `protobuf:"bytes,6,opt,name=rpc_versions,json=rpcVersions,proto3" json:"rpc_versions,omitempty"`
// (Optional) Maximum frame size supported by the server. // (Optional) Maximum frame size supported by the server.
MaxFrameSize uint32 `protobuf:"varint,7,opt,name=max_frame_size,json=maxFrameSize,proto3" json:"max_frame_size,omitempty"` MaxFrameSize uint32 `protobuf:"varint,7,opt,name=max_frame_size,json=maxFrameSize,proto3" json:"max_frame_size,omitempty"`
unknownFields protoimpl.UnknownFields // (Optional) Transport protocol preferences supported by the server.
sizeCache protoimpl.SizeCache TransportProtocolPreferences *TransportProtocolPreferences `protobuf:"bytes,8,opt,name=transport_protocol_preferences,json=transportProtocolPreferences,proto3" json:"transport_protocol_preferences,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *StartServerHandshakeReq) Reset() { func (x *StartServerHandshakeReq) Reset() {
@ -618,6 +629,13 @@ func (x *StartServerHandshakeReq) GetMaxFrameSize() uint32 {
return 0 return 0
} }
func (x *StartServerHandshakeReq) GetTransportProtocolPreferences() *TransportProtocolPreferences {
if x != nil {
return x.TransportProtocolPreferences
}
return nil
}
type NextHandshakeMessageReq struct { type NextHandshakeMessageReq struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// Bytes in out_frames returned from the peer's HandshakerResp. It is possible // Bytes in out_frames returned from the peer's HandshakerResp. It is possible
@ -798,9 +816,11 @@ type HandshakerResult struct {
// The RPC protocol versions supported by the peer. // The RPC protocol versions supported by the peer.
PeerRpcVersions *RpcProtocolVersions `protobuf:"bytes,7,opt,name=peer_rpc_versions,json=peerRpcVersions,proto3" json:"peer_rpc_versions,omitempty"` PeerRpcVersions *RpcProtocolVersions `protobuf:"bytes,7,opt,name=peer_rpc_versions,json=peerRpcVersions,proto3" json:"peer_rpc_versions,omitempty"`
// The maximum frame size of the peer. // The maximum frame size of the peer.
MaxFrameSize uint32 `protobuf:"varint,8,opt,name=max_frame_size,json=maxFrameSize,proto3" json:"max_frame_size,omitempty"` MaxFrameSize uint32 `protobuf:"varint,8,opt,name=max_frame_size,json=maxFrameSize,proto3" json:"max_frame_size,omitempty"`
unknownFields protoimpl.UnknownFields // (Optional) The transport protocol negotiated for this connection.
sizeCache protoimpl.SizeCache TransportProtocol *NegotiatedTransportProtocol `protobuf:"bytes,9,opt,name=transport_protocol,json=transportProtocol,proto3" json:"transport_protocol,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *HandshakerResult) Reset() { func (x *HandshakerResult) Reset() {
@ -889,6 +909,13 @@ func (x *HandshakerResult) GetMaxFrameSize() uint32 {
return 0 return 0
} }
func (x *HandshakerResult) GetTransportProtocol() *NegotiatedTransportProtocol {
if x != nil {
return x.TransportProtocol
}
return nil
}
type HandshakerStatus struct { type HandshakerStatus struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// The status code. This could be the gRPC status code. // The status code. This could be the gRPC status code.
@ -1024,206 +1051,94 @@ func (x *HandshakerResp) GetStatus() *HandshakerStatus {
var File_grpc_gcp_handshaker_proto protoreflect.FileDescriptor var File_grpc_gcp_handshaker_proto protoreflect.FileDescriptor
var file_grpc_gcp_handshaker_proto_rawDesc = string([]byte{ const file_grpc_gcp_handshaker_proto_rawDesc = "" +
0x0a, 0x19, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x63, 0x70, 0x2f, 0x68, 0x61, 0x6e, 0x64, 0x73, "\n" +
0x68, 0x61, 0x6b, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x67, 0x72, 0x70, "\x19grpc/gcp/handshaker.proto\x12\bgrpc.gcp\x1a(grpc/gcp/transport_security_common.proto\"t\n" +
0x63, 0x2e, 0x67, 0x63, 0x70, 0x1a, 0x28, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x63, 0x70, 0x2f, "\bEndpoint\x12\x1d\n" +
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, "\n" +
0x74, 0x79, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, "ip_address\x18\x01 \x01(\tR\tipAddress\x12\x12\n" +
0x74, 0x0a, 0x08, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, "\x04port\x18\x02 \x01(\x05R\x04port\x125\n" +
0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, "\bprotocol\x18\x03 \x01(\x0e2\x19.grpc.gcp.NetworkProtocolR\bprotocol\"\xe8\x01\n" +
0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, "\bIdentity\x12)\n" +
0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x35, "\x0fservice_account\x18\x01 \x01(\tH\x00R\x0eserviceAccount\x12\x1c\n" +
0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, "\bhostname\x18\x02 \x01(\tH\x00R\bhostname\x12B\n" +
0x32, 0x19, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x4e, 0x65, 0x74, 0x77, "\n" +
0x6f, 0x72, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, "attributes\x18\x03 \x03(\v2\".grpc.gcp.Identity.AttributesEntryR\n" +
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0xe8, 0x01, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, "attributes\x1a=\n" +
0x74, 0x79, 0x12, 0x29, 0x0a, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, "\x0fAttributesEntry\x12\x10\n" +
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x73, "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c, 0x0a, "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x10\n" +
0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, "\x0eidentity_oneof\"\xe9\x05\n" +
0x00, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x0a, 0x61, "\x17StartClientHandshakeReq\x12[\n" +
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, "\x1bhandshake_security_protocol\x18\x01 \x01(\x0e2\x1b.grpc.gcp.HandshakeProtocolR\x19handshakeSecurityProtocol\x123\n" +
0x22, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, "\x15application_protocols\x18\x02 \x03(\tR\x14applicationProtocols\x12)\n" +
0x69, 0x74, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, "\x10record_protocols\x18\x03 \x03(\tR\x0frecordProtocols\x12?\n" +
0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, "\x11target_identities\x18\x04 \x03(\v2\x12.grpc.gcp.IdentityR\x10targetIdentities\x129\n" +
0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, "\x0elocal_identity\x18\x05 \x01(\v2\x12.grpc.gcp.IdentityR\rlocalIdentity\x129\n" +
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, "\x0elocal_endpoint\x18\x06 \x01(\v2\x12.grpc.gcp.EndpointR\rlocalEndpoint\x12;\n" +
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, "\x0fremote_endpoint\x18\a \x01(\v2\x12.grpc.gcp.EndpointR\x0eremoteEndpoint\x12\x1f\n" +
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x10, "\vtarget_name\x18\b \x01(\tR\n" +
0x0a, 0x0e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x6e, 0x65, 0x6f, 0x66, "targetName\x12@\n" +
0x22, 0xfb, 0x04, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, "\frpc_versions\x18\t \x01(\v2\x1d.grpc.gcp.RpcProtocolVersionsR\vrpcVersions\x12$\n" +
0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x12, 0x5b, 0x0a, 0x1b, "\x0emax_frame_size\x18\n" +
0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, " \x01(\rR\fmaxFrameSize\x12&\n" +
0x74, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, "\faccess_token\x18\v \x01(\tB\x03\x80\x01\x01R\vaccessToken\x12l\n" +
0x0e, 0x32, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x48, 0x61, 0x6e, "\x1etransport_protocol_preferences\x18\f \x01(\v2&.grpc.gcp.TransportProtocolPreferencesR\x1ctransportProtocolPreferences\"\xaf\x01\n" +
0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x19, "\x19ServerHandshakeParameters\x12)\n" +
0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, "\x10record_protocols\x18\x01 \x03(\tR\x0frecordProtocols\x12=\n" +
0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x70, 0x70, "\x10local_identities\x18\x02 \x03(\v2\x12.grpc.gcp.IdentityR\x0flocalIdentities\x12\x1e\n" +
0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, "\x05token\x18\x03 \x01(\tB\x03\x80\x01\x01H\x00R\x05token\x88\x01\x01B\b\n" +
0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, "\x06_token\"\x93\x05\n" +
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x29, "\x17StartServerHandshakeReq\x123\n" +
0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, "\x15application_protocols\x18\x01 \x03(\tR\x14applicationProtocols\x12m\n" +
0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, "\x14handshake_parameters\x18\x02 \x03(\v2:.grpc.gcp.StartServerHandshakeReq.HandshakeParametersEntryR\x13handshakeParameters\x12\x19\n" +
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x3f, 0x0a, 0x11, 0x74, 0x61, 0x72, "\bin_bytes\x18\x03 \x01(\fR\ainBytes\x129\n" +
0x67, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, "\x0elocal_endpoint\x18\x04 \x01(\v2\x12.grpc.gcp.EndpointR\rlocalEndpoint\x12;\n" +
0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, "\x0fremote_endpoint\x18\x05 \x01(\v2\x12.grpc.gcp.EndpointR\x0eremoteEndpoint\x12@\n" +
0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, "\frpc_versions\x18\x06 \x01(\v2\x1d.grpc.gcp.RpcProtocolVersionsR\vrpcVersions\x12$\n" +
0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0e, 0x6c, 0x6f, "\x0emax_frame_size\x18\a \x01(\rR\fmaxFrameSize\x12l\n" +
0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, "\x1etransport_protocol_preferences\x18\b \x01(\v2&.grpc.gcp.TransportProtocolPreferencesR\x1ctransportProtocolPreferences\x1ak\n" +
0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x49, 0x64, "\x18HandshakeParametersEntry\x12\x10\n" +
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x65, "\x03key\x18\x01 \x01(\x05R\x03key\x129\n" +
0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x65, "\x05value\x18\x02 \x01(\v2#.grpc.gcp.ServerHandshakeParametersR\x05value:\x028\x01\"b\n" +
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, "\x17NextHandshakeMessageReq\x12\x19\n" +
0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, "\bin_bytes\x18\x01 \x01(\fR\ainBytes\x12,\n" +
0x74, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, "\x12network_latency_ms\x18\x02 \x01(\rR\x10networkLatencyMs\"\xe5\x01\n" +
0x12, 0x3b, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, "\rHandshakerReq\x12F\n" +
0x69, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, "\fclient_start\x18\x01 \x01(\v2!.grpc.gcp.StartClientHandshakeReqH\x00R\vclientStart\x12F\n" +
0x2e, 0x67, 0x63, 0x70, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x72, "\fserver_start\x18\x02 \x01(\v2!.grpc.gcp.StartServerHandshakeReqH\x00R\vserverStart\x127\n" +
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, "\x04next\x18\x03 \x01(\v2!.grpc.gcp.NextHandshakeMessageReqH\x00R\x04nextB\v\n" +
0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, "\treq_oneof\"\xf0\x03\n" +
0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, "\x10HandshakerResult\x121\n" +
0x0a, 0x0c, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x09, "\x14application_protocol\x18\x01 \x01(\tR\x13applicationProtocol\x12'\n" +
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, "\x0frecord_protocol\x18\x02 \x01(\tR\x0erecordProtocol\x12\x19\n" +
0x52, 0x70, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, "\bkey_data\x18\x03 \x01(\fR\akeyData\x127\n" +
0x6f, 0x6e, 0x73, 0x52, 0x0b, 0x72, 0x70, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, "\rpeer_identity\x18\x04 \x01(\v2\x12.grpc.gcp.IdentityR\fpeerIdentity\x129\n" +
0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, "\x0elocal_identity\x18\x05 \x01(\v2\x12.grpc.gcp.IdentityR\rlocalIdentity\x12*\n" +
0x7a, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x46, 0x72, 0x61, "\x11keep_channel_open\x18\x06 \x01(\bR\x0fkeepChannelOpen\x12I\n" +
0x6d, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x26, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, "\x11peer_rpc_versions\x18\a \x01(\v2\x1d.grpc.gcp.RpcProtocolVersionsR\x0fpeerRpcVersions\x12$\n" +
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0x80, 0x01, "\x0emax_frame_size\x18\b \x01(\rR\fmaxFrameSize\x12T\n" +
0x01, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xaf, "\x12transport_protocol\x18\t \x01(\v2%.grpc.gcp.NegotiatedTransportProtocolR\x11transportProtocol\"@\n" +
0x01, 0x0a, 0x19, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, "\x10HandshakerStatus\x12\x12\n" +
0x6b, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x29, 0x0a, 0x10, "\x04code\x18\x01 \x01(\rR\x04code\x12\x18\n" +
0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, "\adetails\x18\x02 \x01(\tR\adetails\"\xbe\x01\n" +
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x50, 0x72, "\x0eHandshakerResp\x12\x1d\n" +
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x3d, 0x0a, 0x10, 0x6c, 0x6f, 0x63, 0x61, 0x6c, "\n" +
0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, "out_frames\x18\x01 \x01(\fR\toutFrames\x12%\n" +
0x0b, 0x32, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x49, 0x64, 0x65, "\x0ebytes_consumed\x18\x02 \x01(\rR\rbytesConsumed\x122\n" +
0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x65, 0x6e, "\x06result\x18\x03 \x01(\v2\x1a.grpc.gcp.HandshakerResultR\x06result\x122\n" +
0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, "\x06status\x18\x04 \x01(\v2\x1a.grpc.gcp.HandshakerStatusR\x06status*J\n" +
0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0x80, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, "\x11HandshakeProtocol\x12\"\n" +
0x6b, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, "\x1eHANDSHAKE_PROTOCOL_UNSPECIFIED\x10\x00\x12\a\n" +
0x22, 0xa5, 0x04, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, "\x03TLS\x10\x01\x12\b\n" +
0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x12, 0x33, 0x0a, 0x15, "\x04ALTS\x10\x02*E\n" +
0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x74, "\x0fNetworkProtocol\x12 \n" +
0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x61, 0x70, 0x70, "\x1cNETWORK_PROTOCOL_UNSPECIFIED\x10\x00\x12\a\n" +
0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, "\x03TCP\x10\x01\x12\a\n" +
0x73, 0x12, 0x6d, 0x0a, 0x14, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x70, "\x03UDP\x10\x022[\n" +
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, "\x11HandshakerService\x12F\n" +
0x3a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, "\vDoHandshake\x12\x17.grpc.gcp.HandshakerReq\x1a\x18.grpc.gcp.HandshakerResp\"\x00(\x010\x01Bk\n" +
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, "\x15io.grpc.alts.internalB\x0fHandshakerProtoP\x01Z?google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcpb\x06proto3"
0x65, 0x71, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x50, 0x61, 0x72, 0x61,
0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x68, 0x61, 0x6e,
0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x07, 0x69, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0e, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x45,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x45, 0x6e,
0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0c, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x72, 0x70, 0x63,
0x2e, 0x67, 0x63, 0x70, 0x2e, 0x52, 0x70, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0b, 0x72, 0x70, 0x63, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x72, 0x61,
0x6d, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d,
0x61, 0x78, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x6b, 0x0a, 0x18, 0x48,
0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x67, 0x63, 0x70, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68,
0x61, 0x6b, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x62, 0x0a, 0x17, 0x4e, 0x65, 0x78, 0x74,
0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x52, 0x65, 0x71, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x69, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2c,
0x0a, 0x12, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63,
0x79, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6e, 0x65, 0x74, 0x77,
0x6f, 0x72, 0x6b, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x22, 0xe5, 0x01, 0x0a,
0x0d, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x46,
0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e,
0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x73,
0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x46, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67,
0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x71, 0x48,
0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x37,
0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67,
0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x48, 0x61, 0x6e, 0x64,
0x73, 0x68, 0x61, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x48,
0x00, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x72, 0x65, 0x71, 0x5f, 0x6f,
0x6e, 0x65, 0x6f, 0x66, 0x22, 0x9a, 0x03, 0x0a, 0x10, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61,
0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x61, 0x70, 0x70,
0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x27, 0x0a, 0x0f,
0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x50, 0x72, 0x6f,
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x61, 0x74,
0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61,
0x12, 0x37, 0x0a, 0x0d, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
0x63, 0x70, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0c, 0x70, 0x65, 0x65,
0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0e, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x12, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x49, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0f, 0x6b, 0x65, 0x65, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x70, 0x65, 0x6e,
0x12, 0x49, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x72,
0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x52, 0x70, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0f, 0x70, 0x65, 0x65, 0x72,
0x52, 0x70, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6d,
0x61, 0x78, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x69, 0x7a,
0x65, 0x22, 0x40, 0x0a, 0x10, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x72, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74,
0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x22, 0xbe, 0x01, 0x0a, 0x0e, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b,
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x66, 0x72,
0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x46,
0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x63,
0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x62,
0x79, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x06,
0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b,
0x65, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
0x12, 0x32, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64,
0x73, 0x68, 0x61, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74,
0x61, 0x74, 0x75, 0x73, 0x2a, 0x4a, 0x0a, 0x11, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b,
0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x22, 0x0a, 0x1e, 0x48, 0x41, 0x4e,
0x44, 0x53, 0x48, 0x41, 0x4b, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f,
0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a,
0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x4c, 0x54, 0x53, 0x10, 0x02,
0x2a, 0x45, 0x0a, 0x0f, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f,
0x63, 0x6f, 0x6c, 0x12, 0x20, 0x0a, 0x1c, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x5f, 0x50,
0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x07,
0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x02, 0x32, 0x5b, 0x0a, 0x11, 0x48, 0x61, 0x6e, 0x64, 0x73,
0x68, 0x61, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x0b,
0x44, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x12, 0x17, 0x2e, 0x67, 0x72,
0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65,
0x72, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e,
0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00,
0x28, 0x01, 0x30, 0x01, 0x42, 0x6b, 0x0a, 0x15, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x61, 0x6c, 0x74, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x42, 0x0f, 0x48,
0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x3f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e,
0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x73, 0x2f, 0x61, 0x6c, 0x74, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x67, 0x63,
0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var ( var (
file_grpc_gcp_handshaker_proto_rawDescOnce sync.Once file_grpc_gcp_handshaker_proto_rawDescOnce sync.Once
@ -1240,21 +1155,23 @@ func file_grpc_gcp_handshaker_proto_rawDescGZIP() []byte {
var file_grpc_gcp_handshaker_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_grpc_gcp_handshaker_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_grpc_gcp_handshaker_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_grpc_gcp_handshaker_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_grpc_gcp_handshaker_proto_goTypes = []any{ var file_grpc_gcp_handshaker_proto_goTypes = []any{
(HandshakeProtocol)(0), // 0: grpc.gcp.HandshakeProtocol (HandshakeProtocol)(0), // 0: grpc.gcp.HandshakeProtocol
(NetworkProtocol)(0), // 1: grpc.gcp.NetworkProtocol (NetworkProtocol)(0), // 1: grpc.gcp.NetworkProtocol
(*Endpoint)(nil), // 2: grpc.gcp.Endpoint (*Endpoint)(nil), // 2: grpc.gcp.Endpoint
(*Identity)(nil), // 3: grpc.gcp.Identity (*Identity)(nil), // 3: grpc.gcp.Identity
(*StartClientHandshakeReq)(nil), // 4: grpc.gcp.StartClientHandshakeReq (*StartClientHandshakeReq)(nil), // 4: grpc.gcp.StartClientHandshakeReq
(*ServerHandshakeParameters)(nil), // 5: grpc.gcp.ServerHandshakeParameters (*ServerHandshakeParameters)(nil), // 5: grpc.gcp.ServerHandshakeParameters
(*StartServerHandshakeReq)(nil), // 6: grpc.gcp.StartServerHandshakeReq (*StartServerHandshakeReq)(nil), // 6: grpc.gcp.StartServerHandshakeReq
(*NextHandshakeMessageReq)(nil), // 7: grpc.gcp.NextHandshakeMessageReq (*NextHandshakeMessageReq)(nil), // 7: grpc.gcp.NextHandshakeMessageReq
(*HandshakerReq)(nil), // 8: grpc.gcp.HandshakerReq (*HandshakerReq)(nil), // 8: grpc.gcp.HandshakerReq
(*HandshakerResult)(nil), // 9: grpc.gcp.HandshakerResult (*HandshakerResult)(nil), // 9: grpc.gcp.HandshakerResult
(*HandshakerStatus)(nil), // 10: grpc.gcp.HandshakerStatus (*HandshakerStatus)(nil), // 10: grpc.gcp.HandshakerStatus
(*HandshakerResp)(nil), // 11: grpc.gcp.HandshakerResp (*HandshakerResp)(nil), // 11: grpc.gcp.HandshakerResp
nil, // 12: grpc.gcp.Identity.AttributesEntry nil, // 12: grpc.gcp.Identity.AttributesEntry
nil, // 13: grpc.gcp.StartServerHandshakeReq.HandshakeParametersEntry nil, // 13: grpc.gcp.StartServerHandshakeReq.HandshakeParametersEntry
(*RpcProtocolVersions)(nil), // 14: grpc.gcp.RpcProtocolVersions (*RpcProtocolVersions)(nil), // 14: grpc.gcp.RpcProtocolVersions
(*TransportProtocolPreferences)(nil), // 15: grpc.gcp.TransportProtocolPreferences
(*NegotiatedTransportProtocol)(nil), // 16: grpc.gcp.NegotiatedTransportProtocol
} }
var file_grpc_gcp_handshaker_proto_depIdxs = []int32{ var file_grpc_gcp_handshaker_proto_depIdxs = []int32{
1, // 0: grpc.gcp.Endpoint.protocol:type_name -> grpc.gcp.NetworkProtocol 1, // 0: grpc.gcp.Endpoint.protocol:type_name -> grpc.gcp.NetworkProtocol
@ -1265,27 +1182,30 @@ var file_grpc_gcp_handshaker_proto_depIdxs = []int32{
2, // 5: grpc.gcp.StartClientHandshakeReq.local_endpoint:type_name -> grpc.gcp.Endpoint 2, // 5: grpc.gcp.StartClientHandshakeReq.local_endpoint:type_name -> grpc.gcp.Endpoint
2, // 6: grpc.gcp.StartClientHandshakeReq.remote_endpoint:type_name -> grpc.gcp.Endpoint 2, // 6: grpc.gcp.StartClientHandshakeReq.remote_endpoint:type_name -> grpc.gcp.Endpoint
14, // 7: grpc.gcp.StartClientHandshakeReq.rpc_versions:type_name -> grpc.gcp.RpcProtocolVersions 14, // 7: grpc.gcp.StartClientHandshakeReq.rpc_versions:type_name -> grpc.gcp.RpcProtocolVersions
3, // 8: grpc.gcp.ServerHandshakeParameters.local_identities:type_name -> grpc.gcp.Identity 15, // 8: grpc.gcp.StartClientHandshakeReq.transport_protocol_preferences:type_name -> grpc.gcp.TransportProtocolPreferences
13, // 9: grpc.gcp.StartServerHandshakeReq.handshake_parameters:type_name -> grpc.gcp.StartServerHandshakeReq.HandshakeParametersEntry 3, // 9: grpc.gcp.ServerHandshakeParameters.local_identities:type_name -> grpc.gcp.Identity
2, // 10: grpc.gcp.StartServerHandshakeReq.local_endpoint:type_name -> grpc.gcp.Endpoint 13, // 10: grpc.gcp.StartServerHandshakeReq.handshake_parameters:type_name -> grpc.gcp.StartServerHandshakeReq.HandshakeParametersEntry
2, // 11: grpc.gcp.StartServerHandshakeReq.remote_endpoint:type_name -> grpc.gcp.Endpoint 2, // 11: grpc.gcp.StartServerHandshakeReq.local_endpoint:type_name -> grpc.gcp.Endpoint
14, // 12: grpc.gcp.StartServerHandshakeReq.rpc_versions:type_name -> grpc.gcp.RpcProtocolVersions 2, // 12: grpc.gcp.StartServerHandshakeReq.remote_endpoint:type_name -> grpc.gcp.Endpoint
4, // 13: grpc.gcp.HandshakerReq.client_start:type_name -> grpc.gcp.StartClientHandshakeReq 14, // 13: grpc.gcp.StartServerHandshakeReq.rpc_versions:type_name -> grpc.gcp.RpcProtocolVersions
6, // 14: grpc.gcp.HandshakerReq.server_start:type_name -> grpc.gcp.StartServerHandshakeReq 15, // 14: grpc.gcp.StartServerHandshakeReq.transport_protocol_preferences:type_name -> grpc.gcp.TransportProtocolPreferences
7, // 15: grpc.gcp.HandshakerReq.next:type_name -> grpc.gcp.NextHandshakeMessageReq 4, // 15: grpc.gcp.HandshakerReq.client_start:type_name -> grpc.gcp.StartClientHandshakeReq
3, // 16: grpc.gcp.HandshakerResult.peer_identity:type_name -> grpc.gcp.Identity 6, // 16: grpc.gcp.HandshakerReq.server_start:type_name -> grpc.gcp.StartServerHandshakeReq
3, // 17: grpc.gcp.HandshakerResult.local_identity:type_name -> grpc.gcp.Identity 7, // 17: grpc.gcp.HandshakerReq.next:type_name -> grpc.gcp.NextHandshakeMessageReq
14, // 18: grpc.gcp.HandshakerResult.peer_rpc_versions:type_name -> grpc.gcp.RpcProtocolVersions 3, // 18: grpc.gcp.HandshakerResult.peer_identity:type_name -> grpc.gcp.Identity
9, // 19: grpc.gcp.HandshakerResp.result:type_name -> grpc.gcp.HandshakerResult 3, // 19: grpc.gcp.HandshakerResult.local_identity:type_name -> grpc.gcp.Identity
10, // 20: grpc.gcp.HandshakerResp.status:type_name -> grpc.gcp.HandshakerStatus 14, // 20: grpc.gcp.HandshakerResult.peer_rpc_versions:type_name -> grpc.gcp.RpcProtocolVersions
5, // 21: grpc.gcp.StartServerHandshakeReq.HandshakeParametersEntry.value:type_name -> grpc.gcp.ServerHandshakeParameters 16, // 21: grpc.gcp.HandshakerResult.transport_protocol:type_name -> grpc.gcp.NegotiatedTransportProtocol
8, // 22: grpc.gcp.HandshakerService.DoHandshake:input_type -> grpc.gcp.HandshakerReq 9, // 22: grpc.gcp.HandshakerResp.result:type_name -> grpc.gcp.HandshakerResult
11, // 23: grpc.gcp.HandshakerService.DoHandshake:output_type -> grpc.gcp.HandshakerResp 10, // 23: grpc.gcp.HandshakerResp.status:type_name -> grpc.gcp.HandshakerStatus
23, // [23:24] is the sub-list for method output_type 5, // 24: grpc.gcp.StartServerHandshakeReq.HandshakeParametersEntry.value:type_name -> grpc.gcp.ServerHandshakeParameters
22, // [22:23] is the sub-list for method input_type 8, // 25: grpc.gcp.HandshakerService.DoHandshake:input_type -> grpc.gcp.HandshakerReq
22, // [22:22] is the sub-list for extension type_name 11, // 26: grpc.gcp.HandshakerService.DoHandshake:output_type -> grpc.gcp.HandshakerResp
22, // [22:22] is the sub-list for extension extendee 26, // [26:27] is the sub-list for method output_type
0, // [0:22] is the sub-list for field type_name 25, // [25:26] is the sub-list for method input_type
25, // [25:25] is the sub-list for extension type_name
25, // [25:25] is the sub-list for extension extendee
0, // [0:25] is the sub-list for field type_name
} }
func init() { file_grpc_gcp_handshaker_proto_init() } func init() { file_grpc_gcp_handshaker_proto_init() }

View File

@ -95,7 +95,7 @@ type HandshakerServiceServer interface {
type UnimplementedHandshakerServiceServer struct{} type UnimplementedHandshakerServiceServer struct{}
func (UnimplementedHandshakerServiceServer) DoHandshake(grpc.BidiStreamingServer[HandshakerReq, HandshakerResp]) error { func (UnimplementedHandshakerServiceServer) DoHandshake(grpc.BidiStreamingServer[HandshakerReq, HandshakerResp]) error {
return status.Errorf(codes.Unimplemented, "method DoHandshake not implemented") return status.Error(codes.Unimplemented, "method DoHandshake not implemented")
} }
func (UnimplementedHandshakerServiceServer) mustEmbedUnimplementedHandshakerServiceServer() {} func (UnimplementedHandshakerServiceServer) mustEmbedUnimplementedHandshakerServiceServer() {}
func (UnimplementedHandshakerServiceServer) testEmbeddedByValue() {} func (UnimplementedHandshakerServiceServer) testEmbeddedByValue() {}

View File

@ -17,7 +17,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.4 // protoc-gen-go v1.36.6
// protoc v5.27.1 // protoc v5.27.1
// source: grpc/gcp/transport_security_common.proto // source: grpc/gcp/transport_security_common.proto
@ -144,6 +144,97 @@ func (x *RpcProtocolVersions) GetMinRpcVersion() *RpcProtocolVersions_Version {
return nil return nil
} }
// The ordered list of protocols that the client wishes to use, or the set
// that the server supports.
type TransportProtocolPreferences struct {
state protoimpl.MessageState `protogen:"open.v1"`
TransportProtocol []string `protobuf:"bytes,1,rep,name=transport_protocol,json=transportProtocol,proto3" json:"transport_protocol,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TransportProtocolPreferences) Reset() {
*x = TransportProtocolPreferences{}
mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *TransportProtocolPreferences) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TransportProtocolPreferences) ProtoMessage() {}
func (x *TransportProtocolPreferences) ProtoReflect() protoreflect.Message {
mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TransportProtocolPreferences.ProtoReflect.Descriptor instead.
func (*TransportProtocolPreferences) Descriptor() ([]byte, []int) {
return file_grpc_gcp_transport_security_common_proto_rawDescGZIP(), []int{1}
}
func (x *TransportProtocolPreferences) GetTransportProtocol() []string {
if x != nil {
return x.TransportProtocol
}
return nil
}
// The negotiated transport protocol.
type NegotiatedTransportProtocol struct {
state protoimpl.MessageState `protogen:"open.v1"`
TransportProtocol string `protobuf:"bytes,1,opt,name=transport_protocol,json=transportProtocol,proto3" json:"transport_protocol,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *NegotiatedTransportProtocol) Reset() {
*x = NegotiatedTransportProtocol{}
mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NegotiatedTransportProtocol) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NegotiatedTransportProtocol) ProtoMessage() {}
func (x *NegotiatedTransportProtocol) ProtoReflect() protoreflect.Message {
mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NegotiatedTransportProtocol.ProtoReflect.Descriptor instead.
func (*NegotiatedTransportProtocol) Descriptor() ([]byte, []int) {
return file_grpc_gcp_transport_security_common_proto_rawDescGZIP(), []int{2}
}
func (x *NegotiatedTransportProtocol) GetTransportProtocol() string {
if x != nil {
return x.TransportProtocol
}
return ""
}
// RPC version contains a major version and a minor version. // RPC version contains a major version and a minor version.
type RpcProtocolVersions_Version struct { type RpcProtocolVersions_Version struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
@ -155,7 +246,7 @@ type RpcProtocolVersions_Version struct {
func (x *RpcProtocolVersions_Version) Reset() { func (x *RpcProtocolVersions_Version) Reset() {
*x = RpcProtocolVersions_Version{} *x = RpcProtocolVersions_Version{}
mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[1] mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -167,7 +258,7 @@ func (x *RpcProtocolVersions_Version) String() string {
func (*RpcProtocolVersions_Version) ProtoMessage() {} func (*RpcProtocolVersions_Version) ProtoMessage() {}
func (x *RpcProtocolVersions_Version) ProtoReflect() protoreflect.Message { func (x *RpcProtocolVersions_Version) ProtoReflect() protoreflect.Message {
mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[1] mi := &file_grpc_gcp_transport_security_common_proto_msgTypes[3]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -199,40 +290,24 @@ func (x *RpcProtocolVersions_Version) GetMinor() uint32 {
var File_grpc_gcp_transport_security_common_proto protoreflect.FileDescriptor var File_grpc_gcp_transport_security_common_proto protoreflect.FileDescriptor
var file_grpc_gcp_transport_security_common_proto_rawDesc = string([]byte{ const file_grpc_gcp_transport_security_common_proto_rawDesc = "" +
0x0a, 0x28, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x63, 0x70, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, "\n" +
0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x6f, "(grpc/gcp/transport_security_common.proto\x12\bgrpc.gcp\"\xea\x01\n" +
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x67, 0x72, 0x70, 0x63, "\x13RpcProtocolVersions\x12M\n" +
0x2e, 0x67, 0x63, 0x70, 0x22, 0xea, 0x01, 0x0a, 0x13, 0x52, 0x70, 0x63, 0x50, 0x72, 0x6f, 0x74, "\x0fmax_rpc_version\x18\x01 \x01(\v2%.grpc.gcp.RpcProtocolVersions.VersionR\rmaxRpcVersion\x12M\n" +
0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4d, 0x0a, 0x0f, "\x0fmin_rpc_version\x18\x02 \x01(\v2%.grpc.gcp.RpcProtocolVersions.VersionR\rminRpcVersion\x1a5\n" +
0x6d, 0x61, 0x78, 0x5f, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, "\aVersion\x12\x14\n" +
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, "\x05major\x18\x01 \x01(\rR\x05major\x12\x14\n" +
0x2e, 0x52, 0x70, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, "\x05minor\x18\x02 \x01(\rR\x05minor\"M\n" +
0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x6d, 0x61, "\x1cTransportProtocolPreferences\x12-\n" +
0x78, 0x52, 0x70, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x0f, 0x6d, "\x12transport_protocol\x18\x01 \x03(\tR\x11transportProtocol\"L\n" +
0x69, 0x6e, 0x5f, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, "\x1bNegotiatedTransportProtocol\x12-\n" +
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x63, 0x70, 0x2e, "\x12transport_protocol\x18\x01 \x01(\tR\x11transportProtocol*Q\n" +
0x52, 0x70, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, "\rSecurityLevel\x12\x11\n" +
0x6f, 0x6e, 0x73, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x6d, 0x69, 0x6e, "\rSECURITY_NONE\x10\x00\x12\x12\n" +
0x52, 0x70, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x35, 0x0a, 0x07, 0x56, 0x65, "\x0eINTEGRITY_ONLY\x10\x01\x12\x19\n" +
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x01, "\x15INTEGRITY_AND_PRIVACY\x10\x02Bx\n" +
0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, "\x15io.grpc.alts.internalB\x1cTransportSecurityCommonProtoP\x01Z?google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcpb\x06proto3"
0x69, 0x6e, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f,
0x72, 0x2a, 0x51, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4c, 0x65, 0x76,
0x65, 0x6c, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x45, 0x43, 0x55, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x4e,
0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x52, 0x49,
0x54, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x54,
0x45, 0x47, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x50, 0x52, 0x49, 0x56, 0x41,
0x43, 0x59, 0x10, 0x02, 0x42, 0x78, 0x0a, 0x15, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x61, 0x6c, 0x74, 0x73, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x42, 0x1c, 0x54,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67,
0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
0x73, 0x2f, 0x61, 0x6c, 0x74, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x67, 0x63, 0x70, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var ( var (
file_grpc_gcp_transport_security_common_proto_rawDescOnce sync.Once file_grpc_gcp_transport_security_common_proto_rawDescOnce sync.Once
@ -247,15 +322,17 @@ func file_grpc_gcp_transport_security_common_proto_rawDescGZIP() []byte {
} }
var file_grpc_gcp_transport_security_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_grpc_gcp_transport_security_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_grpc_gcp_transport_security_common_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_grpc_gcp_transport_security_common_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_grpc_gcp_transport_security_common_proto_goTypes = []any{ var file_grpc_gcp_transport_security_common_proto_goTypes = []any{
(SecurityLevel)(0), // 0: grpc.gcp.SecurityLevel (SecurityLevel)(0), // 0: grpc.gcp.SecurityLevel
(*RpcProtocolVersions)(nil), // 1: grpc.gcp.RpcProtocolVersions (*RpcProtocolVersions)(nil), // 1: grpc.gcp.RpcProtocolVersions
(*RpcProtocolVersions_Version)(nil), // 2: grpc.gcp.RpcProtocolVersions.Version (*TransportProtocolPreferences)(nil), // 2: grpc.gcp.TransportProtocolPreferences
(*NegotiatedTransportProtocol)(nil), // 3: grpc.gcp.NegotiatedTransportProtocol
(*RpcProtocolVersions_Version)(nil), // 4: grpc.gcp.RpcProtocolVersions.Version
} }
var file_grpc_gcp_transport_security_common_proto_depIdxs = []int32{ var file_grpc_gcp_transport_security_common_proto_depIdxs = []int32{
2, // 0: grpc.gcp.RpcProtocolVersions.max_rpc_version:type_name -> grpc.gcp.RpcProtocolVersions.Version 4, // 0: grpc.gcp.RpcProtocolVersions.max_rpc_version:type_name -> grpc.gcp.RpcProtocolVersions.Version
2, // 1: grpc.gcp.RpcProtocolVersions.min_rpc_version:type_name -> grpc.gcp.RpcProtocolVersions.Version 4, // 1: grpc.gcp.RpcProtocolVersions.min_rpc_version:type_name -> grpc.gcp.RpcProtocolVersions.Version
2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension type_name
@ -274,7 +351,7 @@ func file_grpc_gcp_transport_security_common_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_gcp_transport_security_common_proto_rawDesc), len(file_grpc_gcp_transport_security_common_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_gcp_transport_security_common_proto_rawDesc), len(file_grpc_gcp_transport_security_common_proto_rawDesc)),
NumEnums: 1, NumEnums: 1,
NumMessages: 2, NumMessages: 4,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@ -145,6 +145,8 @@ func MakeFrame(pl string) []byte {
// FakeHandshaker is a fake implementation of the ALTS handshaker service. // FakeHandshaker is a fake implementation of the ALTS handshaker service.
type FakeHandshaker struct { type FakeHandshaker struct {
altsgrpc.HandshakerServiceServer altsgrpc.HandshakerServiceServer
// ExpectedBoundAccessToken is the expected bound access token in the ClientStart request.
ExpectedBoundAccessToken string
} }
// DoHandshake performs a fake ALTS handshake. // DoHandshake performs a fake ALTS handshake.
@ -221,6 +223,9 @@ func (h *FakeHandshaker) processStartClient(req *altspb.StartClientHandshakeReq)
if len(req.RecordProtocols) != 1 || req.RecordProtocols[0] != "ALTSRP_GCM_AES128_REKEY" { if len(req.RecordProtocols) != 1 || req.RecordProtocols[0] != "ALTSRP_GCM_AES128_REKEY" {
return nil, fmt.Errorf("unexpected record protocols: %v", req.RecordProtocols) return nil, fmt.Errorf("unexpected record protocols: %v", req.RecordProtocols)
} }
if h.ExpectedBoundAccessToken != req.GetAccessToken() {
return nil, fmt.Errorf("unexpected access token: %v", req.GetAccessToken())
}
return &altspb.HandshakerResp{ return &altspb.HandshakerResp{
OutFrames: []byte("ClientInit"), OutFrames: []byte("ClientInit"),
BytesConsumed: 0, BytesConsumed: 0,

View File

@ -120,6 +120,20 @@ type AuthInfo interface {
AuthType() string AuthType() string
} }
// AuthorityValidator validates the authority used to override the `:authority`
// header. This is an optional interface that implementations of AuthInfo can
// implement if they support per-RPC authority overrides. It is invoked when the
// application attempts to override the HTTP/2 `:authority` header using the
// CallAuthority call option.
type AuthorityValidator interface {
// ValidateAuthority checks the authority value used to override the
// `:authority` header. The authority parameter is the override value
// provided by the application via the CallAuthority option. This value
// typically corresponds to the server hostname or endpoint the RPC is
// targeting. It returns non-nil error if the validation fails.
ValidateAuthority(authority string) error
}
// ErrConnDispatched indicates that rawConn has been dispatched out of gRPC // ErrConnDispatched indicates that rawConn has been dispatched out of gRPC
// and the caller should not close rawConn. // and the caller should not close rawConn.
var ErrConnDispatched = errors.New("credentials: rawConn is dispatched out of gRPC") var ErrConnDispatched = errors.New("credentials: rawConn is dispatched out of gRPC")
@ -207,14 +221,32 @@ type RequestInfo struct {
AuthInfo AuthInfo AuthInfo AuthInfo
} }
// requestInfoKey is a struct to be used as the key to store RequestInfo in a
// context.
type requestInfoKey struct{}
// RequestInfoFromContext extracts the RequestInfo from the context if it exists. // RequestInfoFromContext extracts the RequestInfo from the context if it exists.
// //
// This API is experimental. // This API is experimental.
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) { func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
ri, ok = icredentials.RequestInfoFromContext(ctx).(RequestInfo) ri, ok = ctx.Value(requestInfoKey{}).(RequestInfo)
return ri, ok return ri, ok
} }
// NewContextWithRequestInfo creates a new context from ctx and attaches ri to it.
//
// This RequestInfo will be accessible via RequestInfoFromContext.
//
// Intended to be used from tests for PerRPCCredentials implementations (that
// often need to check connection's SecurityLevel). Should not be used from
// non-test code: the gRPC client already prepares a context with the correct
// RequestInfo attached when calling PerRPCCredentials.GetRequestMetadata.
//
// This API is experimental.
func NewContextWithRequestInfo(ctx context.Context, ri RequestInfo) context.Context {
return context.WithValue(ctx, requestInfoKey{}, ri)
}
// ClientHandshakeInfo holds data to be passed to ClientHandshake. This makes // ClientHandshakeInfo holds data to be passed to ClientHandshake. This makes
// it possible to pass arbitrary data to the handshaker from gRPC, resolver, // it possible to pass arbitrary data to the handshaker from gRPC, resolver,
// balancer etc. Individual credential implementations control the actual // balancer etc. Individual credential implementations control the actual

View File

@ -0,0 +1,344 @@
/*
*
* Copyright 2025 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package credentials_test
import (
"context"
"crypto/tls"
"fmt"
"net"
"testing"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/credentials/local"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/grpc/testdata"
testgrpc "google.golang.org/grpc/interop/grpc_testing"
testpb "google.golang.org/grpc/interop/grpc_testing"
)
func authorityChecker(ctx context.Context, wantAuthority string) error {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return status.Error(codes.InvalidArgument, "failed to parse metadata")
}
auths, ok := md[":authority"]
if !ok {
return status.Error(codes.InvalidArgument, "no authority header")
}
if len(auths) != 1 {
return status.Errorf(codes.InvalidArgument, "expected exactly one authority header, got %v", auths)
}
if auths[0] != wantAuthority {
return status.Errorf(codes.InvalidArgument, "invalid authority header %q, want %q", auths[0], wantAuthority)
}
return nil
}
func loadTLSCreds(t *testing.T) (grpc.ServerOption, grpc.DialOption) {
t.Helper()
cert, err := tls.LoadX509KeyPair(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
if err != nil {
t.Fatalf("Failed to load key pair: %v", err)
return nil, nil
}
serverCreds := grpc.Creds(credentials.NewServerTLSFromCert(&cert))
clientCreds, err := credentials.NewClientTLSFromFile(testdata.Path("x509/server_ca_cert.pem"), "x.test.example.com")
if err != nil {
t.Fatalf("Failed to create client credentials: %v", err)
}
return serverCreds, grpc.WithTransportCredentials(clientCreds)
}
// Tests the scenario where the `grpc.CallAuthority` call option is used with
// different transport credentials. The test verifies that the specified
// authority is correctly propagated to the serve when a correct authority is
// used.
func (s) TestCorrectAuthorityWithCreds(t *testing.T) {
const authority = "auth.test.example.com"
tests := []struct {
name string
creds func(t *testing.T) (grpc.ServerOption, grpc.DialOption)
expectedAuth string
}{
{
name: "Insecure",
creds: func(*testing.T) (grpc.ServerOption, grpc.DialOption) {
c := insecure.NewCredentials()
return grpc.Creds(c), grpc.WithTransportCredentials(c)
},
expectedAuth: authority,
},
{
name: "Local",
creds: func(*testing.T) (grpc.ServerOption, grpc.DialOption) {
c := local.NewCredentials()
return grpc.Creds(c), grpc.WithTransportCredentials(c)
},
expectedAuth: authority,
},
{
name: "TLS",
creds: func(t *testing.T) (grpc.ServerOption, grpc.DialOption) {
return loadTLSCreds(t)
},
expectedAuth: authority,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ss := &stubserver.StubServer{
EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
if err := authorityChecker(ctx, tt.expectedAuth); err != nil {
return nil, err
}
return &testpb.Empty{}, nil
},
}
serverOpt, dialOpt := tt.creds(t)
if err := ss.StartServer(serverOpt); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer ss.Stop()
cc, err := grpc.NewClient(ss.Address, dialOpt)
if err != nil {
t.Fatalf("grpc.NewClient(%q) = %v", ss.Address, err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
if _, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(tt.expectedAuth)); err != nil {
t.Fatalf("EmptyCall() rpc failed: %v", err)
}
})
}
}
// Tests the `grpc.CallAuthority` option with TLS credentials. This test verifies
// that the RPC fails with `UNAVAILABLE` status code and doesn't reach the server
// when an incorrect authority is used.
func (s) TestIncorrectAuthorityWithTLS(t *testing.T) {
cert, err := tls.LoadX509KeyPair(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
if err != nil {
t.Fatalf("Failed to load key pair: %s", err)
}
creds, err := credentials.NewClientTLSFromFile(testdata.Path("x509/server_ca_cert.pem"), "x.test.example.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
serverCalled := make(chan struct{})
ss := &stubserver.StubServer{
EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
close(serverCalled)
return nil, nil
},
}
if err := ss.StartServer(grpc.Creds(credentials.NewServerTLSFromCert(&cert))); err != nil {
t.Fatalf("Error starting endpoint server: %v", err)
}
defer ss.Stop()
cc, err := grpc.NewClient(ss.Address, grpc.WithTransportCredentials(creds))
if err != nil {
t.Fatalf("grpc.NewClient(%q) = %v", ss.Address, err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
const authority = "auth.example.com"
if _, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(authority)); status.Code(err) != codes.Unavailable {
t.Fatalf("EmptyCall() returned status %v, want %v", status.Code(err), codes.Unavailable)
}
select {
case <-serverCalled:
t.Fatalf("Server handler should not have been called")
case <-time.After(defaultTestShortTimeout):
}
}
// testAuthInfoNoValidator implements only credentials.AuthInfo and not
// credentials.AuthorityValidator.
type testAuthInfoNoValidator struct{}
// AuthType returns the authentication type.
func (testAuthInfoNoValidator) AuthType() string {
return "test"
}
// testAuthInfoWithValidator implements both credentials.AuthInfo and
// credentials.AuthorityValidator.
type testAuthInfoWithValidator struct {
validAuthority string
}
// AuthType returns the authentication type.
func (testAuthInfoWithValidator) AuthType() string {
return "test"
}
// ValidateAuthority implements credentials.AuthorityValidator.
func (v testAuthInfoWithValidator) ValidateAuthority(authority string) error {
if authority == v.validAuthority {
return nil
}
return fmt.Errorf("invalid authority %q, want %q", authority, v.validAuthority)
}
// testCreds is a test TransportCredentials that can optionally support
// authority validation.
type testCreds struct {
authority string
}
// ClientHandshake performs the client-side handshake.
func (c *testCreds) ClientHandshake(_ context.Context, _ string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if c.authority != "" {
return rawConn, testAuthInfoWithValidator{validAuthority: c.authority}, nil
}
return rawConn, testAuthInfoNoValidator{}, nil
}
// ServerHandshake performs the server-side handshake.
func (c *testCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if c.authority != "" {
return rawConn, testAuthInfoWithValidator{validAuthority: c.authority}, nil
}
return rawConn, testAuthInfoNoValidator{}, nil
}
// Clone creates a copy of testCreds.
func (c *testCreds) Clone() credentials.TransportCredentials {
return &testCreds{authority: c.authority}
}
// Info provides protocol information.
func (c *testCreds) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{}
}
// OverrideServerName overrides the server name used for verification.
func (c *testCreds) OverrideServerName(string) error {
return nil
}
// TestAuthorityValidationFailureWithCustomCreds tests the `grpc.CallAuthority`
// call option using custom credentials. It covers two failure scenarios:
// - The credentials implement AuthorityValidator but authority used to override
// is not valid.
// - The credentials do not implement AuthorityValidator, but an authority
// override is specified.
// In both cases, the RPC is expected to fail with an `UNAVAILABLE` status code.
func (s) TestAuthorityValidationFailureWithCustomCreds(t *testing.T) {
tests := []struct {
name string
creds credentials.TransportCredentials
authority string
}{
{
name: "IncorrectAuthorityWithFakeCreds",
authority: "auth.example.com",
creds: &testCreds{authority: "auth.test.example.com"},
},
{
name: "FakeCredsWithNoAuthValidator",
creds: &testCreds{},
authority: "auth.test.example.com",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
serverCalled := make(chan struct{})
ss := stubserver.StubServer{
EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
close(serverCalled)
return nil, nil
},
}
if err := ss.StartServer(); err != nil {
t.Fatalf("Failed to start stub server: %v", err)
}
defer ss.Stop()
cc, err := grpc.NewClient(ss.Address, grpc.WithTransportCredentials(tt.creds))
if err != nil {
t.Fatalf("grpc.NewClient(%q) = %v", ss.Address, err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
if _, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(tt.authority)); status.Code(err) != codes.Unavailable {
t.Fatalf("EmptyCall() returned status %v, want %v", status.Code(err), codes.Unavailable)
}
select {
case <-serverCalled:
t.Fatalf("Server should not have been called")
case <-time.After(defaultTestShortTimeout):
}
})
}
}
// TestCorrectAuthorityWithCustomCreds tests the `grpc.CallAuthority` call
// option using custom credentials. It verifies that the provided authority is
// correctly propagated to the server when a correct authority is used.
func (s) TestCorrectAuthorityWithCustomCreds(t *testing.T) {
const authority = "auth.test.example.com"
creds := &testCreds{authority: "auth.test.example.com"}
ss := stubserver.StubServer{
EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
if err := authorityChecker(ctx, authority); err != nil {
return nil, err
}
return &testpb.Empty{}, nil
},
}
if err := ss.StartServer(); err != nil {
t.Fatalf("Failed to start stub server: %v", err)
}
defer ss.Stop()
cc, err := grpc.NewClient(ss.Address, grpc.WithTransportCredentials(creds))
if err != nil {
t.Fatalf("grpc.NewClient(%q) = %v", ss.Address, err)
}
defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
if _, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(authority)); status.Code(err) != codes.OK {
t.Fatalf("EmptyCall() returned status %v, want %v", status.Code(err), codes.OK)
}
}

View File

@ -255,7 +255,7 @@ func TestDefaultCredentialsWithOptions(t *testing.T) {
t.Run(tc.desc, func(t *testing.T) { t.Run(tc.desc, func(t *testing.T) {
bundle := NewDefaultCredentialsWithOptions(tc.defaultCredsOpts) bundle := NewDefaultCredentialsWithOptions(tc.defaultCredsOpts)
ri := credentials.RequestInfo{AuthInfo: tc.authInfo} ri := credentials.RequestInfo{AuthInfo: tc.authInfo}
ctx := icredentials.NewRequestInfoContext(ctx, ri) ctx := credentials.NewContextWithRequestInfo(ctx, ri)
got, err := bundle.PerRPCCredentials().GetRequestMetadata(ctx, "uri") got, err := bundle.PerRPCCredentials().GetRequestMetadata(ctx, "uri")
if err != nil { if err != nil {
t.Fatalf("Bundle's PerRPCCredentials().GetRequestMetadata() unexpected error = %v", err) t.Fatalf("Bundle's PerRPCCredentials().GetRequestMetadata() unexpected error = %v", err)

View File

@ -30,7 +30,7 @@ import (
// NewCredentials returns a credentials which disables transport security. // NewCredentials returns a credentials which disables transport security.
// //
// Note that using this credentials with per-RPC credentials which require // Note that using this credentials with per-RPC credentials which require
// transport security is incompatible and will cause grpc.Dial() to fail. // transport security is incompatible and will cause RPCs to fail.
func NewCredentials() credentials.TransportCredentials { func NewCredentials() credentials.TransportCredentials {
return insecureTC{} return insecureTC{}
} }
@ -71,6 +71,12 @@ func (info) AuthType() string {
return "insecure" return "insecure"
} }
// ValidateAuthority allows any value to be overridden for the :authority
// header.
func (info) ValidateAuthority(string) error {
return nil
}
// insecureBundle implements an insecure bundle. // insecureBundle implements an insecure bundle.
// An insecure bundle provides a thin wrapper around insecureTC to support // An insecure bundle provides a thin wrapper around insecureTC to support
// the credentials.Bundle interface. // the credentials.Bundle interface.

View File

@ -49,6 +49,12 @@ func (info) AuthType() string {
return "local" return "local"
} }
// ValidateAuthority allows any value to be overridden for the :authority
// header.
func (info) ValidateAuthority(string) error {
return nil
}
// localTC is the credentials required to establish a local connection. // localTC is the credentials required to establish a local connection.
type localTC struct { type localTC struct {
info credentials.ProtocolInfo info credentials.ProtocolInfo

View File

@ -35,7 +35,6 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
icredentials "google.golang.org/grpc/internal/credentials"
"google.golang.org/grpc/internal/grpctest" "google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/testutils" "google.golang.org/grpc/internal/testutils"
) )
@ -102,7 +101,7 @@ func createTestContext(ctx context.Context, s credentials.SecurityLevel) context
Method: "testInfo", Method: "testInfo",
AuthInfo: auth, AuthInfo: auth,
} }
return icredentials.NewRequestInfoContext(ctx, ri) return credentials.NewContextWithRequestInfo(ctx, ri)
} }
// errReader implements the io.Reader interface and returns an error from the // errReader implements the io.Reader interface and returns an error from the

View File

@ -22,6 +22,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"errors"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
@ -50,6 +51,21 @@ func (t TLSInfo) AuthType() string {
return "tls" return "tls"
} }
// ValidateAuthority validates the provided authority being used to override the
// :authority header by verifying it against the peer certificates. It returns a
// non-nil error if the validation fails.
func (t TLSInfo) ValidateAuthority(authority string) error {
var errs []error
for _, cert := range t.State.PeerCertificates {
var err error
if err = cert.VerifyHostname(authority); err == nil {
return nil
}
errs = append(errs, err)
}
return fmt.Errorf("credentials: invalid authority %q: %v", authority, errors.Join(errs...))
}
// cipherSuiteLookup returns the string version of a TLS cipher suite ID. // cipherSuiteLookup returns the string version of a TLS cipher suite ID.
func cipherSuiteLookup(cipherSuiteID uint16) string { func cipherSuiteLookup(cipherSuiteID uint16) string {
for _, s := range tls.CipherSuites() { for _, s := range tls.CipherSuites() {

View File

@ -24,6 +24,7 @@ import (
"time" "time"
"google.golang.org/grpc/credentials/tls/certprovider" "google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/durationpb"
) )
@ -63,19 +64,24 @@ func pluginConfigFromJSON(jd json.RawMessage) (Options, error) {
// is that the refresh_interval is represented here as a duration proto, // is that the refresh_interval is represented here as a duration proto,
// while in the latter a time.Duration is used. // while in the latter a time.Duration is used.
cfg := &struct { cfg := &struct {
CertificateFile string `json:"certificate_file,omitempty"` CertificateFile string `json:"certificate_file,omitempty"`
PrivateKeyFile string `json:"private_key_file,omitempty"` PrivateKeyFile string `json:"private_key_file,omitempty"`
CACertificateFile string `json:"ca_certificate_file,omitempty"` CACertificateFile string `json:"ca_certificate_file,omitempty"`
RefreshInterval json.RawMessage `json:"refresh_interval,omitempty"` SPIFFETrustBundleMapFile string `json:"spiffe_trust_bundle_map_file,omitempty"`
RefreshInterval json.RawMessage `json:"refresh_interval,omitempty"`
}{} }{}
if err := json.Unmarshal(jd, cfg); err != nil { if err := json.Unmarshal(jd, cfg); err != nil {
return Options{}, fmt.Errorf("pemfile: json.Unmarshal(%s) failed: %v", string(jd), err) return Options{}, fmt.Errorf("pemfile: json.Unmarshal(%s) failed: %v", string(jd), err)
} }
if !envconfig.XDSSPIFFEEnabled {
cfg.SPIFFETrustBundleMapFile = ""
}
opts := Options{ opts := Options{
CertFile: cfg.CertificateFile, CertFile: cfg.CertificateFile,
KeyFile: cfg.PrivateKeyFile, KeyFile: cfg.PrivateKeyFile,
RootFile: cfg.CACertificateFile, RootFile: cfg.CACertificateFile,
SPIFFEBundleMapFile: cfg.SPIFFETrustBundleMapFile,
// Refresh interval is the only field in the configuration for which we // Refresh interval is the only field in the configuration for which we
// support a default value. We cannot possibly have valid defaults for // support a default value. We cannot possibly have valid defaults for
// file paths to watch. Also, it is valid to specify an empty path for // file paths to watch. Also, it is valid to specify an empty path for

View File

@ -21,14 +21,18 @@ package pemfile
import ( import (
"encoding/json" "encoding/json"
"testing" "testing"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/testutils"
) )
func TestParseConfig(t *testing.T) { func TestParseConfig(t *testing.T) {
tests := []struct { tests := []struct {
desc string desc string
input any input any
wantOutput string wantOutput string
wantErr bool wantErr bool
enabledSpiffe bool
}{ }{
{ {
desc: "non JSON input", desc: "non JSON input",
@ -94,7 +98,7 @@ func TestParseConfig(t *testing.T) {
"private_key_file": "/a/b/key.pem", "private_key_file": "/a/b/key.pem",
"ca_certificate_file": "/a/b/ca.pem" "ca_certificate_file": "/a/b/ca.pem"
}`), }`),
wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem:10m0s", wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem::10m0s",
}, },
{ {
desc: "good config", desc: "good config",
@ -105,12 +109,40 @@ func TestParseConfig(t *testing.T) {
"ca_certificate_file": "/a/b/ca.pem", "ca_certificate_file": "/a/b/ca.pem",
"refresh_interval": "200s" "refresh_interval": "200s"
}`), }`),
wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem:3m20s", wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem::3m20s",
},
{
desc: "good config with spiffe disabled",
input: json.RawMessage(`
{
"certificate_file": "/a/b/cert.pem",
"private_key_file": "/a/b/key.pem",
"ca_certificate_file": "/a/b/ca.pem",
"spiffe_trust_bundle_map_file": "/a/b/spiffe_bundle.json",
"refresh_interval": "200s"
}`),
wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem::3m20s",
},
{
desc: "good config with spiffe enabled",
input: json.RawMessage(`
{
"certificate_file": "/a/b/cert.pem",
"private_key_file": "/a/b/key.pem",
"ca_certificate_file": "/a/b/ca.pem",
"spiffe_trust_bundle_map_file": "/a/b/spiffe_bundle.json",
"refresh_interval": "200s"
}`),
wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem:/a/b/spiffe_bundle.json:3m20s",
enabledSpiffe: true,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
if test.enabledSpiffe {
testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true)
}
builder := &pluginBuilder{} builder := &pluginBuilder{}
bc, err := builder.ParseConfig(test.input) bc, err := builder.ParseConfig(test.input)

View File

@ -38,6 +38,7 @@ import (
"google.golang.org/grpc/credentials/tls/certprovider" "google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/credentials/spiffe"
) )
const defaultCertRefreshDuration = 1 * time.Hour const defaultCertRefreshDuration = 1 * time.Hour
@ -61,6 +62,11 @@ type Options struct {
// RootFile is the file that holds trusted root certificate(s). // RootFile is the file that holds trusted root certificate(s).
// Optional. // Optional.
RootFile string RootFile string
// SPIFFEBundleMapFile is the file that holds the spiffe bundle map.
// If a given provider configures both the RootFile and the
// SPIFFEBundleMapFile, the SPIFFEBundleMapFile will be preferred.
// Optional.
SPIFFEBundleMapFile string
// RefreshDuration is the amount of time the plugin waits before checking // RefreshDuration is the amount of time the plugin waits before checking
// for updates in the specified files. // for updates in the specified files.
// Optional. If not set, a default value (1 hour) will be used. // Optional. If not set, a default value (1 hour) will be used.
@ -68,11 +74,11 @@ type Options struct {
} }
func (o Options) canonical() []byte { func (o Options) canonical() []byte {
return []byte(fmt.Sprintf("%s:%s:%s:%s", o.CertFile, o.KeyFile, o.RootFile, o.RefreshDuration)) return []byte(fmt.Sprintf("%s:%s:%s:%s:%s", o.CertFile, o.KeyFile, o.RootFile, o.SPIFFEBundleMapFile, o.RefreshDuration))
} }
func (o Options) validate() error { func (o Options) validate() error {
if o.CertFile == "" && o.KeyFile == "" && o.RootFile == "" { if o.CertFile == "" && o.KeyFile == "" && o.RootFile == "" && o.SPIFFEBundleMapFile == "" {
return fmt.Errorf("pemfile: at least one credential file needs to be specified") return fmt.Errorf("pemfile: at least one credential file needs to be specified")
} }
if keySpecified, certSpecified := o.KeyFile != "", o.CertFile != ""; keySpecified != certSpecified { if keySpecified, certSpecified := o.KeyFile != "", o.CertFile != ""; keySpecified != certSpecified {
@ -109,7 +115,7 @@ func newProvider(o Options) certprovider.Provider {
if o.CertFile != "" && o.KeyFile != "" { if o.CertFile != "" && o.KeyFile != "" {
provider.identityDistributor = newDistributor() provider.identityDistributor = newDistributor()
} }
if o.RootFile != "" { if o.RootFile != "" || o.SPIFFEBundleMapFile != "" {
provider.rootDistributor = newDistributor() provider.rootDistributor = newDistributor()
} }
@ -124,13 +130,14 @@ func newProvider(o Options) certprovider.Provider {
// files and provides the most up-to-date key material for consumption by // files and provides the most up-to-date key material for consumption by
// credentials implementation. // credentials implementation.
type watcher struct { type watcher struct {
identityDistributor distributor identityDistributor distributor
rootDistributor distributor rootDistributor distributor
opts Options opts Options
certFileContents []byte certFileContents []byte
keyFileContents []byte keyFileContents []byte
rootFileContents []byte rootFileContents []byte
cancel context.CancelFunc spiffeBundleMapFileContents []byte
cancel context.CancelFunc
} }
// distributor wraps the methods on certprovider.Distributor which are used by // distributor wraps the methods on certprovider.Distributor which are used by
@ -191,6 +198,35 @@ func (w *watcher) updateRootDistributor() {
return return
} }
// If SPIFFEBundleMap is set, use it and DON'T use the RootFile, even if it
// fails
if w.opts.SPIFFEBundleMapFile != "" {
w.maybeUpdateSPIFFEBundleMap()
} else {
w.maybeUpdateRootFile()
}
}
func (w *watcher) maybeUpdateSPIFFEBundleMap() {
spiffeBundleMapContents, err := os.ReadFile(w.opts.SPIFFEBundleMapFile)
if err != nil {
logger.Warningf("spiffeBundleMapFile (%s) read failed: %v", w.opts.SPIFFEBundleMapFile, err)
return
}
// If the file contents have not changed, skip updating the distributor.
if bytes.Equal(w.spiffeBundleMapFileContents, spiffeBundleMapContents) {
return
}
bundleMap, err := spiffe.BundleMapFromBytes(spiffeBundleMapContents)
if err != nil {
logger.Warning("Failed to parse spiffe bundle map")
return
}
w.spiffeBundleMapFileContents = spiffeBundleMapContents
w.rootDistributor.Set(&certprovider.KeyMaterial{SPIFFEBundleMap: bundleMap}, nil)
}
func (w *watcher) maybeUpdateRootFile() {
rootFileContents, err := os.ReadFile(w.opts.RootFile) rootFileContents, err := os.ReadFile(w.opts.RootFile)
if err != nil { if err != nil {
logger.Warningf("rootFile (%s) read failed: %v", w.opts.RootFile, err) logger.Warningf("rootFile (%s) read failed: %v", w.opts.RootFile, err)
@ -198,7 +234,7 @@ func (w *watcher) updateRootDistributor() {
} }
trustPool := x509.NewCertPool() trustPool := x509.NewCertPool()
if !trustPool.AppendCertsFromPEM(rootFileContents) { if !trustPool.AppendCertsFromPEM(rootFileContents) {
logger.Warning("failed to parse root certificate") logger.Warning("Failed to parse root certificate")
return return
} }
// If the file contents have not changed, skip updating the distributor. // If the file contents have not changed, skip updating the distributor.
@ -249,6 +285,7 @@ func (w *watcher) KeyMaterial(ctx context.Context) (*certprovider.KeyMaterial, e
if err != nil { if err != nil {
return nil, err return nil, err
} }
km.SPIFFEBundleMap = rootKM.SPIFFEBundleMap
km.Roots = rootKM.Roots km.Roots = rootKM.Roots
} }
return km, nil return km, nil

View File

@ -26,6 +26,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/google/go-cmp/cmp"
"google.golang.org/grpc/credentials/tls/certprovider" "google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/internal/grpctest" "google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/testutils" "google.golang.org/grpc/internal/testutils"
@ -35,9 +36,10 @@ import (
const ( const (
// These are the names of files inside temporary directories, which the // These are the names of files inside temporary directories, which the
// plugin is asked to watch. // plugin is asked to watch.
certFile = "cert.pem" certFile = "cert.pem"
keyFile = "key.pem" keyFile = "key.pem"
rootFile = "ca.pem" rootFile = "ca.pem"
spiffeBundleFile = "spiffebundle.json"
defaultTestRefreshDuration = 100 * time.Millisecond defaultTestRefreshDuration = 100 * time.Millisecond
defaultTestTimeout = 5 * time.Second defaultTestTimeout = 5 * time.Second
@ -65,6 +67,10 @@ func compareKeyMaterial(got, want *certprovider.KeyMaterial) error {
return fmt.Errorf("keyMaterial roots = %v, want %v", gotR, wantR) return fmt.Errorf("keyMaterial roots = %v, want %v", gotR, wantR)
} }
if gotBundle, wantBundle := got.SPIFFEBundleMap, want.SPIFFEBundleMap; !cmp.Equal(gotBundle, wantBundle) {
return fmt.Errorf("keyMaterial spiffe bundle map = %v, want %v", gotBundle, wantBundle)
}
return nil return nil
} }
@ -107,12 +113,19 @@ func (s) TestNewProvider(t *testing.T) {
RootFile: testdata.Path("x509/client_ca_cert.pem"), RootFile: testdata.Path("x509/client_ca_cert.pem"),
}, },
}, },
{
desc: "Only spiffe bundle map specified",
options: Options{
SPIFFEBundleMapFile: testdata.Path("spiffe/spiffebundle.json"),
},
},
{ {
desc: "Everything is specified", desc: "Everything is specified",
options: Options{ options: Options{
KeyFile: testdata.Path("x509/client1_key.pem"), KeyFile: testdata.Path("x509/client1_key.pem"),
CertFile: testdata.Path("x509/client1_cert.pem"), CertFile: testdata.Path("x509/client1_cert.pem"),
RootFile: testdata.Path("x509/client_ca_cert.pem"), RootFile: testdata.Path("x509/client_ca_cert.pem"),
SPIFFEBundleMapFile: testdata.Path("spiffe/spiffebundle.json"),
}, },
wantError: false, wantError: false,
}, },
@ -164,11 +177,19 @@ func createTmpFile(t *testing.T, src, dst string) {
t.Logf("%s", string(data)) t.Logf("%s", string(data))
} }
func removeTmpFile(t *testing.T, filePath string) {
t.Helper()
if err := os.Remove(filePath); err != nil {
t.Fatalf("os.RemoveFIle(%q) failed: %v", filePath, err)
}
t.Logf("Removed file at: %s", filePath)
}
// createTempDirWithFiles creates a temporary directory under the system default // createTempDirWithFiles creates a temporary directory under the system default
// tempDir with the given dirSuffix. It also reads from certSrc, keySrc and // tempDir with the given dirSuffix. It also reads from certSrc, keySrc and
// rootSrc files are creates appropriate files under the newly create tempDir. // rootSrc files are creates appropriate files under the newly create tempDir.
// Returns the name of the created tempDir. // Returns the name of the created tempDir.
func createTmpDirWithFiles(t *testing.T, dirSuffix, certSrc, keySrc, rootSrc string) string { func createTmpDirWithFiles(t *testing.T, dirSuffix, certSrc, keySrc, rootSrc, spiffeBundleSrc string) string {
t.Helper() t.Helper()
// Create a temp directory. Passing an empty string for the first argument // Create a temp directory. Passing an empty string for the first argument
@ -182,12 +203,13 @@ func createTmpDirWithFiles(t *testing.T, dirSuffix, certSrc, keySrc, rootSrc str
createTmpFile(t, testdata.Path(certSrc), path.Join(dir, certFile)) createTmpFile(t, testdata.Path(certSrc), path.Join(dir, certFile))
createTmpFile(t, testdata.Path(keySrc), path.Join(dir, keyFile)) createTmpFile(t, testdata.Path(keySrc), path.Join(dir, keyFile))
createTmpFile(t, testdata.Path(rootSrc), path.Join(dir, rootFile)) createTmpFile(t, testdata.Path(rootSrc), path.Join(dir, rootFile))
createTmpFile(t, testdata.Path(spiffeBundleSrc), path.Join(dir, spiffeBundleFile))
return dir return dir
} }
// initializeProvider performs setup steps common to all tests (except the one // initializeProvider performs setup steps common to all tests (except the one
// which uses symlinks). // which uses symlinks).
func initializeProvider(t *testing.T, testName string) (string, certprovider.Provider, *testutils.Channel, func()) { func initializeProvider(t *testing.T, testName string, useSPIFFEBundle bool) (string, certprovider.Provider, *testutils.Channel, func()) {
t.Helper() t.Helper()
// Override the newDistributor to one which pushes on a channel that we // Override the newDistributor to one which pushes on a channel that we
@ -198,13 +220,16 @@ func initializeProvider(t *testing.T, testName string) (string, certprovider.Pro
newDistributor = func() distributor { return d } newDistributor = func() distributor { return d }
// Create a new provider to watch the files in tmpdir. // Create a new provider to watch the files in tmpdir.
dir := createTmpDirWithFiles(t, testName+"*", "x509/client1_cert.pem", "x509/client1_key.pem", "x509/client_ca_cert.pem") dir := createTmpDirWithFiles(t, testName+"*", "x509/client1_cert.pem", "x509/client1_key.pem", "x509/client_ca_cert.pem", "spiffe/spiffebundle.json")
opts := Options{ opts := Options{
CertFile: path.Join(dir, certFile), CertFile: path.Join(dir, certFile),
KeyFile: path.Join(dir, keyFile), KeyFile: path.Join(dir, keyFile),
RootFile: path.Join(dir, rootFile), RootFile: path.Join(dir, rootFile),
RefreshDuration: defaultTestRefreshDuration, RefreshDuration: defaultTestRefreshDuration,
} }
if useSPIFFEBundle {
opts.SPIFFEBundleMapFile = path.Join(dir, spiffeBundleFile)
}
prov, err := NewProvider(opts) prov, err := NewProvider(opts)
if err != nil { if err != nil {
t.Fatalf("NewProvider(%+v) failed: %v", opts, err) t.Fatalf("NewProvider(%+v) failed: %v", opts, err)
@ -218,7 +243,7 @@ func initializeProvider(t *testing.T, testName string) (string, certprovider.Pro
// Since we have root and identity certs, we need to make sure the // Since we have root and identity certs, we need to make sure the
// update is pushed on both of them. // update is pushed on both of them.
if _, err := distCh.Receive(ctx); err != nil { if _, err := distCh.Receive(ctx); err != nil {
t.Fatalf("timeout waiting for provider to read files and push key material to distributor: %v", err) t.Fatalf("Timeout waiting for provider to read files and push key material to distributor: %v", err)
} }
} }
@ -232,21 +257,30 @@ func initializeProvider(t *testing.T, testName string) (string, certprovider.Pro
// successfully, and the underlying files do not change. Verifies that the // successfully, and the underlying files do not change. Verifies that the
// plugin does not push new updates to the distributor in this case. // plugin does not push new updates to the distributor in this case.
func (s) TestProvider_NoUpdate(t *testing.T) { func (s) TestProvider_NoUpdate(t *testing.T) {
_, prov, distCh, cancel := initializeProvider(t, "no_update") baseName := "no_update"
defer cancel() for _, useSPIFFEBundle := range []bool{true, false} {
testName := baseName
if useSPIFFEBundle {
testName = testName + "_" + "withSPIFFEBundle"
}
t.Run(testName, func(t *testing.T) {
_, prov, distCh, cancel := initializeProvider(t, "no_update", useSPIFFEBundle)
defer cancel()
// Make sure the provider is healthy and returns key material. // Make sure the provider is healthy and returns key material.
ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cc() defer cc()
if _, err := prov.KeyMaterial(ctx); err != nil { if _, err := prov.KeyMaterial(ctx); err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err) t.Fatalf("provider.KeyMaterial() failed: %v", err)
} }
// Files haven't change. Make sure no updates are pushed by the provider. // Files haven't change. Make sure no updates are pushed by the provider.
sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration) sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration)
defer sc() defer sc()
if _, err := distCh.Receive(sCtx); err == nil { if _, err := distCh.Receive(sCtx); err == nil {
t.Fatal("new key material pushed to distributor when underlying files did not change") t.Fatal("New key material pushed to distributor when underlying files did not change")
}
})
} }
} }
@ -254,46 +288,59 @@ func (s) TestProvider_NoUpdate(t *testing.T) {
// created successfully and the underlying files change. Verifies that the // created successfully and the underlying files change. Verifies that the
// changes are picked up by the provider. // changes are picked up by the provider.
func (s) TestProvider_UpdateSuccess(t *testing.T) { func (s) TestProvider_UpdateSuccess(t *testing.T) {
dir, prov, distCh, cancel := initializeProvider(t, "update_success") baseName := "update_success"
defer cancel() for _, useSPIFFEBundle := range []bool{true, false} {
testName := baseName
if useSPIFFEBundle {
testName = testName + "_" + "withSPIFFEBundle"
}
t.Run(testName, func(t *testing.T) {
dir, prov, distCh, cancel := initializeProvider(t, "update_success", useSPIFFEBundle)
defer cancel()
// Make sure the provider is healthy and returns key material. // Make sure the provider is healthy and returns key material.
ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cc() defer cc()
km1, err := prov.KeyMaterial(ctx) km1, err := prov.KeyMaterial(ctx)
if err != nil { if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err) t.Fatalf("provider.KeyMaterial() failed: %v", err)
} }
// Change only the root file. // Change only the root file.
createTmpFile(t, testdata.Path("x509/server_ca_cert.pem"), path.Join(dir, rootFile)) if useSPIFFEBundle {
if _, err := distCh.Receive(ctx); err != nil { createTmpFile(t, testdata.Path("spiffe/spiffebundle2.json"), path.Join(dir, spiffeBundleFile))
t.Fatal("timeout waiting for new key material to be pushed to the distributor") } else {
} createTmpFile(t, testdata.Path("x509/server_ca_cert.pem"), path.Join(dir, rootFile))
}
if _, err := distCh.Receive(ctx); err != nil {
t.Fatal("Timeout waiting for new key material to be pushed to the distributor")
}
// Make sure update is picked up. // Make sure update is picked up.
km2, err := prov.KeyMaterial(ctx) km2, err := prov.KeyMaterial(ctx)
if err != nil { if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err) t.Fatalf("provider.KeyMaterial() failed: %v", err)
} }
if err := compareKeyMaterial(km1, km2); err == nil { if err := compareKeyMaterial(km1, km2); err == nil {
t.Fatal("expected provider to return new key material after update to underlying file") t.Fatal("Expected provider to return new key material after update to underlying file")
} }
// Change only cert/key files. // Change only cert/key files.
createTmpFile(t, testdata.Path("x509/client2_cert.pem"), path.Join(dir, certFile)) createTmpFile(t, testdata.Path("x509/client2_cert.pem"), path.Join(dir, certFile))
createTmpFile(t, testdata.Path("x509/client2_key.pem"), path.Join(dir, keyFile)) createTmpFile(t, testdata.Path("x509/client2_key.pem"), path.Join(dir, keyFile))
if _, err := distCh.Receive(ctx); err != nil { if _, err := distCh.Receive(ctx); err != nil {
t.Fatal("timeout waiting for new key material to be pushed to the distributor") t.Fatal("Timeout waiting for new key material to be pushed to the distributor")
} }
// Make sure update is picked up. // Make sure update is picked up.
km3, err := prov.KeyMaterial(ctx) km3, err := prov.KeyMaterial(ctx)
if err != nil { if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err) t.Fatalf("provider.KeyMaterial() failed: %v", err)
} }
if err := compareKeyMaterial(km2, km3); err == nil { if err := compareKeyMaterial(km2, km3); err == nil {
t.Fatal("expected provider to return new key material after update to underlying file") t.Fatal("Expected provider to return new key material after update to underlying file")
}
})
} }
} }
@ -302,82 +349,95 @@ func (s) TestProvider_UpdateSuccess(t *testing.T) {
// symlink is updates to point to new files. Verifies that the changes are // symlink is updates to point to new files. Verifies that the changes are
// picked up by the provider. // picked up by the provider.
func (s) TestProvider_UpdateSuccessWithSymlink(t *testing.T) { func (s) TestProvider_UpdateSuccessWithSymlink(t *testing.T) {
// Override the newDistributor to one which pushes on a channel that we baseName := "update_with_symlink"
// can block on. for _, useSPIFFEBundle := range []bool{true, false} {
origDistributorFunc := newDistributor testName := baseName
distCh := testutils.NewChannel() if useSPIFFEBundle {
d := newWrappedDistributor(distCh) testName = testName + "_" + "withSPIFFEBundle"
newDistributor = func() distributor { return d }
defer func() { newDistributor = origDistributorFunc }()
// Create two tempDirs with different files.
dir1 := createTmpDirWithFiles(t, "update_with_symlink1_*", "x509/client1_cert.pem", "x509/client1_key.pem", "x509/client_ca_cert.pem")
dir2 := createTmpDirWithFiles(t, "update_with_symlink2_*", "x509/server1_cert.pem", "x509/server1_key.pem", "x509/server_ca_cert.pem")
// Create a symlink under a new tempdir, and make it point to dir1.
tmpdir, err := os.MkdirTemp("", "test_symlink_*")
if err != nil {
t.Fatalf("os.MkdirTemp() failed: %v", err)
}
symLinkName := path.Join(tmpdir, "test_symlink")
if err := os.Symlink(dir1, symLinkName); err != nil {
t.Fatalf("failed to create symlink to %q: %v", dir1, err)
}
// Create a provider which watches the files pointed to by the symlink.
opts := Options{
CertFile: path.Join(symLinkName, certFile),
KeyFile: path.Join(symLinkName, keyFile),
RootFile: path.Join(symLinkName, rootFile),
RefreshDuration: defaultTestRefreshDuration,
}
prov, err := NewProvider(opts)
if err != nil {
t.Fatalf("NewProvider(%+v) failed: %v", opts, err)
}
defer prov.Close()
// Make sure the provider picks up the files and pushes the key material on
// to the distributors.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
for i := 0; i < 2; i++ {
// Since we have root and identity certs, we need to make sure the
// update is pushed on both of them.
if _, err := distCh.Receive(ctx); err != nil {
t.Fatalf("timeout waiting for provider to read files and push key material to distributor: %v", err)
} }
} t.Run(testName, func(t *testing.T) {
km1, err := prov.KeyMaterial(ctx) // Override the newDistributor to one which pushes on a channel that we
if err != nil { // can block on.
t.Fatalf("provider.KeyMaterial() failed: %v", err) origDistributorFunc := newDistributor
} distCh := testutils.NewChannel()
d := newWrappedDistributor(distCh)
newDistributor = func() distributor { return d }
defer func() { newDistributor = origDistributorFunc }()
// Update the symlink to point to dir2. // Create two tempDirs with different files.
symLinkTmpName := path.Join(tmpdir, "test_symlink.tmp") dir1 := createTmpDirWithFiles(t, "update_with_symlink1_*", "x509/client1_cert.pem", "x509/client1_key.pem", "x509/client_ca_cert.pem", "spiffe/spiffebundle.json")
if err := os.Symlink(dir2, symLinkTmpName); err != nil { dir2 := createTmpDirWithFiles(t, "update_with_symlink2_*", "x509/server1_cert.pem", "x509/server1_key.pem", "x509/server_ca_cert.pem", "spiffe/spiffebundle2.json")
t.Fatalf("failed to create symlink to %q: %v", dir2, err)
}
if err := os.Rename(symLinkTmpName, symLinkName); err != nil {
t.Fatalf("failed to update symlink: %v", err)
}
// Make sure the provider picks up the new files and pushes the key material // Create a symlink under a new tempdir, and make it point to dir1.
// on to the distributors. tmpdir, err := os.MkdirTemp("", "test_symlink_*")
for i := 0; i < 2; i++ { if err != nil {
// Since we have root and identity certs, we need to make sure the t.Fatalf("os.MkdirTemp() failed: %v", err)
// update is pushed on both of them. }
if _, err := distCh.Receive(ctx); err != nil { symLinkName := path.Join(tmpdir, "test_symlink")
t.Fatalf("timeout waiting for provider to read files and push key material to distributor: %v", err) if err := os.Symlink(dir1, symLinkName); err != nil {
} t.Fatalf("Failed to create symlink to %q: %v", dir1, err)
} }
km2, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
if err := compareKeyMaterial(km1, km2); err == nil { // Create a provider which watches the files pointed to by the symlink.
t.Fatal("expected provider to return new key material after symlink update") opts := Options{
CertFile: path.Join(symLinkName, certFile),
KeyFile: path.Join(symLinkName, keyFile),
RootFile: path.Join(symLinkName, rootFile),
SPIFFEBundleMapFile: path.Join(symLinkName, spiffeBundleFile),
RefreshDuration: defaultTestRefreshDuration,
}
if useSPIFFEBundle {
opts.SPIFFEBundleMapFile = path.Join(symLinkName, spiffeBundleFile)
}
prov, err := NewProvider(opts)
if err != nil {
t.Fatalf("NewProvider(%+v) failed: %v", opts, err)
}
defer prov.Close()
// Make sure the provider picks up the files and pushes the key material on
// to the distributors.
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
for i := 0; i < 2; i++ {
// Since we have root and identity certs, we need to make sure the
// update is pushed on both of them.
if _, err := distCh.Receive(ctx); err != nil {
t.Fatalf("Timeout waiting for provider to read files and push key material to distributor: %v", err)
}
}
km1, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
// Update the symlink to point to dir2.
symLinkTmpName := path.Join(tmpdir, "test_symlink.tmp")
if err := os.Symlink(dir2, symLinkTmpName); err != nil {
t.Fatalf("Failed to create symlink to %q: %v", dir2, err)
}
if err := os.Rename(symLinkTmpName, symLinkName); err != nil {
t.Fatalf("Failed to update symlink: %v", err)
}
// Make sure the provider picks up the new files and pushes the key material
// on to the distributors.
for i := 0; i < 2; i++ {
// Since we have root and identity certs, we need to make sure the
// update is pushed on both of them.
if _, err := distCh.Receive(ctx); err != nil {
t.Fatalf("Timeout waiting for provider to read files and push key material to distributor: %v", err)
}
}
km2, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
if err := compareKeyMaterial(km1, km2); err == nil {
t.Fatal("Expected provider to return new key material after symlink update")
}
})
} }
} }
@ -386,7 +446,7 @@ func (s) TestProvider_UpdateSuccessWithSymlink(t *testing.T) {
// distributor. Then the update succeeds, and the test verifies that the key // distributor. Then the update succeeds, and the test verifies that the key
// material is updated. // material is updated.
func (s) TestProvider_UpdateFailure_ThenSuccess(t *testing.T) { func (s) TestProvider_UpdateFailure_ThenSuccess(t *testing.T) {
dir, prov, distCh, cancel := initializeProvider(t, "update_failure") dir, prov, distCh, cancel := initializeProvider(t, "update_failure", false)
defer cancel() defer cancel()
// Make sure the provider is healthy and returns key material. // Make sure the provider is healthy and returns key material.
@ -403,6 +463,116 @@ func (s) TestProvider_UpdateFailure_ThenSuccess(t *testing.T) {
// the midst of an update. // the midst of an update.
createTmpFile(t, testdata.Path("x509/server1_cert.pem"), path.Join(dir, certFile)) createTmpFile(t, testdata.Path("x509/server1_cert.pem"), path.Join(dir, certFile))
// Since the last update left the files in an incompatible state, the update
// should not be picked up by our provider.
sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration)
defer sc()
if _, err := distCh.Receive(sCtx); err == nil {
t.Fatal("New key material pushed to distributor when underlying files did not change")
}
// The provider should return key material corresponding to the old state.
km2, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
if err := compareKeyMaterial(km1, km2); err != nil {
t.Fatalf("Expected provider to not update key material: %v", err)
}
// Update the key file to match the cert file.
createTmpFile(t, testdata.Path("x509/server1_key.pem"), path.Join(dir, keyFile))
// Make sure update is picked up.
if _, err := distCh.Receive(ctx); err != nil {
t.Fatal("Timeout waiting for new key material to be pushed to the distributor")
}
km3, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
if err := compareKeyMaterial(km2, km3); err == nil {
t.Fatal("Expected provider to return new key material after update to underlying file")
}
}
// TestProvider_UpdateFailure_ThenSuccess tests the case where updating cert/key
// files fail. Verifies that the failed update does not push anything on the
// distributor. Then the update succeeds, and the test verifies that the key
// material is updated.
func (s) TestProvider_UpdateFailureSPIFFE(t *testing.T) {
tests := []struct {
name string
badFile string
}{
{
name: "malformed spiffe",
badFile: "spiffe/spiffebundle_malformed.json",
},
{
name: "invalid bundle",
badFile: "spiffe/spiffebundle_wrong_kty.json",
},
{
name: "cert in the x5c field is invalid",
badFile: "spiffe/spiffebundle_corrupted_cert.json",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
dir, prov, distCh, cancel := initializeProvider(t, tc.name, true)
defer cancel()
// Make sure the provider is healthy and returns key material.
ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cc()
km1, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
// Update the file with a bad update
createTmpFile(t, testdata.Path(tc.badFile), path.Join(dir, spiffeBundleFile))
// Since the last update left the files in an incompatible state, the update
// should not be picked up by our provider.
sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration)
defer sc()
if _, err := distCh.Receive(sCtx); err == nil {
t.Fatal("New key material pushed to distributor when underlying files did not change")
}
// The provider should return key material corresponding to the old state.
km2, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
if err := compareKeyMaterial(km1, km2); err != nil {
t.Fatalf("Expected provider to not update key material: %v", err)
}
})
}
}
// TestProvider_UpdateFailure_ThenSuccess tests the case where updating cert/key
// files fail. Verifies that the failed update does not push anything on the
// distributor. Then the update succeeds, and the test verifies that the key
// material is updated.
func (s) TestProvider_UpdateFailureSPIFFE_MissingFile(t *testing.T) {
dir, prov, distCh, cancel := initializeProvider(t, "Delete spiffe file being read", true)
defer cancel()
// Make sure the provider is healthy and returns key material.
ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cc()
km1, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
// Remove the file that we are reading
removeTmpFile(t, path.Join(dir, spiffeBundleFile))
// Since the last update left the files in an incompatible state, the update // Since the last update left the files in an incompatible state, the update
// should not be picked up by our provider. // should not be picked up by our provider.
sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration) sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration)
@ -419,19 +589,41 @@ func (s) TestProvider_UpdateFailure_ThenSuccess(t *testing.T) {
if err := compareKeyMaterial(km1, km2); err != nil { if err := compareKeyMaterial(km1, km2); err != nil {
t.Fatalf("expected provider to not update key material: %v", err) t.Fatalf("expected provider to not update key material: %v", err)
} }
}
// Update the key file to match the cert file. // TestProvider_UpdateFailure_ThenSuccess tests the case where updating cert/key
createTmpFile(t, testdata.Path("x509/server1_key.pem"), path.Join(dir, keyFile)) // files fail. Verifies that the failed update does not push anything on the
// distributor. Then the update succeeds, and the test verifies that the key
// material is updated.
func (s) TestProvider_UpdateFailureRoot_MissingFile(t *testing.T) {
dir, prov, distCh, cancel := initializeProvider(t, "Delete root file being read", false)
defer cancel()
// Make sure update is picked up. // Make sure the provider is healthy and returns key material.
if _, err := distCh.Receive(ctx); err != nil { ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout)
t.Fatal("timeout waiting for new key material to be pushed to the distributor") defer cc()
} km1, err := prov.KeyMaterial(ctx)
km3, err := prov.KeyMaterial(ctx)
if err != nil { if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err) t.Fatalf("provider.KeyMaterial() failed: %v", err)
} }
if err := compareKeyMaterial(km2, km3); err == nil {
t.Fatal("expected provider to return new key material after update to underlying file") // Remove the file that we are reading
removeTmpFile(t, path.Join(dir, rootFile))
// Since the last update left the files in an incompatible state, the update
// should not be picked up by our provider.
sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration)
defer sc()
if _, err := distCh.Receive(sCtx); err == nil {
t.Fatal("new key material pushed to distributor when underlying files did not change")
}
// The provider should return key material corresponding to the old state.
km2, err := prov.KeyMaterial(ctx)
if err != nil {
t.Fatalf("provider.KeyMaterial() failed: %v", err)
}
if err := compareKeyMaterial(km1, km2); err != nil {
t.Fatalf("expected provider to not update key material: %v", err)
} }
} }

View File

@ -30,6 +30,7 @@ import (
"crypto/x509" "crypto/x509"
"errors" "errors"
"github.com/spiffe/go-spiffe/v2/bundle/spiffebundle"
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal"
) )
@ -93,7 +94,12 @@ type KeyMaterial struct {
// Certs contains a slice of cert/key pairs used to prove local identity. // Certs contains a slice of cert/key pairs used to prove local identity.
Certs []tls.Certificate Certs []tls.Certificate
// Roots contains the set of trusted roots to validate the peer's identity. // Roots contains the set of trusted roots to validate the peer's identity.
// This field will only be used if the `SPIFFEBundleMap` field is unset.
Roots *x509.CertPool Roots *x509.CertPool
// SPIFFEBundleMap is an in-memory representation of a spiffe trust bundle
// map. If this value exists, it will be used to find the roots for a given
// trust domain rather than the Roots in this struct.
SPIFFEBundleMap map[string]*spiffebundle.Bundle
} }
// BuildOptions contains parameters passed to a Provider at build time. // BuildOptions contains parameters passed to a Provider at build time.

View File

@ -43,6 +43,7 @@ import (
) )
const defaultTestTimeout = 10 * time.Second const defaultTestTimeout = 10 * time.Second
const defaultTestShortTimeout = 10 * time.Millisecond
type s struct { type s struct {
grpctest.Tester grpctest.Tester

View File

@ -23,9 +23,7 @@ package xds
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509"
"errors" "errors"
"fmt"
"net" "net"
"sync/atomic" "sync/atomic"
"time" "time"
@ -138,40 +136,6 @@ func (c *credsImpl) ClientHandshake(ctx context.Context, authority string, rawCo
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
cfg.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
// Parse all raw certificates presented by the peer.
var certs []*x509.Certificate
for _, rc := range rawCerts {
cert, err := x509.ParseCertificate(rc)
if err != nil {
return err
}
certs = append(certs, cert)
}
// Build the intermediates list and verify that the leaf certificate
// is signed by one of the root certificates.
intermediates := x509.NewCertPool()
for _, cert := range certs[1:] {
intermediates.AddCert(cert)
}
opts := x509.VerifyOptions{
Roots: cfg.RootCAs,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
if _, err := certs[0].Verify(opts); err != nil {
return err
}
// The SANs sent by the MeshCA are encoded as SPIFFE IDs. We need to
// only look at the SANs on the leaf cert.
if cert := certs[0]; !hi.MatchingSANExists(cert) {
// TODO: Print the complete certificate once the x509 package
// supports a String() method on the Certificate type.
return fmt.Errorf("xds: received SANs {DNSNames: %v, EmailAddresses: %v, IPAddresses: %v, URIs: %v} do not match any of the accepted SANs", cert.DNSNames, cert.EmailAddresses, cert.IPAddresses, cert.URIs)
}
return nil
}
// Perform the TLS handshake with the tls.Config that we have. We run the // Perform the TLS handshake with the tls.Config that we have. We run the
// actual Handshake() function in a goroutine because we need to respect the // actual Handshake() function in a goroutine because we need to respect the

View File

@ -213,6 +213,7 @@ func WithReadBufferSize(s int) DialOption {
func WithInitialWindowSize(s int32) DialOption { func WithInitialWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialWindowSize = s o.copts.InitialWindowSize = s
o.copts.StaticWindowSize = true
}) })
} }
@ -222,6 +223,26 @@ func WithInitialWindowSize(s int32) DialOption {
func WithInitialConnWindowSize(s int32) DialOption { func WithInitialConnWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialConnWindowSize = s o.copts.InitialConnWindowSize = s
o.copts.StaticWindowSize = true
})
}
// WithStaticStreamWindowSize returns a DialOption which sets the initial
// stream window size to the value provided and disables dynamic flow control.
func WithStaticStreamWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialWindowSize = s
o.copts.StaticWindowSize = true
})
}
// WithStaticConnWindowSize returns a DialOption which sets the initial
// connection window size to the value provided and disables dynamic flow
// control.
func WithStaticConnWindowSize(s int32) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.InitialConnWindowSize = s
o.copts.StaticWindowSize = true
}) })
} }
@ -360,7 +381,7 @@ func WithReturnConnectionError() DialOption {
// //
// Note that using this DialOption with per-RPC credentials (through // Note that using this DialOption with per-RPC credentials (through
// WithCredentialsBundle or WithPerRPCCredentials) which require transport // WithCredentialsBundle or WithPerRPCCredentials) which require transport
// security is incompatible and will cause grpc.Dial() to fail. // security is incompatible and will cause RPCs to fail.
// //
// Deprecated: use WithTransportCredentials and insecure.NewCredentials() // Deprecated: use WithTransportCredentials and insecure.NewCredentials()
// instead. Will be supported throughout 1.x. // instead. Will be supported throughout 1.x.

View File

@ -112,7 +112,7 @@ func (c *errProtoCodec) Name() string {
// Tests the case where encoding fails on the server. Verifies that there is // Tests the case where encoding fails on the server. Verifies that there is
// no panic and that the encoding error is propagated to the client. // no panic and that the encoding error is propagated to the client.
func (s) TestEncodeDoesntPanicOnServer(t *testing.T) { func (s) TestEncodeDoesntPanicOnServer(t *testing.T) {
grpctest.TLogger.ExpectError("grpc: server failed to encode response") grpctest.ExpectError("grpc: server failed to encode response")
// Create a codec that errors when encoding messages. // Create a codec that errors when encoding messages.
encodingErr := errors.New("encoding failed") encodingErr := errors.New("encoding failed")
@ -334,7 +334,7 @@ func (s) TestForceCodecName(t *testing.T) {
// Create a test service backend that pushes the received content-type on a // Create a test service backend that pushes the received content-type on a
// channel for the test to inspect. // channel for the test to inspect.
ss := &stubserver.StubServer{ ss := &stubserver.StubServer{
EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
md, ok := metadata.FromIncomingContext(ctx) md, ok := metadata.FromIncomingContext(ctx)
if !ok { if !ok {
return nil, status.Errorf(codes.Internal, "no metadata in context") return nil, status.Errorf(codes.Internal, "no metadata in context")

View File

@ -61,7 +61,7 @@ func main() {
cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials())) cc, err := grpc.NewClient(mr.Scheme()+":///", grpc.WithResolvers(mr), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil { if err != nil {
log.Fatalf("Failed to dial: %v", err) log.Fatalf("grpc.NewClient() failed: %v", err)
} }
defer cc.Close() defer cc.Close()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)

View File

@ -1,7 +1,9 @@
# OpenTelemetry # OpenTelemetry
This example shows how to configure OpenTelemetry on a client and server, and shows This example shows how to configure OpenTelemetry on a client and server, and
what type of telemetry data it can produce for certain RPC's. shows what type of telemetry data it can produce for certain RPCs.
This example shows how to enable experimental gRPC metrics, which are disabled
by default and must be explicitly configured on the client and/or server.
## Try it ## Try it
@ -20,13 +22,18 @@ curl localhost:9465/metrics
## Explanation ## Explanation
The client continuously makes RPC's to a server. The client and server both The client continuously makes RPCs to a server. The client and server both
expose a prometheus exporter to listen and provide metrics. This defaults to expose a prometheus exporter to listen and provide metrics. This defaults to
:9464 for the server and :9465 for the client. :9464 for the server and :9465 for the client. The client and server are also
configured to output traces directly to their standard output streams using
`stdouttrace`.
OpenTelemetry is configured on both the client and the server, and exports to OpenTelemetry is configured on both the client and the server, and exports to
the Prometheus exporter. The exporter exposes metrics on the Prometheus ports the Prometheus exporter. The exporter exposes metrics on the Prometheus ports
described above. described above. OpenTelemetry exports traces using the `stdouttrace` exporter,
which prints structured trace data to the console output of both the client and
server. Each RPC call produces trace information that captures the execution
flow and timing of operations.
Curling to the exposed Prometheus ports outputs the metrics recorded on the Curling to the exposed Prometheus ports outputs the metrics recorded on the
client and server. client and server.

View File

@ -27,19 +27,24 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel/exporters/prometheus"
otelstdouttrace "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
otelpropagation "go.opentelemetry.io/otel/propagation"
otelmetric "go.opentelemetry.io/otel/sdk/metric"
otelresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/examples/features/proto/echo" "google.golang.org/grpc/examples/features/proto/echo"
oteltracing "google.golang.org/grpc/experimental/opentelemetry"
"google.golang.org/grpc/stats/opentelemetry" "google.golang.org/grpc/stats/opentelemetry"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/sdk/metric"
) )
var ( var (
addr = flag.String("addr", ":50051", "the server address to connect to") addr = flag.String("addr", ":50051", "the server address to connect to")
prometheusEndpoint = flag.String("prometheus_endpoint", ":9465", "the Prometheus exporter endpoint") prometheusEndpoint = flag.String("prometheus_endpoint", ":9465", "the Prometheus exporter endpoint for metrics")
) )
func main() { func main() {
@ -47,18 +52,40 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("Failed to start prometheus exporter: %v", err) log.Fatalf("Failed to start prometheus exporter: %v", err)
} }
provider := metric.NewMeterProvider(metric.WithReader(exporter)) // Configure meter provider for metrics
go http.ListenAndServe(*prometheusEndpoint, promhttp.Handler()) meterProvider := otelmetric.NewMeterProvider(otelmetric.WithReader(exporter))
// Configure exporter for traces
traceExporter, err := otelstdouttrace.New(otelstdouttrace.WithPrettyPrint())
if err != nil {
log.Fatalf("Failed to create stdouttrace exporter: %v", err)
}
traceProvider := sdktrace.NewTracerProvider(sdktrace.WithBatcher(traceExporter), sdktrace.WithResource(otelresource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceName("grpc-client"))))
// Configure W3C Trace Context Propagator for traces
textMapPropagator := otelpropagation.TraceContext{}
do := opentelemetry.DialOption(opentelemetry.Options{
MetricsOptions: opentelemetry.MetricsOptions{
MeterProvider: meterProvider,
// These are example experimental gRPC metrics, which are disabled
// by default and must be explicitly enabled. For the full,
// up-to-date list of metrics, see:
// https://grpc.io/docs/guides/opentelemetry-metrics/#instruments
Metrics: opentelemetry.DefaultMetrics().Add(
"grpc.client.attempt.started",
"grpc.client.attempt.duration",
),
},
TraceOptions: oteltracing.TraceOptions{TracerProvider: traceProvider, TextMapPropagator: textMapPropagator},
})
ctx := context.Background() go http.ListenAndServe(*prometheusEndpoint, promhttp.Handler())
do := opentelemetry.DialOption(opentelemetry.Options{MetricsOptions: opentelemetry.MetricsOptions{MeterProvider: provider}})
cc, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()), do) cc, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()), do)
if err != nil { if err != nil {
log.Fatalf("Failed to start NewClient: %v", err) log.Fatalf("grpc.NewClient() failed: %v", err)
} }
defer cc.Close() defer cc.Close()
c := echo.NewEchoClient(cc) c := echo.NewEchoClient(cc)
ctx := context.Background()
// Make an RPC every second. This should trigger telemetry to be emitted from // Make an RPC every second. This should trigger telemetry to be emitted from
// the client and the server. // the client and the server.

View File

@ -27,18 +27,23 @@ import (
"net" "net"
"net/http" "net/http"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/features/proto/echo"
"google.golang.org/grpc/stats/opentelemetry"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/sdk/metric" otelstdouttrace "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
otelpropagation "go.opentelemetry.io/otel/propagation"
otelmetric "go.opentelemetry.io/otel/sdk/metric"
otelresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/features/proto/echo"
oteltracing "google.golang.org/grpc/experimental/opentelemetry"
"google.golang.org/grpc/stats/opentelemetry"
) )
var ( var (
addr = flag.String("addr", ":50051", "the server address to connect to") addr = flag.String("addr", ":50051", "the server address to connect to")
prometheusEndpoint = flag.String("prometheus_endpoint", ":9464", "the Prometheus exporter endpoint") prometheusEndpoint = flag.String("prometheus_endpoint", ":9464", "the Prometheus exporter endpoint for metrics")
) )
type echoServer struct { type echoServer struct {
@ -55,10 +60,31 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("Failed to start prometheus exporter: %v", err) log.Fatalf("Failed to start prometheus exporter: %v", err)
} }
provider := metric.NewMeterProvider(metric.WithReader(exporter)) // Configure meter provider for metrics
go http.ListenAndServe(*prometheusEndpoint, promhttp.Handler()) meterProvider := otelmetric.NewMeterProvider(otelmetric.WithReader(exporter))
// Configure exporter for traces
traceExporter, err := otelstdouttrace.New(otelstdouttrace.WithPrettyPrint())
if err != nil {
log.Fatalf("Failed to create stdouttrace exporter: %v", err)
}
traceProvider := sdktrace.NewTracerProvider(sdktrace.WithBatcher(traceExporter), sdktrace.WithResource(otelresource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceName("grpc-server"))))
// Configure W3C Trace Context Propagator for traces
textMapPropagator := otelpropagation.TraceContext{}
so := opentelemetry.ServerOption(opentelemetry.Options{
MetricsOptions: opentelemetry.MetricsOptions{
MeterProvider: meterProvider,
// These are example experimental gRPC metrics, which are disabled
// by default and must be explicitly enabled. For the full,
// up-to-date list of metrics, see:
// https://grpc.io/docs/guides/opentelemetry-metrics/#instruments
Metrics: opentelemetry.DefaultMetrics().Add(
"grpc.server.call.started",
"grpc.server.call.duration",
),
},
TraceOptions: oteltracing.TraceOptions{TracerProvider: traceProvider, TextMapPropagator: textMapPropagator}})
so := opentelemetry.ServerOption(opentelemetry.Options{MetricsOptions: opentelemetry.MetricsOptions{MeterProvider: provider}}) go http.ListenAndServe(*prometheusEndpoint, promhttp.Handler())
lis, err := net.Listen("tcp", *addr) lis, err := net.Listen("tcp", *addr)
if err != nil { if err != nil {

View File

@ -95,6 +95,7 @@ func (orcaLBBuilder) Build(cc balancer.ClientConn, _ balancer.BuildOptions) bala
// designed to run within. // designed to run within.
type orcaLB struct { type orcaLB struct {
cc balancer.ClientConn cc balancer.ClientConn
sc balancer.SubConn
} }
func (o *orcaLB) UpdateClientConnState(ccs balancer.ClientConnState) error { func (o *orcaLB) UpdateClientConnState(ccs balancer.ClientConnState) error {
@ -112,6 +113,7 @@ func (o *orcaLB) UpdateClientConnState(ccs balancer.ClientConnState) error {
return fmt.Errorf("orcaLB: error creating SubConn: %v", err) return fmt.Errorf("orcaLB: error creating SubConn: %v", err)
} }
sc.Connect() sc.Connect()
o.sc = sc
// Register a simple ORCA OOB listener on the SubConn. We request a 1 // Register a simple ORCA OOB listener on the SubConn. We request a 1
// second report interval, but in this example the server indicated the // second report interval, but in this example the server indicated the
@ -124,6 +126,12 @@ func (o *orcaLB) UpdateClientConnState(ccs balancer.ClientConnState) error {
func (o *orcaLB) ResolverError(error) {} func (o *orcaLB) ResolverError(error) {}
func (o *orcaLB) ExitIdle() {
if o.sc != nil {
o.sc.Connect()
}
}
// TODO: unused; remove when no longer required. // TODO: unused; remove when no longer required.
func (o *orcaLB) UpdateSubConnState(balancer.SubConn, balancer.SubConnState) {} func (o *orcaLB) UpdateSubConnState(balancer.SubConn, balancer.SubConnState) {}

View File

@ -17,7 +17,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.4 // protoc-gen-go v1.36.6
// protoc v5.27.1 // protoc v5.27.1
// source: examples/features/proto/echo/echo.proto // source: examples/features/proto/echo/echo.proto
@ -130,45 +130,18 @@ func (x *EchoResponse) GetMessage() string {
var File_examples_features_proto_echo_echo_proto protoreflect.FileDescriptor var File_examples_features_proto_echo_echo_proto protoreflect.FileDescriptor
var file_examples_features_proto_echo_echo_proto_rawDesc = string([]byte{ const file_examples_features_proto_echo_echo_proto_rawDesc = "" +
0x0a, 0x27, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x74, 0x75, "\n" +
0x72, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x65, "'examples/features/proto/echo/echo.proto\x12\x12grpc.examples.echo\"'\n" +
0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x67, 0x72, 0x70, 0x63, 0x2e, "\vEchoRequest\x12\x18\n" +
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x22, 0x27, 0x0a, "\amessage\x18\x01 \x01(\tR\amessage\"(\n" +
0x0b, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, "\fEchoResponse\x12\x18\n" +
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, "\amessage\x18\x01 \x01(\tR\amessage2\xfb\x02\n" +
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, "\x04Echo\x12P\n" +
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, "\tUnaryEcho\x12\x1f.grpc.examples.echo.EchoRequest\x1a .grpc.examples.echo.EchoResponse\"\x00\x12\\\n" +
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, "\x13ServerStreamingEcho\x12\x1f.grpc.examples.echo.EchoRequest\x1a .grpc.examples.echo.EchoResponse\"\x000\x01\x12\\\n" +
0x32, 0xfb, 0x02, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x50, 0x0a, 0x09, 0x55, 0x6e, 0x61, "\x13ClientStreamingEcho\x12\x1f.grpc.examples.echo.EchoRequest\x1a .grpc.examples.echo.EchoResponse\"\x00(\x01\x12e\n" +
0x72, 0x79, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, "\x1aBidirectionalStreamingEcho\x12\x1f.grpc.examples.echo.EchoRequest\x1a .grpc.examples.echo.EchoResponse\"\x00(\x010\x01B5Z3google.golang.org/grpc/examples/features/proto/echob\x06proto3"
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68,
0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x13, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x45, 0x63,
0x68, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5c, 0x0a, 0x13, 0x43, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x45, 0x63, 0x68, 0x6f,
0x12, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x20, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x65, 0x0a, 0x1a, 0x42, 0x69, 0x64, 0x69, 0x72,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e,
0x67, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x35,
0x5a, 0x33, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e,
0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x73, 0x2f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x65, 0x63, 0x68, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var ( var (
file_examples_features_proto_echo_echo_proto_rawDescOnce sync.Once file_examples_features_proto_echo_echo_proto_rawDescOnce sync.Once

Some files were not shown because too many files have changed in this diff Show More