Skip to content

Commit aec0c08

Browse files
authored
Add defaults for local storage mode (#235)
1 parent 69f92be commit aec0c08

File tree

5 files changed

+227
-95
lines changed

5 files changed

+227
-95
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
target
22
data
3+
staging
34
examples
45
Cargo.lock
56
cert.pem

server/Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@ base64 = "0.20.0"
1919
bytes = "1"
2020
chrono = "0.4.19"
2121
chrono-humanize = "0.2.2"
22-
clap = { version = "4.0.8", features = ["derive", "env"] }
22+
clap = { version = "4.0.32", default-features = false, features = [
23+
"std",
24+
"color",
25+
"help",
26+
"derive",
27+
"env",
28+
"cargo",
29+
"error-context",
30+
] }
2331
crossterm = "0.25"
2432
datafusion = "13.0"
2533
object_store = { version = "0.5.1", features = ["aws"] }

server/src/option.rs

Lines changed: 187 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
*/
1818

19-
use clap::{Parser, Subcommand};
19+
use clap::{command, value_parser, Arg, Args, Command, FromArgMatches};
2020
use crossterm::style::Stylize;
2121
use std::path::{Path, PathBuf};
2222
use 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-
3936
pub struct Config {
4037
pub parseable: Server,
4138
storage: Arc<dyn ObjectStorageProvider + Send + Sync>,
@@ -44,18 +41,42 @@ pub struct Config {
4441

4542
impl 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)]
168193
pub 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+
233253
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+
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

247341
pub 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
}

server/src/storage/localfs.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,18 @@ use super::{LogStream, ObjectStorage, ObjectStorageError, ObjectStorageProvider}
4343
#[derive(Debug, Clone, clap::Args)]
4444
#[command(
4545
name = "Local filesystem config",
46-
about = "Start Parseable with local filesystem as storage backend (non production use only)"
46+
about = "Start Parseable with local filesystem as storage backend (non production use only)",
47+
help_template = "\
48+
{about-section}
49+
{all-args}
50+
"
4751
)]
4852
pub struct FSConfig {
49-
#[arg(env = "P_FS_PATH", value_name = "filesystem path")]
53+
#[arg(
54+
env = "P_FS_PATH",
55+
value_name = "filesystem path",
56+
default_value = "./data"
57+
)]
5058
root: PathBuf,
5159
}
5260

0 commit comments

Comments
 (0)