etcds 0.16.0

An etcd v3 API server - light server version for queue management
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
use std::collections::HashSet;
use std::io::{BufReader, Read};
use clap::Parser;
use clap_serde_derive::{
    clap::{self},
    serde::Serialize,
    ClapSerde,
};

#[derive(ClapSerde, Clone, Debug)]
pub struct EtcdConfig {
    /// Node UUID
    #[arg(long, required = false, default_value = "")]
    pub node : String,

    /// Cluster UUID
    #[arg(long, required = false, default_value = "")]
    pub cluster: String,

    /// Logical term (epoch) for this node, incremented on restart/reconfigure.
    #[arg(long, required = false, default_value = "1")]
    pub term: u64,

// all fields bellow copied from etcd configuration

    /// Human-readable name for this member.
    #[arg(long, required = false, default_value = "default")]
    pub name: String,
    /// Path to the data directory.
    /// NOT USE in this impl.
    #[arg(long, required = false, default_value = "${name}.etcd")]
    pub data_dir: String,

    /// Path to the dedicated wal directory.
    /// NOT USE in this impl.
    #[arg(long, required = false, default_value = "")]
    pub wal_dir : String, // ''

    /// Number of committed transactions to trigger a snapshot to disk.
    /// NOT USE in this impl.
    #[arg(long, required = false, default_value = "100000")]
    pub snapshot_count: u32,

    /// Time (in milliseconds) of a heartbeat interval.
    /// NOT USE in this impl.
    #[arg(long, required = false, default_value = "100")]
    pub heartbeat_interval: u32,

    /// Time (in milliseconds) for an election to timeout. See tuning documentation for details.
    /// NOT USE in this impl.
    #[arg(long, required = false, default_value = "1000")]
    pub election_timeout: u32,

    /// Whether to fast_forward initial election ticks on boot for faster election.
    /// NOT USE in this impl.
    #[arg(long, required = false, default_value = "true")]
    pub initial_election_tick_advance: bool,

    /// List of URLs to listen on for peer traffic.
    /// The URLs needed to be a comma-separated list.
    #[arg(long, required = false, default_value = "http://localhost:2380")]
    pub listen_peer_urls: String,

    /// List of URLs to listen on for client grpc traffic and http as long as --listen-client-http-urls is not specified.
    /// The URLs needed to be a comma-separated list.
    #[arg(long, required = false, default_value = "localhost:2379")]
    pub listen_client_urls: String,

    ///List of URLs to listen on for http only client traffic. Enabling this flag removes http services from --listen-client-urls.
    #[arg(long, required = false, default_value = "")]
    pub listen_client_http_urls : String,

    /// Maximum number of snapshot files to retain (0 is unlimited).
    #[arg(long, required = false, default_value = "5")]
    pub max_snapshots: u32,

    /// Maximum number of wal files to retain (0 is unlimited).
    #[arg(long, required = false, default_value = "5")]
    pub max_wals: u32,

    /// Raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
    #[arg(long, required = false, default_value = "0")]
    pub quota_backend_bytes: u32,

    /// BackendFreelistType specifies the type of freelist that boltdb backend uses(array and map are supported types).
    #[arg(long, required = false, default_value = "map")]
    pub backend_bbolt_freelist_type: String,

    /// BackendBatchInterval is the maximum time before commit the backend transaction.
    #[arg(long, required = false, default_value = "")]
    pub backend_batch_interval : String, // ''

    /// BackendBatchLimit is the maximum operations before commit the backend transaction.
    #[arg(long, required = false, default_value = "0")]
    pub backend_batch_limit: u32,

    /// Maximum number of operations permitted in a transaction.
    #[arg(long, required = false, default_value = "128")]
    pub max_txn_ops: u32,

    /// Maximum client request size in bytes the server will accept.
    #[arg(long, required = false, default_value = "1572864")]
    pub max_request_bytes: u32,

    /// Minimum duration interval that a client should wait before pinging server.
    #[arg(long, required = false, default_value = "5s")]
    pub grpc_keepalive_min_time: String,

    /// Frequency duration of server_to_client ping to check if a connection is alive (0 to disable).
    #[arg(long, required = false, default_value = "2h")]
    pub grpc_keepalive_interval: String,

    /// Additional duration of wait before closing a non_responsive connection (0 to disable).
    #[arg(long, required = false, default_value = "20s")]
    pub grpc_keepalive_timeout: String,

    /// Enable to set socket option SO_REUSEPORT on listeners allowing rebinding of a port already in use.
    #[arg(long, required = false, default_value = "false")]
    pub socket_reuse_port : bool, // ''

    /// Enable to set socket option SO_REUSEADDR on listeners allowing binding to an address in TIME_WAIT state.
    #[arg(long, required = false, default_value = "false")]
    pub socket_reuse_address : bool, // ''




    /// List of this member's peer URLs to advertise to the rest of the cluster.
    /// The URLs needed to be a comma-separated list: http://localhost:2380
    #[arg(long, required = false, default_value = "")]
    pub initial_advertise_peer_urls: String,

    /// Comma separated string of initial cluster configuration for bootstrapping.
    /// Example: initial-cluster: "infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380"
    #[arg(long, required = false, default_value = "http://localhost:2380")]
    pub initial_cluster: String, // 'default='

    /// Initial cluster state ('new' or 'existing').
    #[arg(long, required = false, default_value = "new")]
    pub initial_cluster_state: String,

    /// Initial cluster token for the etcd cluster during bootstrap.

    /// Specifying this can protect you from unintended cross_cluster interaction when running multiple clusters.
    #[arg(long, required = false, default_value = "etcd-cluster")]
    pub initial_cluster_token: String,


    /// List of this member's client URLs to advertise to the public.

    /// The client URLs advertised should be accessible to machines that talk to etcd cluster. etcd client libraries parse these URLs to connect to the cluster.
    /// The URLs needed to be a comma-separated list.
    #[arg(long, required = false, default_value = "http://localhost:2379")]
    pub advertise_client_urls: String,

    /// Discovery URL used to bootstrap the cluster.
    #[arg(long, required = false, default_value = "")]
    pub discovery : String, // ''


    /// Expected behavior ('exit' or 'proxy') when discovery services fails.

    /// "proxy" supports v2 API only.
    #[arg(long, required = false, default_value = "proxy")]
    pub discovery_fallback: String,


    /// HTTP proxy to use for traffic to discovery service.
    #[arg(long, required = false, default_value = "")]
    pub discovery_proxy : String, // ''

    /// DNS srv domain used to bootstrap the cluster.
    #[arg(long, required = false, default_value = "")]
    pub discovery_srv : String, // ''

    /// Suffix to the dns srv name queried when bootstrapping.
    #[arg(long, required = false, default_value = "")]
    pub discovery_srv_name : String, // ''

    /// Reject reconfiguration requests that would cause quorum loss.
    #[arg(long, required = false, default_value = "true")]
    pub strict_reconfig_check: bool,

    /// Enable the raft Pre_Vote algorithm to prevent disruption when a node that has been partitioned away rejoins the cluster.
    #[arg(long, required = false, default_value = "true")]
    pub pre_vote: bool,

    /// Auto compaction retention length. 0 means disable auto compaction.
    #[arg(long, required = false, default_value = "0")]
    pub auto_compaction_retention: u32,

    /// Interpret 'auto_compaction_retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.
    #[arg(long, required = false, default_value = "periodic")]
    pub auto_compaction_mode: String,

    /// Accept etcd V2 client requests. Deprecated and to be decommissioned in v3.6.
    #[arg(long, required = false, default_value = "false")]
    pub enable_v2 : bool, // ''

    #[arg(long, required = false, default_value = "not_yet")]
    pub v2_deprecation: String,

    // Phase of v2store deprecation. Allows to opt_in for higher compatibility mode.

    // Supported values:

    // 'not_yet'
    //
    //
    // // Issues a warning if v2store have meaningful content (default in v3.5)

    // 'write_only'
    //
    //  // Custom v2 state is not allowed (planned default in v3.6)

    // 'write_only_drop_data' // Custom v2 state will get DELETED !

    // 'gone'
    //
    //
    //  // v2store is not maintained any longer. (planned default in v3.7)


    // client_transport_security:


    /// Path to the client server TLS cert file.
    #[arg(long, required = false, default_value = "")]
    pub cert_file : String, // ''

    /// Path to the client server TLS key file.
    #[arg(long, required = false, default_value = "")]
    pub key_file : String, // ''

    /// Enable client cert authentication.

    /// It's recommended to enable client cert authentication to prevent attacks from unauthenticated clients (e.g. CVE_2023_44487), especially when running etcd as a public service.
    #[arg(long, required = false, default_value = "false")]
    pub client_cert_auth : bool, // ''
    /// Path to the client certificate revocation list file.
    #[arg(long, required = false, default_value = "")]
    pub client_crl_file : String, // ''

    /// Allowed TLS hostname for client cert authentication.
    #[arg(long, required = false, default_value = "")]
    pub client_cert_allowed_hostname : String, // ''

    /// Path to the client server TLS trusted CA cert file.

    /// //Note setting this parameter will also automatically enable client cert authentication no matter what value is set for `
    ///
    #[arg(long, required = false, default_value = "")]
    pub trusted_ca_file : String, // ''#[arg(long, required = false, default_value = "")]
    // client_cert_auth`.

    /// Client TLS using generated certificates.
    #[arg(long, required = false, default_value = "false")]
    pub auto_tls : bool, // ''

    // peer_transport_security:


    /// Path to the peer server TLS cert file.
    #[arg(long, required = false, default_value = "")]
    pub peer_cert_file : String, // ''

    /// Path to the peer server TLS key file.
    #[arg(long, required = false, default_value = "")]
    pub peer_key_file : String, // ''

    #[arg(long, required = false, default_value = "false")]
    pub peer_client_cert_auth : bool, // ''

    /// Enable peer client cert authentication.

    /// It's recommended to enable peer client cert authentication to prevent attacks from unauthenticated forged peers (e.g. CVE_2023_44487).

    /// Path to the peer server TLS trusted CA file.
    #[arg(long, required = false, default_value = "")]
    pub peer_trusted_ca_file : String, // ''

    /// Required CN for client certs connecting to the peer endpoint.
    #[arg(long, required = false, default_value = "")]
    pub peer_cert_allowed_cn : String, // ''

    /// Allowed TLS hostname for inter peer authentication.
    #[arg(long, required = false, default_value = "")]
    pub peer_cert_allowed_hostname : String, // ''

    ///   Peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided.
    #[arg(long, required = false, default_value = "false")]
    pub peer_auto_tls : bool,

    /// The validity period of the client and peer certificates that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS, the unit is year, and the default is 1.
    #[arg(long, required = false, default_value = "1")]
    pub self_signed_cert_validity: u32,

    /// Path to the peer certificate revocation list file.
    #[arg(long, required = false, default_value = "")]
    pub peer_crl_file : String, // ''

    /// Comma_separated list of supported TLS cipher suites between client/server and peers (empty will be auto_populated by Go).
    #[arg(long, required = false, default_value = "")]
    pub cipher_suites : String, // ''


    /// Comma_separated whitelist of origins for CORS, or cross_origin resource sharing, (empty or * means allow all).
    #[arg(long, required = false, default_value = "*")]
    pub cors: String,

    /// Acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).
    #[arg(long, required = false, default_value = "*")]
    pub host_whitelist: String,

    /// Minimum TLS version supported by etcd.
    #[arg(long, required = false, default_value = "TLS1.2")]
    pub tls_min_version: String,

    /// Maximum TLS version supported by etcd (empty will be auto_populated by Go).
    #[arg(long, required = false, default_value = "")]
    pub tls_max_version : String, // ''



    /// Specify a v3 authentication token type and token specific options, especially for JWT. Its format is "type,var1=val1,var2=val2,...". Possible type is 'simple' or 'jwt'. Possible variables are 'sign_method' for specifying a sign method of jwt (its possible values are 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', or 'PS512'), 'pub_key' for specifying a path to a public key for verifying jwt, 'priv_key' for specifying a path to a private key for signing jwt, and 'ttl' for specifying TTL of jwt tokens.
    #[arg(long, required = false, default_value = "simple")]
    pub auth_token: String,

    /// Specify the cost / strength of the bcrypt algorithm for hashing auth passwords. Valid values are between 4 and 31.
    #[arg(long, required = false, default_value = "10")]
    pub bcrypt_cost: u32,

    /// Time (in seconds) of the auth_token_ttl.
    #[arg(long, required = false, default_value = "300")]
    pub auth_token_ttl: u32,



    /// Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"
    #[arg(long, required = false, default_value = "false")]
    pub enable_pprof : bool, // ''

    /// Set level of detail for exported metrics, specify 'extensive' to include server side grpc histogram metrics.
    #[arg(long, required = false, default_value = "basic")]
    pub metrics: String,

    /// List of URLs to listen on for the metrics and health endpoints.
    #[arg(long, required = false, default_value = "")]
    pub listen_metrics_urls : String, // ''



    /// Currently only supports 'zap' for structured logging.
    /// NOT USE in this impl.
    #[arg(long, required = false, default_value = "zap")]
    pub logger: String,

    /// Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.
    #[arg(long, required = false, default_value = "default")]
    pub log_outputs: String,

    /// Configures log level. Only supports debug, info, warn, error, panic, or fatal.
    #[arg(long, required = false, default_value = "info")]
    pub log_level: String,

    /// Enable log rotation of a single log_outputs file target.
    #[arg(long, required = false, default_value = "false")]
    pub enable_log_rotation : bool,

    /// Configures log rotation if enabled with a JSON logger config. MaxSize(MB), MaxAge(days,0=no limit), MaxBackups(0=no limit), LocalTime(use computers local time), Compress(gzip)".
    #[arg(long, required = false, default_value = "{\"maxsize\": 100, \"maxage\": 0, \"maxbackups\": 0, \"localtime\": false, \"compress\": false}")]
    pub log_rotation_config_json: String,



    /// Enable experimental distributed tracing.
    #[arg(long, required = false, default_value = "false")]
    pub experimental_enable_distributed_tracing : bool,

    /// Distributed tracing collector address.
    #[arg(long, required = false, default_value = "localhost:4317")]
    pub experimental_distributed_tracing_address: String,


    /// Distributed tracing service name, must be same across all etcd instances.
    #[arg(long, required = false, default_value = "etcd")]
    pub experimental_distributed_tracing_service_name: String,


    /// Distributed tracing instance ID, must be unique per each etcd instance.
    #[arg(long, required = false, default_value = "")]
    pub experimental_distributed_tracing_instance_id : String,


    /// Number of samples to collect per million spans for OpenTelemetry Tracing (if enabled with experimental_enable_distributed_tracing flag).
    #[arg(long, required = false, default_value = "0")]
    pub experimental_distributed_tracing_sampling_rate: u32,


    // #[clap_serde]
    // #[command(flatten)]
    // pub client_transport_security: ClientConfig,
}

#[derive(ClapSerde, Serialize)]
#[derive(Debug)]
pub struct ClientConfig {
    /// Path to the client server TLS cert file.
    #[arg(long)]
    cert_file: String,
    /// Path to the client server TLS key file.
    #[arg(long, default_value = "false")]
    key_file: bool,

    /// Enable client cert authentication.
    #[arg(long, default_value = "false")]
    client_cert_auth: bool,

    /// Path to the client server TLS trusted CA cert file.
    #[arg(long)]
    trusted_ca_file: String,

    /// Client TLS using generated certificates
    #[arg(long, default_value = "false")]
    auto_tls: bool

}

#[derive(Parser)]
// author, version, about,
#[command(after_help = "https://etcd.io/docs/v3.5/op-guide/configuration/")]
pub struct EtcdCliArgs {
    /// Input files
    input: Vec<std::path::PathBuf>,

    /// Config file
    #[arg(long = "config", default_value = "config.yml")]
    pub config_path: std::path::PathBuf,

    /// Rest of arguments
    #[command(flatten)]
    pub config: <EtcdConfig as ClapSerde>::Opt,
}

impl EtcdCliArgs {
    pub fn parse_from<R: Read>(&mut self, input: BufReader<R>) -> Result<EtcdConfig, String> {
        serde_yaml::from_reader::<_, <EtcdConfig as ClapSerde>::Opt>(input)
            .map_err(|e| format!("Error in configuration file [{}]:\n{}", self.config_path.display(), e)) 
            .map(|config| EtcdConfig::from(config).merge(&mut self.config))
    }
}

impl EtcdConfig {
    pub fn with_defaults() -> Self {
        let args = <EtcdCliArgs as Parser>::parse_from(["etcd"]);
        let mut config = args.config;
        EtcdConfig::from(&mut config)
    }

    pub fn peers(&self) -> HashSet<&str> {
        let urls: HashSet<&str> = HashSet::from_iter(self.listen_client_urls.split(","));
        let mut clients: HashSet<&str> = HashSet::from_iter(self.initial_advertise_peer_urls.split(",")
            .filter(|c| c.trim().len() > 0 && c.starts_with("http")));
        for h in urls {
            if h.contains("//") {
                clients.remove(h);
            } else {
                clients.remove(format!("http:://{}",h).as_str());
            }
        }
        clients
    }
}