use std::collections::HashMap;
use crate::server::common::audit::Auditor;
use manta_backend_dispatcher::types::K8sDetails;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum BackendTechnology {
Csm,
Ochami,
}
impl BackendTechnology {
pub fn as_str(&self) -> &'static str {
match self {
Self::Csm => "csm",
Self::Ochami => "ochami",
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Site {
pub backend: BackendTechnology,
pub socks5_proxy: Option<String>,
pub shasta_base_url: String,
pub k8s: Option<K8sDetails>,
pub root_ca_cert_file: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ServerSettings {
#[serde(default)]
pub listen_address: Option<String>,
#[serde(default)]
pub port: Option<u16>,
pub cert: Option<String>,
pub key: Option<String>,
pub console_inactivity_timeout_secs: u64,
pub auth_rate_limit_per_minute: Option<u32>,
#[serde(default = "default_request_timeout_secs")]
pub request_timeout_secs: u64,
#[serde(default = "default_shutdown_grace_period_secs")]
pub shutdown_grace_period_secs: u64,
#[serde(default)]
pub migrate_backup_root: Option<String>,
#[serde(default)]
pub allow_http: bool,
}
impl ServerSettings {
pub const DEFAULT_LISTEN_ADDRESS: &'static str = "0.0.0.0";
pub fn default_port(has_tls: bool) -> u16 {
if has_tls { 8443 } else { 8080 }
}
}
fn default_shutdown_grace_period_secs() -> u64 {
30
}
fn default_request_timeout_secs() -> u64 {
600
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ServerConfiguration {
pub log: String,
pub server: ServerSettings,
pub sites: HashMap<String, Site>,
pub auditor: Option<Auditor>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn site_deserialize_missing_backend_fails() {
let bad_toml = r#"
shasta_base_url = "https://api.example.com"
root_ca_cert_file = "cert.pem"
# missing backend
"#;
let result = toml::from_str::<Site>(bad_toml);
assert!(result.is_err());
}
#[test]
fn backend_technology_as_str() {
assert_eq!(BackendTechnology::Csm.as_str(), "csm");
assert_eq!(BackendTechnology::Ochami.as_str(), "ochami");
}
#[test]
fn backend_technology_roundtrip_toml() {
#[derive(Serialize, Deserialize)]
struct Wrapper {
backend: BackendTechnology,
}
let w = Wrapper {
backend: BackendTechnology::Csm,
};
let s = toml::to_string(&w).unwrap();
assert!(s.contains("\"csm\"") || s.contains("csm"));
let parsed: Wrapper = toml::from_str(&s).unwrap();
assert_eq!(parsed.backend, BackendTechnology::Csm);
}
fn make_minimal_site() -> Site {
Site {
backend: BackendTechnology::Csm,
socks5_proxy: None,
shasta_base_url: "https://api.example.com".to_string(),
k8s: None,
root_ca_cert_file: "cert.pem".to_string(),
}
}
#[test]
fn server_configuration_roundtrip_toml_minimal() {
let mut sites = HashMap::new();
sites.insert("alps".to_string(), make_minimal_site());
let cfg = ServerConfiguration {
log: "info".to_string(),
server: ServerSettings {
listen_address: Some("0.0.0.0".to_string()),
port: Some(8443),
cert: Some("/etc/manta/tls/server.crt".to_string()),
key: Some("/etc/manta/tls/server.key".to_string()),
console_inactivity_timeout_secs: 1800,
auth_rate_limit_per_minute: Some(60),
request_timeout_secs: 300,
shutdown_grace_period_secs: 30,
migrate_backup_root: None,
allow_http: false,
},
sites,
auditor: None,
};
let toml_str = toml::to_string(&cfg).unwrap();
let parsed: ServerConfiguration = toml::from_str(&toml_str).unwrap();
assert_eq!(parsed.server.port, Some(8443));
assert_eq!(parsed.server.listen_address.as_deref(), Some("0.0.0.0"));
assert_eq!(parsed.server.console_inactivity_timeout_secs, 1800);
assert_eq!(parsed.server.request_timeout_secs, 300);
assert_eq!(
parsed.server.cert.as_deref(),
Some("/etc/manta/tls/server.crt")
);
}
#[test]
fn server_settings_default_port_depends_on_tls() {
assert_eq!(ServerSettings::default_port(true), 8443);
assert_eq!(ServerSettings::default_port(false), 8080);
}
#[test]
fn server_settings_request_timeout_secs_defaults_to_600() {
let toml_str = r#"
listen_address = "0.0.0.0"
port = 8443
console_inactivity_timeout_secs = 1800
"#;
let parsed: ServerSettings = toml::from_str(toml_str).unwrap();
assert_eq!(parsed.request_timeout_secs, 600);
}
#[test]
fn server_settings_listen_address_and_port_default_to_none() {
let toml_str = r#"
console_inactivity_timeout_secs = 1800
"#;
let parsed: ServerSettings = toml::from_str(toml_str).unwrap();
assert!(parsed.listen_address.is_none());
assert!(parsed.port.is_none());
}
#[test]
fn server_settings_request_timeout_field_defaults_when_omitted() {
let toml_str = r#"
listen_address = "0.0.0.0"
port = 8443
console_inactivity_timeout_secs = 1800
"#;
let parsed: ServerSettings = toml::from_str(toml_str).unwrap();
assert_eq!(parsed.request_timeout_secs, 600);
}
#[test]
fn server_configuration_deserialize_missing_server_section_fails() {
let bad_toml = r#"
log = "info"
[sites]
"#;
let result = toml::from_str::<ServerConfiguration>(bad_toml);
assert!(result.is_err());
}
#[test]
fn server_settings_optional_tls_paths() {
let toml_str = r#"
listen_address = "0.0.0.0"
port = 8443
console_inactivity_timeout_secs = 1800
"#;
let parsed: ServerSettings = toml::from_str(toml_str).unwrap();
assert!(parsed.cert.is_none());
assert!(parsed.key.is_none());
}
}