Skip to content

Commit

Permalink
Merge pull request #169 from 56quarters/env
Browse files Browse the repository at this point in the history
Add support for setting arguments using environment variables
  • Loading branch information
56quarters authored Jul 25, 2024
2 parents 72f1f88 + 3e9088d commit 0cd428c
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 44 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## v0.13.0 - unreleased

- Add support for using environment variables to set all optional arguments
of the `mtop`, `mc`, and `dns` binaries. #169

## v0.12.1 - 2024-07-24

- Document architecture and limitations of internal DNS resolver. #166
Expand Down
2 changes: 1 addition & 1 deletion mtop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ keywords = ["top", "memcached"]
edition = "2021"

[dependencies]
clap = { version = "4.1.8", default-features = false, features = ["cargo", "derive", "help", "error-context", "std", "string", "usage", "wrap_help"] }
clap = { version = "4.1.8", default-features = false, features = ["cargo", "derive", "env", "help", "error-context", "std", "string", "usage", "wrap_help"] }
crossterm = "0.27.0"
mtop-client = { path = "../mtop-client", version = "0.12.1" }
rand = "0.8.5"
Expand Down
6 changes: 2 additions & 4 deletions mtop/src/bin/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::runtime::Handle;
use tracing::{Instrument, Level};

const DEFAULT_LOG_LEVEL: Level = Level::INFO;
const DEFAULT_RECORD_TYPE: RecordType = RecordType::A;
const DEFAULT_RECORD_CLASS: RecordClass = RecordClass::INET;
const DEFAULT_PING_INTERVAL_SECS: f64 = 1.0;
const MIN_PING_INTERVAL_SECS: f64 = 0.01;

/// dns: Make DNS queries or read/write binary format DNS messages
Expand All @@ -26,7 +24,7 @@ const MIN_PING_INTERVAL_SECS: f64 = 0.01;
struct DnsConfig {
/// Logging verbosity. Allowed values are 'trace', 'debug', 'info', 'warn', and 'error'
/// (case-insensitive).
#[arg(long, default_value_t = DEFAULT_LOG_LEVEL)]
#[arg(long, default_value_t = Level::INFO)]
log_level: Level,

/// Output pprof protobuf profile data to this file if profiling support was enabled
Expand All @@ -50,7 +48,7 @@ enum Action {
#[derive(Debug, Args)]
struct PingCommand {
/// How often to run queries, in seconds. Fractional seconds are allowed.
#[arg(long, value_parser = parse_interval, default_value_t = DEFAULT_PING_INTERVAL_SECS)]
#[arg(long, value_parser = parse_interval, default_value_t = 1.0)]
interval_secs: f64,

/// Stop after performing `count` queries. Default is to run until interrupted.
Expand Down
43 changes: 19 additions & 24 deletions mtop/src/bin/mc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,64 +17,59 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader, BufWriter};
use tokio::runtime::Handle;
use tracing::{Instrument, Level};

const DEFAULT_LOG_LEVEL: Level = Level::INFO;
const DEFAULT_HOST: &str = "localhost:11211";
const DEFAULT_TIMEOUT_SECS: u64 = 30;
const DEFAULT_CONNECTIONS_PER_HOST: u64 = 4;

/// mc: memcached command line utility
#[derive(Debug, Parser)]
#[command(name = "mc", version = clap::crate_version!())]
struct McConfig {
/// Logging verbosity. Allowed values are 'trace', 'debug', 'info', 'warn', and 'error'
/// (case-insensitive).
#[arg(long, default_value_t = DEFAULT_LOG_LEVEL)]
#[arg(long, env = "MC_LOG_LEVEL", default_value_t = Level::INFO)]
log_level: Level,

/// Path to resolv.conf file for loading DNS configuration information. If this file
/// can't be loaded, default values for DNS configuration are used instead.
#[arg(long, default_value = default_resolv_conf().into_os_string(), value_hint = ValueHint::FilePath)]
#[arg(long, env = "MC_RESOLV_CONF", default_value = default_resolv_conf().into_os_string(), value_hint = ValueHint::FilePath)]
resolv_conf: PathBuf,

/// Memcached host to connect to in the form 'hostname:port'.
#[arg(long, default_value_t = DEFAULT_HOST.to_owned(), value_hint = ValueHint::Hostname)]
#[arg(long, env = "MC_HOST", default_value = "localhost:11211", value_hint = ValueHint::Hostname)]
host: String,

/// Timeout for Memcached network operations, in seconds.
#[arg(long, default_value_t = DEFAULT_TIMEOUT_SECS)]
#[arg(long, env = "MC_TIMEOUT_SECS", default_value_t = 30)]
timeout_secs: u64,

/// Maximum number of idle connections to maintain per host.
#[arg(long, default_value_t = DEFAULT_CONNECTIONS_PER_HOST)]
#[arg(long, env = "MC_CONNECTIONS", default_value_t = 4)]
connections: u64,

/// Output pprof protobuf profile data to this file if profiling support was enabled
/// at build time.
#[arg(long, value_hint = ValueHint::FilePath)]
#[arg(long, env = "MC_PROFILE_OUTPUT", value_hint = ValueHint::FilePath)]
profile_output: Option<PathBuf>,

/// Enable TLS connections to the Memcached server.
#[arg(long)]
#[arg(long, env = "MC_TLS_ENABLED")]
tls_enabled: bool,

/// Optional certificate authority to use for validating the server certificate instead of
/// the default root certificates.
#[arg(long, value_hint = ValueHint::FilePath)]
#[arg(long, env = "MC_TLS_CA", value_hint = ValueHint::FilePath)]
tls_ca: Option<PathBuf>,

/// Optional server name to use for validating the server certificate. If not set, the
/// hostname of the server is used for checking that the certificate matches the server.
#[arg(long, value_parser = parse_server_name)]
#[arg(long, env = "MC_TLS_SERVER_NAME", value_parser = parse_server_name)]
tls_server_name: Option<ServerName<'static>>,

/// Optional client certificate to use to authenticate with the Memcached server. Note that
/// this may or may not be required based on how the Memcached server is configured.
#[arg(long, requires = "tls_key", value_hint = ValueHint::FilePath)]
#[arg(long, env = "MC_TLS_CERT", requires = "tls_key", value_hint = ValueHint::FilePath)]
tls_cert: Option<PathBuf>,

/// Optional client key to use to authenticate with the Memcached server. Note that this may
/// or may not be required based on how the Memcached server is configured.
#[arg(long, requires = "tls_cert", value_hint = ValueHint::FilePath)]
#[arg(long, env = "MC_TLS_KEY", requires = "tls_cert", value_hint = ValueHint::FilePath)]
tls_key: Option<PathBuf>,

#[command(subcommand)]
Expand Down Expand Up @@ -134,21 +129,21 @@ struct AddCommand {
#[derive(Debug, Args)]
struct BenchCommand {
/// How long to run the benchmark for in seconds.
#[arg(long, default_value_t = 60)]
#[arg(long, env = "MC_BENCH_TIME_SECS", default_value_t = 60)]
time_secs: u64,

/// How many writes to the cache as a percentage of reads from the cache, 0 to 1.
///
/// A value of `1.0` means that for 100 gets, there will be 100 sets. A value of `0.5`
/// means that for 100 gets, there will be 50 sets. Default is to perform many more gets
/// than sets since cache workloads tend to have more reads than writes.
#[arg(long, default_value_t = Percent::unchecked(0.05))]
#[arg(long, env = "MC_BENCH_WRITE_PERCENT", default_value_t = Percent::unchecked(0.05))]
write_percent: Percent,

/// How many workers to run at once, performing gets and sets against the cache.
///
/// Each worker does 10,000 gets and 500 sets per second in the default configuration.
#[arg(long, default_value_t = 1)]
#[arg(long, env = "MC_BENCH_CONCURRENCY", default_value_t = 1)]
concurrency: usize,

/// How long to wait between each batch of gets and sets performed against the cache.
Expand All @@ -157,11 +152,11 @@ struct BenchCommand {
/// there will be 10,000 gets and 500 sets per second. To increase the number of gets
/// and sets performed by a worker, reduce this number. To decrease the number of gets
/// and sets performed by a worker, increase this number.
#[arg(long, default_value_t = 100)]
#[arg(long, env = "MC_BENCH_DELAY_MILLIS", default_value_t = 100)]
delay_millis: u64,

/// TTL to use for test values stored in the cache in seconds.
#[arg(long, default_value_t = 300)]
#[arg(long, env = "MC_BENCH_TTL_SECS", default_value_t = 300)]
ttl_secs: u32,
}

Expand All @@ -174,11 +169,11 @@ struct BenchCommand {
#[derive(Debug, Args)]
struct CheckCommand {
/// How long to run the checks for in seconds.
#[arg(long, default_value_t = 60)]
#[arg(long, env = "MC_CHECK_TIME_SECS", default_value_t = 60)]
time_secs: u64,

/// How long to wait between each health check in milliseconds.
#[arg(long, default_value_t = 100)]
#[arg(long, env = "MC_CHECK_DELAY_MILLIS", default_value_t = 100)]
delay_millis: u64,
}

Expand Down Expand Up @@ -232,7 +227,7 @@ struct IncrCommand {
struct KeysCommand {
/// Print key name, expiration as a UNIX timestamp, and value size in bytes as tab separated
/// values instead of only the key name.
#[arg(long)]
#[arg(long, env = "MC_KEYS_DETAILS")]
details: bool,
}

Expand Down
27 changes: 12 additions & 15 deletions mtop/src/bin/mtop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@ use tokio::task;
use tracing::instrument::WithSubscriber;
use tracing::{Instrument, Level};

const DEFAULT_LOG_LEVEL: Level = Level::INFO;
const DEFAULT_THEME: Theme = TAILWIND;
// Update interval of more than a second to minimize the chance that stats returned by the
// memcached server have the exact same "time" value (which has one-second granularity).
const DEFAULT_STATS_INTERVAL: Duration = Duration::from_millis(1073);
const DEFAULT_TIMEOUT_SECS: u64 = 5;
const STATS_INTERVAL: Duration = Duration::from_millis(1073);
const NUM_MEASUREMENTS: usize = 10;

/// mtop: top for memcached
Expand All @@ -29,49 +26,49 @@ const NUM_MEASUREMENTS: usize = 10;
struct MtopConfig {
/// Logging verbosity. Allowed values are 'trace', 'debug', 'info', 'warn', and 'error'
/// (case-insensitive).
#[arg(long, default_value_t = DEFAULT_LOG_LEVEL)]
#[arg(long, env = "MTOP_LOG_LEVEL", default_value_t = Level::INFO)]
log_level: Level,

/// Path to resolv.conf file for loading DNS configuration information. If this file
/// can't be loaded, default values for DNS configuration are used instead.
#[arg(long, default_value = default_resolv_conf().into_os_string(), value_hint = ValueHint::FilePath)]
#[arg(long, env = "MTOP_RESOLV_CONF", default_value = default_resolv_conf().into_os_string(), value_hint = ValueHint::FilePath)]
resolv_conf: PathBuf,

/// Timeout for connecting to Memcached and fetching statistics, in seconds.
#[arg(long, default_value_t = DEFAULT_TIMEOUT_SECS)]
#[arg(long, env = "MTOP_TIMEOUT_SECS", default_value_t = 5)]
timeout_secs: u64,

/// File to log errors to since they cannot be logged to the console. If the path is not
/// writable, mtop will not start.
#[arg(long, default_value = default_log_file().into_os_string(), value_hint = ValueHint::FilePath)]
#[arg(long, env = "MTOP_LOG_FILE", default_value = default_log_file().into_os_string(), value_hint = ValueHint::FilePath)]
log_file: PathBuf,

/// Color scheme to use for the UI. Available options are "ansi", "material", and "tailwind".
#[arg(long, default_value_t = DEFAULT_THEME)]
#[arg(long, env = "MTOP_THEME", default_value_t = TAILWIND)]
theme: Theme,

/// Enable TLS connections to the Memcached server.
#[arg(long)]
#[arg(long, env = "MTOP_TLS_ENABLED")]
tls_enabled: bool,

/// Optional certificate authority to use for validating the server certificate instead of
/// the default root certificates.
#[arg(long, value_hint = ValueHint::FilePath)]
#[arg(long, env = "MTOP_TLS_CA", value_hint = ValueHint::FilePath)]
tls_ca: Option<PathBuf>,

/// Optional server name to use for validating the server certificate. If not set, the
/// hostname of the server is used for checking that the certificate matches the server.
#[arg(long, value_parser = parse_server_name)]
#[arg(long, env = "MTOP_TLS_SERVER_NAME", value_parser = parse_server_name)]
tls_server_name: Option<ServerName<'static>>,

/// Optional client certificate to use to authenticate with the Memcached server. Note that
/// this may or may not be required based on how the Memcached server is configured.
#[arg(long, requires = "tls_key", value_hint = ValueHint::FilePath)]
#[arg(long, env = "MTOP_TLS_CERT", requires = "tls_key", value_hint = ValueHint::FilePath)]
tls_cert: Option<PathBuf>,

/// Optional client key to use to authenticate with the Memcached server. Note that this may
/// or may not be required based on how the Memcached server is configured.
#[arg(long, requires = "tls_cert", value_hint = ValueHint::FilePath)]
#[arg(long, env = "MTOP_TLS_KEY", requires = "tls_cert", value_hint = ValueHint::FilePath)]
tls_key: Option<PathBuf>,

/// Memcached hosts to connect to in the form 'hostname:port'. Must be specified at least
Expand Down Expand Up @@ -150,7 +147,7 @@ async fn main() -> ExitCode {

task::spawn(
async move {
let mut interval = tokio::time::interval(DEFAULT_STATS_INTERVAL);
let mut interval = tokio::time::interval(STATS_INTERVAL);
loop {
let _ = interval.tick().await;
if let Err(e) = update_task
Expand Down
13 changes: 13 additions & 0 deletions tools/tls-env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# no shebang, source this file

# mtop
export MTOP_TLS_ENABLED=true
export MTOP_TLS_CA=memcached-ca-cert.pem
export MTOP_TLS_CERT=memcached-client-cert.pem
export MTOP_TLS_KEY=memcached-client-key.pem

# mc
export MC_TLS_ENABLED=true
export MC_TLS_CA=memcached-ca-cert.pem
export MC_TLS_CERT=memcached-client-cert.pem
export MC_TLS_KEY=memcached-client-key.pem

0 comments on commit 0cd428c

Please sign in to comment.