From 55036478a0d4bb46a5534b0bc36b7adf999ab163 Mon Sep 17 00:00:00 2001 From: Chip Senkbeil Date: Sat, 18 Mar 2023 14:12:22 -0500 Subject: [PATCH] Add default configuration and ability to generate default configuration --- .gitignore | 1 + Cargo.lock | 5 +- distant-net/Cargo.toml | 1 + distant-net/src/common/port.rs | 193 ++++++++++++++++++++++++++- distant-net/src/server/config.rs | 21 ++- src/cli/commands/generate.rs | 16 ++- src/config.rs | 216 ++++++++++++++++++++++++++++++- src/config.toml | 178 +++++++++++++++++++++++++ src/config/client.rs | 2 +- src/config/client/action.rs | 2 +- src/config/client/connect.rs | 3 +- src/config/client/launch.rs | 5 +- src/config/client/repl.rs | 2 +- src/config/common.rs | 2 +- src/config/generate.rs | 2 +- src/config/manager.rs | 32 +---- src/config/network.rs | 2 +- src/config/server.rs | 2 +- src/config/server/listen.rs | 23 +++- 19 files changed, 656 insertions(+), 52 deletions(-) create mode 100644 src/config.toml diff --git a/.gitignore b/.gitignore index b329048..7f29c2f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/.DS_Store /distant-core/Cargo.lock /distant-ssh2/Cargo.lock +**/*.un~ diff --git a/Cargo.lock b/Cargo.lock index e2b89ad..79b675f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -922,6 +922,7 @@ dependencies = [ "schemars", "serde", "serde_bytes", + "serde_json", "sha2 0.10.6", "strum", "tempfile", @@ -2855,9 +2856,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", diff --git a/distant-net/Cargo.toml b/distant-net/Cargo.toml index 747b913..67517ce 100644 --- a/distant-net/Cargo.toml +++ b/distant-net/Cargo.toml @@ -36,5 +36,6 @@ schemars = { version = "0.8.11", optional = true } [dev-dependencies] env_logger = "0.9.3" +serde_json = "1.0.94" tempfile = "3.3.0" test-log = "0.2.11" diff --git a/distant-net/src/common/port.rs b/distant-net/src/common/port.rs index ad73972..964f157 100644 --- a/distant-net/src/common/port.rs +++ b/distant-net/src/common/port.rs @@ -1,13 +1,14 @@ use derive_more::Display; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Serialize}; use std::{ + fmt, net::{IpAddr, SocketAddr}, ops::RangeInclusive, str::FromStr, }; /// Represents some range of ports -#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Display, PartialEq, Eq)] #[display( fmt = "{}{}", start, @@ -87,6 +88,140 @@ impl FromStr for PortRange { } } +impl Serialize for PortRange { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + String::serialize(&self.to_string(), serializer) + } +} + +impl<'de> Deserialize<'de> for PortRange { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + struct PortRangeVisitor; + impl<'de> de::Visitor<'de> for PortRangeVisitor { + type Value = PortRange; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a port in the form NUMBER or START:END") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + FromStr::from_str(s).map_err(de::Error::custom) + } + + fn visit_u8(self, v: u8) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v as u16, + end: None, + }) + } + + fn visit_u16(self, v: u16) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v, + end: None, + }) + } + + fn visit_u32(self, v: u32) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + + fn visit_u128(self, v: u128) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + + fn visit_i8(self, v: i8) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + + fn visit_i16(self, v: i16) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + + fn visit_i32(self, v: i32) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + + fn visit_i64(self, v: i64) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + + fn visit_i128(self, v: i128) -> Result + where + E: de::Error, + { + Ok(PortRange { + start: v.try_into().map_err(de::Error::custom)?, + end: None, + }) + } + } + + deserializer.deserialize_any(PortRangeVisitor) + } +} + #[cfg(test)] mod tests { use super::*; @@ -187,4 +322,58 @@ mod tests { } ); } + + #[test] + fn serialize_should_leverage_tostring() { + assert_eq!( + serde_json::to_value(PortRange { + start: 123, + end: None, + }) + .unwrap(), + serde_json::Value::String("123".to_string()) + ); + + assert_eq!( + serde_json::to_value(PortRange { + start: 123, + end: Some(456), + }) + .unwrap(), + serde_json::Value::String("123:456".to_string()) + ); + } + + #[test] + fn deserialize_should_use_single_number_as_start() { + // Supports parsing numbers + assert_eq!( + serde_json::from_str::("123").unwrap(), + PortRange { + start: 123, + end: None + } + ); + } + + #[test] + fn deserialize_should_leverage_fromstr_for_strings() { + // Supports string number + assert_eq!( + serde_json::from_str::("\"123\"").unwrap(), + PortRange { + start: 123, + end: None + } + ); + + // Supports string start:end + assert_eq!( + serde_json::from_str::("\"123:456\"").unwrap(), + PortRange { + start: 123, + end: Some(456) + } + ); + } } diff --git a/distant-net/src/server/config.rs b/distant-net/src/server/config.rs index b4a4975..501abaf 100644 --- a/distant-net/src/server/config.rs +++ b/distant-net/src/server/config.rs @@ -29,7 +29,7 @@ impl Default for ServerConfig { } /// Rules for how a server will shut itself down automatically -#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Display, PartialEq, Eq)] pub enum Shutdown { /// Server should shutdown immediately after duration exceeded #[display(fmt = "after={}", "_0.as_secs_f32()")] @@ -105,3 +105,22 @@ impl FromStr for Shutdown { } } } + +impl Serialize for Shutdown { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + String::serialize(&self.to_string(), serializer) + } +} + +impl<'de> Deserialize<'de> for Shutdown { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} diff --git a/src/cli/commands/generate.rs b/src/cli/commands/generate.rs index 731406a..b0e40c4 100644 --- a/src/cli/commands/generate.rs +++ b/src/cli/commands/generate.rs @@ -1,4 +1,8 @@ -use crate::{cli::Opt, config::GenerateConfig, CliResult}; +use crate::{ + cli::Opt, + config::{Config, GenerateConfig}, + CliResult, +}; use anyhow::Context; use clap::{CommandFactory, Subcommand}; use clap_complete::{generate as clap_generate, Shell}; @@ -10,6 +14,12 @@ use std::{fs, io, path::PathBuf}; #[derive(Debug, Subcommand)] pub enum GenerateSubcommand { + /// Generate configuration file with base settings + Config { + /// Path to where the configuration file should be created + file: PathBuf, + }, + /// Generate JSON schema for server request/response Schema { /// If specified, will output to the file at the given path instead of stdout @@ -37,6 +47,10 @@ impl GenerateSubcommand { async fn async_run(self) -> CliResult { match self { + Self::Config { file } => tokio::fs::write(file, Config::default_raw_str()) + .await + .context("Failed to write default config to {file:?}")?, + Self::Schema { file } => { let request_schema = serde_json::to_value(&Request::>::root_schema()) diff --git a/src/config.rs b/src/config.rs index e0835ef..02aec93 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use crate::paths; use anyhow::Context; +use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::{ io, @@ -21,8 +22,10 @@ pub use manager::*; pub use network::*; pub use server::*; +const DEFAULT_RAW_STR: &str = include_str!("config.toml"); + /// Represents configuration settings for all of distant -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Config { pub client: ClientConfig, pub generate: GenerateConfig, @@ -31,6 +34,11 @@ pub struct Config { } impl Config { + /// Returns a reference to the default config file as a raw str. + pub const fn default_raw_str() -> &'static str { + DEFAULT_RAW_STR + } + /// Loads the configuration from multiple sources in a blocking fashion /// /// 1. If `custom` is provided, it is used by itself as the source for configuration @@ -124,3 +132,209 @@ impl Config { tokio::fs::write(path, text).await } } + +impl Default for Config { + fn default() -> Self { + static DEFAULT_CONFIG: Lazy = Lazy::new(|| { + toml_edit::de::from_str(Config::default_raw_str()) + .expect("Default config failed to parse") + }); + + DEFAULT_CONFIG.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use distant_core::net::common::{Host, Map, PortRange}; + use distant_core::net::map; + use distant_core::net::server::Shutdown; + use std::net::Ipv4Addr; + use std::time::Duration; + use test_log::test; + + #[test] + fn default_should_parse_config_from_internal_toml() { + let config = Config::default(); + assert_eq!( + config, + Config { + client: ClientConfig { + action: ClientActionConfig { timeout: Some(0.) }, + common: CommonConfig { + log_level: Some(LogLevel::Info), + log_file: None + }, + connect: ClientConnectConfig { + options: Map::new() + }, + launch: ClientLaunchConfig { + distant: ClientLaunchDistantConfig { + bin: Some("distant".to_owned()), + bind_server: Some(BindAddress::Ssh), + args: Some("".to_string()) + }, + options: Map::new(), + }, + network: NetworkConfig { + unix_socket: None, + windows_pipe: None + }, + repl: ClientReplConfig { timeout: Some(0.) }, + }, + generate: GenerateConfig { + common: CommonConfig { + log_level: Some(LogLevel::Info), + log_file: None + }, + }, + manager: ManagerConfig { + access: Some(AccessControl::Owner), + common: CommonConfig { + log_level: Some(LogLevel::Info), + log_file: None + }, + network: NetworkConfig { + unix_socket: None, + windows_pipe: None + }, + }, + server: ServerConfig { + common: CommonConfig { + log_level: Some(LogLevel::Info), + log_file: None + }, + listen: ServerListenConfig { + host: Some(BindAddress::Any), + port: Some(0.into()), + use_ipv6: false, + shutdown: Some(Shutdown::Never), + current_dir: None, + }, + }, + } + ); + } + + #[test(tokio::test)] + async fn default_should_parse_config_from_specified_file() { + use assert_fs::prelude::*; + let config_file = assert_fs::NamedTempFile::new("config.toml").unwrap(); + config_file + .write_str( + r#" +[client] +log_file = "client-log-file" +log_level = "trace" +unix_socket = "client-unix-socket" +windows_pipe = "client-windows-pipe" + +[client.action] +timeout = 123 + +[client.connect] +options = "key=\"value\",key2=\"value2\"" + +[client.launch] +bin = "some-bin" +bind_server = "any" +args = "a b c" +options = "key3=\"value3\",key4=\"value4\"" + +[client.repl] +timeout = 456 + +[generate] +log_file = "generate-log-file" +log_level = "debug" + +[manager] +log_file = "manager-log-file" +log_level = "warn" +access = "anyone" +unix_socket = "manager-unix-socket" +windows_pipe = "manager-windows-pipe" + +[server] +log_file = "server-log-file" +log_level = "error" + +[server.listen] +host = "127.0.0.1" +port = "8080:8089" +use_ipv6 = true +shutdown = "after=123" +current_dir = "server-current-dir" +"#, + ) + .unwrap(); + + let config = Config::load(config_file.path()).await.unwrap(); + assert_eq!( + config, + Config { + client: ClientConfig { + action: ClientActionConfig { + timeout: Some(123.) + }, + common: CommonConfig { + log_level: Some(LogLevel::Trace), + log_file: Some(PathBuf::from("client-log-file")), + }, + connect: ClientConnectConfig { + options: map!("key" -> "value", "key2" -> "value2"), + }, + launch: ClientLaunchConfig { + distant: ClientLaunchDistantConfig { + bin: Some("some-bin".to_owned()), + bind_server: Some(BindAddress::Any), + args: Some(String::from("a b c")) + }, + options: map!("key3" -> "value3", "key4" -> "value4"), + }, + network: NetworkConfig { + unix_socket: Some(PathBuf::from("client-unix-socket")), + windows_pipe: Some(String::from("client-windows-pipe")) + }, + repl: ClientReplConfig { + timeout: Some(456.) + }, + }, + generate: GenerateConfig { + common: CommonConfig { + log_level: Some(LogLevel::Debug), + log_file: Some(PathBuf::from("generate-log-file")) + }, + }, + manager: ManagerConfig { + access: Some(AccessControl::Anyone), + common: CommonConfig { + log_level: Some(LogLevel::Warn), + log_file: Some(PathBuf::from("manager-log-file")) + }, + network: NetworkConfig { + unix_socket: Some(PathBuf::from("manager-unix-socket")), + windows_pipe: Some(String::from("manager-windows-pipe")), + }, + }, + server: ServerConfig { + common: CommonConfig { + log_level: Some(LogLevel::Error), + log_file: Some(PathBuf::from("server-log-file")), + }, + listen: ServerListenConfig { + host: Some(BindAddress::Host(Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1)))), + port: Some(PortRange { + start: 8080, + end: Some(8089) + }), + use_ipv6: true, + shutdown: Some(Shutdown::After(Duration::from_secs(123))), + current_dir: Some(PathBuf::from("server-current-dir")), + }, + }, + } + ); + } +} diff --git a/src/config.toml b/src/config.toml new file mode 100644 index 0000000..4a43136 --- /dev/null +++ b/src/config.toml @@ -0,0 +1,178 @@ +############################################################################### +# All configuration specific to the distant client will be found under +# this heading +############################################################################### +[client] + +# Specifies an alternative path to use when logging information while the +# client is running +# log_file = "path/to/file" + +# Specifies the log level used when logging information while the client +# is running +# +# Choices are off, error, warn, info, debug, trace +# The default setting is info +log_level = "info" + +# Alternative unix domain socket to connect to when using a manger (Unix only) +# unix_socket = "path/to/socket" + +# Alternative name for a local named Windows pipe to connect to when using a +# manager (Windows only) +# windows_pipe = "some_name" + +# Configuration related to the client's action command +[client.action] + +# Maximum time (in seconds) to wait for a network request before timing out +# where 0 indicates no timeout will occur +timeout = 0 + +# Configuration related to the client's connect command +[client.connect] + +# Additional options to provide, typically forwarded to the handler within +# the manager facilitating the connection. Options are key-value pairs separated +# by comma. +# +# E.g. `key="value",key2="value2"` +options = "" + +# Configuration related to the client's launch command +[client.launch] + +# Path to distant program on remote machine to execute via ssh; by default, +# this program needs to be available within PATH as specified when compiling +# ssh (not your login shell). +bin = "distant" + +# Control the IP address that the server binds to. +# +# The default is `ssh', in which case the server will reply from the IP address +# that the SSH connection came from (as found in the SSH_CONNECTION environment +# variable). This is useful for multihomed servers. +# +# With --bind-server=any, the server will reply on the default interface and +# will not bind to a particular IP address. This can be useful if the +# connection is made through sslh or another tool that makes the SSH connection +# appear to come from localhost. +# +# With --bind-server=IP, the server will attempt to bind to the specified IP +# address. +bind_server = "ssh" + +# Additional arguments to provide to the server when launching it. +args = "" + +# Additional options to provide, typically forwarded to the handler within the +# manager facilitating the launch of a distant server. Options are key-value +# pairs separated by comma. +# +# E.g. `key="value",key2="value2"` +options = "" + +# Configuration related to the client's repl command +[client.repl] + +# Maximum time (in seconds) to wait for a network request before timing out +# where 0 indicates no timeout will occur +timeout = 0 + +############################################################################### +# All configuration specific to the distant generate option will be found under +# this heading +############################################################################### +[generate] + +# Specifies an alternative path to use when logging information related +# to generation +# log_file = "path/to/file" + +# Specifies the log level used when logging information related to generation +# Choices are off, error, warn, info, debug, trace +# The default setting is info +log_level = "info" + +############################################################################### +# All configuration specific to the distant manager will be found under +# this heading +############################################################################### +[manager] + +# Specifies an alternative path to use when logging information while the +# manager is running +# log_file = "path/to/file" + +# Specifies the log level used when logging information while the manager +# is running +# +# Choices are off, error, warn, info, debug, trace +# The default setting is info +log_level = "info" + +# Level of access control to the unix socket or windows pipe. +# +# * "owner": equates to `0o600` on Unix (read & write for owner). +# * "group": equates to `0o660` on Unix (read & write for owner and group). +# # "anyone": equates to `0o666` on Unix (read & write for owner, group, and other). +access = "owner" + +# Alternative unix domain socket to listen on (Unix only) +# unix_socket = "path/to/socket" + +# Alternative name for a local named Windows pipe to listen on (Windows only) +# windows_pipe = "some_name" + +############################################################################### +# All configuration specific to the distant server will be found under +# this heading +############################################################################### +[server] + +# Specifies an alternative path to use when logging information while the +# server is running +# log_file = "path/to/file" + +# Specifies the log level used when logging information while the server +# is running +# +# Choices are off, error, warn, info, debug, trace +# The default setting is info +log_level = "info" + +# Configuration related to the server's listen command +[server.listen] + +# IP address that the server will bind to. This can be one of three things: +# +# 1. "ssh": the server will reply from the IP address that the SSH connection +# came from (as found in the SSH_CONNECTION environment variable). +# This is useful for multihomed servers. +# 2. "any": the server will reply on the default interface and will not bind to +# a particular IP address. This can be useful if the connection is +# made through ssh or another tool that makes the SSH connection +# appear to come from localhost. +# 3. "{IP}": the server will attempt to bind to the specified IP address. +host = "any" + +# Set the port(s) that the server will attempt to bind to. +# +# This can be in the form of PORT1 or PORT1:PORTN to provide a range of ports. +# With "0", the server will let the operating system pick an available TCP port. +# +# Please note that this option does not affect the server-side port used by SSH. +port = "0" + +# If true, will bind to the ipv6 interface if host is any instead of ipv4 +use_ipv6 = false + +# Logic to apply to server when determining when to shutdown automatically. +# +# 1. "never" means the server will never automatically shut down +# 2. "after=" means the server will shut down after N seconds +# 3. "lonely=" means the server will shut down after N seconds with no connections +shutdown = "never" + +# Changes the current working directory (cwd) to the specified directory. +# current_dir = "path/to/dir" diff --git a/src/config/client.rs b/src/config/client.rs index d8b5da2..47eccec 100644 --- a/src/config/client.rs +++ b/src/config/client.rs @@ -12,7 +12,7 @@ pub use launch::*; pub use repl::*; /// Represents configuration settings for the distant client -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct ClientConfig { #[serde(flatten)] pub common: CommonConfig, diff --git a/src/config/client/action.rs b/src/config/client/action.rs index 82fda0e..b6169f3 100644 --- a/src/config/client/action.rs +++ b/src/config/client/action.rs @@ -1,7 +1,7 @@ use clap::Args; use serde::{Deserialize, Serialize}; -#[derive(Args, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct ClientActionConfig { /// Represents the maximum time (in seconds) to wait for a network request before timing out pub timeout: Option, diff --git a/src/config/client/connect.rs b/src/config/client/connect.rs index a9d3323..fc63cc1 100644 --- a/src/config/client/connect.rs +++ b/src/config/client/connect.rs @@ -2,14 +2,13 @@ use clap::Args; use distant_core::net::common::Map; use serde::{Deserialize, Serialize}; -#[derive(Args, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ClientConnectConfig { /// Additional options to provide, typically forwarded to the handler within the manager /// facilitating the connection. Options are key-value pairs separated by comma. /// /// E.g. `key="value",key2="value2"` #[clap(long, default_value_t)] - #[serde(flatten)] pub options: Map, } diff --git a/src/config/client/launch.rs b/src/config/client/launch.rs index 132a5fc..47607f9 100644 --- a/src/config/client/launch.rs +++ b/src/config/client/launch.rs @@ -3,7 +3,7 @@ use clap::Args; use distant_core::net::common::Map; use serde::{Deserialize, Serialize}; -#[derive(Args, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ClientLaunchConfig { #[clap(flatten)] #[serde(flatten)] @@ -15,7 +15,6 @@ pub struct ClientLaunchConfig { /// /// E.g. `key="value",key2="value2"` #[clap(long, default_value_t)] - #[serde(flatten)] pub options: Map, } @@ -56,7 +55,7 @@ impl From for Map { } } -#[derive(Args, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ClientLaunchDistantConfig { /// Path to distant program on remote machine to execute via ssh; /// by default, this program needs to be available within PATH as diff --git a/src/config/client/repl.rs b/src/config/client/repl.rs index a29e7d6..a72785c 100644 --- a/src/config/client/repl.rs +++ b/src/config/client/repl.rs @@ -1,7 +1,7 @@ use clap::Args; use serde::{Deserialize, Serialize}; -#[derive(Args, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct ClientReplConfig { /// Represents the maximum time (in seconds) to wait for a network request before timing out pub timeout: Option, diff --git a/src/config/common.rs b/src/config/common.rs index 8f33f8c..d7341ab 100644 --- a/src/config/common.rs +++ b/src/config/common.rs @@ -34,7 +34,7 @@ impl Default for LogLevel { } /// Contains options that are common across subcommands -#[derive(Args, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct CommonConfig { /// Log level to use throughout the application #[clap(long, global = true, value_enum)] diff --git a/src/config/generate.rs b/src/config/generate.rs index a5f4863..f876ec4 100644 --- a/src/config/generate.rs +++ b/src/config/generate.rs @@ -2,7 +2,7 @@ use super::CommonConfig; use serde::{Deserialize, Serialize}; /// Represents configuration settings for the distant generate -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct GenerateConfig { #[serde(flatten)] pub common: CommonConfig, diff --git a/src/config/manager.rs b/src/config/manager.rs index 95f64df..17a1421 100644 --- a/src/config/manager.rs +++ b/src/config/manager.rs @@ -1,11 +1,9 @@ use super::{AccessControl, CommonConfig, NetworkConfig}; use clap::Args; -use distant_core::net::common::Destination; use serde::{Deserialize, Serialize}; -use service_manager::ServiceManagerKind; /// Represents configuration settings for the distant manager -#[derive(Args, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ManagerConfig { /// Type of access to apply to created unix socket or windows pipe #[clap(long, value_enum)] @@ -15,35 +13,7 @@ pub struct ManagerConfig { #[serde(flatten)] pub common: CommonConfig, - #[clap(skip)] - pub connections: Vec, - #[clap(flatten)] #[serde(flatten)] pub network: NetworkConfig, - - #[clap(value_enum)] - pub service: Option, -} - -/// Represents configuration for some managed connection -#[derive(Debug, Serialize, Deserialize)] -pub enum ManagerConnectionConfig { - Distant(ManagerDistantConnectionConfig), - Ssh(ManagerSshConnectionConfig), -} - -/// Represents configuration for a distant connection -#[derive(Debug, Serialize, Deserialize)] -pub struct ManagerDistantConnectionConfig { - pub name: String, - pub destination: Destination, - pub key_cmd: Option, -} - -/// Represents configuration for an SSH connection -#[derive(Debug, Serialize, Deserialize)] -pub struct ManagerSshConnectionConfig { - pub name: String, - pub destination: Destination, } diff --git a/src/config/network.rs b/src/config/network.rs index a7839ac..8f5c330 100644 --- a/src/config/network.rs +++ b/src/config/network.rs @@ -35,7 +35,7 @@ impl Default for AccessControl { } /// Represents common networking configuration -#[derive(Args, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct NetworkConfig { /// Override the path to the Unix socket used by the manager (unix-only) #[clap(long)] diff --git a/src/config/server.rs b/src/config/server.rs index fc2515f..660001c 100644 --- a/src/config/server.rs +++ b/src/config/server.rs @@ -5,7 +5,7 @@ mod listen; pub use listen::*; /// Represents configuration settings for the distant server -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ServerConfig { #[serde(flatten)] pub common: CommonConfig, diff --git a/src/config/server/listen.rs b/src/config/server/listen.rs index 4d77f24..6fdc22d 100644 --- a/src/config/server/listen.rs +++ b/src/config/server/listen.rs @@ -10,7 +10,7 @@ use std::{ str::FromStr, }; -#[derive(Args, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct ServerListenConfig { /// Control the IP address that the distant binds to /// @@ -104,7 +104,7 @@ impl From for Map { } /// Represents options for binding a server to an IP address -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum BindAddress { /// Should read address from `SSH_CONNECTION` environment variable, which contains four /// space-separated values: @@ -148,6 +148,25 @@ impl FromStr for BindAddress { } } +impl Serialize for BindAddress { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + String::serialize(&self.to_string(), serializer) + } +} + +impl<'de> Deserialize<'de> for BindAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} + impl BindAddress { /// Resolves address into valid IP; in the case of "any", will leverage the /// `use_ipv6` flag to determine if binding should use ipv4 or ipv6