16
16
*
17
17
*/
18
18
19
- use clap:: { Parser , Subcommand } ;
19
+ use clap:: { command , value_parser , Arg , Args , Command , FromArgMatches } ;
20
20
use crossterm:: style:: Stylize ;
21
21
use std:: path:: { Path , PathBuf } ;
22
22
use std:: sync:: Arc ;
@@ -33,9 +33,6 @@ lazy_static::lazy_static! {
33
33
pub static ref CONFIG : Arc <Config > = Arc :: new( Config :: new( ) ) ;
34
34
}
35
35
36
- pub const USERNAME_ENV : & str = "P_USERNAME" ;
37
- pub const PASSWORD_ENV : & str = "P_PASSWORD" ;
38
-
39
36
pub struct Config {
40
37
pub parseable : Server ,
41
38
storage : Arc < dyn ObjectStorageProvider + Send + Sync > ,
@@ -44,18 +41,42 @@ pub struct Config {
44
41
45
42
impl Config {
46
43
fn new ( ) -> Self {
47
- let cli = Cli :: parse ( ) ;
48
- match cli. command {
49
- SubCmd :: ServerS3 { server, storage } => Config {
50
- parseable : server,
51
- storage : Arc :: new ( storage) ,
52
- storage_name : "s3" ,
53
- } ,
54
- SubCmd :: ServerDrive { server, storage } => Config {
55
- parseable : server,
56
- storage : Arc :: new ( storage) ,
57
- storage_name : "drive" ,
58
- } ,
44
+ let cli = parseable_cli_command ( ) . get_matches ( ) ;
45
+
46
+ match cli. subcommand ( ) {
47
+ Some ( ( "--local-store" , m) ) => {
48
+ let server = match Server :: from_arg_matches ( m) {
49
+ Ok ( server) => server,
50
+ Err ( err) => err. exit ( ) ,
51
+ } ;
52
+ let storage = match FSConfig :: from_arg_matches ( m) {
53
+ Ok ( server) => server,
54
+ Err ( err) => err. exit ( ) ,
55
+ } ;
56
+
57
+ Config {
58
+ parseable : server,
59
+ storage : Arc :: new ( storage) ,
60
+ storage_name : "drive" ,
61
+ }
62
+ }
63
+ Some ( ( "--s3-store" , m) ) => {
64
+ let server = match Server :: from_arg_matches ( m) {
65
+ Ok ( server) => server,
66
+ Err ( err) => err. exit ( ) ,
67
+ } ;
68
+ let storage = match S3Config :: from_arg_matches ( m) {
69
+ Ok ( server) => server,
70
+ Err ( err) => err. exit ( ) ,
71
+ } ;
72
+
73
+ Config {
74
+ parseable : server,
75
+ storage : Arc :: new ( storage) ,
76
+ storage_name : "s3" ,
77
+ }
78
+ }
79
+ _ => unreachable ! ( ) ,
59
80
}
60
81
}
61
82
@@ -133,104 +154,112 @@ impl Default for Config {
133
154
}
134
155
}
135
156
136
- #[ derive( Parser ) ] // requires `derive` feature
137
- #[ command(
138
- name = "Parseable" ,
139
- bin_name = "parseable" ,
140
- about = "Parseable is a log storage and observability platform." ,
141
- version
142
- ) ]
143
- struct Cli {
144
- #[ command( subcommand) ]
145
- command : SubCmd ,
146
- }
157
+ fn parseable_cli_command ( ) -> Command {
158
+ let local = Server :: get_clap_command ( "--local-store" ) ;
159
+ let local = <FSConfig as Args >:: augment_args_for_update ( local) ;
160
+
161
+ let local = local
162
+ . mut_arg ( Server :: USERNAME , |arg| {
163
+ arg. required ( false ) . default_value ( "admin" )
164
+ } )
165
+ . mut_arg ( Server :: PASSWORD , |arg| {
166
+ arg. required ( false ) . default_value ( "admin" )
167
+ } ) ;
168
+
169
+ let s3 = Server :: get_clap_command ( "--s3-store" ) ;
170
+ let s3 = <S3Config as Args >:: augment_args_for_update ( s3) ;
147
171
148
- #[ derive( Subcommand , Clone ) ]
149
- enum SubCmd {
150
- #[ command( name = "--s3-store" ) ]
151
- ServerS3 {
152
- #[ command( flatten) ]
153
- server : Server ,
154
- #[ command( flatten) ]
155
- storage : S3Config ,
156
- } ,
157
- #[ command( name = "--local-store" ) ]
158
- ServerDrive {
159
- #[ command( flatten) ]
160
- server : Server ,
161
- #[ command( flatten) ]
162
- storage : FSConfig ,
163
- } ,
172
+ command ! ( )
173
+ . name ( "Parseable" )
174
+ . bin_name ( "parseable" )
175
+ . about ( "Parseable is a log storage and observability platform." )
176
+ . propagate_version ( true )
177
+ . next_line_help ( false )
178
+ . help_template (
179
+ r#"
180
+ {name} - v{version}
181
+ {about-with-newline}
182
+ {all-args}
183
+ {after-help}
184
+ {author}
185
+ "# ,
186
+ )
187
+ . after_help ( "Checkout https://parseable.io for documentation" )
188
+ . subcommand_required ( true )
189
+ . subcommands ( [ local, s3] )
164
190
}
165
191
166
- #[ derive( clap:: Args , Debug , Clone ) ]
167
- #[ clap( name = "server" , about = "Start the Parseable server" ) ]
192
+ #[ derive( Debug , Default ) ]
168
193
pub struct Server {
169
194
/// The location of TLS Cert file
170
- #[ arg(
171
- long,
172
- env = "P_TLS_CERT_PATH" ,
173
- value_name = "path" ,
174
- value_parser = validation:: file_path
175
- ) ]
176
195
pub tls_cert_path : Option < PathBuf > ,
177
196
178
197
/// The location of TLS Private Key file
179
- #[ arg(
180
- long,
181
- env = "P_TLS_KEY_PATH" ,
182
- value_name = "path" ,
183
- value_parser = validation:: file_path
184
- ) ]
185
198
pub tls_key_path : Option < PathBuf > ,
186
199
187
200
/// The address on which the http server will listen.
188
- #[ arg(
189
- long,
190
- env = "P_ADDR" ,
191
- default_value = "0.0.0.0:8000" ,
192
- value_name = "url"
193
- ) ]
194
201
pub address : String ,
195
202
196
203
/// The local staging path is used as a temporary landing point
197
204
/// for incoming events and local cache
198
- #[ arg(
199
- long,
200
- env = "P_STAGING_DIR" ,
201
- default_value = "./data" ,
202
- value_name = "path"
203
- ) ]
204
205
pub local_staging_path : PathBuf ,
205
206
206
207
/// Interval in seconds after which uncommited data would be
207
208
/// uploaded to the storage platform.
208
- #[ arg(
209
- long,
210
- env = "P_STORAGE_UPLOAD_INTERVAL" ,
211
- default_value = "60" ,
212
- value_name = "seconds"
213
- ) ]
214
209
pub upload_interval : u64 ,
215
210
216
211
/// Username for the basic authentication on the server
217
- #[ arg(
218
- long,
219
- env = USERNAME_ENV ,
220
- value_name = "username" ,
221
- ) ]
222
212
pub username : String ,
223
213
224
214
/// Password for the basic authentication on the server
225
- #[ arg(
226
- long,
227
- env = PASSWORD_ENV ,
228
- value_name = "password" ,
229
- ) ]
230
215
pub password : String ,
231
216
}
232
217
218
+ impl FromArgMatches for Server {
219
+ fn from_arg_matches ( m : & clap:: ArgMatches ) -> Result < Self , clap:: Error > {
220
+ let mut s: Self = Self :: default ( ) ;
221
+ s. update_from_arg_matches ( m) ?;
222
+ Ok ( s)
223
+ }
224
+
225
+ fn update_from_arg_matches ( & mut self , m : & clap:: ArgMatches ) -> Result < ( ) , clap:: Error > {
226
+ self . tls_cert_path = m. get_one :: < PathBuf > ( Self :: TLS_CERT ) . cloned ( ) ;
227
+ self . tls_key_path = m. get_one :: < PathBuf > ( Self :: TLS_KEY ) . cloned ( ) ;
228
+ self . address = m
229
+ . get_one :: < String > ( Self :: ADDRESS )
230
+ . cloned ( )
231
+ . expect ( "default value for address" ) ;
232
+ self . local_staging_path = m
233
+ . get_one :: < PathBuf > ( Self :: STAGING )
234
+ . cloned ( )
235
+ . expect ( "default value for staging" ) ;
236
+ self . upload_interval = m
237
+ . get_one :: < u64 > ( Self :: UPLOAD_INTERVAL )
238
+ . cloned ( )
239
+ . expect ( "default value for upload" ) ;
240
+ self . username = m
241
+ . get_one :: < String > ( Self :: USERNAME )
242
+ . cloned ( )
243
+ . expect ( "default for username" ) ;
244
+ self . password = m
245
+ . get_one :: < String > ( Self :: PASSWORD )
246
+ . cloned ( )
247
+ . expect ( "default for password" ) ;
248
+
249
+ Ok ( ( ) )
250
+ }
251
+ }
252
+
233
253
impl Server {
254
+ // identifiers for arguments
255
+ pub const TLS_CERT : & str = "tls-cert-path" ;
256
+ pub const TLS_KEY : & str = "tls-key-path" ;
257
+ pub const ADDRESS : & str = "address" ;
258
+ pub const STAGING : & str = "local-staging-path" ;
259
+ pub const UPLOAD_INTERVAL : & str = "upload-interval" ;
260
+ pub const USERNAME : & str = "username" ;
261
+ pub const PASSWORD : & str = "password" ;
262
+
234
263
pub fn local_stream_data_path ( & self , stream_name : & str ) -> PathBuf {
235
264
self . local_staging_path . join ( stream_name)
236
265
}
@@ -242,10 +271,75 @@ impl Server {
242
271
243
272
"http" . to_string ( )
244
273
}
274
+
275
+ pub fn get_clap_command ( name : & ' static str ) -> Command {
276
+ Command :: new ( name) . next_line_help ( false )
277
+ . arg (
278
+ Arg :: new ( Self :: TLS_CERT )
279
+ . long ( Self :: TLS_CERT )
280
+ . env ( "P_TLS_CERT_PATH" )
281
+ . value_name ( "PATH" )
282
+ . value_parser ( validation:: file_path)
283
+ . help ( "The location of TLS Cert file" ) ,
284
+ )
285
+ . arg (
286
+ Arg :: new ( Self :: TLS_KEY )
287
+ . long ( Self :: TLS_KEY )
288
+ . env ( "P_TLS_KEY_PATH" )
289
+ . value_name ( "PATH" )
290
+ . value_parser ( validation:: file_path)
291
+ . help ( "The location of TLS Private Key file" ) ,
292
+ )
293
+ . arg (
294
+ Arg :: new ( Self :: ADDRESS )
295
+ . long ( Self :: ADDRESS )
296
+ . env ( "P_ADDR" )
297
+ . value_name ( "ADDR:PORT" )
298
+ . default_value ( "0.0.0.0:8000" )
299
+ . value_parser ( validation:: socket_addr)
300
+ . help ( "The address on which the http server will listen." ) ,
301
+ )
302
+ . arg (
303
+ Arg :: new ( Self :: STAGING )
304
+ . long ( Self :: STAGING )
305
+ . env ( "P_STAGING_DIR" )
306
+ . value_name ( "DIR" )
307
+ . default_value ( "./staging" )
308
+ . value_parser ( value_parser ! ( PathBuf ) )
309
+ . help ( "The local staging path is used as a temporary landing point for incoming events and local cache" )
310
+ . next_line_help ( true ) ,
311
+ )
312
+ . arg (
313
+ Arg :: new ( Self :: UPLOAD_INTERVAL )
314
+ . long ( Self :: UPLOAD_INTERVAL )
315
+ . env ( "P_STORAGE_UPLOAD_INTERVAL" )
316
+ . value_name ( "SECONDS" )
317
+ . default_value ( "60" )
318
+ . value_parser ( value_parser ! ( u64 ) )
319
+ . help ( "Interval in seconds after which uncommited data would be uploaded to the storage platform." )
320
+ . next_line_help ( true ) ,
321
+ )
322
+ . arg (
323
+ Arg :: new ( Self :: USERNAME )
324
+ . long ( Self :: USERNAME )
325
+ . env ( "P_USERNAME" )
326
+ . value_name ( "STRING" )
327
+ . required ( true )
328
+ . help ( "Username for the basic authentication on the server" ) ,
329
+ )
330
+ . arg (
331
+ Arg :: new ( Self :: PASSWORD )
332
+ . long ( Self :: PASSWORD )
333
+ . env ( "P_PASSWORD" )
334
+ . value_name ( "STRING" )
335
+ . required ( true )
336
+ . help ( "Password for the basic authentication on the server" ) ,
337
+ )
338
+ }
245
339
}
246
340
247
341
pub mod validation {
248
- use std:: path:: PathBuf ;
342
+ use std:: { net :: ToSocketAddrs , path:: PathBuf } ;
249
343
250
344
pub fn file_path ( s : & str ) -> Result < PathBuf , String > {
251
345
if s. is_empty ( ) {
@@ -260,4 +354,11 @@ pub mod validation {
260
354
261
355
Ok ( path)
262
356
}
357
+
358
+ pub fn socket_addr ( s : & str ) -> Result < String , String > {
359
+ s. to_socket_addrs ( )
360
+ . is_ok ( )
361
+ . then_some ( s. to_string ( ) )
362
+ . ok_or_else ( || "Socket Address for server is invalid" . to_string ( ) )
363
+ }
263
364
}
0 commit comments