use crate::metrics::CloudInfo;
use std::time::Duration;
use ureq::config::Config as UreqConfig;
mod alicloud;
mod aws;
mod azure;
mod gcp;
mod hetzner;
mod ovh;
mod upcloud;
const IMDS_TIMEOUT: Duration = Duration::from_secs(1);
fn new_imds_agent() -> ureq::Agent {
UreqConfig::builder()
.timeout_global(Some(IMDS_TIMEOUT))
.build()
.new_agent()
}
fn imds_get(agent: &ureq::Agent, url: &str) -> Option<String> {
imds_get_headers(agent, url, &[])
}
fn imds_get_headers(agent: &ureq::Agent, url: &str, headers: &[(&str, &str)]) -> Option<String> {
let mut req = agent.get(url);
for (k, v) in headers {
req = req.header(*k, *v);
}
req.call()
.ok()
.and_then(|mut r| r.body_mut().read_to_string().ok())
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
}
const PROBES: &[fn() -> Option<CloudInfo>] = &[
aws::probe,
gcp::probe,
azure::probe,
hetzner::probe,
upcloud::probe,
alicloud::probe,
ovh::probe,
];
pub fn probe_cloud() -> CloudInfo {
let mut handles = Vec::new();
let mut deferred = Vec::new();
for &p in PROBES {
match crate::thread_util::spawn_named("cloud-probe", p) {
Some(h) => handles.push(h),
None => deferred.push(p),
}
}
for handle in handles {
if let Ok(Some(info)) = handle.join() {
return info;
}
}
for p in deferred {
if let Some(info) = p() {
return info;
}
}
CloudInfo::default()
}
pub fn spawn_cloud_discovery() -> Option<std::thread::JoinHandle<CloudInfo>> {
crate::thread_util::spawn_named("cloud-discovery", probe_cloud)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spawn_cloud_discovery_joins_without_panic() {
let cloud = match spawn_cloud_discovery() {
Some(handle) => handle.join().expect("cloud discovery thread panicked"),
None => probe_cloud(),
};
let _cloud = cloud;
}
}