diff --git a/client/v3/config.go b/client/v3/config.go index 29726160852a..7c112c6f9763 100644 --- a/client/v3/config.go +++ b/client/v3/config.go @@ -17,6 +17,7 @@ package clientv3 import ( "context" "crypto/tls" + "go.etcd.io/etcd/client/pkg/v3/transport" "time" "go.uber.org/zap" @@ -118,3 +119,56 @@ type AuthConfig struct { Username string `json:"username"` Password string `json:"password"` } + +// NewClientConfig creates a Config based on the provided ConfigSpec. +func NewClientConfig(confSpec *ConfigSpec, lg *zap.Logger) (*Config, error) { + var cfgtls *transport.TLSInfo + + if confSpec.Secure != nil { + scfg := confSpec.Secure + if scfg.Cert != "" || scfg.Key != "" || scfg.Cacert != "" || scfg.ServerName != "" { + cfgtls = &transport.TLSInfo{ + CertFile: scfg.Cert, + KeyFile: scfg.Key, + TrustedCAFile: scfg.Cacert, + ServerName: scfg.ServerName, + Logger: lg, + } + } + } + + cfg := &Config{ + Endpoints: confSpec.Endpoints, + DialTimeout: confSpec.DialTimeout, + DialKeepAliveTime: confSpec.KeepAliveTime, + DialKeepAliveTimeout: confSpec.KeepAliveTimeout, + } + + if cfgtls != nil { + if clientTLS, err := cfgtls.ClientConfig(); err == nil { + cfg.TLS = clientTLS + } else { + return nil, err + } + } + + // If key/cert is not given but user wants secure connection, we + // should still setup an empty tls configuration for gRPC to setup + // secure connection. + if cfg.TLS == nil && !confSpec.Secure.InsecureTransport { + cfg.TLS = &tls.Config{} + } + + // If the user wants to skip TLS verification then we should set + // the InsecureSkipVerify flag in tls configuration. + if cfg.TLS != nil && confSpec.Secure.InsecureSkipVerify { + cfg.TLS.InsecureSkipVerify = true + } + + if confSpec.Auth != nil { + cfg.Username = confSpec.Auth.Username + cfg.Password = confSpec.Auth.Password + } + + return cfg, nil +} diff --git a/etcdctl/ctlv3/command/global.go b/etcdctl/ctlv3/command/global.go index 57adae28f00a..9200b76068c7 100644 --- a/etcdctl/ctlv3/command/global.go +++ b/etcdctl/ctlv3/command/global.go @@ -15,7 +15,6 @@ package command import ( - "crypto/tls" "errors" "fmt" "io" @@ -138,7 +137,8 @@ func clientConfigFromCmd(cmd *cobra.Command) *clientv3.ConfigSpec { func mustClientCfgFromCmd(cmd *cobra.Command) *clientv3.Config { cc := clientConfigFromCmd(cmd) - cfg, err := newClientCfg(cc.Endpoints, cc.DialTimeout, cc.KeepAliveTime, cc.KeepAliveTimeout, cc.Secure, cc.Auth) + lg, _ := zap.NewProduction() + cfg, err := clientv3.NewClientConfig(cc, lg) if err != nil { cobrautl.ExitWithError(cobrautl.ExitBadArgs, err) } @@ -151,7 +151,8 @@ func mustClientFromCmd(cmd *cobra.Command) *clientv3.Client { } func mustClient(cc *clientv3.ConfigSpec) *clientv3.Client { - cfg, err := newClientCfg(cc.Endpoints, cc.DialTimeout, cc.KeepAliveTime, cc.KeepAliveTimeout, cc.Secure, cc.Auth) + lg, _ := zap.NewProduction() + cfg, err := clientv3.NewClientConfig(cc, lg) if err != nil { cobrautl.ExitWithError(cobrautl.ExitBadArgs, err) } @@ -165,64 +166,16 @@ func mustClient(cc *clientv3.ConfigSpec) *clientv3.Client { } func newClientCfg(endpoints []string, dialTimeout, keepAliveTime, keepAliveTimeout time.Duration, scfg *clientv3.SecureConfig, acfg *clientv3.AuthConfig) (*clientv3.Config, error) { - // set tls if any one tls option set - var cfgtls *transport.TLSInfo - tlsinfo := transport.TLSInfo{} - tlsinfo.Logger, _ = zap.NewProduction() - if scfg.Cert != "" { - tlsinfo.CertFile = scfg.Cert - cfgtls = &tlsinfo - } - - if scfg.Key != "" { - tlsinfo.KeyFile = scfg.Key - cfgtls = &tlsinfo - } - - if scfg.Cacert != "" { - tlsinfo.TrustedCAFile = scfg.Cacert - cfgtls = &tlsinfo - } - - if scfg.ServerName != "" { - tlsinfo.ServerName = scfg.ServerName - cfgtls = &tlsinfo - } - - cfg := &clientv3.Config{ - Endpoints: endpoints, - DialTimeout: dialTimeout, - DialKeepAliveTime: keepAliveTime, - DialKeepAliveTimeout: keepAliveTimeout, - } - - if cfgtls != nil { - clientTLS, err := cfgtls.ClientConfig() - if err != nil { - return nil, err - } - cfg.TLS = clientTLS - } - - // if key/cert is not given but user wants secure connection, we - // should still setup an empty tls configuration for gRPC to setup - // secure connection. - if cfg.TLS == nil && !scfg.InsecureTransport { - cfg.TLS = &tls.Config{} - } - - // If the user wants to skip TLS verification then we should set - // the InsecureSkipVerify flag in tls configuration. - if scfg.InsecureSkipVerify && cfg.TLS != nil { - cfg.TLS.InsecureSkipVerify = true - } - - if acfg != nil { - cfg.Username = acfg.Username - cfg.Password = acfg.Password - } - - return cfg, nil + lg, _ := zap.NewProduction() + confSpec := &clientv3.ConfigSpec{ + Endpoints: endpoints, + DialTimeout: dialTimeout, + KeepAliveTime: keepAliveTime, + KeepAliveTimeout: keepAliveTimeout, + Secure: scfg, + Auth: acfg, + } + return clientv3.NewClientConfig(confSpec, lg) } func argOrStdin(args []string, stdin io.Reader, i int) (string, error) { diff --git a/server/etcdserver/api/v3discovery/discovery.go b/server/etcdserver/api/v3discovery/discovery.go index 1c274cdba17f..f5be6b8f7ba0 100644 --- a/server/etcdserver/api/v3discovery/discovery.go +++ b/server/etcdserver/api/v3discovery/discovery.go @@ -18,7 +18,6 @@ package v3discovery import ( "context" - "crypto/tls" "errors" "math" @@ -28,7 +27,6 @@ import ( "strings" "time" - "go.etcd.io/etcd/client/pkg/v3/transport" "go.etcd.io/etcd/client/pkg/v3/types" "go.etcd.io/etcd/client/v3" @@ -173,7 +171,7 @@ func newDiscovery(lg *zap.Logger, dcfg *DiscoveryConfig, id types.ID) (*discover } lg = lg.With(zap.String("discovery-token", dcfg.Token), zap.String("discovery-endpoints", strings.Join(dcfg.Endpoints, ","))) - cfg, err := newClientCfg(dcfg, lg) + cfg, err := clientv3.NewClientConfig(&dcfg.ConfigSpec, lg) if err != nil { return nil, err } @@ -192,53 +190,6 @@ func newDiscovery(lg *zap.Logger, dcfg *DiscoveryConfig, id types.ID) (*discover }, nil } -// The following function follows the same logic as etcdctl, refer to -// https://github.com/etcd-io/etcd/blob/f9a8c49c695b098d66a07948666664ea10d01a82/etcdctl/ctlv3/command/global.go#L191-L250 -func newClientCfg(dcfg *DiscoveryConfig, lg *zap.Logger) (*clientv3.Config, error) { - var cfgtls *transport.TLSInfo - - if dcfg.Secure.Cert != "" || dcfg.Secure.Key != "" || dcfg.Secure.Cacert != "" { - cfgtls = &transport.TLSInfo{ - CertFile: dcfg.Secure.Cert, - KeyFile: dcfg.Secure.Key, - TrustedCAFile: dcfg.Secure.Cacert, - Logger: lg, - } - } - - cfg := &clientv3.Config{ - Endpoints: dcfg.Endpoints, - DialTimeout: dcfg.DialTimeout, - DialKeepAliveTime: dcfg.KeepAliveTime, - DialKeepAliveTimeout: dcfg.KeepAliveTimeout, - Username: dcfg.Auth.Username, - Password: dcfg.Auth.Password, - } - - if cfgtls != nil { - if clientTLS, err := cfgtls.ClientConfig(); err == nil { - cfg.TLS = clientTLS - } else { - return nil, err - } - } - - // If key/cert is not given but user wants secure connection, we - // should still setup an empty tls configuration for gRPC to setup - // secure connection. - if cfg.TLS == nil && !dcfg.Secure.InsecureTransport { - cfg.TLS = &tls.Config{} - } - - // If the user wants to skip TLS verification then we should set - // the InsecureSkipVerify flag in tls configuration. - if cfg.TLS != nil && dcfg.Secure.InsecureSkipVerify { - cfg.TLS.InsecureSkipVerify = true - } - - return cfg, nil -} - func (d *discovery) getCluster() (string, error) { cls, clusterSize, rev, err := d.checkCluster() if err != nil {