rusticity_core/
ec2.rs

1use crate::config::AwsConfig;
2use anyhow::Result;
3
4#[derive(Clone, Debug)]
5pub struct Instance {
6    pub instance_id: String,
7    pub name: String,
8    pub state: String,
9    pub instance_type: String,
10    pub availability_zone: String,
11    pub public_ipv4_dns: String,
12    pub public_ipv4_address: String,
13    pub elastic_ip: String,
14    pub ipv6_ips: String,
15    pub monitoring: String,
16    pub security_groups: String,
17    pub key_name: String,
18    pub launch_time: String,
19    pub platform_details: String,
20    pub status_checks: String,
21    pub alarm_status: String,
22}
23
24pub struct Ec2Client {
25    config: AwsConfig,
26}
27
28impl Ec2Client {
29    pub fn new(config: AwsConfig) -> Self {
30        Self { config }
31    }
32
33    pub async fn list_instances(&self) -> Result<Vec<Instance>> {
34        let client = self.config.ec2_client().await;
35        let mut instances = Vec::new();
36        let mut next_token: Option<String> = None;
37
38        loop {
39            let mut request = client.describe_instances();
40            if let Some(token) = next_token {
41                request = request.next_token(token);
42            }
43
44            let response = request.send().await?;
45
46            if let Some(reservations) = response.reservations {
47                for reservation in reservations {
48                    if let Some(insts) = reservation.instances {
49                        for inst in insts {
50                            let tags: std::collections::HashMap<String, String> = inst
51                                .tags()
52                                .iter()
53                                .filter_map(|t| {
54                                    Some((t.key()?.to_string(), t.value()?.to_string()))
55                                })
56                                .collect();
57
58                            let name = tags.get("Name").cloned().unwrap_or_default();
59
60                            let state = inst
61                                .state()
62                                .and_then(|s| s.name())
63                                .map(|n| n.as_str().to_string())
64                                .unwrap_or_default();
65
66                            let security_groups = inst
67                                .security_groups()
68                                .iter()
69                                .filter_map(|sg| sg.group_name())
70                                .collect::<Vec<_>>()
71                                .join(", ");
72
73                            let ipv6_ips = inst
74                                .network_interfaces()
75                                .iter()
76                                .flat_map(|ni| ni.ipv6_addresses())
77                                .filter_map(|ip| ip.ipv6_address())
78                                .collect::<Vec<_>>()
79                                .join(", ");
80
81                            let launch_time = inst
82                                .launch_time()
83                                .map(|dt| {
84                                    let timestamp = dt.secs();
85                                    chrono::DateTime::from_timestamp(timestamp, 0)
86                                        .map(|dt| dt.format("%Y-%m-%d %H:%M:%S (UTC)").to_string())
87                                        .unwrap_or_default()
88                                })
89                                .unwrap_or_default();
90
91                            instances.push(Instance {
92                                instance_id: inst.instance_id().unwrap_or("").to_string(),
93                                name,
94                                state,
95                                instance_type: inst
96                                    .instance_type()
97                                    .map(|t| t.as_str().to_string())
98                                    .unwrap_or_default(),
99                                availability_zone: inst
100                                    .placement()
101                                    .and_then(|p| p.availability_zone())
102                                    .unwrap_or("")
103                                    .to_string(),
104                                public_ipv4_dns: inst.public_dns_name().unwrap_or("").to_string(),
105                                public_ipv4_address: inst
106                                    .public_ip_address()
107                                    .unwrap_or("")
108                                    .to_string(),
109                                elastic_ip: String::new(),
110                                ipv6_ips,
111                                monitoring: inst
112                                    .monitoring()
113                                    .and_then(|m| m.state())
114                                    .map(|s| s.as_str().to_string())
115                                    .unwrap_or_default(),
116                                security_groups,
117                                key_name: inst.key_name().unwrap_or("").to_string(),
118                                launch_time,
119                                platform_details: inst.platform_details().unwrap_or("").to_string(),
120                                status_checks: String::new(),
121                                alarm_status: String::new(),
122                            });
123                        }
124                    }
125                }
126            }
127
128            next_token = response.next_token;
129            if next_token.is_none() {
130                break;
131            }
132        }
133
134        Ok(instances)
135    }
136}