Skip to content
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

client: add WithResolvers options for specifying client-local resolvers #3320

Merged
merged 2 commits into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions clientconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
// Only try to parse target when resolver builder is not already set.
cc.parsedTarget = parseTarget(cc.target)
grpclog.Infof("parsed scheme: %q", cc.parsedTarget.Scheme)
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme)
cc.dopts.resolverBuilder = cc.getResolver(cc.parsedTarget.Scheme)
if cc.dopts.resolverBuilder == nil {
// If resolver builder is still nil, the parsed target's scheme is
// not registered. Fallback to default resolver and set Endpoint to
Expand All @@ -253,7 +253,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
Scheme: resolver.GetDefaultScheme(),
Endpoint: target,
}
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme)
cc.dopts.resolverBuilder = cc.getResolver(cc.parsedTarget.Scheme)
}
} else {
cc.parsedTarget = resolver.Target{Endpoint: target}
Expand Down Expand Up @@ -1542,3 +1542,12 @@ func (c *channelzChannel) ChannelzMetric() *channelz.ChannelInternalMetric {
// Deprecated: This error is never returned by grpc and should not be
// referenced by users.
var ErrClientConnTimeout = errors.New("grpc: timed out when dialing")

func (cc *ClientConn) getResolver(scheme string) resolver.Builder {
for _, rb := range cc.dopts.resolvers {
if cc.parsedTarget.Scheme == rb.Scheme() {
return rb
}
}
return resolver.Get(cc.parsedTarget.Scheme)
}
4 changes: 2 additions & 2 deletions clientconn_state_transition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ func (s) TestStateTransitions_TriesAllAddrsBeforeTransientFailure(t *testing.T)
{Addr: lis1.Addr().String()},
{Addr: lis2.Addr().String()},
}})
client, err := DialContext(ctx, "this-gets-overwritten", WithInsecure(), WithBalancerName(stateRecordingBalancerName), withResolverBuilder(rb))
client, err := DialContext(ctx, "whatever:///this-gets-overwritten", WithInsecure(), WithBalancerName(stateRecordingBalancerName), WithResolvers(rb))
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -414,7 +414,7 @@ func (s) TestStateTransitions_MultipleAddrsEntersReady(t *testing.T) {
{Addr: lis1.Addr().String()},
{Addr: lis2.Addr().String()},
}})
client, err := DialContext(ctx, "this-gets-overwritten", WithInsecure(), WithBalancerName(stateRecordingBalancerName), withResolverBuilder(rb))
client, err := DialContext(ctx, "whatever:///this-gets-overwritten", WithInsecure(), WithBalancerName(stateRecordingBalancerName), WithResolvers(rb))
if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 4 additions & 4 deletions clientconn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,10 +539,10 @@ func (s) TestDial_OneBackoffPerRetryGroup(t *testing.T) {
{Addr: lis1.Addr().String()},
{Addr: lis2.Addr().String()},
}})
client, err := DialContext(ctx, "this-gets-overwritten",
client, err := DialContext(ctx, "whatever:///this-gets-overwritten",
WithInsecure(),
WithBalancerName(stateRecordingBalancerName),
withResolverBuilder(rb),
WithResolvers(rb),
withMinConnectDeadline(getMinConnectTimeout))
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -1085,9 +1085,9 @@ func (s) TestUpdateAddresses_RetryFromFirstAddr(t *testing.T) {
rb := manual.NewBuilderWithScheme("whatever")
rb.InitialState(resolver.State{Addresses: addrsList})

client, err := Dial("this-gets-overwritten",
client, err := Dial("whatever:///this-gets-overwritten",
WithInsecure(),
withResolverBuilder(rb),
WithResolvers(rb),
withBackoff(noBackoff{}),
WithBalancerName(stateRecordingBalancerName),
withMinConnectDeadline(func() time.Duration { return time.Hour }))
Expand Down
13 changes: 13 additions & 0 deletions dialoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type dialOptions struct {
// resolver.ResolveNow(). The user will have no need to configure this, but
// we need to be able to configure this in tests.
resolveNowBackoff func(int) time.Duration
resolvers []resolver.Builder
}

// DialOption configures how we set up the connection.
Expand Down Expand Up @@ -589,3 +590,15 @@ func withResolveNowBackoff(f func(int) time.Duration) DialOption {
o.resolveNowBackoff = f
})
}

// WithResolvers allows a list of resolver implementations to be registered
// locally with the ClientConn without needing to be globally registered via
// resolver.Register. They will be matched against the scheme used for the
// current Dial only, and will take precedence over the global registry.
//
// This API is EXPERIMENTAL.
func WithResolvers(rs ...resolver.Builder) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.resolvers = rs
})
}