1use petgraph::Graph;
2
3use oct_cloud::aws::types::InstanceType;
4use oct_cloud::infra;
5
6pub mod backend;
7pub mod user_state;
8
9pub struct OrchestratorWithGraph;
10
11impl OrchestratorWithGraph {
12 pub async fn genesis(&self) -> Result<(), Box<dyn std::error::Error>> {
14 let config = oct_config::Config::new(None)?;
15
16 let infra_state_backend =
17 backend::get_state_backend::<infra::state::State>(&config.project.state_backend);
18
19 let user_services_graph = config.to_graph()?;
22 let instance_type = get_instance_type(&user_services_graph)?;
23
24 let genesis_spec_graph = infra::graph::GraphManager::get_genesis_graph(instance_type);
25
26 let infra_graph_manager = infra::graph::GraphManager::new().await;
27 let (resource_graph, _vm) = infra_graph_manager
28 .deploy_genesis_graph(&genesis_spec_graph)
29 .await?;
30
31 let state = infra::state::State::from_graph(&resource_graph);
32 let () = infra_state_backend.save(&state).await?;
33
34 Ok(())
35 }
36
37 pub async fn apply(&self) -> Result<(), Box<dyn std::error::Error>> {
38 let config = oct_config::Config::new(None)?;
39
40 let infra_state_backend =
41 backend::get_state_backend::<infra::state::State>(&config.project.state_backend);
42 let (infra_state, _loaded) = infra_state_backend.load().await?;
43
44 let vms = infra_state.get_vms();
45 let leader_vm = vms.first().ok_or("No VMs available")?;
46
47 let oct_ctl_client = oct_ctl_sdk::Client::new(leader_vm.public_ip.clone());
48 let () = oct_ctl_client.apply(config).await?;
49
50 Ok(())
51 }
52
53 pub async fn destroy(&self) -> Result<(), Box<dyn std::error::Error>> {
54 let config = oct_config::Config::new(None)?;
55
56 let infra_state_backend =
57 backend::get_state_backend::<infra::state::State>(&config.project.state_backend);
58 let (infra_state, _loaded) = infra_state_backend.load().await?;
59
60 let vms = infra_state.get_vms();
61 let leader_vm = vms.first().ok_or("No VMs available")?;
62
63 let oct_ctl_client = oct_ctl_sdk::Client::new(leader_vm.public_ip.clone());
64 let () = oct_ctl_client.destroy().await?;
65
66 let mut resource_graph = infra_state.to_graph();
67
68 let graph_manager = infra::graph::GraphManager::new().await;
69 let destroy_result = graph_manager.destroy(&mut resource_graph).await;
70
71 match destroy_result {
72 Ok(()) => {
73 infra_state_backend.remove().await?;
74
75 Ok(())
76 }
77 Err(e) => {
78 log::error!("Failed to destroy: {e}");
79
80 let current_infra_state = infra::state::State::from_graph(&resource_graph);
81
82 if let Err(save_err) = infra_state_backend.save(¤t_infra_state).await {
83 return Err(format!(
84 "Destruction failed: {e}. Additionally, failed to save state: {save_err}"
85 )
86 .into());
87 }
88
89 Err(format!("Partial destruction: {e}. Remaining resources saved to state.").into())
90 }
91 }
92 }
93}
94
95fn get_instance_type(
97 services_graph: &Graph<oct_config::Node, String>,
98) -> Result<InstanceType, Box<dyn std::error::Error>> {
99 let sorted_graph = infra::graph::kahn_traverse(services_graph)?;
100
101 let (total_services_cpus, total_services_memory) = sorted_graph
102 .iter()
103 .filter_map(|node_index| {
104 if let oct_config::Node::Resource(service) = &services_graph[*node_index] {
105 return Some(service);
106 }
107
108 None
109 })
110 .fold((0u32, 0u64), |(cpus, mem), service| {
111 (cpus + service.cpus, mem + service.memory)
112 });
113
114 let instance_type = InstanceType::from_resources(total_services_cpus, total_services_memory);
115
116 match instance_type {
117 Some(instance_type) => Ok(instance_type),
118 None => Err("Failed to get instance type to fit all services".into()),
119 }
120}
121
122#[cfg(test)]
123mod tests {}