Skip to content

Commit 9c0862d

Browse files
authored
Add demo flag for config (#149)
Parseable is demo mode by default. After moving to clap 4.0 it became possible to add a demo mode flag which will run Parseable with default credential and storage configuration. Demo flag is exclusive and cannot be used with other configuration options. A warning is printed when one is running with `--demo` flag changes - unify s3 config to main config as sub argument - add demo flag - default arguments only in demo mode Fixes #130
1 parent f3fffb9 commit 9c0862d

File tree

2 files changed

+93
-97
lines changed

2 files changed

+93
-97
lines changed

server/src/option.rs

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

19+
use clap::builder::ArgPredicate;
1920
use clap::Parser;
2021
use crossterm::style::Stylize;
2122
use std::path::PathBuf;
@@ -27,42 +28,45 @@ use crate::storage::{ObjectStorage, ObjectStorageError, LOCAL_SYNC_INTERVAL};
2728

2829
lazy_static::lazy_static! {
2930
#[derive(Debug)]
30-
pub static ref CONFIG: Arc<Config> = {
31-
let storage = Box::new(S3Config::parse());
32-
Arc::new(Config::new(storage))
33-
};
31+
pub static ref CONFIG: Arc<Config<S3Config>> = Arc::new(Config::new());
3432
}
3533

3634
pub const USERNAME_ENV: &str = "P_USERNAME";
37-
pub const PASSOWRD_ENV: &str = "P_PASSWORD";
35+
pub const PASSWORD_ENV: &str = "P_PASSWORD";
3836
pub const DEFAULT_USERNAME: &str = "parseable";
3937
pub const DEFAULT_PASSWORD: &str = "parseable";
4038

4139
pub trait StorageOpt: Sync + Send {
4240
fn bucket_name(&self) -> &str;
4341
fn endpoint_url(&self) -> &str;
44-
fn warning(&self);
45-
fn is_default_url(&self) -> bool;
4642
}
4743

48-
pub struct Config {
49-
pub parseable: Opt,
50-
pub storage: Box<dyn StorageOpt>,
44+
pub struct Config<S>
45+
where
46+
S: Clone + clap::Args + StorageOpt,
47+
{
48+
pub parseable: Opt<S>,
5149
}
5250

53-
impl Config {
54-
fn new(storage: Box<dyn StorageOpt>) -> Config {
51+
impl<S> Config<S>
52+
where
53+
S: Clone + clap::Args + StorageOpt,
54+
{
55+
fn new() -> Self {
5556
Config {
56-
parseable: Opt::parse(),
57-
storage,
57+
parseable: Opt::<S>::parse(),
5858
}
5959
}
6060

61+
pub fn storage(&self) -> &S {
62+
&self.parseable.objectstore_config
63+
}
64+
6165
pub fn print(&self) {
6266
let scheme = CONFIG.parseable.get_scheme();
6367
self.status_info(&scheme);
6468
banner::version::print();
65-
self.warning();
69+
self.demo();
6670
self.storage_info();
6771
banner::system_info();
6872
println!();
@@ -80,11 +84,11 @@ impl Config {
8084
Err(ObjectStorageError::NoSuchBucket(name)) => panic!(
8185
"Could not start because the bucket doesn't exist. Please ensure bucket {bucket} exists on {url}",
8286
bucket = name,
83-
url = self.storage.endpoint_url()
87+
url = self.storage().endpoint_url()
8488
),
8589
Err(ObjectStorageError::ConnectionError(inner)) => panic!(
8690
"Failed to connect to the Object Storage Service on {url}\nCaused by: {cause}",
87-
url = self.storage.endpoint_url(),
91+
url = self.storage().endpoint_url(),
8892
cause = inner
8993
),
9094
Err(ObjectStorageError::AuthenticationError(inner)) => panic!(
@@ -96,15 +100,15 @@ impl Config {
96100
}
97101

98102
fn status_info(&self, scheme: &str) {
99-
let url = format!("{}://{}", scheme, CONFIG.parseable.address).underlined();
103+
let url = format!("{}://{}", scheme, self.parseable.address).underlined();
100104
eprintln!(
101105
"
102106
{}
103107
{}
104108
{}",
105109
format!("Parseable server started at: {}", url).bold(),
106-
format!("Username: {}", CONFIG.parseable.username).bold(),
107-
format!("Password: {}", CONFIG.parseable.password).bold(),
110+
format!("Username: {}", self.parseable.username).bold(),
111+
format!("Password: {}", self.parseable.password).bold(),
108112
)
109113
}
110114

@@ -116,60 +120,39 @@ impl Config {
116120
Object Storage: {}/{}",
117121
"Storage:".to_string().blue().bold(),
118122
self.parseable.local_disk_path.to_string_lossy(),
119-
self.storage.endpoint_url(),
120-
self.storage.bucket_name()
123+
self.storage().endpoint_url(),
124+
self.storage().bucket_name()
121125
)
122126
}
123127

124-
fn warning(&self) {
125-
match (self.storage.is_default_url(), self.is_default_cred()) {
126-
(true, true) => {
127-
banner::warning_line();
128-
self.cred_warning();
129-
self.storage.warning();
130-
}
131-
(true, _) => {
132-
banner::warning_line();
133-
self.storage.warning();
134-
}
135-
(_, true) => {
136-
banner::warning_line();
137-
self.cred_warning();
138-
}
139-
_ => {}
140-
}
141-
}
142-
143-
fn is_default_cred(&self) -> bool {
144-
CONFIG.parseable.username == DEFAULT_USERNAME
145-
&& CONFIG.parseable.password == DEFAULT_PASSWORD
146-
}
147-
148-
fn cred_warning(&self) {
149-
if self.is_default_cred() {
128+
fn demo(&self) {
129+
if self.is_demo() {
130+
banner::warning_line();
150131
eprintln!(
151132
"
152-
{}
153133
{}",
154-
"Parseable server is using default credentials."
134+
"Parseable is in demo mode with default credentials and open object store. Please use this for demo purposes only."
155135
.to_string()
156136
.red(),
157-
format!(
158-
"Setup your credentials with {} and {} before storing production logs.",
159-
USERNAME_ENV, PASSOWRD_ENV
160137
)
161-
.red()
162-
)
163138
}
164139
}
140+
141+
fn is_demo(&self) -> bool {
142+
self.parseable.demo
143+
}
165144
}
166145

167146
#[derive(Debug, Clone, Parser)]
168147
#[command(
169-
name = "Parseable config",
170-
about = "configuration for Parseable server"
148+
name = "Parseable",
149+
about = "Configuration for Parseable server",
150+
version
171151
)]
172-
pub struct Opt {
152+
pub struct Opt<S>
153+
where
154+
S: Clone + clap::Args + StorageOpt,
155+
{
173156
/// The location of TLS Cert file
174157
#[arg(long, env = "P_TLS_CERT_PATH")]
175158
pub tls_cert_path: Option<PathBuf>,
@@ -194,15 +177,33 @@ pub struct Opt {
194177
pub upload_interval: u64,
195178

196179
/// Optional username to enable basic auth on the server
197-
#[arg(long, env = USERNAME_ENV, default_value = DEFAULT_USERNAME)]
180+
#[arg(
181+
long,
182+
env = USERNAME_ENV,
183+
default_value_if("demo", ArgPredicate::IsPresent, DEFAULT_USERNAME)
184+
)]
198185
pub username: String,
199186

200187
/// Optional password to enable basic auth on the server
201-
#[arg(long, env = PASSOWRD_ENV, default_value = DEFAULT_PASSWORD)]
188+
#[arg(
189+
long,
190+
env = PASSWORD_ENV,
191+
default_value_if("demo", ArgPredicate::IsPresent, DEFAULT_PASSWORD)
192+
)]
202193
pub password: String,
194+
195+
#[clap(flatten)]
196+
pub objectstore_config: S,
197+
198+
/// Run Parseable in demo mode with default credentials and open object store
199+
#[arg(short, long, exclusive = true)]
200+
pub demo: bool,
203201
}
204202

205-
impl Opt {
203+
impl<S> Opt<S>
204+
where
205+
S: Clone + clap::Args + StorageOpt,
206+
{
206207
pub fn get_cache_path(&self, stream_name: &str) -> PathBuf {
207208
self.local_disk_path.join(stream_name)
208209
}

server/src/s3.rs

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use aws_sdk_s3::RetryConfig;
77
use aws_sdk_s3::{Client, Credentials, Endpoint, Region};
88
use aws_smithy_async::rt::sleep::default_async_sleep;
99
use bytes::Bytes;
10-
use clap::Parser;
11-
use crossterm::style::Stylize;
10+
use clap::builder::ArgPredicate;
1211
use datafusion::arrow::datatypes::Schema;
1312
use datafusion::arrow::record_batch::RecordBatch;
1413
use datafusion::datasource::file_format::parquet::ParquetFormat;
@@ -42,8 +41,6 @@ const DEFAULT_S3_BUCKET: &str = "parseable";
4241
const DEFAULT_S3_ACCESS_KEY: &str = "minioadmin";
4342
const DEFAULT_S3_SECRET_KEY: &str = "minioadmin";
4443

45-
const S3_URL_ENV_VAR: &str = "P_S3_URL";
46-
4744
// max concurrent request allowed for datafusion object store
4845
const MAX_OBJECT_STORE_REQUESTS: usize = 1000;
4946

@@ -63,13 +60,13 @@ impl ObjectStoreFormat {
6360

6461
lazy_static::lazy_static! {
6562
#[derive(Debug)]
66-
pub static ref S3_CONFIG: Arc<S3Config> = Arc::new(S3Config::parse());
63+
pub static ref S3_CONFIG: Arc<S3Config> = Arc::new(CONFIG.storage().clone());
6764

6865
// runtime to be used in query session
6966
pub static ref STORAGE_RUNTIME: Arc<RuntimeEnv> = {
7067

7168
let s3 = AmazonS3Builder::new()
72-
.with_region(&S3_CONFIG.s3_default_region)
69+
.with_region(&S3_CONFIG.s3_region)
7370
.with_endpoint(&S3_CONFIG.s3_endpoint_url)
7471
.with_bucket_name(&S3_CONFIG.s3_bucket_name)
7572
.with_access_key_id(&S3_CONFIG.s3_access_key_id)
@@ -94,27 +91,47 @@ lazy_static::lazy_static! {
9491
};
9592
}
9693

97-
#[derive(Debug, Clone, Parser)]
94+
#[derive(Debug, Clone, clap::Args)]
9895
#[command(name = "S3 config", about = "configuration for AWS S3 SDK")]
9996
pub struct S3Config {
10097
/// The endpoint to AWS S3 or compatible object storage platform
101-
#[arg(long, env = S3_URL_ENV_VAR, default_value = DEFAULT_S3_URL )]
98+
#[arg(
99+
long,
100+
env = "P_S3_URL",
101+
default_value_if("demo", ArgPredicate::IsPresent, DEFAULT_S3_URL)
102+
)]
102103
pub s3_endpoint_url: String,
103104

104105
/// The access key for AWS S3 or compatible object storage platform
105-
#[arg(long, env = "P_S3_ACCESS_KEY", default_value = DEFAULT_S3_ACCESS_KEY)]
106+
#[arg(
107+
long,
108+
env = "P_S3_ACCESS_KEY",
109+
default_value_if("demo", ArgPredicate::IsPresent, DEFAULT_S3_ACCESS_KEY)
110+
)]
106111
pub s3_access_key_id: String,
107112

108113
/// The secret key for AWS S3 or compatible object storage platform
109-
#[arg(long, env = "P_S3_SECRET_KEY", default_value = DEFAULT_S3_SECRET_KEY)]
114+
#[arg(
115+
long,
116+
env = "P_S3_SECRET_KEY",
117+
default_value_if("demo", ArgPredicate::IsPresent, DEFAULT_S3_SECRET_KEY)
118+
)]
110119
pub s3_secret_key: String,
111120

112121
/// The region for AWS S3 or compatible object storage platform
113-
#[arg(long, env = "P_S3_REGION", default_value = DEFAULT_S3_REGION)]
114-
pub s3_default_region: String,
122+
#[arg(
123+
long,
124+
env = "P_S3_REGION",
125+
default_value_if("demo", ArgPredicate::IsPresent, DEFAULT_S3_REGION)
126+
)]
127+
pub s3_region: String,
115128

116129
/// The AWS S3 or compatible object storage bucket to be used for storage
117-
#[arg(long, env = "P_S3_BUCKET", default_value = DEFAULT_S3_BUCKET)]
130+
#[arg(
131+
long,
132+
env = "P_S3_BUCKET",
133+
default_value_if("demo", ArgPredicate::IsPresent, DEFAULT_S3_BUCKET)
134+
)]
118135
pub s3_bucket_name: String,
119136
}
120137

@@ -126,28 +143,6 @@ impl StorageOpt for S3Config {
126143
fn endpoint_url(&self) -> &str {
127144
&self.s3_endpoint_url
128145
}
129-
130-
fn is_default_url(&self) -> bool {
131-
self.s3_endpoint_url == DEFAULT_S3_URL
132-
}
133-
134-
fn warning(&self) {
135-
if self.is_default_url() {
136-
eprintln!(
137-
"
138-
{}
139-
{}",
140-
"Parseable server is using default object storage backend with public access."
141-
.to_string()
142-
.red(),
143-
format!(
144-
"Setup your object storage backend with {} before storing production logs.",
145-
S3_URL_ENV_VAR
146-
)
147-
.red()
148-
)
149-
}
150-
}
151146
}
152147

153148
struct S3Options {
@@ -160,7 +155,7 @@ impl S3Options {
160155
fn new() -> Self {
161156
let uri = S3_CONFIG.s3_endpoint_url.parse::<Uri>().unwrap();
162157
let endpoint = Endpoint::immutable(uri);
163-
let region = Region::new(&S3_CONFIG.s3_default_region);
158+
let region = Region::new(&S3_CONFIG.s3_region);
164159
let creds = Credentials::new(
165160
&S3_CONFIG.s3_access_key_id,
166161
&S3_CONFIG.s3_secret_key,

0 commit comments

Comments
 (0)