diff --git a/database/kv/etcd/v3/client.go b/database/kv/etcd/v3/client.go index 7931736..55b954e 100644 --- a/database/kv/etcd/v3/client.go +++ b/database/kv/etcd/v3/client.go @@ -19,13 +19,13 @@ package gxetcd import ( "context" + "crypto/tls" "log" "strings" "sync" "time" perrors "github.com/pkg/errors" - clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "google.golang.org/grpc" @@ -63,7 +63,7 @@ func NewConfigClientWithErr(opts ...Option) (*Client, error) { opt(options) } - newClient, err := NewClient(options.Name, options.Endpoints, options.Timeout, options.Heartbeat) + newClient, err := NewClientWithOptions(context.Background(), options) if err != nil { log.Printf("new etcd client (Name{%s}, etcd addresses{%v}, Timeout{%d}) = error{%v}", options.Name, options.Endpoints, options.Timeout, err) @@ -82,6 +82,9 @@ type Client struct { endpoints []string timeout time.Duration heartbeat int + username string + password string + tls *tls.Config ctx context.Context // if etcd server connection lose, the ctx.Done will be sent msg cancel context.CancelFunc // cancel the ctx, all watcher will stopped @@ -126,6 +129,47 @@ func NewClient(name string, endpoints []string, timeout time.Duration, heartbeat return c, nil } +// NewClientWithOptions create a client instance from Options. +func NewClientWithOptions(ctx context.Context, opts *Options) (*Client, error) { + nctx, cancel := context.WithCancel(ctx) + + rawClient, err := clientv3.New(clientv3.Config{ + Context: nctx, + Endpoints: opts.Endpoints, + DialTimeout: opts.Timeout, + TLS: opts.TLS, + Username: opts.Username, + Password: opts.Password, + DialOptions: []grpc.DialOption{grpc.WithBlock()}, + }) + if err != nil { + cancel() + return nil, perrors.WithMessage(err, "new raw client block connect to server") + } + + c := &Client{ + name: opts.Name, + timeout: opts.Timeout, + endpoints: opts.Endpoints, + heartbeat: opts.Heartbeat, + username: opts.Username, + password: opts.Password, + tls: opts.TLS, + + ctx: nctx, + cancel: cancel, + rawClient: rawClient, + + exit: make(chan struct{}), + } + + if err := c.keepSession(); err != nil { + cancel() + return nil, perrors.WithMessage(err, "client keep session") + } + return c, nil +} + // NOTICE: need to get the lock before calling this method func (c *Client) clean() { // close raw client diff --git a/database/kv/etcd/v3/options.go b/database/kv/etcd/v3/options.go index 83ebce0..aed6141 100644 --- a/database/kv/etcd/v3/options.go +++ b/database/kv/etcd/v3/options.go @@ -18,6 +18,7 @@ package gxetcd import ( + "crypto/tls" "time" ) @@ -44,6 +45,12 @@ type Options struct { Timeout time.Duration // Heartbeat second Heartbeat int + // Username is a user name for authentication. + Username string + // Password is a password for authentication. + Password string + // TLS holds the client secure credentials, if any. + TLS *tls.Config } // Option will define a function of handling Options @@ -76,3 +83,18 @@ func WithHeartbeat(heartbeat int) Option { opt.Heartbeat = heartbeat } } + +// WithAuthentication sets etcd client authentication +func WithAuthentication(username, password string) Option { + return func(opt *Options) { + opt.Username = username + opt.Password = password + } +} + +// WithTLS sets etcd client secure credentials +func WithTLS(tls *tls.Config) Option { + return func(opt *Options) { + opt.TLS = tls + } +}