cloud_detect/blocking/mod.rs
1//! Blocking API for cloud provider detection.
2//!
3//! This module provides a blocking API for detecting the host's cloud provider. It is built on top of the asynchronous API
4//! and executes the blocking provider identification within threads.
5//!
6//! This module is intended for use in synchronous applications or in situations where the asynchronous API is not suitable.
7//! While not guaranteed, the performance of this module should be comparable to the asynchronous API.
8//!
9//! ## Optional
10//!
11//! This requires the `blocking` feature to be enabled.
12//!
13//! ## Usage
14//!
15//! Add the following to your `Cargo.toml`:
16//!
17//! ```toml
18//! # ...
19//! cloud_detect = { version = "2", features = ["blocking"] }
20//! tracing-subscriber = { version = "0.3", features = ["env-filter"] } # Optional; for logging
21//! ```
22//!
23//! ## Examples
24//!
25//! Detect the cloud provider and print the result (with default timeout).
26//!
27//! ```rust
28//! use cloud_detect::blocking::detect;
29//!
30//! tracing_subscriber::fmt::init(); // Optional; for logging
31//!
32//! let provider = detect(None).unwrap();
33//! println!("Detected provider: {:?}", provider);
34//! ```
35//!
36//! Detect the cloud provider and print the result (with custom timeout).
37//!
38//! ```rust
39//! use std::time::Duration;
40//!
41//! use cloud_detect::blocking::detect;
42//!
43//! tracing_subscriber::fmt::init(); // Optional; for logging
44//!
45//! let provider = detect(Some(Duration::from_secs(10))).unwrap();
46//! println!("Detected provider: {:?}", provider);
47//! ```
48
49pub(crate) mod providers;
50
51use std::sync::mpsc::RecvTimeoutError;
52use std::sync::mpsc::SyncSender;
53use std::sync::{mpsc, Arc, LazyLock, Mutex};
54use std::time::Duration;
55
56use anyhow::Result;
57
58use crate::blocking::providers::*;
59use crate::{ProviderId, DEFAULT_DETECTION_TIMEOUT};
60
61/// Represents a cloud service provider.
62#[allow(dead_code)]
63pub(crate) trait Provider: Send + Sync {
64 fn identifier(&self) -> ProviderId;
65 fn identify(&self, tx: SyncSender<ProviderId>, timeout: Duration);
66}
67
68type P = Arc<dyn Provider>;
69
70static PROVIDERS: LazyLock<Mutex<Vec<P>>> = LazyLock::new(|| {
71 Mutex::new(vec![
72 Arc::new(akamai::Akamai) as P,
73 Arc::new(alibaba::Alibaba) as P,
74 Arc::new(aws::Aws) as P,
75 Arc::new(azure::Azure) as P,
76 Arc::new(digitalocean::DigitalOcean) as P,
77 Arc::new(gcp::Gcp) as P,
78 Arc::new(oci::Oci) as P,
79 Arc::new(openstack::OpenStack) as P,
80 Arc::new(vultr::Vultr) as P,
81 ])
82});
83
84/// Returns a list of currently supported providers.
85///
86/// # Examples
87///
88/// Print the list of supported providers.
89///
90/// ```
91/// use cloud_detect::blocking::supported_providers;
92///
93/// let providers = supported_providers().unwrap();
94/// println!("Supported providers: {:?}", providers);
95/// ```
96pub fn supported_providers() -> Result<Vec<String>> {
97 let guard = PROVIDERS
98 .lock()
99 .map_err(|_| anyhow::anyhow!("Error locking providers"))?;
100 let providers: Vec<String> = guard.iter().map(|p| p.identifier().to_string()).collect();
101
102 drop(guard);
103
104 Ok(providers)
105}
106
107/// Detects the host's cloud provider.
108///
109/// Returns [ProviderId::Unknown] if the detection failed or timed out. If the detection was successful, it returns
110/// a value from [ProviderId](enum.ProviderId.html).
111///
112/// # Arguments
113///
114/// * `timeout` - Maximum time (seconds) allowed for detection. Defaults to [DEFAULT_DETECTION_TIMEOUT](constant.DEFAULT_DETECTION_TIMEOUT.html) if `None`.
115///
116/// # Examples
117///
118/// Detect the cloud provider and print the result (with default timeout).
119///
120/// ```
121/// use cloud_detect::blocking::detect;
122///
123/// let provider = detect(None).unwrap();
124/// println!("Detected provider: {:?}", provider);
125/// ```
126///
127/// Detect the cloud provider and print the result (with custom timeout).
128///
129/// ```
130/// use std::time::Duration;
131///
132/// use cloud_detect::blocking::detect;
133///
134/// let provider = detect(Some(Duration::from_secs(10))).unwrap();
135/// println!("Detected provider: {:?}", provider);
136/// ```
137pub fn detect(timeout: Option<Duration>) -> Result<ProviderId> {
138 let timeout = timeout.unwrap_or(DEFAULT_DETECTION_TIMEOUT);
139 let (tx, rx) = mpsc::sync_channel::<ProviderId>(1);
140 let guard = PROVIDERS
141 .lock()
142 .map_err(|_| anyhow::anyhow!("Error locking providers"))?;
143 let provider_entries: Vec<P> = guard.iter().cloned().collect();
144
145 for provider in provider_entries {
146 let tx = tx.clone();
147 std::thread::spawn(move || provider.identify(tx, timeout));
148 }
149
150 match rx.recv_timeout(timeout) {
151 Ok(provider_id) => Ok(provider_id),
152 Err(err) => match err {
153 RecvTimeoutError::Timeout => Ok(ProviderId::Unknown),
154 RecvTimeoutError::Disconnected => Err(anyhow::anyhow!("Error receiving message")),
155 },
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use anyhow::Result;
162
163 use super::*;
164
165 #[test]
166 fn test_supported_providers() -> Result<()> {
167 let providers = supported_providers()?;
168 assert_eq!(providers.len(), 9);
169 assert!(providers.contains(&akamai::IDENTIFIER.to_string()));
170 assert!(providers.contains(&alibaba::IDENTIFIER.to_string()));
171 assert!(providers.contains(&aws::IDENTIFIER.to_string()));
172 assert!(providers.contains(&azure::IDENTIFIER.to_string()));
173 assert!(providers.contains(&digitalocean::IDENTIFIER.to_string()));
174 assert!(providers.contains(&gcp::IDENTIFIER.to_string()));
175 assert!(providers.contains(&oci::IDENTIFIER.to_string()));
176 assert!(providers.contains(&openstack::IDENTIFIER.to_string()));
177 assert!(providers.contains(&vultr::IDENTIFIER.to_string()));
178
179 Ok(())
180 }
181}