use monocore::{
config::{Group, Monocore, Service},
orchestration::{LogRetentionPolicy, Orchestrator},
utils,
};
use std::{net::Ipv4Addr, time::Duration};
use tokio::time;
use tracing::info;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
let build_dir = format!("{}/build", env!("CARGO_MANIFEST_DIR"));
let oci_dir = format!("{}/oci", build_dir);
let rootfs_dir = format!("{}/rootfs", build_dir);
let image_ref = "library/alpine:latest";
let (_, _, rootfs_name) = utils::parse_image_ref(image_ref).unwrap();
let ref_rootfs_dir = format!("{}/reference/{}", rootfs_dir, rootfs_name);
utils::pull_docker_image(&oci_dir, image_ref).await?;
utils::merge_image_layers(&oci_dir, &ref_rootfs_dir, image_ref).await?;
let supervisor_path = format!("{}/../target/release/monokrun", env!("CARGO_MANIFEST_DIR"));
info!("Phase 1: Starting initial services with first Orchestrator");
{
let mut orchestrator = Orchestrator::new(&build_dir, &supervisor_path).await?;
let initial_config = create_services_config()?;
orchestrator.up(initial_config).await?;
time::sleep(Duration::from_secs(5)).await;
info!("Initial services status:");
print_service_status(&orchestrator).await?;
info!("Dropping initial Orchestrator (simulating process restart)");
}
info!("\nPhase 2: Loading existing services into new Orchestrator");
let mut loaded_orchestrator = Orchestrator::load_with_log_retention_policy(
&build_dir,
supervisor_path,
LogRetentionPolicy::with_max_age_days(7),
)
.await?;
info!("Services loaded from existing state:");
print_service_status(&loaded_orchestrator).await?;
time::sleep(Duration::from_secs(5)).await;
info!("\nServices still running after 5 seconds:");
print_service_status(&loaded_orchestrator).await?;
info!("\nCleaning up services");
loaded_orchestrator.down(None).await?;
Ok(())
}
async fn print_service_status(orchestrator: &Orchestrator) -> anyhow::Result<()> {
let statuses = orchestrator.status().await?;
println!("\nService Status:");
println!();
println!(
"{:<15} {:<10} {:<8} {:<8} {:<10} {:<10} {:<15} {:<15} {:<10} {:<10}",
"Service",
"Group",
"vCPUs",
"RAM",
"Sup PID",
"VM PID",
"Status",
"Assigned IP",
"CPU Usage",
"Mem Usage"
);
println!("{:-<120}", "");
for status in statuses {
let sup_pid = orchestrator
.get_running_services()
.get(status.get_name())
.copied()
.unwrap_or(0);
let cpu_pct = (status.get_state().get_metrics().get_cpu_usage() * 100.0).ceil();
let mem_mib =
(status.get_state().get_metrics().get_memory_usage() as f64) / (1024.0 * 1024.0);
println!(
"{:<15} {:<10} {:<8} {:<8} {:<10} {:<10} {:<15} {:<15} {:<10} {:<10}",
status.get_name(),
status.get_state().get_group().get_name(),
status.get_state().get_service().get_cpus(),
status.get_state().get_service().get_ram(),
sup_pid,
status.get_pid().unwrap_or(0),
format!("{:?}", status.get_state().get_status()),
status
.get_state()
.get_group_ip()
.map_or_else(|| Ipv4Addr::LOCALHOST, |ip| ip),
format!("{}%", cpu_pct as u64),
format!("{}MiB", mem_mib.ceil() as u64)
);
}
println!();
Ok(())
}
fn create_services_config() -> anyhow::Result<Monocore> {
let main_group = Group::builder().name("main").build();
let counter_service = Service::builder()
.name("counter")
.base("library/alpine:latest")
.group("main")
.command("/bin/sh")
.args([
"-c",
"i=0; while true; do echo Count: $i; i=$((i+1)); sleep 5; done",
])
.build();
let date_service = Service::builder()
.name("date-service")
.base("library/alpine:latest")
.group("main")
.command("/bin/sh")
.args(["-c", "while true; do date; sleep 3; done"])
.build();
let uptime_service = Service::builder()
.name("uptime")
.base("library/alpine:latest")
.group("main")
.command("/bin/sh")
.args(["-c", "while true; do uptime; sleep 4; done"])
.build();
let config = Monocore::builder()
.services(vec![counter_service, date_service, uptime_service])
.groups(vec![main_group])
.build()?;
Ok(config)
}