cloud_node_discovery/
lib.rs

1//! Cloud Node Discovery for Rust
2//!
3//! This crate provides functionality to discover nodes in various cloud environments.
4//! Currently supports:
5//!
6//! - AWS EC2 instances (with tag-based filtering)
7//!
8//! # Example
9//!
10//! ```rust
11//! use cloud_node_discovery::{Discovery, DiscoveryError};
12//!
13//! #[tokio::main]
14//! async fn main() -> Result<(), DiscoveryError> {
15//!     let discovery = Discovery::new("aws", "region=us-east-1,tag_key=foo,tag_value=bar").await?;
16//!     let nodes = discovery.discover().await?;
17//!     println!("{:?}", nodes);
18//!     Ok(())
19//! }
20//! ```
21//!
22use async_trait::async_trait;
23use serde::Deserialize;
24use std::collections::HashMap;
25use thiserror::Error;
26
27use crate::config::parse_config;
28
29mod config;
30mod providers;
31
32#[derive(Debug, Error)]
33pub enum DiscoveryError {
34    #[error("Invalid configuration: {0}")]
35    ConfigError(String),
36    #[error("Provider error: {0}")]
37    ProviderError(String),
38    #[error("Unknown provider: {0}")]
39    UnknownProvider(String),
40}
41
42#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
43pub struct Node {
44    pub address: String,
45    pub meta: HashMap<String, String>,
46}
47
48#[async_trait]
49pub trait Provider {
50    async fn discover(&self) -> Result<Vec<Node>, DiscoveryError>;
51}
52
53pub struct Discovery {
54    provider: Box<dyn Provider>,
55}
56
57impl Discovery {
58    pub async fn new(provider_name: &str, config: &str) -> Result<Self, DiscoveryError> {
59        let config = parse_config(config)?;
60        println!("{:?}", config);
61        let provider: Box<dyn Provider> = match provider_name {
62            #[cfg(feature = "aws")]
63            "aws" => Box::new(providers::aws::AwsProvider::new(&config).await?),
64            #[cfg(feature = "upcloud")]
65            "upcloud" => Box::new(providers::upcloud::UpcloudProvider::new(&config).await?),
66            _ => return Err(DiscoveryError::UnknownProvider(provider_name.to_string())),
67        };
68
69        Ok(Discovery { provider })
70    }
71
72    pub async fn discover(&self) -> Result<Vec<Node>, DiscoveryError> {
73        self.provider.discover().await
74    }
75}
76
77pub struct DiscoveryBuilder {
78    provider_name: String,
79    config: HashMap<String, String>,
80}
81
82impl DiscoveryBuilder {
83    pub fn new(provider_name: &str) -> Self {
84        Self {
85            provider_name: provider_name.to_string(),
86            config: HashMap::new(),
87        }
88    }
89
90    pub fn with_config(mut self, key: &str, value: &str) -> Self {
91        self.config.insert(key.to_string(), value.to_string());
92        self
93    }
94
95    pub async fn build(self) -> Result<Discovery, DiscoveryError> {
96        Discovery::new(&self.provider_name, &self.config_to_string()).await
97    }
98
99    fn config_to_string(&self) -> String {
100        self.config
101            .iter()
102            .map(|(k, v)| format!("{}={}", k, v))
103            .collect::<Vec<_>>()
104            .join(",")
105    }
106}