1616 *
1717 */
1818
19- use clap:: { Parser , Subcommand } ;
19+ use clap:: { command , value_parser , Arg , Args , Command , FromArgMatches } ;
2020use crossterm:: style:: Stylize ;
2121use std:: path:: { Path , PathBuf } ;
2222use std:: sync:: Arc ;
@@ -33,9 +33,6 @@ lazy_static::lazy_static! {
3333 pub static ref CONFIG : Arc <Config > = Arc :: new( Config :: new( ) ) ;
3434}
3535
36- pub const USERNAME_ENV : & str = "P_USERNAME" ;
37- pub const PASSWORD_ENV : & str = "P_PASSWORD" ;
38-
3936pub struct Config {
4037 pub parseable : Server ,
4138 storage : Arc < dyn ObjectStorageProvider + Send + Sync > ,
@@ -44,18 +41,42 @@ pub struct Config {
4441
4542impl Config {
4643 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 ! ( ) ,
5980 }
6081 }
6182
@@ -133,104 +154,112 @@ impl Default for Config {
133154 }
134155}
135156
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) ;
147171
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] )
164190}
165191
166- #[ derive( clap:: Args , Debug , Clone ) ]
167- #[ clap( name = "server" , about = "Start the Parseable server" ) ]
192+ #[ derive( Debug , Default ) ]
168193pub struct Server {
169194 /// 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- ) ]
176195 pub tls_cert_path : Option < PathBuf > ,
177196
178197 /// 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- ) ]
185198 pub tls_key_path : Option < PathBuf > ,
186199
187200 /// 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- ) ]
194201 pub address : String ,
195202
196203 /// The local staging path is used as a temporary landing point
197204 /// for incoming events and local cache
198- #[ arg(
199- long,
200- env = "P_STAGING_DIR" ,
201- default_value = "./data" ,
202- value_name = "path"
203- ) ]
204205 pub local_staging_path : PathBuf ,
205206
206207 /// Interval in seconds after which uncommited data would be
207208 /// uploaded to the storage platform.
208- #[ arg(
209- long,
210- env = "P_STORAGE_UPLOAD_INTERVAL" ,
211- default_value = "60" ,
212- value_name = "seconds"
213- ) ]
214209 pub upload_interval : u64 ,
215210
216211 /// Username for the basic authentication on the server
217- #[ arg(
218- long,
219- env = USERNAME_ENV ,
220- value_name = "username" ,
221- ) ]
222212 pub username : String ,
223213
224214 /// Password for the basic authentication on the server
225- #[ arg(
226- long,
227- env = PASSWORD_ENV ,
228- value_name = "password" ,
229- ) ]
230215 pub password : String ,
231216}
232217
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+
233253impl 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+
234263 pub fn local_stream_data_path ( & self , stream_name : & str ) -> PathBuf {
235264 self . local_staging_path . join ( stream_name)
236265 }
@@ -242,10 +271,75 @@ impl Server {
242271
243272 "http" . to_string ( )
244273 }
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+ }
245339}
246340
247341pub mod validation {
248- use std:: path:: PathBuf ;
342+ use std:: { net :: ToSocketAddrs , path:: PathBuf } ;
249343
250344 pub fn file_path ( s : & str ) -> Result < PathBuf , String > {
251345 if s. is_empty ( ) {
@@ -260,4 +354,11 @@ pub mod validation {
260354
261355 Ok ( path)
262356 }
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+ }
263364}
0 commit comments