2021-05-12 01:01:01 +00:00
|
|
|
use std::fmt::{Display, Formatter};
|
2021-06-16 19:31:03 +00:00
|
|
|
use std::fs::{File, OpenOptions};
|
2021-07-09 21:18:43 +00:00
|
|
|
use std::hint::unreachable_unchecked;
|
2021-06-16 19:31:03 +00:00
|
|
|
use std::io::{ErrorKind, Write};
|
2021-07-09 23:14:53 +00:00
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
|
|
use std::num::NonZeroU16;
|
2021-07-09 21:18:43 +00:00
|
|
|
use std::path::{Path, PathBuf};
|
2021-05-12 01:01:01 +00:00
|
|
|
use std::str::FromStr;
|
2021-07-09 21:18:43 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2021-03-26 01:06:54 +00:00
|
|
|
|
2021-12-02 06:08:40 +00:00
|
|
|
use clap::{crate_authors, crate_description, crate_version, Parser};
|
2021-07-09 21:18:43 +00:00
|
|
|
use log::LevelFilter;
|
2021-07-15 16:29:55 +00:00
|
|
|
use once_cell::sync::OnceCell;
|
2021-06-16 19:31:03 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-07-09 23:48:25 +00:00
|
|
|
use thiserror::Error;
|
2021-07-13 03:23:51 +00:00
|
|
|
use tracing::level_filters::LevelFilter as TracingLevelFilter;
|
2021-04-19 03:06:18 +00:00
|
|
|
use url::Url;
|
2021-03-26 01:06:54 +00:00
|
|
|
|
2021-07-09 21:18:43 +00:00
|
|
|
use crate::units::{KilobitsPerSecond, Mebibytes, Port};
|
2021-06-16 19:31:03 +00:00
|
|
|
|
2021-03-26 02:58:07 +00:00
|
|
|
// Validate tokens is an atomic because it's faster than locking on rwlock.
|
|
|
|
pub static VALIDATE_TOKENS: AtomicBool = AtomicBool::new(false);
|
2021-05-23 03:06:05 +00:00
|
|
|
pub static OFFLINE_MODE: AtomicBool = AtomicBool::new(false);
|
2021-07-15 16:29:55 +00:00
|
|
|
pub static USE_PROXY: OnceCell<Url> = OnceCell::new();
|
|
|
|
pub static DISABLE_CERT_VALIDATION: AtomicBool = AtomicBool::new(false);
|
2021-05-23 03:06:05 +00:00
|
|
|
|
2021-07-09 23:48:25 +00:00
|
|
|
#[derive(Error, Debug)]
|
|
|
|
pub enum ConfigError {
|
|
|
|
#[error("No config found. One has been created for you to modify.")]
|
|
|
|
NotInitialized,
|
|
|
|
#[error(transparent)]
|
|
|
|
Io(#[from] std::io::Error),
|
|
|
|
#[error(transparent)]
|
|
|
|
Parse(#[from] serde_yaml::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_config() -> Result<Config, ConfigError> {
|
2021-07-09 21:18:43 +00:00
|
|
|
// Load cli args first
|
|
|
|
let cli_args: CliArgs = CliArgs::parse();
|
|
|
|
|
|
|
|
// Load yaml file next
|
|
|
|
let config_file: Result<YamlArgs, _> = {
|
|
|
|
let config_path = cli_args
|
|
|
|
.config_path
|
2021-07-09 21:20:15 +00:00
|
|
|
.as_deref()
|
|
|
|
.unwrap_or_else(|| Path::new("./settings.yaml"));
|
2021-07-09 21:18:43 +00:00
|
|
|
match File::open(config_path) {
|
|
|
|
Ok(file) => serde_yaml::from_reader(file),
|
|
|
|
Err(e) if e.kind() == ErrorKind::NotFound => {
|
|
|
|
let mut file = OpenOptions::new()
|
|
|
|
.write(true)
|
|
|
|
.create_new(true)
|
|
|
|
.open(config_path)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let default_config = include_str!("../settings.sample.yaml");
|
|
|
|
file.write_all(default_config.as_bytes()).unwrap();
|
2021-07-09 23:48:25 +00:00
|
|
|
|
|
|
|
return Err(ConfigError::NotInitialized);
|
2021-07-09 21:18:43 +00:00
|
|
|
}
|
2021-07-09 23:48:25 +00:00
|
|
|
Err(e) => return Err(e.into()),
|
2021-06-16 19:31:03 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-09 21:18:43 +00:00
|
|
|
// generate config
|
|
|
|
let config = Config::from_cli_and_file(cli_args, config_file?);
|
|
|
|
|
|
|
|
// initialize globals
|
|
|
|
OFFLINE_MODE.store(
|
|
|
|
config
|
|
|
|
.unstable_options
|
|
|
|
.contains(&UnstableOptions::OfflineMode),
|
|
|
|
Ordering::Release,
|
|
|
|
);
|
|
|
|
|
2021-07-15 23:13:31 +00:00
|
|
|
if let Some(socket) = config.proxy.clone() {
|
2021-07-15 16:29:55 +00:00
|
|
|
USE_PROXY
|
|
|
|
.set(socket)
|
|
|
|
.expect("USE_PROXY to be set only by this function");
|
2021-07-15 23:13:31 +00:00
|
|
|
}
|
2021-07-15 16:29:55 +00:00
|
|
|
|
|
|
|
DISABLE_CERT_VALIDATION.store(
|
|
|
|
config
|
|
|
|
.unstable_options
|
|
|
|
.contains(&UnstableOptions::DisableCertValidation),
|
|
|
|
Ordering::Release,
|
|
|
|
);
|
|
|
|
|
2021-07-09 21:18:43 +00:00
|
|
|
Ok(config)
|
|
|
|
}
|
|
|
|
|
2021-07-09 23:51:48 +00:00
|
|
|
#[derive(Debug)]
|
2021-07-09 23:14:53 +00:00
|
|
|
/// Represents a fully parsed config, from a variety of sources.
|
2021-07-09 21:18:43 +00:00
|
|
|
pub struct Config {
|
|
|
|
pub cache_type: CacheType,
|
|
|
|
pub cache_path: PathBuf,
|
|
|
|
pub shutdown_timeout: NonZeroU16,
|
2021-07-13 03:23:51 +00:00
|
|
|
pub log_level: TracingLevelFilter,
|
2021-07-09 21:18:43 +00:00
|
|
|
pub client_secret: ClientSecret,
|
|
|
|
pub port: Port,
|
|
|
|
pub bind_address: SocketAddr,
|
|
|
|
pub external_address: Option<SocketAddr>,
|
|
|
|
pub ephemeral_disk_encryption: bool,
|
|
|
|
pub network_speed: KilobitsPerSecond,
|
|
|
|
pub disk_quota: Mebibytes,
|
|
|
|
pub memory_quota: Mebibytes,
|
2021-07-09 23:14:53 +00:00
|
|
|
pub unstable_options: Vec<UnstableOptions>,
|
2021-07-09 21:18:43 +00:00
|
|
|
pub override_upstream: Option<Url>,
|
2021-07-09 23:14:53 +00:00
|
|
|
pub enable_metrics: bool,
|
2021-07-15 02:32:05 +00:00
|
|
|
pub geoip_license_key: Option<ClientSecret>,
|
2021-07-15 16:29:55 +00:00
|
|
|
pub proxy: Option<Url>,
|
2021-07-17 16:52:02 +00:00
|
|
|
pub redis_url: Option<Url>,
|
2021-06-16 19:31:03 +00:00
|
|
|
}
|
|
|
|
|
2021-07-09 21:18:43 +00:00
|
|
|
impl Config {
|
|
|
|
fn from_cli_and_file(cli_args: CliArgs, file_args: YamlArgs) -> Self {
|
2021-07-09 23:14:53 +00:00
|
|
|
let file_extended_options = file_args.extended_options.unwrap_or_default();
|
|
|
|
|
2021-07-09 21:18:43 +00:00
|
|
|
let log_level = match (cli_args.quiet, cli_args.verbose) {
|
2021-07-13 03:23:51 +00:00
|
|
|
(n, _) if n > 2 => TracingLevelFilter::OFF,
|
|
|
|
(2, _) => TracingLevelFilter::ERROR,
|
|
|
|
(1, _) => TracingLevelFilter::WARN,
|
2021-07-09 21:18:43 +00:00
|
|
|
// Use log level from file if no flags were provided to CLI
|
2021-07-14 03:12:29 +00:00
|
|
|
(0, 0) => {
|
|
|
|
file_extended_options
|
|
|
|
.logging_level
|
|
|
|
.map_or(TracingLevelFilter::INFO, |filter| match filter {
|
|
|
|
LevelFilter::Off => TracingLevelFilter::OFF,
|
|
|
|
LevelFilter::Error => TracingLevelFilter::ERROR,
|
|
|
|
LevelFilter::Warn => TracingLevelFilter::WARN,
|
|
|
|
LevelFilter::Info => TracingLevelFilter::INFO,
|
|
|
|
LevelFilter::Debug => TracingLevelFilter::DEBUG,
|
|
|
|
LevelFilter::Trace => TracingLevelFilter::TRACE,
|
|
|
|
})
|
|
|
|
}
|
2021-07-13 03:23:51 +00:00
|
|
|
(_, 1) => TracingLevelFilter::DEBUG,
|
|
|
|
(_, n) if n > 1 => TracingLevelFilter::TRACE,
|
2021-07-09 21:18:43 +00:00
|
|
|
// compiler can't figure it out
|
|
|
|
_ => unsafe { unreachable_unchecked() },
|
|
|
|
};
|
2021-07-09 23:14:53 +00:00
|
|
|
|
|
|
|
let bind_port = cli_args
|
|
|
|
.port
|
|
|
|
.unwrap_or(file_args.server_settings.port)
|
|
|
|
.get();
|
|
|
|
|
|
|
|
// This needs to be outside because rust isn't smart enough yet to
|
|
|
|
// realize a disjointed borrow of a moved value is ok. This will be
|
|
|
|
// fixed in Rust 2021.
|
|
|
|
let external_port = file_args
|
|
|
|
.server_settings
|
|
|
|
.external_port
|
|
|
|
.map_or(bind_port, Port::get);
|
|
|
|
|
|
|
|
Self {
|
|
|
|
cache_type: cli_args
|
|
|
|
.cache_type
|
|
|
|
.or(file_extended_options.cache_type)
|
|
|
|
.unwrap_or_default(),
|
|
|
|
cache_path: cli_args
|
|
|
|
.cache_path
|
|
|
|
.or(file_extended_options.cache_path)
|
|
|
|
.unwrap_or_else(|| PathBuf::from_str("./cache").unwrap()),
|
|
|
|
shutdown_timeout: file_args
|
|
|
|
.server_settings
|
|
|
|
.graceful_shutdown_wait_seconds
|
|
|
|
.unwrap_or(unsafe { NonZeroU16::new_unchecked(60) }),
|
|
|
|
log_level,
|
|
|
|
// secret should never be in CLI
|
2021-07-09 23:48:25 +00:00
|
|
|
client_secret: if let Ok(v) = std::env::var("CLIENT_SECRET") {
|
|
|
|
ClientSecret(v)
|
|
|
|
} else {
|
|
|
|
file_args.server_settings.secret
|
|
|
|
},
|
2021-07-09 23:14:53 +00:00
|
|
|
port: cli_args.port.unwrap_or(file_args.server_settings.port),
|
|
|
|
bind_address: SocketAddr::new(
|
|
|
|
file_args
|
|
|
|
.server_settings
|
|
|
|
.hostname
|
|
|
|
.unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))),
|
|
|
|
bind_port,
|
|
|
|
),
|
|
|
|
external_address: file_args
|
|
|
|
.server_settings
|
|
|
|
.external_ip
|
|
|
|
.map(|ip_addr| SocketAddr::new(ip_addr, external_port)),
|
2021-07-10 22:53:28 +00:00
|
|
|
ephemeral_disk_encryption: cli_args.ephemeral_disk_encryption
|
|
|
|
|| file_extended_options
|
|
|
|
.ephemeral_disk_encryption
|
|
|
|
.unwrap_or_default(),
|
2021-07-09 23:14:53 +00:00
|
|
|
network_speed: cli_args
|
|
|
|
.network_speed
|
|
|
|
.unwrap_or(file_args.server_settings.external_max_kilobits_per_second),
|
|
|
|
disk_quota: cli_args
|
|
|
|
.disk_quota
|
|
|
|
.unwrap_or(file_args.max_cache_size_in_mebibytes),
|
|
|
|
memory_quota: cli_args
|
|
|
|
.memory_quota
|
|
|
|
.or(file_extended_options.memory_quota)
|
|
|
|
.unwrap_or_default(),
|
|
|
|
enable_metrics: file_extended_options.enable_metrics.unwrap_or_default(),
|
|
|
|
|
|
|
|
// Unstable options (and related) should never be in yaml config
|
|
|
|
unstable_options: cli_args.unstable_options,
|
|
|
|
override_upstream: cli_args.override_upstream,
|
2021-07-15 02:32:05 +00:00
|
|
|
geoip_license_key: file_args.metric_settings.and_then(|args| {
|
|
|
|
if args.enable_geoip.unwrap_or_default() {
|
|
|
|
args.geoip_license_key
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}),
|
2021-07-15 16:29:55 +00:00
|
|
|
proxy: cli_args.proxy,
|
2021-07-17 16:52:02 +00:00
|
|
|
redis_url: file_extended_options.redis_url,
|
2021-07-09 23:14:53 +00:00
|
|
|
}
|
2021-07-09 21:18:43 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-16 19:31:03 +00:00
|
|
|
|
2021-07-09 23:51:48 +00:00
|
|
|
// this intentionally does not implement display
|
2021-07-09 23:48:25 +00:00
|
|
|
#[derive(Deserialize, Serialize, Clone)]
|
2021-07-09 21:18:43 +00:00
|
|
|
pub struct ClientSecret(String);
|
2021-06-16 19:31:03 +00:00
|
|
|
|
2021-07-15 06:14:04 +00:00
|
|
|
impl ClientSecret {
|
|
|
|
pub fn as_str(&self) -> &str {
|
|
|
|
self.0.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-09 23:51:48 +00:00
|
|
|
impl std::fmt::Debug for ClientSecret {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "[client secret]")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-15 01:56:29 +00:00
|
|
|
#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
|
2021-07-09 21:18:43 +00:00
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
pub enum CacheType {
|
2021-06-16 19:31:03 +00:00
|
|
|
OnDisk,
|
|
|
|
Lru,
|
|
|
|
Lfu,
|
2021-07-17 16:52:02 +00:00
|
|
|
Redis,
|
2021-06-16 19:31:03 +00:00
|
|
|
}
|
|
|
|
|
2021-07-09 21:18:43 +00:00
|
|
|
impl FromStr for CacheType {
|
|
|
|
type Err = String;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
match s {
|
|
|
|
"on_disk" => Ok(Self::OnDisk),
|
|
|
|
"lru" => Ok(Self::Lru),
|
|
|
|
"lfu" => Ok(Self::Lfu),
|
2021-07-17 16:52:02 +00:00
|
|
|
"redis" => Ok(Self::Redis),
|
2021-07-09 21:18:43 +00:00
|
|
|
_ => Err(format!("Unknown option: {}", s)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for CacheType {
|
2021-06-16 19:31:03 +00:00
|
|
|
fn default() -> Self {
|
|
|
|
Self::OnDisk
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-10 01:25:08 +00:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct YamlArgs {
|
|
|
|
// Naming is legacy
|
|
|
|
max_cache_size_in_mebibytes: Mebibytes,
|
|
|
|
server_settings: YamlServerSettings,
|
2021-07-15 02:32:05 +00:00
|
|
|
metric_settings: Option<YamlMetricSettings>,
|
|
|
|
// This implementation's custom options
|
2021-07-10 01:25:08 +00:00
|
|
|
extended_options: Option<YamlExtendedOptions>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Naming is legacy
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct YamlServerSettings {
|
|
|
|
secret: ClientSecret,
|
|
|
|
#[serde(default)]
|
|
|
|
port: Port,
|
|
|
|
external_max_kilobits_per_second: KilobitsPerSecond,
|
|
|
|
external_port: Option<Port>,
|
|
|
|
graceful_shutdown_wait_seconds: Option<NonZeroU16>,
|
|
|
|
hostname: Option<IpAddr>,
|
|
|
|
external_ip: Option<IpAddr>,
|
|
|
|
}
|
|
|
|
|
2021-07-15 02:32:05 +00:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct YamlMetricSettings {
|
|
|
|
enable_geoip: Option<bool>,
|
|
|
|
geoip_license_key: Option<ClientSecret>,
|
|
|
|
}
|
|
|
|
|
2021-07-10 01:25:08 +00:00
|
|
|
#[derive(Deserialize, Default)]
|
|
|
|
struct YamlExtendedOptions {
|
|
|
|
memory_quota: Option<Mebibytes>,
|
|
|
|
cache_type: Option<CacheType>,
|
|
|
|
ephemeral_disk_encryption: Option<bool>,
|
|
|
|
enable_metrics: Option<bool>,
|
|
|
|
logging_level: Option<LevelFilter>,
|
|
|
|
cache_path: Option<PathBuf>,
|
2021-07-17 16:52:02 +00:00
|
|
|
redis_url: Option<Url>,
|
2021-07-10 01:25:08 +00:00
|
|
|
}
|
|
|
|
|
2021-12-02 06:08:40 +00:00
|
|
|
#[derive(Parser, Clone)]
|
2021-04-18 02:13:36 +00:00
|
|
|
#[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())]
|
2021-07-09 21:18:43 +00:00
|
|
|
struct CliArgs {
|
2021-03-26 01:06:54 +00:00
|
|
|
/// The port to listen on.
|
2021-07-09 23:14:53 +00:00
|
|
|
#[clap(short, long)]
|
|
|
|
pub port: Option<Port>,
|
|
|
|
/// How large, in mebibytes, the in-memory cache should be. Note that this
|
|
|
|
/// does not include runtime memory usage.
|
2021-07-09 21:18:43 +00:00
|
|
|
#[clap(long)]
|
2021-07-09 23:14:53 +00:00
|
|
|
pub memory_quota: Option<Mebibytes>,
|
|
|
|
/// How large, in mebibytes, the on-disk cache should be. Note that actual
|
2021-03-26 01:06:54 +00:00
|
|
|
/// values may be larger for metadata information.
|
2021-06-16 19:31:03 +00:00
|
|
|
#[clap(long)]
|
2021-07-09 23:14:53 +00:00
|
|
|
pub disk_quota: Option<Mebibytes>,
|
2021-03-26 01:06:54 +00:00
|
|
|
/// Sets the location of the disk cache.
|
2021-07-09 23:14:53 +00:00
|
|
|
#[clap(long)]
|
|
|
|
pub cache_path: Option<PathBuf>,
|
2021-03-26 01:06:54 +00:00
|
|
|
/// The network speed to advertise to Mangadex@Home control server.
|
2021-06-16 19:31:03 +00:00
|
|
|
#[clap(long)]
|
2021-07-09 23:14:53 +00:00
|
|
|
pub network_speed: Option<KilobitsPerSecond>,
|
2021-04-20 18:12:20 +00:00
|
|
|
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
|
|
|
|
/// verbose flags increases the verbosity to DEBUG and TRACE, respectively.
|
2021-07-09 23:14:53 +00:00
|
|
|
#[clap(short, long, parse(from_occurrences), conflicts_with = "quiet")]
|
2021-04-18 03:19:27 +00:00
|
|
|
pub verbose: usize,
|
2021-04-20 18:12:20 +00:00
|
|
|
/// Changes verbosity. Default verbosity is INFO, while increasing counts of
|
|
|
|
/// quiet flags decreases the verbosity to WARN, ERROR, and no logs,
|
|
|
|
/// respectively.
|
|
|
|
#[clap(short, long, parse(from_occurrences), conflicts_with = "verbose")]
|
|
|
|
pub quiet: usize,
|
2021-07-15 06:11:04 +00:00
|
|
|
/// Unstable options. Intentionally not documented.
|
2021-04-25 16:55:31 +00:00
|
|
|
#[clap(short = 'Z', long)]
|
|
|
|
pub unstable_options: Vec<UnstableOptions>,
|
2021-07-15 06:11:04 +00:00
|
|
|
/// Override the image server with the one provided. Do not set this unless
|
|
|
|
/// you know what you're doing.
|
2021-04-19 03:06:18 +00:00
|
|
|
#[clap(long)]
|
|
|
|
pub override_upstream: Option<Url>,
|
2021-05-12 01:01:01 +00:00
|
|
|
/// Enables ephemeral disk encryption. Items written to disk are first
|
|
|
|
/// encrypted with a key generated at runtime. There are implications to
|
|
|
|
/// performance, privacy, and usability with this flag enabled.
|
|
|
|
#[clap(short, long)]
|
2021-07-10 22:53:28 +00:00
|
|
|
pub ephemeral_disk_encryption: bool,
|
2021-07-15 06:11:04 +00:00
|
|
|
/// The path to the config file. Default value is `./settings.yaml`.
|
2021-06-16 19:31:03 +00:00
|
|
|
#[clap(short, long)]
|
|
|
|
pub config_path: Option<PathBuf>,
|
2021-07-15 06:11:04 +00:00
|
|
|
/// Whether to use an in-memory cache in addition to the disk cache. Default
|
2021-07-17 16:52:02 +00:00
|
|
|
/// value is "on_disk", other options are "lfu", "lru", and "redis".
|
2021-07-09 23:17:56 +00:00
|
|
|
#[clap(short = 't', long)]
|
2021-07-09 23:14:53 +00:00
|
|
|
pub cache_type: Option<CacheType>,
|
2021-07-15 16:29:55 +00:00
|
|
|
/// Whether or not to use a proxy for upstream requests. This affects all
|
|
|
|
/// requests except for the shutdown request.
|
|
|
|
#[clap(short = 'P', long)]
|
|
|
|
pub proxy: Option<Url>,
|
2021-04-25 16:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
pub enum UnstableOptions {
|
|
|
|
/// Overrides the upstream URL to fetch images from. Don't use this unless
|
|
|
|
/// you know what you're dealing with.
|
|
|
|
OverrideUpstream,
|
|
|
|
|
|
|
|
/// Disables token validation. Don't use this unless you know the
|
|
|
|
/// ramifications of this command.
|
|
|
|
DisableTokenValidation,
|
2021-05-23 03:06:05 +00:00
|
|
|
|
|
|
|
/// Tries to run without communication to MangaDex.
|
|
|
|
OfflineMode,
|
|
|
|
|
|
|
|
/// Serves HTTP in plaintext
|
|
|
|
DisableTls,
|
2021-07-15 16:29:55 +00:00
|
|
|
|
|
|
|
/// Disable certificate validation. Only useful for debugging with a MITM
|
|
|
|
/// proxy
|
|
|
|
DisableCertValidation,
|
2021-04-25 16:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for UnstableOptions {
|
2021-05-23 03:06:05 +00:00
|
|
|
type Err = String;
|
2021-04-25 16:55:31 +00:00
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
match s {
|
|
|
|
"override-upstream" => Ok(Self::OverrideUpstream),
|
|
|
|
"disable-token-validation" => Ok(Self::DisableTokenValidation),
|
2021-05-23 03:06:05 +00:00
|
|
|
"offline-mode" => Ok(Self::OfflineMode),
|
|
|
|
"disable-tls" => Ok(Self::DisableTls),
|
2021-07-15 16:29:55 +00:00
|
|
|
"disable-cert-validation" => Ok(Self::DisableCertValidation),
|
2021-05-23 03:06:05 +00:00
|
|
|
_ => Err(format!("Unknown unstable option '{}'", s)),
|
2021-04-25 16:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for UnstableOptions {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::OverrideUpstream => write!(f, "override-upstream"),
|
|
|
|
Self::DisableTokenValidation => write!(f, "disable-token-validation"),
|
2021-05-23 03:06:05 +00:00
|
|
|
Self::OfflineMode => write!(f, "offline-mode"),
|
|
|
|
Self::DisableTls => write!(f, "disable-tls"),
|
2021-07-15 16:29:55 +00:00
|
|
|
Self::DisableCertValidation => write!(f, "disable-cert-validation"),
|
2021-04-25 16:55:31 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-26 01:06:54 +00:00
|
|
|
}
|
2021-07-10 01:25:08 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod sample_yaml {
|
|
|
|
use crate::config::YamlArgs;
|
|
|
|
|
|
|
|
#[test]
|
2021-07-15 01:56:29 +00:00
|
|
|
fn parses() {
|
2021-07-11 17:19:37 +00:00
|
|
|
assert!(serde_yaml::from_str::<YamlArgs>(include_str!("../settings.sample.yaml")).is_ok());
|
2021-07-10 01:25:08 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-15 01:56:29 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod config {
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
use log::LevelFilter;
|
|
|
|
use tracing::level_filters::LevelFilter as TracingLevelFilter;
|
|
|
|
|
|
|
|
use crate::config::{CacheType, ClientSecret, Config, YamlExtendedOptions, YamlServerSettings};
|
|
|
|
use crate::units::{KilobitsPerSecond, Mebibytes, Port};
|
|
|
|
|
|
|
|
use super::{CliArgs, YamlArgs};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn cli_has_priority() {
|
|
|
|
let cli_config = CliArgs {
|
|
|
|
port: Port::new(1234),
|
|
|
|
memory_quota: Some(Mebibytes::new(10)),
|
|
|
|
disk_quota: Some(Mebibytes::new(10)),
|
|
|
|
cache_path: Some(PathBuf::from("a")),
|
|
|
|
network_speed: KilobitsPerSecond::new(10),
|
|
|
|
verbose: 1,
|
|
|
|
quiet: 0,
|
|
|
|
unstable_options: vec![],
|
|
|
|
override_upstream: None,
|
|
|
|
ephemeral_disk_encryption: true,
|
|
|
|
config_path: None,
|
|
|
|
cache_type: Some(CacheType::Lfu),
|
2021-07-15 16:29:55 +00:00
|
|
|
proxy: None,
|
2021-07-15 01:56:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let yaml_args = YamlArgs {
|
|
|
|
max_cache_size_in_mebibytes: Mebibytes::new(50),
|
|
|
|
server_settings: YamlServerSettings {
|
|
|
|
secret: ClientSecret(String::new()),
|
|
|
|
port: Port::new(4321).expect("to work?"),
|
|
|
|
external_max_kilobits_per_second: KilobitsPerSecond::new(50).expect("to work?"),
|
|
|
|
external_port: None,
|
|
|
|
graceful_shutdown_wait_seconds: None,
|
|
|
|
hostname: None,
|
|
|
|
external_ip: None,
|
|
|
|
},
|
2021-07-15 02:32:05 +00:00
|
|
|
metric_settings: None,
|
2021-07-15 01:56:29 +00:00
|
|
|
extended_options: Some(YamlExtendedOptions {
|
|
|
|
memory_quota: Some(Mebibytes::new(50)),
|
|
|
|
cache_type: Some(CacheType::Lru),
|
|
|
|
ephemeral_disk_encryption: Some(false),
|
|
|
|
enable_metrics: None,
|
|
|
|
logging_level: Some(LevelFilter::Error),
|
|
|
|
cache_path: Some(PathBuf::from("b")),
|
2021-07-17 16:52:02 +00:00
|
|
|
redis_url: None,
|
2021-07-15 01:56:29 +00:00
|
|
|
}),
|
|
|
|
};
|
|
|
|
|
|
|
|
let config = Config::from_cli_and_file(cli_config, yaml_args);
|
|
|
|
assert_eq!(Some(config.port), Port::new(1234));
|
|
|
|
assert_eq!(config.memory_quota, Mebibytes::new(10));
|
|
|
|
assert_eq!(config.disk_quota, Mebibytes::new(10));
|
|
|
|
assert_eq!(config.cache_path, PathBuf::from("a"));
|
|
|
|
assert_eq!(Some(config.network_speed), KilobitsPerSecond::new(10));
|
|
|
|
assert_eq!(config.log_level, TracingLevelFilter::DEBUG);
|
|
|
|
assert_eq!(config.ephemeral_disk_encryption, true);
|
|
|
|
assert_eq!(config.cache_type, CacheType::Lfu);
|
|
|
|
}
|
|
|
|
}
|