...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package config
16
17 import (
18 "context"
19 "fmt"
20 "path/filepath"
21 "sort"
22 "strings"
23 "time"
24
25 "go.etcd.io/etcd/client/pkg/v3/transport"
26 "go.etcd.io/etcd/client/pkg/v3/types"
27 "go.etcd.io/etcd/pkg/v3/netutil"
28 "go.etcd.io/etcd/server/v3/datadir"
29 "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
30
31 bolt "go.etcd.io/bbolt"
32 "go.uber.org/zap"
33 )
34
35
36 type ServerConfig struct {
37 Name string
38 DiscoveryURL string
39 DiscoveryProxy string
40 ClientURLs types.URLs
41 PeerURLs types.URLs
42 DataDir string
43
44
45 DedicatedWALDir string
46
47 SnapshotCount uint64
48
49
50
51
52
53
54
55 SnapshotCatchUpEntries uint64
56
57 MaxSnapFiles uint
58 MaxWALFiles uint
59
60
61 BackendBatchInterval time.Duration
62
63 BackendBatchLimit int
64
65
66 BackendFreelistType bolt.FreelistType
67
68 InitialPeerURLsMap types.URLsMap
69 InitialClusterToken string
70 NewCluster bool
71 PeerTLSInfo transport.TLSInfo
72
73 CORS map[string]struct{}
74
75
76
77
78 HostWhitelist map[string]struct{}
79
80 TickMs uint
81 ElectionTicks int
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 InitialElectionTickAdvance bool
111
112 BootstrapTimeout time.Duration
113
114 AutoCompactionRetention time.Duration
115 AutoCompactionMode string
116 CompactionBatchLimit int
117 QuotaBackendBytes int64
118 MaxTxnOps uint
119
120
121 MaxRequestBytes uint
122
123
124
125 MaxConcurrentStreams uint32
126
127 WarningApplyDuration time.Duration
128
129 StrictReconfigCheck bool
130
131
132 ClientCertAuthEnabled bool
133
134 AuthToken string
135 BcryptCost uint
136 TokenTTL uint
137
138
139
140 InitialCorruptCheck bool
141 CorruptCheckTime time.Duration
142 CompactHashCheckEnabled bool
143 CompactHashCheckTime time.Duration
144
145
146 PreVote bool
147
148
149 SocketOpts transport.SocketOpts
150
151
152 Logger *zap.Logger
153
154 ForceNewCluster bool
155
156
157 EnableLeaseCheckpoint bool
158
159 LeaseCheckpointInterval time.Duration
160
161 LeaseCheckpointPersist bool
162
163 EnableGRPCGateway bool
164
165
166 ExperimentalEnableDistributedTracing bool
167
168 ExperimentalTracerOptions []otelgrpc.Option
169
170 WatchProgressNotifyInterval time.Duration
171
172
173
174 UnsafeNoFsync bool `json:"unsafe-no-fsync"`
175
176 DowngradeCheckTime time.Duration
177
178
179
180
181
182
183
184 ExperimentalMemoryMlock bool `json:"experimental-memory-mlock"`
185
186
187
188 ExperimentalTxnModeWriteWithSharedBuffer bool `json:"experimental-txn-mode-write-with-shared-buffer"`
189
190
191
192 ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes"`
193
194
195 V2Deprecation V2DeprecationEnum `json:"v2-deprecation"`
196 }
197
198
199
200 func (c *ServerConfig) VerifyBootstrap() error {
201 if err := c.hasLocalMember(); err != nil {
202 return err
203 }
204 if err := c.advertiseMatchesCluster(); err != nil {
205 return err
206 }
207 if CheckDuplicateURL(c.InitialPeerURLsMap) {
208 return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
209 }
210 if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
211 return fmt.Errorf("initial cluster unset and no discovery URL found")
212 }
213 return nil
214 }
215
216
217
218 func (c *ServerConfig) VerifyJoinExisting() error {
219
220
221 if err := c.hasLocalMember(); err != nil {
222 return err
223 }
224 if CheckDuplicateURL(c.InitialPeerURLsMap) {
225 return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
226 }
227 if c.DiscoveryURL != "" {
228 return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
229 }
230 return nil
231 }
232
233
234 func (c *ServerConfig) hasLocalMember() error {
235 if urls := c.InitialPeerURLsMap[c.Name]; urls == nil {
236 return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name)
237 }
238 return nil
239 }
240
241
242 func (c *ServerConfig) advertiseMatchesCluster() error {
243 urls, apurls := c.InitialPeerURLsMap[c.Name], c.PeerURLs.StringSlice()
244 urls.Sort()
245 sort.Strings(apurls)
246 ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
247 defer cancel()
248 ok, err := netutil.URLStringsEqual(ctx, c.Logger, apurls, urls.StringSlice())
249 if ok {
250 return nil
251 }
252
253 initMap, apMap := make(map[string]struct{}), make(map[string]struct{})
254 for _, url := range c.PeerURLs {
255 apMap[url.String()] = struct{}{}
256 }
257 for _, url := range c.InitialPeerURLsMap[c.Name] {
258 initMap[url.String()] = struct{}{}
259 }
260
261 missing := []string{}
262 for url := range initMap {
263 if _, ok := apMap[url]; !ok {
264 missing = append(missing, url)
265 }
266 }
267 if len(missing) > 0 {
268 for i := range missing {
269 missing[i] = c.Name + "=" + missing[i]
270 }
271 mstr := strings.Join(missing, ",")
272 apStr := strings.Join(apurls, ",")
273 return fmt.Errorf("--initial-cluster has %s but missing from --initial-advertise-peer-urls=%s (%v)", mstr, apStr, err)
274 }
275
276 for url := range apMap {
277 if _, ok := initMap[url]; !ok {
278 missing = append(missing, url)
279 }
280 }
281 if len(missing) > 0 {
282 mstr := strings.Join(missing, ",")
283 umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
284 return fmt.Errorf("--initial-advertise-peer-urls has %s but missing from --initial-cluster=%s", mstr, umap.String())
285 }
286
287
288 apStr := strings.Join(apurls, ",")
289 umap := types.URLsMap(map[string]types.URLs{c.Name: c.PeerURLs})
290 return fmt.Errorf("failed to resolve %s to match --initial-cluster=%s (%v)", apStr, umap.String(), err)
291 }
292
293 func (c *ServerConfig) MemberDir() string { return datadir.ToMemberDir(c.DataDir) }
294
295 func (c *ServerConfig) WALDir() string {
296 if c.DedicatedWALDir != "" {
297 return c.DedicatedWALDir
298 }
299 return datadir.ToWalDir(c.DataDir)
300 }
301
302 func (c *ServerConfig) SnapDir() string { return filepath.Join(c.MemberDir(), "snap") }
303
304 func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }
305
306
307 func (c *ServerConfig) ReqTimeout() time.Duration {
308
309
310 return 5*time.Second + 2*time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
311 }
312
313 func (c *ServerConfig) ElectionTimeout() time.Duration {
314 return time.Duration(c.ElectionTicks*int(c.TickMs)) * time.Millisecond
315 }
316
317 func (c *ServerConfig) PeerDialTimeout() time.Duration {
318
319 return time.Second + time.Duration(c.ElectionTicks*int(c.TickMs))*time.Millisecond
320 }
321
322 func CheckDuplicateURL(urlsmap types.URLsMap) bool {
323 um := make(map[string]bool)
324 for _, urls := range urlsmap {
325 for _, url := range urls {
326 u := url.String()
327 if um[u] {
328 return true
329 }
330 um[u] = true
331 }
332 }
333 return false
334 }
335
336 func (c *ServerConfig) BootstrapTimeoutEffective() time.Duration {
337 if c.BootstrapTimeout != 0 {
338 return c.BootstrapTimeout
339 }
340 return time.Second
341 }
342
343 func (c *ServerConfig) BackendPath() string { return datadir.ToBackendFileName(c.DataDir) }
344
View as plain text