use crate::node::Node;
use std::env;
pub struct RankDetector;
impl RankDetector {
pub fn identify_main_rank(nodes: &[Node]) -> Option<usize> {
if nodes.is_empty() {
return None;
}
if Self::is_backendai_main() {
if let Some(host) = Self::get_backendai_host() {
if let Some(idx) = nodes.iter().position(|n| n.host == host) {
return Some(idx);
}
}
}
Some(0)
}
fn is_backendai_main() -> bool {
env::var("BACKENDAI_CLUSTER_ROLE")
.ok()
.map(|role| role.to_lowercase() == "main")
.unwrap_or(false)
}
fn get_backendai_host() -> Option<String> {
env::var("BACKENDAI_CLUSTER_HOST").ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
fn test_empty_nodes_returns_none() {
let nodes: Vec<Node> = vec![];
assert_eq!(RankDetector::identify_main_rank(&nodes), None);
}
#[test]
#[serial]
fn test_fallback_to_first_node() {
env::remove_var("BACKENDAI_CLUSTER_ROLE");
env::remove_var("BACKENDAI_CLUSTER_HOST");
let nodes = vec![
Node::new("host1".to_string(), 22, "user".to_string()),
Node::new("host2".to_string(), 22, "user".to_string()),
Node::new("host3".to_string(), 22, "user".to_string()),
];
assert_eq!(RankDetector::identify_main_rank(&nodes), Some(0));
}
#[test]
#[serial]
fn test_backendai_role_detection() {
env::set_var("BACKENDAI_CLUSTER_ROLE", "main");
assert!(RankDetector::is_backendai_main());
env::remove_var("BACKENDAI_CLUSTER_ROLE");
}
#[test]
#[serial]
fn test_backendai_role_case_insensitive() {
env::set_var("BACKENDAI_CLUSTER_ROLE", "MAIN");
assert!(RankDetector::is_backendai_main());
env::set_var("BACKENDAI_CLUSTER_ROLE", "Main");
assert!(RankDetector::is_backendai_main());
env::remove_var("BACKENDAI_CLUSTER_ROLE");
}
#[test]
#[serial]
fn test_backendai_role_non_main() {
env::set_var("BACKENDAI_CLUSTER_ROLE", "sub");
assert!(!RankDetector::is_backendai_main());
env::remove_var("BACKENDAI_CLUSTER_ROLE");
}
#[test]
#[serial]
fn test_backendai_host_matching() {
let nodes = vec![
Node::new("host1".to_string(), 22, "user".to_string()),
Node::new("host2".to_string(), 22, "user".to_string()),
Node::new("host3".to_string(), 22, "user".to_string()),
];
env::set_var("BACKENDAI_CLUSTER_ROLE", "main");
env::set_var("BACKENDAI_CLUSTER_HOST", "host2");
assert_eq!(RankDetector::identify_main_rank(&nodes), Some(1));
env::remove_var("BACKENDAI_CLUSTER_ROLE");
env::remove_var("BACKENDAI_CLUSTER_HOST");
}
#[test]
#[serial]
fn test_backendai_host_not_found_fallback() {
let nodes = vec![
Node::new("host1".to_string(), 22, "user".to_string()),
Node::new("host2".to_string(), 22, "user".to_string()),
];
env::set_var("BACKENDAI_CLUSTER_ROLE", "main");
env::set_var("BACKENDAI_CLUSTER_HOST", "nonexistent");
assert_eq!(RankDetector::identify_main_rank(&nodes), Some(0));
env::remove_var("BACKENDAI_CLUSTER_ROLE");
env::remove_var("BACKENDAI_CLUSTER_HOST");
}
#[test]
fn test_single_node() {
let nodes = vec![Node::new("host1".to_string(), 22, "user".to_string())];
assert_eq!(RankDetector::identify_main_rank(&nodes), Some(0));
}
}