-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
grpc: Add perTargetDialOption type and global list #7234
Changes from 2 commits
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 |
---|---|---|
|
@@ -152,6 +152,16 @@ func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) | |
for _, opt := range opts { | ||
opt.apply(&cc.dopts) | ||
} | ||
|
||
// Determine the resolver to use. | ||
if err := cc.parseTargetAndFindResolver(); err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, opt := range globalPerTargetDialOptions { | ||
opt.DialOption(cc.parsedTarget.URL).apply(&cc.dopts) | ||
} | ||
|
||
chainUnaryClientInterceptors(cc) | ||
chainStreamClientInterceptors(cc) | ||
|
||
|
@@ -168,25 +178,14 @@ func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) | |
} | ||
cc.mkp = cc.dopts.copts.KeepaliveParams | ||
|
||
// Register ClientConn with channelz. | ||
cc.channelzRegistration(target) | ||
|
||
// TODO: Ideally it should be impossible to error from this function after | ||
// channelz registration. This will require removing some channelz logs | ||
// from the following functions that can error. Errors can be returned to | ||
// the user, and successful logs can be emitted here, after the checks have | ||
// passed and channelz is subsequently registered. | ||
|
||
// Determine the resolver to use. | ||
if err := cc.parseTargetAndFindResolver(); err != nil { | ||
channelz.RemoveEntry(cc.channelz.ID) | ||
return nil, err | ||
} | ||
if err = cc.determineAuthority(); err != nil { | ||
channelz.RemoveEntry(cc.channelz.ID) | ||
return nil, err | ||
} | ||
|
||
// Register ClientConn with channelz. | ||
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. Maybe something that says "Note that this is only done after channel creation cannot fail", so that nobody adds error cases after this. Maybe also move immediately before the 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 comment. I don't think I can move it right before return because the component creation reads the channelz pointer of the channel to log in future, and if I move to right before return would read off a nil pointer and not log anything. |
||
cc.channelzRegistration(target) | ||
channelz.Infof(logger, cc.channelz, "Channel authority set to %q", cc.authority) | ||
|
||
cc.csMgr = newConnectivityStateManager(cc.ctx, cc.channelz) | ||
cc.pickerWrapper = newPickerWrapper(cc.dopts.copts.StatsHandlers) | ||
|
||
|
@@ -1681,14 +1680,14 @@ func (cc *ClientConn) connectionError() error { | |
// | ||
// Doesn't grab cc.mu as this method is expected to be called only at Dial time. | ||
func (cc *ClientConn) parseTargetAndFindResolver() error { | ||
channelz.Infof(logger, cc.channelz, "original dial target is: %q", cc.target) | ||
logger.Infof("original dial target is: %q", cc.target) | ||
dfawley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
var rb resolver.Builder | ||
parsedTarget, err := parseTarget(cc.target) | ||
if err != nil { | ||
channelz.Infof(logger, cc.channelz, "dial target %q parse failed: %v", cc.target, err) | ||
logger.Infof("dial target %q parse failed: %v", cc.target, err) | ||
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.
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. Deleted (and switched conditional to err == nil). |
||
} else { | ||
channelz.Infof(logger, cc.channelz, "parsed dial target is: %#v", parsedTarget) | ||
logger.Infof("parsed dial target is: %#v", parsedTarget) | ||
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.
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. Done. |
||
rb = cc.getResolver(parsedTarget.URL.Scheme) | ||
if rb != nil { | ||
cc.parsedTarget = parsedTarget | ||
|
@@ -1707,15 +1706,15 @@ func (cc *ClientConn) parseTargetAndFindResolver() error { | |
defScheme = resolver.GetDefaultScheme() | ||
} | ||
|
||
channelz.Infof(logger, cc.channelz, "fallback to scheme %q", defScheme) | ||
logger.Infof("fallback to scheme %q", defScheme) | ||
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.
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. Done, yeah because this now gets logged. |
||
canonicalTarget := defScheme + ":///" + cc.target | ||
|
||
parsedTarget, err = parseTarget(canonicalTarget) | ||
if err != nil { | ||
channelz.Infof(logger, cc.channelz, "dial target %q parse failed: %v", canonicalTarget, err) | ||
logger.Infof("dial target %q parse failed: %v", canonicalTarget, err) | ||
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. Seems unnecessary as we will return this error to the user. 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. Good point, deleted. |
||
return err | ||
} | ||
channelz.Infof(logger, cc.channelz, "parsed dial target is: %+v", parsedTarget) | ||
logger.Infof("parsed dial target is: %+v", parsedTarget) | ||
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.
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. Done. |
||
rb = cc.getResolver(parsedTarget.URL.Scheme) | ||
if rb == nil { | ||
return fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.URL.Scheme) | ||
|
@@ -1838,6 +1837,5 @@ func (cc *ClientConn) determineAuthority() error { | |
} else { | ||
cc.authority = encodeAuthority(endpoint) | ||
} | ||
channelz.Infof(logger, cc.channelz, "Channel authority set to %q", cc.authority) | ||
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. LGTM |
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ package grpc | |
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
"strings" | ||
"testing" | ||
|
||
|
@@ -74,6 +75,34 @@ func (s) TestDisableGlobalOptions(t *testing.T) { | |
internal.ClearGlobalDialOptions() | ||
} | ||
|
||
type testPerTargetDialOption struct{} | ||
|
||
func (do *testPerTargetDialOption) DialOption(parsedTarget url.URL) DialOption { | ||
if parsedTarget.Scheme == "passthrough" { | ||
return WithTransportCredentials(insecure.NewCredentials()) // credentials provided, should pass NewClient. | ||
} | ||
return EmptyDialOption{} // no credentials, should fail NewClient | ||
} | ||
|
||
// TestGlobalPerTargetDialOption configures a global per target dial option that | ||
// produces transport credentials for channels using "passthrough" scheme. | ||
// Channels that use the passthrough scheme should this be successfully created | ||
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" makes no sense. 😆 (Delete "this") 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. Whoops haha, deleted. |
||
// due to picking up transport credentials, whereas other channels should fail | ||
// at creation due to not having transport credentials. | ||
func (s) TestGlobalPerTargetDialOption(t *testing.T) { | ||
internal.AddGlobalPerTargetDialOptions.(func(opt any))(&testPerTargetDialOption{}) | ||
noTSecStr := "no transport security set" | ||
if _, err := NewClient("dns:///fake"); !strings.Contains(fmt.Sprint(err), noTSecStr) { | ||
t.Fatalf("Dialing received unexpected error: %v, want error containing \"%v\"", err, noTSecStr) | ||
} | ||
cc, err := NewClient("passthrough:///nice") | ||
if err != nil { | ||
t.Fatalf("Dialing with insecure credentials failed: %v", err) | ||
} | ||
defer cc.Close() | ||
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: just 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. Sure, changed. |
||
internal.ClearGlobalPerTargetDialOptions() | ||
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. Defer at the start, otherwise if this fails it impacts other tests. 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. Changed, and also changed the other tests in this file I wrote for gcp observability :). |
||
} | ||
|
||
func (s) TestAddGlobalServerOptions(t *testing.T) { | ||
const maxRecvSize = 998765 | ||
// Set and check the ServerOptions | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ package grpc | |
import ( | ||
"context" | ||
"net" | ||
"net/url" | ||
"time" | ||
|
||
"google.golang.org/grpc/backoff" | ||
|
@@ -43,6 +44,14 @@ func init() { | |
internal.ClearGlobalDialOptions = func() { | ||
globalDialOptions = nil | ||
} | ||
internal.AddGlobalPerTargetDialOptions = func(opt any) { | ||
if ptdo, ok := opt.(perTargetDialOption); ok { | ||
globalPerTargetDialOptions = append(globalPerTargetDialOptions, ptdo) | ||
} | ||
} | ||
internal.ClearGlobalPerTargetDialOptions = func() { | ||
globalPerTargetDialOptions = nil | ||
} | ||
internal.WithBinaryLogger = withBinaryLogger | ||
internal.JoinDialOptions = newJoinDialOption | ||
internal.DisableGlobalDialOptions = newDisableGlobalDialOptions | ||
|
@@ -89,6 +98,17 @@ type DialOption interface { | |
|
||
var globalDialOptions []DialOption | ||
|
||
// perTargetDialOption takes a parsed target and returns a dial option to apply. | ||
// | ||
// This gets called after NewClient() parses the target, and allows per target | ||
// configuration set through a returned DialOption. | ||
type perTargetDialOption interface { | ||
// DialOption returns a Dial Option to apply. | ||
DialOption(parsedTarget url.URL) DialOption | ||
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. Bikeshed: 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. Sure, done. |
||
} | ||
|
||
var globalPerTargetDialOptions []perTargetDialOption | ||
|
||
// EmptyDialOption does not alter the dial configuration. It can be embedded in | ||
// another structure to build custom dial options. | ||
// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind converting this and
cc.determineAuthority
so they are notcc
methods? Otherwise they might forget they're not supposed to be usingcc.channelz
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reads and writes a lot of stuff from cc. Unless you want me to pass in the target, dial options, and return a bunch of things to NewClient to write to cc's stuff. For the sake of readability I think it's better to keep it on cc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This used to all be in the function body inline and Easwar rewrote it (which I reviewed) to be on cc to factor out functionality into readable helpers and keep this function body clean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess let's rename these to
initParsedTargetAndResolverBuilder
andinitAuthority
and then at least it's obvious they're being called before the cc is fully initialized.