rs-zero 0.2.8

Rust-first microservice framework inspired by go-zero engineering practices
Documentation
use serde::{Deserialize, Serialize};

use crate::discovery::{InstanceEndpoint, ServiceInstance};

use crate::discovery_kube::{KubeDiscoveryConfig, KubeDiscoveryError, KubeDiscoveryResult};

/// Minimal Kubernetes endpoint fixture model.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KubeEndpoint {
    /// Service name.
    pub service: String,
    /// Instance id.
    pub id: String,
    /// IP or DNS name.
    pub address: String,
    /// Port number.
    pub port: u16,
    /// Optional port name.
    pub port_name: Option<String>,
    /// Readiness flag.
    pub ready: bool,
}

/// Maps endpoint fixtures into service instances.
pub fn map_endpoints(
    config: &KubeDiscoveryConfig,
    payload: &str,
) -> KubeDiscoveryResult<Vec<ServiceInstance>> {
    let endpoints: Vec<KubeEndpoint> = serde_json::from_str(payload)?;
    endpoints
        .into_iter()
        .filter(|endpoint| endpoint.ready)
        .filter(|endpoint| {
            config
                .port_name
                .as_ref()
                .is_none_or(|port_name| endpoint.port_name.as_ref() == Some(port_name))
        })
        .map(|endpoint| {
            let mut instance = ServiceInstance::new(
                endpoint.service,
                endpoint.id,
                InstanceEndpoint::new(endpoint.address, endpoint.port)
                    .map_err(|error| KubeDiscoveryError::Backend(error.to_string()))?,
            );
            for (key, value) in &config.metadata {
                instance = instance.with_metadata(key, value);
            }
            Ok(instance)
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use crate::discovery_kube::{KubeDiscoveryConfig, map_endpoints};

    #[test]
    fn mapper_filters_ready_named_ports() {
        let mut config = KubeDiscoveryConfig {
            port_name: Some("http".to_string()),
            ..Default::default()
        };
        config
            .metadata
            .insert("namespace".to_string(), "default".to_string());
        let instances = map_endpoints(
            &config,
            r#"[{"service":"api","id":"pod-a","address":"10.0.0.1","port":8080,"port_name":"http","ready":true},
                {"service":"api","id":"pod-b","address":"10.0.0.2","port":9090,"port_name":"admin","ready":true}]"#,
        )
        .expect("instances");
        assert_eq!(instances.len(), 1);
        assert_eq!(instances[0].metadata["namespace"], "default");
    }
}