blob: 8351828d2f90cd3785e88adff0d87c34c3e0d03d [file] [log] [blame]
Abhay Kumara2ae5992025-11-10 14:02:24 +00001// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package clientv3
16
17import (
18 "context"
19 "crypto/tls"
20 "time"
21
22 "go.uber.org/zap"
23 "google.golang.org/grpc"
24
25 "go.etcd.io/etcd/client/pkg/v3/transport"
26)
27
28type Config struct {
29 // Endpoints is a list of URLs.
30 Endpoints []string `json:"endpoints"`
31
32 // AutoSyncInterval is the interval to update endpoints with its latest members.
33 // 0 disables auto-sync. By default auto-sync is disabled.
34 AutoSyncInterval time.Duration `json:"auto-sync-interval"`
35
36 // DialTimeout is the timeout for failing to establish a connection.
37 DialTimeout time.Duration `json:"dial-timeout"`
38
39 // DialKeepAliveTime is the time after which client pings the server to see if
40 // transport is alive.
41 DialKeepAliveTime time.Duration `json:"dial-keep-alive-time"`
42
43 // DialKeepAliveTimeout is the time that the client waits for a response for the
44 // keep-alive probe. If the response is not received in this time, the connection is closed.
45 DialKeepAliveTimeout time.Duration `json:"dial-keep-alive-timeout"`
46
47 // MaxCallSendMsgSize is the client-side request send limit in bytes.
48 // If 0, it defaults to 2.0 MiB (2 * 1024 * 1024).
49 // Make sure that "MaxCallSendMsgSize" < server-side default send/recv limit.
50 // ("--max-request-bytes" flag to etcd or "embed.Config.MaxRequestBytes").
51 MaxCallSendMsgSize int
52
53 // MaxCallRecvMsgSize is the client-side response receive limit.
54 // If 0, it defaults to "math.MaxInt32", because range response can
55 // easily exceed request send limits.
56 // Make sure that "MaxCallRecvMsgSize" >= server-side default send/recv limit.
57 // ("--max-recv-bytes" flag to etcd).
58 MaxCallRecvMsgSize int
59
60 // TLS holds the client secure credentials, if any.
61 TLS *tls.Config
62
63 // Username is a user name for authentication.
64 Username string `json:"username"`
65
66 // Password is a password for authentication.
67 Password string `json:"password"`
68
69 // RejectOldCluster when set will refuse to create a client against an outdated cluster.
70 RejectOldCluster bool `json:"reject-old-cluster"`
71
72 // DialOptions is a list of dial options for the grpc client (e.g., for interceptors).
73 // For example, pass "grpc.WithBlock()" to block until the underlying connection is up.
74 // Without this, Dial returns immediately and connecting the server happens in background.
75 DialOptions []grpc.DialOption
76
77 // Context is the default client context; it can be used to cancel grpc dial out and
78 // other operations that do not have an explicit context.
79 Context context.Context
80
81 // Logger sets client-side logger.
82 // If nil, fallback to building LogConfig.
83 Logger *zap.Logger
84
85 // LogConfig configures client-side logger.
86 // If nil, use the default logger.
87 // TODO: configure gRPC logger
88 LogConfig *zap.Config
89
90 // PermitWithoutStream when set will allow client to send keepalive pings to server without any active streams(RPCs).
91 PermitWithoutStream bool `json:"permit-without-stream"`
92
93 // MaxUnaryRetries is the maximum number of retries for unary RPCs.
94 MaxUnaryRetries uint `json:"max-unary-retries"`
95
96 // BackoffWaitBetween is the wait time before retrying an RPC.
97 BackoffWaitBetween time.Duration `json:"backoff-wait-between"`
98
99 // BackoffJitterFraction is the jitter fraction to randomize backoff wait time.
100 BackoffJitterFraction float64 `json:"backoff-jitter-fraction"`
101
102 // TODO: support custom balancer picker
103}
104
105// ConfigSpec is the configuration from users, which comes from command-line flags,
106// environment variables or config file. It is a fully declarative configuration,
107// and can be serialized & deserialized to/from JSON.
108type ConfigSpec struct {
109 Endpoints []string `json:"endpoints"`
110 RequestTimeout time.Duration `json:"request-timeout"`
111 DialTimeout time.Duration `json:"dial-timeout"`
112 KeepAliveTime time.Duration `json:"keepalive-time"`
113 KeepAliveTimeout time.Duration `json:"keepalive-timeout"`
114 MaxCallSendMsgSize int `json:"max-request-bytes"`
115 MaxCallRecvMsgSize int `json:"max-recv-bytes"`
116 Secure *SecureConfig `json:"secure"`
117 Auth *AuthConfig `json:"auth"`
118}
119
120type SecureConfig struct {
121 Cert string `json:"cert"`
122 Key string `json:"key"`
123 Cacert string `json:"cacert"`
124 ServerName string `json:"server-name"`
125
126 InsecureTransport bool `json:"insecure-transport"`
127 InsecureSkipVerify bool `json:"insecure-skip-tls-verify"`
128}
129
130type AuthConfig struct {
131 Username string `json:"username"`
132 Password string `json:"password"`
133}
134
135func (cs *ConfigSpec) Clone() *ConfigSpec {
136 if cs == nil {
137 return nil
138 }
139
140 clone := *cs
141
142 if len(cs.Endpoints) > 0 {
143 clone.Endpoints = make([]string, len(cs.Endpoints))
144 copy(clone.Endpoints, cs.Endpoints)
145 }
146
147 if cs.Secure != nil {
148 clone.Secure = &SecureConfig{}
149 *clone.Secure = *cs.Secure
150 }
151 if cs.Auth != nil {
152 clone.Auth = &AuthConfig{}
153 *clone.Auth = *cs.Auth
154 }
155
156 return &clone
157}
158
159func (cfg AuthConfig) Empty() bool {
160 return cfg.Username == "" && cfg.Password == ""
161}
162
163// NewClientConfig creates a Config based on the provided ConfigSpec.
164func NewClientConfig(confSpec *ConfigSpec, lg *zap.Logger) (*Config, error) {
165 tlsCfg, err := newTLSConfig(confSpec.Secure, lg)
166 if err != nil {
167 return nil, err
168 }
169
170 cfg := &Config{
171 Endpoints: confSpec.Endpoints,
172 DialTimeout: confSpec.DialTimeout,
173 DialKeepAliveTime: confSpec.KeepAliveTime,
174 DialKeepAliveTimeout: confSpec.KeepAliveTimeout,
175 MaxCallSendMsgSize: confSpec.MaxCallSendMsgSize,
176 MaxCallRecvMsgSize: confSpec.MaxCallRecvMsgSize,
177 TLS: tlsCfg,
178 }
179
180 if confSpec.Auth != nil {
181 cfg.Username = confSpec.Auth.Username
182 cfg.Password = confSpec.Auth.Password
183 }
184
185 return cfg, nil
186}
187
188func newTLSConfig(scfg *SecureConfig, lg *zap.Logger) (*tls.Config, error) {
189 var (
190 tlsCfg *tls.Config
191 err error
192 )
193
194 if scfg == nil {
195 return nil, nil
196 }
197
198 if scfg.Cert != "" || scfg.Key != "" || scfg.Cacert != "" || scfg.ServerName != "" {
199 cfgtls := &transport.TLSInfo{
200 CertFile: scfg.Cert,
201 KeyFile: scfg.Key,
202 TrustedCAFile: scfg.Cacert,
203 ServerName: scfg.ServerName,
204 Logger: lg,
205 }
206 if tlsCfg, err = cfgtls.ClientConfig(); err != nil {
207 return nil, err
208 }
209 }
210
211 // If key/cert is not given but user wants secure connection, we
212 // should still setup an empty tls configuration for gRPC to setup
213 // secure connection.
214 if tlsCfg == nil && !scfg.InsecureTransport {
215 tlsCfg = &tls.Config{}
216 }
217
218 // If the user wants to skip TLS verification then we should set
219 // the InsecureSkipVerify flag in tls configuration.
220 if scfg.InsecureSkipVerify {
221 if tlsCfg == nil {
222 tlsCfg = &tls.Config{}
223 }
224 tlsCfg.InsecureSkipVerify = scfg.InsecureSkipVerify
225 }
226
227 return tlsCfg, nil
228}