use k8s_openapi::api::core::v1::ConfigMap; use kube::api::ObjectMeta; use kube::Resource; use crate::types::ErgoNetwork; pub fn build_configmap(network: &ErgoNetwork, oper_hash: Option<&str>, oauth2_secret: Option<&str>) -> ConfigMap { let name = network.metadata.name.clone().unwrap(); let ns = network.metadata.namespace.clone().unwrap(); let oref = network.controller_owner_ref(&()).unwrap(); let config = generate_ergo_config(network, oper_hash, oauth2_secret); ConfigMap { metadata: ObjectMeta { name: Some(format!("{name}-config")), namespace: Some(ns), owner_references: Some(vec![oref]), ..Default::default() }, data: Some([("ircd.yaml".to_string(), config)].into()), ..Default::default() } } fn generate_ergo_config(network: &ErgoNetwork, oper_hash: Option<&str>, oauth2_secret: Option<&str>) -> String { let spec = &network.spec; let network_name = spec .network_name .as_deref() .unwrap_or(&spec.hostname); let mut lines = Vec::new(); lines.push(format!("server:\n name: {}", spec.hostname)); lines.push(" listeners:".to_string()); for listener in &spec.listeners { if listener.tls { lines.push(format!( " \"{}\":\n tls:\n cert: /config/tls/tls.crt\n key: /config/tls/tls.key", listener.address )); } else { lines.push(format!(" \"{}\":", listener.address)); } } lines.push(" max-sendq: 96k".to_string()); if let Some(cloaking) = &spec.cloaking { lines.push(format!( " ip-cloaking:\n enabled: true\n enabled-for-always-on: true\n netname: \"{}\"\n cidr-len-ipv4: 32\n cidr-len-ipv6: 64\n num-bits: 64", cloaking.netname )); } lines.push(format!("\nnetwork:\n name: \"{}\"", network_name)); lines.push("\ndatastore:\n path: /ircd/ircd.db\n autoupgrade: true".to_string()); lines.push("\nlimits:\n nicklen: 32\n identlen: 20\n realnamelen: 150\n channellen: 64\n awaylen: 390\n kicklen: 390\n topiclen: 390\n monitor-entries: 100\n whowas-entries: 100\n chan-list-modes: 100\n registration-messages: 1024\n multiline:\n max-bytes: 4096\n max-lines: 100".to_string()); if let Some(hash) = oper_hash { lines.push(format!( "\nopers:\n admin:\n class: server-admin\n password: \"{}\"", hash )); } lines.push("\naccounts:\n vhosts:\n enabled: true\n max-length: 64\n valid-regexp: '^[a-zA-Z0-9.\\-]+$'".to_string()); if let Some(oauth2) = &spec.oauth2 { if let Some(secret) = oauth2_secret { lines.push(format!( " oauth2:\n enabled: true\n autocreate: {}\n introspection-url: \"{}\"\n introspection-timeout: 10s\n client-id: ergo\n client-secret: \"{}\"", oauth2.autocreate, oauth2.introspection_url, secret )); } } lines.push(String::new()); lines.join("\n") } #[cfg(test)] mod tests { use super::*; use crate::testutil::test_network; #[test] fn config_contains_hostname() { let cm = build_configmap(&test_network(), None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains("test-network.irc.now")); } #[test] fn config_contains_network_name() { let cm = build_configmap(&test_network(), None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains("Test Network")); } #[test] fn config_contains_listeners() { let cm = build_configmap(&test_network(), None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains(":6697")); assert!(config.contains(":6667")); } #[test] fn config_contains_tls_paths() { let cm = build_configmap(&test_network(), None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains("/config/tls/tls.crt")); assert!(config.contains("/config/tls/tls.key")); } #[test] fn config_contains_oper_hash() { let hash = "$2a$10$examplehash"; let cm = build_configmap(&test_network(), Some(hash), None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains(hash)); assert!(config.contains("server-admin")); } #[test] fn config_without_oper_omits_opers_section() { let cm = build_configmap(&test_network(), None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(!config.contains("opers")); } #[test] fn network_name_defaults_to_hostname() { let mut network = test_network(); network.spec.network_name = None; let cm = build_configmap(&network, None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains("test-network.irc.now")); } #[test] fn configmap_named_correctly() { let cm = build_configmap(&test_network(), None, None); assert_eq!(cm.metadata.name.unwrap(), "test-network-config"); } #[test] fn configmap_has_owner_reference() { let cm = build_configmap(&test_network(), None, None); let orefs = cm.metadata.owner_references.unwrap(); assert_eq!(orefs.len(), 1); assert_eq!(orefs[0].name, "test-network"); } #[test] fn config_contains_oauth2_when_configured() { let cm = build_configmap(&test_network(), None, Some("test-secret")); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains("oauth2")); assert!(config.contains("introspection")); assert!(config.contains("autocreate: true")); } #[test] fn config_omits_oauth2_when_not_configured() { let mut network = test_network(); network.spec.oauth2 = None; let cm = build_configmap(&network, None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(!config.contains("oauth2")); } #[test] fn config_omits_oauth2_without_secret() { let cm = build_configmap(&test_network(), None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(!config.contains("oauth2")); } #[test] fn config_contains_cloaking_when_configured() { let cm = build_configmap(&test_network(), None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(config.contains("ip-cloaking")); assert!(config.contains("irc.now")); } #[test] fn config_omits_cloaking_when_not_configured() { let mut network = test_network(); network.spec.cloaking = None; let cm = build_configmap(&network, None, None); let data = cm.data.unwrap(); let config = &data["ircd.yaml"]; assert!(!config.contains("ip-cloaking")); } }