Skip to content

Commit 1309571

Browse files
authored
proxy: switch to structopt for clap parsing (#4714)
Using `#[clap]` for parsing cli opts, which is easier to maintain. --------- Signed-off-by: Alex Chi Z <[email protected]>
1 parent 9a69b6c commit 1309571

File tree

1 file changed

+84
-135
lines changed

1 file changed

+84
-135
lines changed

proxy/src/bin/proxy.rs

Lines changed: 84 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use proxy::http;
55
use proxy::metrics;
66

77
use anyhow::bail;
8-
use clap::{self, Arg};
98
use proxy::config::{self, ProxyConfig};
109
use std::pin::pin;
1110
use std::{borrow::Cow, net::SocketAddr};
@@ -18,6 +17,70 @@ use utils::{project_git_version, sentry_init::init_sentry};
1817

1918
project_git_version!(GIT_VERSION);
2019

20+
use clap::{Parser, ValueEnum};
21+
22+
#[derive(Clone, Debug, ValueEnum)]
23+
enum AuthBackend {
24+
Console,
25+
Postgres,
26+
Link,
27+
}
28+
29+
/// Neon proxy/router
30+
#[derive(Parser)]
31+
#[command(version = GIT_VERSION, about)]
32+
struct ProxyCliArgs {
33+
/// listen for incoming client connections on ip:port
34+
#[clap(short, long, default_value = "127.0.0.1:4432")]
35+
proxy: String,
36+
#[clap(value_enum, long, default_value_t = AuthBackend::Link)]
37+
auth_backend: AuthBackend,
38+
/// listen for management callback connection on ip:port
39+
#[clap(short, long, default_value = "127.0.0.1:7000")]
40+
mgmt: String,
41+
/// listen for incoming http connections (metrics, etc) on ip:port
42+
#[clap(long, default_value = "127.0.0.1:7001")]
43+
http: String,
44+
/// listen for incoming wss connections on ip:port
45+
#[clap(long)]
46+
wss: Option<String>,
47+
/// redirect unauthenticated users to the given uri in case of link auth
48+
#[clap(short, long, default_value = "http://localhost:3000/psql_session/")]
49+
uri: String,
50+
/// cloud API endpoint for authenticating users
51+
#[clap(
52+
short,
53+
long,
54+
default_value = "http://localhost:3000/authenticate_proxy_request/"
55+
)]
56+
auth_endpoint: String,
57+
/// path to TLS key for client postgres connections
58+
///
59+
/// tls-key and tls-cert are for backwards compatibility, we can put all certs in one dir
60+
#[clap(short = 'k', long, alias = "ssl-key")]
61+
tls_key: Option<String>,
62+
/// path to TLS cert for client postgres connections
63+
///
64+
/// tls-key and tls-cert are for backwards compatibility, we can put all certs in one dir
65+
#[clap(short = 'c', long, alias = "ssl-cert")]
66+
tls_cert: Option<String>,
67+
/// path to directory with TLS certificates for client postgres connections
68+
#[clap(long)]
69+
certs_dir: Option<String>,
70+
/// http endpoint to receive periodic metric updates
71+
#[clap(long)]
72+
metric_collection_endpoint: Option<String>,
73+
/// how often metrics should be sent to a collection endpoint
74+
#[clap(long)]
75+
metric_collection_interval: Option<String>,
76+
/// cache for `wake_compute` api method (use `size=0` to disable)
77+
#[clap(long, default_value = config::CacheOptions::DEFAULT_OPTIONS_NODE_INFO)]
78+
wake_compute_cache: String,
79+
/// Allow self-signed certificates for compute nodes (for testing)
80+
#[clap(long, default_value_t = false, value_parser = clap::builder::BoolishValueParser::new(), action = clap::ArgAction::Set)]
81+
allow_self_signed_compute: bool,
82+
}
83+
2184
#[tokio::main]
2285
async fn main() -> anyhow::Result<()> {
2386
let _logging_guard = proxy::logging::init().await?;
@@ -27,21 +90,21 @@ async fn main() -> anyhow::Result<()> {
2790
info!("Version: {GIT_VERSION}");
2891
::metrics::set_build_info_metric(GIT_VERSION);
2992

30-
let args = cli().get_matches();
93+
let args = ProxyCliArgs::parse();
3194
let config = build_config(&args)?;
3295

3396
info!("Authentication backend: {}", config.auth_backend);
3497

3598
// Check that we can bind to address before further initialization
36-
let http_address: SocketAddr = args.get_one::<String>("http").unwrap().parse()?;
99+
let http_address: SocketAddr = args.http.parse()?;
37100
info!("Starting http on {http_address}");
38101
let http_listener = TcpListener::bind(http_address).await?.into_std()?;
39102

40-
let mgmt_address: SocketAddr = args.get_one::<String>("mgmt").unwrap().parse()?;
103+
let mgmt_address: SocketAddr = args.mgmt.parse()?;
41104
info!("Starting mgmt on {mgmt_address}");
42105
let mgmt_listener = TcpListener::bind(mgmt_address).await?;
43106

44-
let proxy_address: SocketAddr = args.get_one::<String>("proxy").unwrap().parse()?;
107+
let proxy_address: SocketAddr = args.proxy.parse()?;
45108
info!("Starting proxy on {proxy_address}");
46109
let proxy_listener = TcpListener::bind(proxy_address).await?;
47110
let cancellation_token = CancellationToken::new();
@@ -55,7 +118,7 @@ async fn main() -> anyhow::Result<()> {
55118
cancellation_token.clone(),
56119
));
57120

58-
if let Some(wss_address) = args.get_one::<String>("wss") {
121+
if let Some(wss_address) = args.wss {
59122
let wss_address: SocketAddr = wss_address.parse()?;
60123
info!("Starting wss on {wss_address}");
61124
let wss_listener = TcpListener::bind(wss_address).await?;
@@ -102,31 +165,24 @@ async fn main() -> anyhow::Result<()> {
102165
}
103166

104167
/// ProxyConfig is created at proxy startup, and lives forever.
105-
fn build_config(args: &clap::ArgMatches) -> anyhow::Result<&'static ProxyConfig> {
106-
let tls_config = match (
107-
args.get_one::<String>("tls-key"),
108-
args.get_one::<String>("tls-cert"),
109-
) {
168+
fn build_config(args: &ProxyCliArgs) -> anyhow::Result<&'static ProxyConfig> {
169+
let tls_config = match (&args.tls_key, &args.tls_cert) {
110170
(Some(key_path), Some(cert_path)) => Some(config::configure_tls(
111171
key_path,
112172
cert_path,
113-
args.get_one::<String>("certs-dir"),
173+
args.certs_dir.as_ref(),
114174
)?),
115175
(None, None) => None,
116176
_ => bail!("either both or neither tls-key and tls-cert must be specified"),
117177
};
118178

119-
let allow_self_signed_compute: bool = args
120-
.get_one::<String>("allow-self-signed-compute")
121-
.unwrap()
122-
.parse()?;
123-
if allow_self_signed_compute {
179+
if args.allow_self_signed_compute {
124180
warn!("allowing self-signed compute certificates");
125181
}
126182

127183
let metric_collection = match (
128-
args.get_one::<String>("metric-collection-endpoint"),
129-
args.get_one::<String>("metric-collection-interval"),
184+
&args.metric_collection_endpoint,
185+
&args.metric_collection_interval,
130186
) {
131187
(Some(endpoint), Some(interval)) => Some(config::MetricCollectionConfig {
132188
endpoint: endpoint.parse()?,
@@ -139,145 +195,38 @@ fn build_config(args: &clap::ArgMatches) -> anyhow::Result<&'static ProxyConfig>
139195
),
140196
};
141197

142-
let auth_backend = match args.get_one::<String>("auth-backend").unwrap().as_str() {
143-
"console" => {
144-
let config::CacheOptions { size, ttl } = args
145-
.get_one::<String>("wake-compute-cache")
146-
.unwrap()
147-
.parse()?;
198+
let auth_backend = match &args.auth_backend {
199+
AuthBackend::Console => {
200+
let config::CacheOptions { size, ttl } = args.wake_compute_cache.parse()?;
148201

149202
info!("Using NodeInfoCache (wake_compute) with size={size} ttl={ttl:?}");
150203
let caches = Box::leak(Box::new(console::caches::ApiCaches {
151204
node_info: console::caches::NodeInfoCache::new("node_info_cache", size, ttl),
152205
}));
153206

154-
let url = args.get_one::<String>("auth-endpoint").unwrap().parse()?;
207+
let url = args.auth_endpoint.parse()?;
155208
let endpoint = http::Endpoint::new(url, http::new_client());
156209

157210
let api = console::provider::neon::Api::new(endpoint, caches);
158211
auth::BackendType::Console(Cow::Owned(api), ())
159212
}
160-
"postgres" => {
161-
let url = args.get_one::<String>("auth-endpoint").unwrap().parse()?;
213+
AuthBackend::Postgres => {
214+
let url = args.auth_endpoint.parse()?;
162215
let api = console::provider::mock::Api::new(url);
163216
auth::BackendType::Postgres(Cow::Owned(api), ())
164217
}
165-
"link" => {
166-
let url = args.get_one::<String>("uri").unwrap().parse()?;
218+
AuthBackend::Link => {
219+
let url = args.uri.parse()?;
167220
auth::BackendType::Link(Cow::Owned(url))
168221
}
169-
other => bail!("unsupported auth backend: {other}"),
170222
};
171223

172224
let config = Box::leak(Box::new(ProxyConfig {
173225
tls_config,
174226
auth_backend,
175227
metric_collection,
176-
allow_self_signed_compute,
228+
allow_self_signed_compute: args.allow_self_signed_compute,
177229
}));
178230

179231
Ok(config)
180232
}
181-
182-
fn cli() -> clap::Command {
183-
clap::Command::new("Neon proxy/router")
184-
.disable_help_flag(true)
185-
.version(GIT_VERSION)
186-
.arg(
187-
Arg::new("proxy")
188-
.short('p')
189-
.long("proxy")
190-
.help("listen for incoming client connections on ip:port")
191-
.default_value("127.0.0.1:4432"),
192-
)
193-
.arg(
194-
Arg::new("auth-backend")
195-
.long("auth-backend")
196-
.value_parser(["console", "postgres", "link"])
197-
.default_value("link"),
198-
)
199-
.arg(
200-
Arg::new("mgmt")
201-
.short('m')
202-
.long("mgmt")
203-
.help("listen for management callback connection on ip:port")
204-
.default_value("127.0.0.1:7000"),
205-
)
206-
.arg(
207-
Arg::new("http")
208-
.long("http")
209-
.help("listen for incoming http connections (metrics, etc) on ip:port")
210-
.default_value("127.0.0.1:7001"),
211-
)
212-
.arg(
213-
Arg::new("wss")
214-
.long("wss")
215-
.help("listen for incoming wss connections on ip:port"),
216-
)
217-
.arg(
218-
Arg::new("uri")
219-
.short('u')
220-
.long("uri")
221-
.help("redirect unauthenticated users to the given uri in case of link auth")
222-
.default_value("http://localhost:3000/psql_session/"),
223-
)
224-
.arg(
225-
Arg::new("auth-endpoint")
226-
.short('a')
227-
.long("auth-endpoint")
228-
.help("cloud API endpoint for authenticating users")
229-
.default_value("http://localhost:3000/authenticate_proxy_request/"),
230-
)
231-
.arg(
232-
Arg::new("tls-key")
233-
.short('k')
234-
.long("tls-key")
235-
.alias("ssl-key") // backwards compatibility
236-
.help("path to TLS key for client postgres connections"),
237-
)
238-
.arg(
239-
Arg::new("tls-cert")
240-
.short('c')
241-
.long("tls-cert")
242-
.alias("ssl-cert") // backwards compatibility
243-
.help("path to TLS cert for client postgres connections"),
244-
)
245-
// tls-key and tls-cert are for backwards compatibility, we can put all certs in one dir
246-
.arg(
247-
Arg::new("certs-dir")
248-
.long("certs-dir")
249-
.help("path to directory with TLS certificates for client postgres connections"),
250-
)
251-
.arg(
252-
Arg::new("metric-collection-endpoint")
253-
.long("metric-collection-endpoint")
254-
.help("http endpoint to receive periodic metric updates"),
255-
)
256-
.arg(
257-
Arg::new("metric-collection-interval")
258-
.long("metric-collection-interval")
259-
.help("how often metrics should be sent to a collection endpoint"),
260-
)
261-
.arg(
262-
Arg::new("wake-compute-cache")
263-
.long("wake-compute-cache")
264-
.help("cache for `wake_compute` api method (use `size=0` to disable)")
265-
.default_value(config::CacheOptions::DEFAULT_OPTIONS_NODE_INFO),
266-
)
267-
.arg(
268-
Arg::new("allow-self-signed-compute")
269-
.long("allow-self-signed-compute")
270-
.help("Allow self-signed certificates for compute nodes (for testing)")
271-
.default_value("false"),
272-
)
273-
}
274-
275-
#[cfg(test)]
276-
mod tests {
277-
use super::*;
278-
279-
#[test]
280-
fn verify_cli() {
281-
cli().debug_assert();
282-
}
283-
}

0 commit comments

Comments
 (0)