use crate::connector::{ServiceConnector, ServiceInfo, ServiceLifecycle, ServiceStatus};
use super::helpers::{binary_on_path, detect_service};
pub struct AgentsConnector {
_priv: (),
}
impl AgentsConnector {
pub fn new() -> Self {
Self { _priv: () }
}
}
impl Default for AgentsConnector {
fn default() -> Self {
Self::new()
}
}
impl ServiceConnector for AgentsConnector {
fn id(&self) -> &'static str {
"trusty-agents"
}
fn display_name(&self) -> &'static str {
"Trusty Agents"
}
fn detect(&self) -> ServiceInfo {
if let Ok(dir) = trusty_common::resolve_data_dir("trusty-agents") {
return detect_service(
self.id(),
self.display_name(),
"tagent",
dir.join("http_addr"),
);
}
ServiceInfo {
id: self.id().to_string(),
display_name: self.display_name().to_string(),
status: if binary_on_path("tagent") {
ServiceStatus::Available
} else {
ServiceStatus::Absent
},
version: None,
url: None,
hint: None,
lifecycle: ServiceLifecycle::Daemon,
}
}
}
#[cfg(test)]
mod tests {
use super::super::ENV_LOCK;
use super::*;
use std::fs;
use std::net::TcpListener;
use tempfile::TempDir;
use trusty_common::DATA_DIR_OVERRIDE_ENV;
#[test]
fn agents_connector_absent_binary() {
if which::which("tagent").is_ok() {
return;
}
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let data_tmp = TempDir::new().expect("data-tempdir");
unsafe {
std::env::set_var(DATA_DIR_OVERRIDE_ENV, data_tmp.path());
}
let info = AgentsConnector::new().detect();
unsafe {
std::env::remove_var(DATA_DIR_OVERRIDE_ENV);
}
assert_eq!(info.status, ServiceStatus::Absent);
assert_eq!(info.id, "trusty-agents");
assert_eq!(info.display_name, "Trusty Agents");
}
#[test]
fn agents_connector_no_addr_file() {
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let data_tmp = TempDir::new().expect("data-tempdir");
unsafe {
std::env::set_var(DATA_DIR_OVERRIDE_ENV, data_tmp.path());
}
let info = AgentsConnector::new().detect();
unsafe {
std::env::remove_var(DATA_DIR_OVERRIDE_ENV);
}
assert!(info.url.is_none());
if which::which("tagent").is_ok() {
assert_eq!(info.status, ServiceStatus::Available);
} else {
assert_eq!(info.status, ServiceStatus::Absent);
}
}
#[test]
fn agents_connector_surfaces_url_via_http_addr() {
let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let data_tmp = TempDir::new().expect("data-tempdir");
unsafe {
std::env::set_var(DATA_DIR_OVERRIDE_ENV, data_tmp.path());
}
let agents_dir = data_tmp.path().join("trusty-agents");
fs::create_dir_all(&agents_dir).expect("mkdir");
let listener = TcpListener::bind("127.0.0.1:0").expect("bind free port");
let addr = listener.local_addr().expect("local_addr").to_string();
fs::write(agents_dir.join("http_addr"), &addr).expect("write addr");
let info = AgentsConnector::new().detect();
drop(listener);
unsafe {
std::env::remove_var(DATA_DIR_OVERRIDE_ENV);
}
if which::which("tagent").is_ok() {
assert_eq!(
info.status,
ServiceStatus::Running,
"http_addr present + port open must yield Running, got: {info:?}"
);
assert_eq!(
info.url,
Some(format!("http://{addr}")),
"Running status must include daemon base URL for proxy routing"
);
} else {
assert_eq!(info.status, ServiceStatus::Absent);
}
}
}