-
Notifications
You must be signed in to change notification settings - Fork 4.7k
xds: introduce simple grpc transport for generic xds clients #8066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
ce4737e
eb5e8bd
dfcd686
56b0d41
61bfdda
84b4f2b
9049d97
5050360
f7be854
b175bc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| /* | ||
| * | ||
| * 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 grpctransport provides an implementation of the | ||
| // clients.TransportBuilder interface using gRPC. | ||
| package grpctransport | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "time" | ||
|
|
||
| "google.golang.org/grpc" | ||
| "google.golang.org/grpc/credentials" | ||
| "google.golang.org/grpc/keepalive" | ||
| "google.golang.org/grpc/xds/internal/clients" | ||
| ) | ||
|
|
||
| // ServerConfigExtension holds settings for connecting to a gRPC server, | ||
| // such as an xDS management or an LRS server. | ||
| type ServerConfigExtension struct { | ||
| // Credentials will be used for all gRPC transports. If it is unset, | ||
| // transport creation will fail. | ||
| Credentials credentials.Bundle | ||
| } | ||
|
|
||
| // Builder creates gRPC-based Transports. It must be paired with ServerConfigs | ||
| // that contain an Extension field of type ServerConfigExtension. | ||
| type Builder struct{} | ||
|
|
||
| // Build returns a gRPC-based clients.Transport. | ||
| // | ||
| // The Extension field of the ServerConfig must be a ServerConfigExtension. | ||
| func (b *Builder) Build(sc clients.ServerConfig) (clients.Transport, error) { | ||
| if sc.ServerURI == "" { | ||
| return nil, fmt.Errorf("grpctransport: ServerURI is not set in ServerConfig") | ||
| } | ||
| if sc.Extensions == nil { | ||
| return nil, fmt.Errorf("grpctransport: Extensions is not set in ServerConfig") | ||
| } | ||
| sce, ok := sc.Extensions.(ServerConfigExtension) | ||
| if !ok { | ||
| return nil, fmt.Errorf("grpctransport: Extensions field is %T, but must be %T in ServerConfig", sc.Extensions, ServerConfigExtension{}) | ||
| } | ||
| if sce.Credentials == nil { | ||
| return nil, fmt.Errorf("grptransport: Credentials field is not set in ServerConfigExtension") | ||
| } | ||
|
|
||
| // TODO: Incorporate reference count map for existing transports and | ||
| // deduplicate transports based on the provided ServerConfig so that | ||
| // transport channel to same server can be shared between xDS and LRS | ||
| // client. | ||
|
|
||
| // Create a new gRPC client/channel for the server with the provided | ||
| // credentials, server URI, and a byte codec to send and receive messages. | ||
| // Also set a static keepalive configuration that is common across gRPC | ||
| // language implementations. | ||
| kpCfg := grpc.WithKeepaliveParams(keepalive.ClientParameters{ | ||
| Time: 5 * time.Minute, | ||
| Timeout: 20 * time.Second, | ||
| }) | ||
| cc, err := grpc.NewClient(sc.ServerURI, kpCfg, grpc.WithCredentialsBundle(sce.Credentials), grpc.WithDefaultCallOptions(grpc.ForceCodec(&byteCodec{}))) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("grpctransport: failed to create transport to server %q: %v", sc.ServerURI, err) | ||
| } | ||
|
|
||
| return &grpcTransport{cc: cc}, nil | ||
| } | ||
|
|
||
| type grpcTransport struct { | ||
| cc *grpc.ClientConn | ||
| } | ||
|
|
||
| // NewStream creates a new gRPC stream to the server for the specified method. | ||
| func (g *grpcTransport) NewStream(ctx context.Context, method string) (clients.Stream, error) { | ||
| s, err := g.cc.NewStream(ctx, &grpc.StreamDesc{ClientStreams: true, ServerStreams: true}, method) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This means that this transport only support bidirectional streams. It doesn't matter because both ADS and LRS are bidirectional streams. But I'm wondering if this should be documented?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now I don't see it needs to be mention. Since, both interface and this implementation mentions that its for xDS and LRS servers, its obvious that it has to be bidi stream. But, we can see later after complete implementation, if its good to mention. |
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return &stream{stream: s}, nil | ||
| } | ||
|
|
||
| // Close closes the gRPC channel to the server. | ||
| func (g *grpcTransport) Close() error { | ||
| return g.cc.Close() | ||
| } | ||
|
|
||
| type stream struct { | ||
| stream grpc.ClientStream | ||
| } | ||
|
|
||
| // Send sends a message to the server. | ||
| func (s *stream) Send(msg []byte) error { | ||
| return s.stream.SendMsg(msg) | ||
|
dfawley marked this conversation as resolved.
|
||
| } | ||
|
|
||
| // Recv receives a message from the server. | ||
| func (s *stream) Recv() ([]byte, error) { | ||
|
dfawley marked this conversation as resolved.
|
||
| var typedRes []byte | ||
|
|
||
| if err := s.stream.RecvMsg(&typedRes); err != nil { | ||
| return nil, err | ||
| } | ||
| return typedRes, nil | ||
|
dfawley marked this conversation as resolved.
|
||
| } | ||
|
|
||
| type byteCodec struct{} | ||
|
|
||
| func (c *byteCodec) Marshal(v any) ([]byte, error) { | ||
| if b, ok := v.([]byte); ok { | ||
| return b, nil | ||
| } | ||
| return nil, fmt.Errorf("message is %T, but must be a []byte", v) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Adding a package prefix to this error message would also be useful I think. So, maybe something like:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added |
||
| } | ||
|
|
||
| func (c *byteCodec) Unmarshal(data []byte, v any) error { | ||
| if b, ok := v.(*[]byte); ok { | ||
| *b = data | ||
| return nil | ||
| } | ||
| return fmt.Errorf("target is %T, but must be *[]byte", v) | ||
| } | ||
|
|
||
| func (c *byteCodec) Name() string { | ||
| return "grpctransport.byteCodec" | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.