sui_aws_orchestrator/client/
mod.rsuse std::{
fmt::Display,
net::{Ipv4Addr, SocketAddr},
};
use serde::{Deserialize, Serialize};
use super::error::CloudProviderResult;
pub mod aws;
#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash)]
pub struct Instance {
pub id: String,
pub region: String,
pub main_ip: Ipv4Addr,
pub tags: Vec<String>,
pub specs: String,
pub status: String,
}
impl Instance {
pub fn is_active(&self) -> bool {
self.status.to_lowercase() == "running"
}
pub fn is_inactive(&self) -> bool {
!self.is_active()
}
pub fn is_terminated(&self) -> bool {
self.status.to_lowercase() == "terminated"
}
pub fn ssh_address(&self) -> SocketAddr {
format!("{}:22", self.main_ip).parse().unwrap()
}
#[cfg(test)]
pub fn new_for_test(id: String) -> Self {
Self {
id,
region: Default::default(),
main_ip: Ipv4Addr::new(127, 0, 0, 1),
tags: Default::default(),
specs: Default::default(),
status: Default::default(),
}
}
}
#[async_trait::async_trait]
pub trait ServerProviderClient: Display {
const USERNAME: &'static str;
async fn list_instances(&self) -> CloudProviderResult<Vec<Instance>>;
async fn start_instances<'a, I>(&self, instances: I) -> CloudProviderResult<()>
where
I: Iterator<Item = &'a Instance> + Send;
async fn stop_instances<'a, I>(&self, instance_ids: I) -> CloudProviderResult<()>
where
I: Iterator<Item = &'a Instance> + Send;
async fn create_instance<S>(&self, region: S) -> CloudProviderResult<Instance>
where
S: Into<String> + Serialize + Send;
async fn delete_instance(&self, instance: Instance) -> CloudProviderResult<()>;
async fn register_ssh_public_key(&self, public_key: String) -> CloudProviderResult<()>;
async fn instance_setup_commands(&self) -> CloudProviderResult<Vec<String>>;
}
#[cfg(test)]
pub mod test_client {
use std::{fmt::Display, sync::Mutex};
use serde::Serialize;
use crate::{error::CloudProviderResult, settings::Settings};
use super::{Instance, ServerProviderClient};
pub struct TestClient {
settings: Settings,
instances: Mutex<Vec<Instance>>,
}
impl TestClient {
pub fn new(settings: Settings) -> Self {
Self {
settings,
instances: Mutex::new(Vec::new()),
}
}
}
impl Display for TestClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TestClient")
}
}
#[async_trait::async_trait]
impl ServerProviderClient for TestClient {
const USERNAME: &'static str = "root";
async fn list_instances(&self) -> CloudProviderResult<Vec<Instance>> {
let guard = self.instances.lock().unwrap();
Ok(guard.clone())
}
async fn start_instances<'a, I>(&self, instances: I) -> CloudProviderResult<()>
where
I: Iterator<Item = &'a Instance> + Send,
{
let instance_ids: Vec<_> = instances.map(|x| x.id.clone()).collect();
let mut guard = self.instances.lock().unwrap();
for instance in guard.iter_mut().filter(|x| instance_ids.contains(&x.id)) {
instance.status = "running".into();
}
Ok(())
}
async fn stop_instances<'a, I>(&self, instances: I) -> CloudProviderResult<()>
where
I: Iterator<Item = &'a Instance> + Send,
{
let instance_ids: Vec<_> = instances.map(|x| x.id.clone()).collect();
let mut guard = self.instances.lock().unwrap();
for instance in guard.iter_mut().filter(|x| instance_ids.contains(&x.id)) {
instance.status = "stopped".into();
}
Ok(())
}
async fn create_instance<S>(&self, region: S) -> CloudProviderResult<Instance>
where
S: Into<String> + Serialize + Send,
{
let mut guard = self.instances.lock().unwrap();
let id = guard.len();
let instance = Instance {
id: id.to_string(),
region: region.into(),
main_ip: format!("0.0.0.{id}").parse().unwrap(),
tags: Vec::new(),
specs: self.settings.specs.clone(),
status: "running".into(),
};
guard.push(instance.clone());
Ok(instance)
}
async fn delete_instance(&self, instance: Instance) -> CloudProviderResult<()> {
let mut guard = self.instances.lock().unwrap();
guard.retain(|x| x.id != instance.id);
Ok(())
}
async fn register_ssh_public_key(&self, _public_key: String) -> CloudProviderResult<()> {
Ok(())
}
async fn instance_setup_commands(&self) -> CloudProviderResult<Vec<String>> {
Ok(Vec::new())
}
}
}