diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 937ff70efa..ce4ce860dc 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -461,6 +461,7 @@ struct Router { enable_mesh: bool, mesh_server_name: Option, mesh_host: String, + mesh_advertise_host: Option, mesh_port: u16, mesh_peer_urls: Vec, } @@ -475,6 +476,19 @@ impl Router { core::ConnectionMode::Http } + fn parse_mesh_socket_addr( + host: &str, + port: u16, + field: &str, + ) -> PyResult { + let addr = format!("{host}:{port}"); + addr.parse::().map_err(|e| { + pyo3::exceptions::PyValueError::new_err(format!( + "Invalid value for {field}='{host}': invalid mesh socket address '{addr}': {e}" + )) + }) + } + pub fn to_router_config(&self) -> config::ConfigResult { use config::{ DiscoveryConfig, MetricsConfig, PolicyConfig as ConfigPolicyConfig, RoutingMode, @@ -831,6 +845,7 @@ impl Router { mesh_host = String::from("0.0.0.0"), mesh_port = 39527u16, mesh_peer_urls = vec![], + mesh_advertise_host = None, ))] #[expect(clippy::too_many_arguments)] #[expect( @@ -936,6 +951,7 @@ impl Router { mesh_host: String, mesh_port: u16, mesh_peer_urls: Vec, + mesh_advertise_host: Option, ) -> PyResult { let mut all_urls = worker_urls.clone(); @@ -1049,6 +1065,7 @@ impl Router { enable_mesh, mesh_server_name, mesh_host, + mesh_advertise_host, mesh_port, mesh_peer_urls, }) @@ -1145,15 +1162,28 @@ impl Router { }) }) .transpose()?; - let self_addr_str = format!("{}:{}", self.mesh_host, self.mesh_port); - let self_addr = self_addr_str.parse::().map_err(|e| { - pyo3::exceptions::PyValueError::new_err(format!( - "Invalid mesh address '{self_addr_str}': {e}" - )) - })?; + let bind_addr = + Self::parse_mesh_socket_addr(&self.mesh_host, self.mesh_port, "mesh_host")?; + let (advertise_host, advertise_field) = + if let Some(host) = self.mesh_advertise_host.as_deref() { + (host, "mesh_advertise_host") + } else { + (self.mesh_host.as_str(), "mesh_host") + }; + let advertise_addr = Self::parse_mesh_socket_addr( + advertise_host, + self.mesh_port, + advertise_field, + )?; + if advertise_addr.ip().is_unspecified() { + return Err(pyo3::exceptions::PyValueError::new_err(format!( + "Invalid value for {advertise_field}='{advertise_host}': mesh advertise address cannot be unspecified; set mesh_advertise_host to a routable node IP" + ))); + } Some(smg_mesh::MeshServerConfig { self_name, - self_addr, + bind_addr, + advertise_addr, init_peer: peer, mtls_config: None, }) diff --git a/bindings/python/src/smg/router_args.py b/bindings/python/src/smg/router_args.py index 01c525b912..4c6b8f1850 100644 --- a/bindings/python/src/smg/router_args.py +++ b/bindings/python/src/smg/router_args.py @@ -160,6 +160,7 @@ class RouterArgs: enable_mesh: bool = False mesh_server_name: str | None = None mesh_host: str = "0.0.0.0" + mesh_advertise_host: str | None = None mesh_port: int = 39527 mesh_peer_urls: list[str] = dataclasses.field(default_factory=list) @@ -1065,6 +1066,15 @@ def add_cli_args( default="0.0.0.0", help="Mesh server bind address (default: 0.0.0.0)", ) + mesh_group.add_argument( + f"--{prefix}mesh-advertise-host", + type=str, + default=None, + help=( + "Routable mesh address to advertise to peers." + " Required when --mesh-host binds to 0.0.0.0." + ), + ) mesh_group.add_argument( f"--{prefix}mesh-port", type=int, diff --git a/bindings/python/tests/test_arg_parser.py b/bindings/python/tests/test_arg_parser.py index 4f1ea04e62..aa78aad7a0 100644 --- a/bindings/python/tests/test_arg_parser.py +++ b/bindings/python/tests/test_arg_parser.py @@ -43,6 +43,7 @@ def test_default_values(self): assert args.cb_failure_threshold == 10 assert args.disable_retries is False assert args.disable_circuit_breaker is False + assert args.mesh_advertise_host is None def test_parse_selector_valid(self): """Test parsing valid selector arguments.""" @@ -595,6 +596,28 @@ def test_parse_health_check_args(self): assert router_args.health_check_interval_secs == 30 assert router_args.health_check_endpoint == "/healthz" + def test_parse_mesh_advertise_host_args(self): + """Test parsing mesh advertise host arguments.""" + args = [ + "--enable-mesh", + "--mesh-host", + "0.0.0.0", + "--mesh-advertise-host", + "10.0.0.42", + "--mesh-port", + "39527", + "--mesh-peer-urls", + "10.0.0.43:39527", + ] + + router_args = parse_router_args(args) + + assert router_args.enable_mesh is True + assert router_args.mesh_host == "0.0.0.0" + assert router_args.mesh_advertise_host == "10.0.0.42" + assert router_args.mesh_port == 39527 + assert router_args.mesh_peer_urls == ["10.0.0.43:39527"] + def test_parse_cors_args(self): """Test parsing CORS arguments.""" args = [ diff --git a/crates/mesh/src/README.md b/crates/mesh/src/README.md index 04c88aefbf..be941bdc67 100644 --- a/crates/mesh/src/README.md +++ b/crates/mesh/src/README.md @@ -982,7 +982,8 @@ Configure mesh in your router configuration file: mesh: enabled: true self_name: "router-node-1" - self_addr: "0.0.0.0:8000" + bind_addr: "0.0.0.0:8000" + advertise_addr: "10.0.0.11:8000" init_peer: "router-node-2:8000" # Optional: initial peer for bootstrap ``` diff --git a/crates/mesh/src/controller.rs b/crates/mesh/src/controller.rs index fdeeaf08ac..941d214987 100644 --- a/crates/mesh/src/controller.rs +++ b/crates/mesh/src/controller.rs @@ -781,6 +781,7 @@ impl MeshController { let service = GossipService::new( Arc::new(parking_lot::RwLock::new(BTreeMap::new())), SocketAddr::from(([0, 0, 0, 0], 0)), + SocketAddr::from(([0, 0, 0, 0], 0)), &self_name, ) .with_stores(stores.clone()) diff --git a/crates/mesh/src/ping_server.rs b/crates/mesh/src/ping_server.rs index a13bbc3daa..9970f6ed6b 100644 --- a/crates/mesh/src/ping_server.rs +++ b/crates/mesh/src/ping_server.rs @@ -44,7 +44,8 @@ use super::{ #[derive(Debug)] pub struct GossipService { state: ClusterState, - self_addr: SocketAddr, + listen_addr: SocketAddr, + advertise_addr: SocketAddr, self_name: String, stores: Option>, // Optional state stores for CRDT-based sync sync_manager: Option>, // Optional sync manager for applying remote updates @@ -232,10 +233,16 @@ impl GossipService { } impl GossipService { - pub fn new(state: ClusterState, self_addr: SocketAddr, self_name: &str) -> Self { + pub fn new( + state: ClusterState, + listen_addr: SocketAddr, + advertise_addr: SocketAddr, + self_name: &str, + ) -> Self { Self { state, - self_addr, + listen_addr, + advertise_addr, self_name: self_name.to_string(), stores: None, sync_manager: None, @@ -277,7 +284,7 @@ impl GossipService { self, signal: F, ) -> Result<()> { - let listen_addr = self.self_addr; + let listen_addr = self.listen_addr; let service = GossipServer::new(self) .max_decoding_message_size(MAX_MESSAGE_SIZE) .max_encoding_message_size(MAX_MESSAGE_SIZE) @@ -361,7 +368,7 @@ impl Gossip for GossipService { }; Ok(Response::new(NodeUpdate { name: self.self_name.clone(), - address: self.self_addr.to_string(), + address: self.advertise_addr.to_string(), status: current_status, })) } @@ -800,7 +807,8 @@ impl Gossip for GossipService { // Generate and send snapshot chunks let service = GossipService { state: state.clone(), - self_addr: SocketAddr::from(([0, 0, 0, 0], 0)), // Not used in snapshot generation + listen_addr: SocketAddr::from(([0, 0, 0, 0], 0)), // Not used in snapshot generation + advertise_addr: SocketAddr::from(([0, 0, 0, 0], 0)), // Not used in snapshot generation self_name: self_name.clone(), stores: stores.clone(), sync_manager: sync_manager.clone(), diff --git a/crates/mesh/src/service.rs b/crates/mesh/src/service.rs index b5369252f7..63a4ed199c 100644 --- a/crates/mesh/src/service.rs +++ b/crates/mesh/src/service.rs @@ -41,7 +41,8 @@ pub type ClusterState = Arc>>; pub struct MeshServerConfig { pub self_name: String, - pub self_addr: SocketAddr, + pub bind_addr: SocketAddr, + pub advertise_addr: SocketAddr, pub init_peer: Option, pub mtls_config: Option, } @@ -267,18 +268,24 @@ pub struct MeshServerBuilder { state: ClusterState, stores: Arc, self_name: String, - self_addr: SocketAddr, + bind_addr: SocketAddr, + advertise_addr: SocketAddr, init_peer: Option, mtls_manager: Option>, } impl MeshServerBuilder { - pub fn new(self_name: String, self_addr: SocketAddr, init_peer: Option) -> Self { + pub fn new( + self_name: String, + bind_addr: SocketAddr, + advertise_addr: SocketAddr, + init_peer: Option, + ) -> Self { let state = Arc::new(RwLock::new(BTreeMap::from([( self_name.clone(), NodeState { name: self_name.clone(), - address: self_addr.to_string(), + address: advertise_addr.to_string(), status: NodeStatus::Alive as i32, version: 1, metadata: HashMap::new(), @@ -289,7 +296,8 @@ impl MeshServerBuilder { state, stores, self_name, - self_addr, + bind_addr, + advertise_addr, init_peer, mtls_manager: None, } @@ -319,7 +327,8 @@ impl MeshServerBuilder { stores: self.stores.clone(), sync_manager: sync_manager.clone(), self_name: self.self_name.clone(), - self_addr: self.self_addr, + bind_addr: self.bind_addr, + advertise_addr: self.advertise_addr, init_peer: self.init_peer, signal_rx, partition_detector: Some(partition_detector.clone()), @@ -330,7 +339,7 @@ impl MeshServerBuilder { stores: self.stores.clone(), sync_manager, self_name: self.self_name.clone(), - _self_addr: self.self_addr, + _self_addr: self.advertise_addr, signal_tx, partition_detector: Some(partition_detector), state_machine: Some(state_machine), @@ -342,8 +351,12 @@ impl MeshServerBuilder { impl From<&MeshServerConfig> for MeshServerBuilder { fn from(value: &MeshServerConfig) -> Self { - let mut builder = - MeshServerBuilder::new(value.self_name.clone(), value.self_addr, value.init_peer); + let mut builder = MeshServerBuilder::new( + value.self_name.clone(), + value.bind_addr, + value.advertise_addr, + value.init_peer, + ); if let Some(mtls_config) = &value.mtls_config { builder = builder.with_mtls(mtls_config.clone()); } @@ -356,7 +369,8 @@ pub struct MeshServer { stores: Arc, sync_manager: Arc, self_name: String, - self_addr: SocketAddr, + bind_addr: SocketAddr, + advertise_addr: SocketAddr, init_peer: Option, signal_rx: watch::Receiver, partition_detector: Option>, @@ -365,13 +379,18 @@ pub struct MeshServer { impl MeshServer { fn build_ping_server(&self) -> GossipService { - GossipService::new(self.state.clone(), self.self_addr, &self.self_name) + GossipService::new( + self.state.clone(), + self.bind_addr, + self.advertise_addr, + &self.self_name, + ) } fn build_controller(&self) -> MeshController { MeshController::new( self.state.clone(), - self.self_addr, + self.advertise_addr, &self.self_name, self.init_peer, self.stores.clone(), @@ -388,20 +407,24 @@ impl MeshServer { let bound_addr = listener .local_addr() .map_err(|e| anyhow::anyhow!("Failed to read listener local addr: {e}"))?; - if bound_addr != self.self_addr { + if bound_addr != self.bind_addr { return Err(anyhow::anyhow!( - "Listener/self_addr mismatch: listener={}, self_addr={}", + "Listener/bind_addr mismatch: listener={}, bind_addr={}", bound_addr, - self.self_addr + self.bind_addr )); } self.start_inner(Some(listener)).await } async fn start_inner(self, listener: Option) -> Result<()> { - log::info!("Mesh server listening on {}", self.self_addr); + log::info!( + "Mesh server listening on {} and advertising {}", + self.bind_addr, + self.advertise_addr + ); let self_name = self.self_name.clone(); - let self_address = self.self_addr; + let advertise_address = self.advertise_addr; #[expect( clippy::expect_used, @@ -456,7 +479,7 @@ impl MeshServer { log::info!( "Mesh server {} at {} is shutting down", self_name, - self_address + advertise_address ); Ok(()) } @@ -622,7 +645,7 @@ macro_rules! mesh_run { tracing::info!("Starting mesh server : {}", $addr); use $crate::MeshServerBuilder; let (server, handler) = - MeshServerBuilder::new($name.to_string(), $addr, $init_peer).build(); + MeshServerBuilder::new($name.to_string(), $addr, $addr, $init_peer).build(); #[expect(clippy::disallowed_methods, reason = "test macro: spawned server runs for the test lifetime and handler is returned for assertions")] tokio::spawn(async move { if let Err(e) = server.start().await { @@ -636,7 +659,7 @@ macro_rules! mesh_run { tracing::info!("Starting mesh server : {}", $addr); use $crate::MeshServerBuilder; let (server, handler) = - MeshServerBuilder::new($name.to_string(), $addr, $init_peer).build(); + MeshServerBuilder::new($name.to_string(), $addr, $addr, $init_peer).build(); #[expect(clippy::disallowed_methods, reason = "test macro: spawned server runs for the test lifetime and handler is returned for assertions")] tokio::spawn(async move { if let Err(e) = server.start_with_listener($listener).await { @@ -673,6 +696,52 @@ mod tests { }); } + #[tokio::test] + async fn test_ping_advertises_configured_address() { + init(); + + let (listener, bind_addr) = bind_node().await; + let advertise_addr = SocketAddr::from(([10, 20, 30, 40], bind_addr.port())); + let (server, handler) = + MeshServerBuilder::new("A".to_string(), bind_addr, advertise_addr, None).build(); + + #[expect( + clippy::disallowed_methods, + reason = "test server runs in the background for the duration of the assertion" + )] + tokio::spawn(async move { + if let Err(e) = server.start_with_listener(listener).await { + tracing::error!("Mesh server failed: {}", e); + } + }); + + wait_for( + || std::net::TcpStream::connect(bind_addr).is_ok(), + Duration::from_secs(5), + "mesh listener started", + ) + .await; + + let response = try_ping( + &NodeState { + name: "A".to_string(), + address: bind_addr.to_string(), + status: NodeStatus::Alive as i32, + version: 1, + metadata: HashMap::new(), + }, + Some(gossip_message::Payload::Ping(Ping { + state_sync: Some(StateSync { nodes: vec![] }), + })), + None, + ) + .await + .unwrap(); + + assert_eq!(response.address, advertise_addr.to_string()); + handler.shutdown(); + } + #[tokio::test] #[ignore = "SWIM failure detection for hard-shutdown nodes needs many gossip rounds; flaky under parallel CI load"] async fn test_state_synchronization() { diff --git a/docs/concepts/architecture/high-availability.md b/docs/concepts/architecture/high-availability.md index fc043df351..266866f8ab 100644 --- a/docs/concepts/architecture/high-availability.md +++ b/docs/concepts/architecture/high-availability.md @@ -116,10 +116,10 @@ Enable mesh networking with minimal flags: ```bash # Start first node -smg --enable-mesh --mesh-port 39527 +smg --enable-mesh --mesh-host 0.0.0.0 --mesh-advertise-host 10.0.0.1 --mesh-port 39527 # Start second node, joining the first -smg --enable-mesh --mesh-port 39528 --mesh-peer-urls 10.0.0.1:39527 +smg --enable-mesh --mesh-host 0.0.0.0 --mesh-advertise-host 10.0.0.2 --mesh-port 39528 --mesh-peer-urls 10.0.0.1:39527 ``` ### Command Line Options @@ -128,17 +128,20 @@ smg --enable-mesh --mesh-port 39528 --mesh-peer-urls 10.0.0.1:39527 |------|---------|-------------| | `--enable-mesh` | `false` | Enable mesh networking for HA deployments | | `--mesh-server-name` | (auto) | Unique identifier for this node in the cluster | -| `--mesh-host` | `0.0.0.0` | Host address for mesh communication | +| `--mesh-host` | `0.0.0.0` | Bind address for mesh communication | +| `--mesh-advertise-host` | `--mesh-host` | Routable address advertised to mesh peers | | `--mesh-port` | `39527` | Port for mesh gRPC communication | | `--mesh-peer-urls` | (none) | Initial peer URLs for cluster bootstrap | | `--router-selector` | (none) | Label selector for Kubernetes pod discovery (e.g. `app=smg tier=router`) | ### Python Entrypoint -`--enable-mesh` is also available in the Python entrypoint used by the Docker image: +`--enable-mesh` is also available in the Python entrypoint used by the Docker image. When +`--mesh-host` is left at `0.0.0.0`, set `--mesh-advertise-host` to a routable address such as +the pod IP: ```bash -smg launch --enable-mesh --mesh-port 39527 +smg launch --enable-mesh --mesh-host 0.0.0.0 --mesh-advertise-host 10.0.0.11 --mesh-port 39527 ``` ### Basic Configuration @@ -153,6 +156,7 @@ smg launch --enable-mesh --mesh-port 39527 smg --enable-mesh \ --mesh-server-name node1 \ --mesh-host 0.0.0.0 \ + --mesh-advertise-host 10.0.0.11 \ --mesh-port 39527 \ --host 0.0.0.0 \ --port 8000 @@ -167,8 +171,10 @@ smg --enable-mesh \ ```bash smg --enable-mesh \ --mesh-server-name node2 \ + --mesh-host 0.0.0.0 \ + --mesh-advertise-host 10.0.0.12 \ --mesh-port 39527 \ - --mesh-peer-urls "node1:39527" \ + --mesh-peer-urls "10.0.0.11:39527" \ --host 0.0.0.0 \ --port 8000 ``` @@ -182,8 +188,10 @@ smg --enable-mesh \ ```bash smg --enable-mesh \ --mesh-server-name node3 \ + --mesh-host 0.0.0.0 \ + --mesh-advertise-host 10.0.0.13 \ --mesh-port 39527 \ - --mesh-peer-urls "node1:39527,node2:39527" \ + --mesh-peer-urls "10.0.0.11:39527,10.0.0.12:39527" \ --host 0.0.0.0 \ --port 8000 ``` @@ -198,8 +206,9 @@ smg --enable-mesh \ export SMG_ENABLE_MESH=true export SMG_MESH_SERVER_NAME=node1 export SMG_MESH_HOST=0.0.0.0 +export SMG_MESH_ADVERTISE_HOST=10.0.0.11 export SMG_MESH_PORT=39527 -export SMG_MESH_PEER_URLS="node1:39527,node2:39527" +export SMG_MESH_PEER_URLS="10.0.0.11:39527,10.0.0.12:39527" ``` --- @@ -403,6 +412,7 @@ spec: - --enable-mesh - --mesh-server-name=$(POD_NAME) - --mesh-host=0.0.0.0 + - --mesh-advertise-host=$(POD_IP) - --mesh-port=39527 - --mesh-peer-urls=smg-0.smg-mesh:39527,smg-1.smg-mesh:39527,smg-2.smg-mesh:39527 - --worker-urls=$(WORKER_URLS) diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 74e48774a8..3b0d7d0f65 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -385,6 +385,7 @@ High-availability mesh networking for multi-router coordination. | `--enable-mesh` | Enable mesh server for HA multi-router coordination. Requires at least two SMG instances. | `false` | | `--mesh-server-name` | Name for this mesh node. If not set, a random name is generated (e.g., `Mesh_a1b2`). | Auto-generated | | `--mesh-host` | Bind address for the mesh server. | `0.0.0.0` | +| `--mesh-advertise-host` | Routable address advertised to other mesh peers. Required when `--mesh-host` is an unspecified bind address such as `0.0.0.0`. | `--mesh-host` | | `--mesh-port` | Port for the mesh server. | `39527` | | `--mesh-peer-urls` | Peer mesh node addresses to join (format: `host:port`). Used for initial cluster formation. | (none) | @@ -393,6 +394,7 @@ High-availability mesh networking for multi-router coordination. smg \ --enable-mesh \ --mesh-server-name router-1 \ + --mesh-advertise-host 192.168.1.10 \ --mesh-port 39527 \ --mesh-peer-urls 192.168.1.10:39527 ``` @@ -765,6 +767,7 @@ smg \ smg \ --enable-mesh \ --mesh-server-name router-1 \ + --mesh-advertise-host 192.168.1.10 \ --mesh-port 39527 \ --mesh-peer-urls 192.168.1.11:39527 \ --worker-urls http://worker1:8000 @@ -773,6 +776,7 @@ smg \ smg \ --enable-mesh \ --mesh-server-name router-2 \ + --mesh-advertise-host 192.168.1.11 \ --mesh-port 39527 \ --mesh-peer-urls 192.168.1.10:39527 \ --worker-urls http://worker2:8000 diff --git a/model_gateway/src/main.rs b/model_gateway/src/main.rs index 70bafb634f..d1a7f161ca 100644 --- a/model_gateway/src/main.rs +++ b/model_gateway/src/main.rs @@ -631,9 +631,15 @@ struct CliArgs { #[arg(long)] mesh_server_name: Option, + /// Bind address for the mesh listener. #[arg(long, default_value = "0.0.0.0")] mesh_host: String, + /// Advertised address for this mesh node. + /// Required when `--mesh-host` is an unspecified bind address such as `0.0.0.0`. + #[arg(long)] + mesh_advertise_host: Option, + #[arg(long, default_value_t = 39527)] mesh_port: u16, @@ -792,6 +798,74 @@ impl CliArgs { map } + fn parse_mesh_socket_addr( + host: &str, + port: u16, + field: &str, + ) -> ConfigResult { + let addr = format!("{host}:{port}"); + addr.parse::() + .map_err(|e| ConfigError::InvalidValue { + field: field.to_string(), + value: host.to_string(), + reason: format!("invalid mesh socket address '{addr}': {e}"), + }) + } + + fn build_mesh_server_config(&self) -> ConfigResult> { + if !self.enable_mesh { + return Ok(None); + } + + let self_name = if let Some(name) = &self.mesh_server_name { + name.to_string() + } else { + let mut rng = rand::rng(); + let random_string: String = (0..4).map(|_| rng.sample(Alphanumeric) as char).collect(); + format!("Mesh_{random_string}") + }; + + let peer = self + .mesh_peer_urls + .first() + .map(|url| { + url.parse::() + .map_err(|e| ConfigError::InvalidValue { + field: "mesh_peer_urls".to_string(), + value: url.clone(), + reason: format!("invalid socket address: {e}"), + }) + }) + .transpose()?; + + let bind_addr = Self::parse_mesh_socket_addr(&self.mesh_host, self.mesh_port, "mesh_host")?; + let (advertise_host, advertise_field) = + if let Some(host) = self.mesh_advertise_host.as_deref() { + (host, "mesh_advertise_host") + } else { + (self.mesh_host.as_str(), "mesh_host") + }; + let advertise_addr = + Self::parse_mesh_socket_addr(advertise_host, self.mesh_port, advertise_field)?; + + if advertise_addr.ip().is_unspecified() { + return Err(ConfigError::InvalidValue { + field: advertise_field.to_string(), + value: advertise_host.to_string(), + reason: + "mesh advertise address cannot be unspecified; set --mesh-advertise-host to a routable node IP".to_string(), + }); + } + + Ok(Some(MeshServerConfig { + self_name, + bind_addr, + advertise_addr, + init_peer: peer, + mtls_config: None, + })) + } + #[expect( clippy::panic, reason = "unreachable: clap value_parser restricts valid assignment modes" @@ -1229,37 +1303,7 @@ impl CliArgs { }; // ==================== Mesh Server ==================== - let mesh_server_config = if self.enable_mesh { - let self_name = if let Some(name) = &self.mesh_server_name { - name.to_string() - } else { - // If name is not set, use a random name - let mut rng = rand::rng(); - let random_string: String = - (0..4).map(|_| rng.sample(Alphanumeric) as char).collect(); - format!("Mesh_{random_string}") - }; - - let peer = self - .mesh_peer_urls - .first() - .and_then(|url| url.parse::().ok()); - if let Ok(addr) = - format!("{}:{}", self.mesh_host, self.mesh_port).parse::() - { - Some(MeshServerConfig { - self_name, - self_addr: addr, - init_peer: peer, - mtls_config: None, - }) - } else { - tracing::warn!("Invalid mesh server address, so mesh server will not be started"); - None - } - } else { - None - }; + let mesh_server_config = self.build_mesh_server_config()?; Ok(ServerConfig { host: self.host.clone(), diff --git a/model_gateway/src/server.rs b/model_gateway/src/server.rs index 1e24b3eac9..1c93aa32a0 100644 --- a/model_gateway/src/server.rs +++ b/model_gateway/src/server.rs @@ -1147,7 +1147,7 @@ pub async fn startup(config: ServerConfig) -> Result<(), Box