Skip to content

Commit d21d95f

Browse files
cfc4nchaochn47
authored andcommitted
clientv3: get AuthToken gracefully without extra connection.
Signed-off-by: Chao Chen <[email protected]>
1 parent 1d6bb5b commit d21d95f

File tree

3 files changed

+55
-85
lines changed

3 files changed

+55
-85
lines changed

clientv3/auth.go

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ import (
1919
"fmt"
2020
"strings"
2121

22+
"google.golang.org/grpc"
23+
2224
"go.etcd.io/etcd/auth/authpb"
2325
pb "go.etcd.io/etcd/etcdserver/etcdserverpb"
24-
"google.golang.org/grpc"
2526
)
2627

2728
type (
@@ -55,6 +56,9 @@ const (
5556
type UserAddOptions authpb.UserAddOptions
5657

5758
type Auth interface {
59+
// Authenticate login and get token
60+
Authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error)
61+
5862
// AuthEnable enables auth of an etcd cluster.
5963
AuthEnable(ctx context.Context) (*AuthEnableResponse, error)
6064

@@ -117,6 +121,19 @@ func NewAuth(c *Client) Auth {
117121
return api
118122
}
119123

124+
func NewAuthFromAuthClient(remote pb.AuthClient, c *Client) Auth {
125+
api := &authClient{remote: remote}
126+
if c != nil {
127+
api.callOpts = c.callOpts
128+
}
129+
return api
130+
}
131+
132+
func (auth *authClient) Authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error) {
133+
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password}, auth.callOpts...)
134+
return (*AuthenticateResponse)(resp), toErr(ctx, err)
135+
}
136+
120137
func (auth *authClient) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
121138
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{}, auth.callOpts...)
122139
return (*AuthEnableResponse)(resp), toErr(ctx, err)
@@ -209,34 +226,3 @@ func StrToPermissionType(s string) (PermissionType, error) {
209226
}
210227
return PermissionType(-1), fmt.Errorf("invalid permission type: %s", s)
211228
}
212-
213-
type authenticator struct {
214-
conn *grpc.ClientConn // conn in-use
215-
remote pb.AuthClient
216-
callOpts []grpc.CallOption
217-
}
218-
219-
func (auth *authenticator) authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error) {
220-
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password}, auth.callOpts...)
221-
return (*AuthenticateResponse)(resp), toErr(ctx, err)
222-
}
223-
224-
func (auth *authenticator) close() {
225-
auth.conn.Close()
226-
}
227-
228-
func newAuthenticator(ctx context.Context, target string, opts []grpc.DialOption, c *Client) (*authenticator, error) {
229-
conn, err := grpc.DialContext(ctx, target, opts...)
230-
if err != nil {
231-
return nil, err
232-
}
233-
234-
api := &authenticator{
235-
conn: conn,
236-
remote: pb.NewAuthClient(conn),
237-
}
238-
if c != nil {
239-
api.callOpts = c.callOpts
240-
}
241-
return api, nil
242-
}

clientv3/client.go

Lines changed: 24 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -292,43 +292,20 @@ func (c *Client) Dial(ep string) (*grpc.ClientConn, error) {
292292

293293
func (c *Client) getToken(ctx context.Context) error {
294294
var err error // return last error in a case of fail
295-
var auth *authenticator
296295

297-
eps := c.Endpoints()
298-
for _, ep := range eps {
299-
// use dial options without dopts to avoid reusing the client balancer
300-
var dOpts []grpc.DialOption
301-
_, host, _ := endpoint.ParseEndpoint(ep)
302-
target := c.resolverGroup.Target(host)
303-
creds := c.dialWithBalancerCreds(ep)
304-
dOpts, err = c.dialSetupOpts(creds, c.cfg.DialOptions...)
305-
if err != nil {
306-
err = fmt.Errorf("failed to configure auth dialer: %v", err)
307-
continue
308-
}
309-
dOpts = append(dOpts, grpc.WithBalancerName(roundRobinBalancerName))
310-
auth, err = newAuthenticator(ctx, target, dOpts, c)
311-
if err != nil {
312-
continue
313-
}
314-
defer auth.close()
315-
316-
var resp *AuthenticateResponse
317-
resp, err = auth.authenticate(ctx, c.Username, c.Password)
318-
if err != nil {
319-
// return err without retrying other endpoints
320-
if err == rpctypes.ErrAuthNotEnabled {
321-
c.authTokenBundle.UpdateAuthToken("")
322-
return err
323-
}
324-
continue
325-
}
326-
327-
c.authTokenBundle.UpdateAuthToken(resp.Token)
296+
if c.Username == "" || c.Password == "" {
328297
return nil
329298
}
330299

331-
return err
300+
resp, err := c.Auth.Authenticate(ctx, c.Username, c.Password)
301+
if err != nil {
302+
if err == rpctypes.ErrAuthNotEnabled {
303+
return nil
304+
}
305+
return err
306+
}
307+
c.authTokenBundle.UpdateAuthToken(resp.Token)
308+
return nil
332309
}
333310

334311
// dialWithBalancer dials the client's current load balanced resolver group. The scheme of the host
@@ -349,25 +326,7 @@ func (c *Client) dial(target string, creds grpccredentials.TransportCredentials,
349326

350327
if c.Username != "" && c.Password != "" {
351328
c.authTokenBundle = credentials.NewBundle(credentials.Config{})
352-
353-
ctx, cancel := c.ctx, func() {}
354-
if c.cfg.DialTimeout > 0 {
355-
ctx, cancel = context.WithTimeout(ctx, c.cfg.DialTimeout)
356-
}
357-
358-
err = c.getToken(ctx)
359-
if err != nil {
360-
if toErr(ctx, err) != rpctypes.ErrAuthNotEnabled {
361-
if err == ctx.Err() && ctx.Err() != c.ctx.Err() {
362-
err = context.DeadlineExceeded
363-
}
364-
cancel()
365-
return nil, err
366-
}
367-
} else {
368-
opts = append(opts, grpc.WithPerRPCCredentials(c.authTokenBundle.PerRPCCredentials()))
369-
}
370-
cancel()
329+
opts = append(opts, grpc.WithPerRPCCredentials(c.authTokenBundle.PerRPCCredentials()))
371330
}
372331

373332
opts = append(opts, c.cfg.DialOptions...)
@@ -510,6 +469,19 @@ func newClient(cfg *Config) (*Client, error) {
510469
client.Auth = NewAuth(client)
511470
client.Maintenance = NewMaintenance(client)
512471

472+
//get token with established connection
473+
ctx, cancel = client.ctx, func() {}
474+
if client.cfg.DialTimeout > 0 {
475+
ctx, cancel = context.WithTimeout(ctx, client.cfg.DialTimeout)
476+
}
477+
err = client.getToken(ctx)
478+
if err != nil {
479+
client.Close()
480+
cancel()
481+
return nil, err
482+
}
483+
cancel()
484+
513485
if cfg.RejectOldCluster {
514486
if err := client.checkVersion(); err != nil {
515487
client.Close()

clientv3/maintenance.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,19 @@ func NewMaintenance(c *Client) Maintenance {
8383
if err != nil {
8484
return nil, nil, fmt.Errorf("failed to dial endpoint %s with maintenance client: %v", endpoint, err)
8585
}
86-
cancel := func() { conn.Close() }
86+
87+
//get token with established connection
88+
dctx := c.ctx
89+
cancel := func() {}
90+
if c.cfg.DialTimeout > 0 {
91+
dctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
92+
}
93+
err = c.getToken(dctx)
94+
cancel()
95+
if err != nil {
96+
return nil, nil, fmt.Errorf("failed to getToken from endpoint %s with maintenance client: %v", endpoint, err)
97+
}
98+
cancel = func() { conn.Close() }
8799
return RetryMaintenanceClient(c, conn), cancel, nil
88100
},
89101
remote: RetryMaintenanceClient(c, c.conn),

0 commit comments

Comments
 (0)