krata: rework into daemon / controller structure

This commit is contained in:
Alex Zenla
2024-03-05 11:35:25 +00:00
parent 17889d1c64
commit 8653fd6249
45 changed files with 1597 additions and 493 deletions

View File

@ -1,20 +1,20 @@
use anyhow::{anyhow, Result};
use clap::{Parser, Subcommand};
use env_logger::Env;
use kratactrl::{
ctl::{
console::ControllerConsole, destroy::ControllerDestroy, launch::ControllerLaunch,
ControllerContext,
},
launch::GuestLaunchRequest,
use krata::control::{
ConsoleStreamRequest, DestroyRequest, LaunchRequest, ListRequest, Request, Response,
};
use std::path::PathBuf;
use kratactl::{
client::{KrataClient, KrataClientTransport},
console::XenConsole,
};
use tokio::net::UnixStream;
#[derive(Parser, Debug)]
#[command(version, about)]
struct ControllerArgs {
#[arg(short, long, default_value = "auto")]
store: String,
#[arg(long, default_value = "/var/lib/krata/daemon.socket")]
connection: String,
#[command(subcommand)]
command: Commands,
@ -25,10 +25,6 @@ enum Commands {
List {},
Launch {
#[arg(short, long, default_value = "auto")]
kernel: String,
#[arg(short = 'r', long, default_value = "auto")]
initrd: String,
#[arg(short, long, default_value_t = 1)]
cpus: u32,
#[arg(short, long, default_value_t = 512)]
@ -37,8 +33,6 @@ enum Commands {
env: Option<Vec<String>>,
#[arg(short, long)]
attach: bool,
#[arg(long)]
debug: bool,
#[arg()]
image: String,
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
@ -46,11 +40,11 @@ enum Commands {
},
Destroy {
#[arg()]
container: String,
guest: String,
},
Console {
#[arg()]
container: String,
guest: String,
},
}
@ -59,77 +53,81 @@ async fn main() -> Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("warn")).init();
let args = ControllerArgs::parse();
let store_path = if args.store == "auto" {
default_store_path().ok_or_else(|| anyhow!("unable to determine default store path"))
} else {
Ok(PathBuf::from(args.store))
}?;
let store_path = store_path
.to_str()
.map(|x| x.to_string())
.ok_or_else(|| anyhow!("unable to convert store path to string"))?;
let mut context = ControllerContext::new(store_path.clone()).await?;
let stream = UnixStream::connect(&args.connection).await?;
let transport = KrataClientTransport::new(stream).await?;
let client = KrataClient::new(transport).await?;
match args.command {
Commands::Launch {
kernel,
initrd,
image,
cpus,
mem,
attach,
env,
run,
debug,
} => {
let kernel = map_kernel_path(&store_path, kernel);
let initrd = map_initrd_path(&store_path, initrd);
let mut launch = ControllerLaunch::new(&mut context);
let request = GuestLaunchRequest {
kernel_path: &kernel,
initrd_path: &initrd,
image: &image,
let request = LaunchRequest {
image,
vcpus: cpus,
mem,
env,
run: if run.is_empty() { None } else { Some(run) },
debug,
};
let info = launch.perform(request).await?;
println!("launched guest: {}", info.uuid);
let Response::Launch(response) = client.send(Request::Launch(request)).await? else {
return Err(anyhow!("invalid response type"));
};
println!("launched guest: {}", response.guest.id);
if attach {
let mut console = ControllerConsole::new(&mut context);
console.perform(&info.uuid.to_string()).await?;
let request = ConsoleStreamRequest {
guest: response.guest.id.clone(),
};
let Response::ConsoleStream(response) =
client.send(Request::ConsoleStream(request)).await?
else {
return Err(anyhow!("invalid response type"));
};
let stream = client.acquire(response.stream).await?;
let console = XenConsole::new(stream).await?;
console.attach().await?;
}
}
Commands::Destroy { container } => {
let mut destroy = ControllerDestroy::new(&mut context);
destroy.perform(&container).await?;
Commands::Destroy { guest } => {
let request = DestroyRequest { guest };
let Response::Destroy(response) = client.send(Request::Destroy(request)).await? else {
return Err(anyhow!("invalid response type"));
};
println!("destroyed guest: {}", response.guest);
}
Commands::Console { container } => {
let mut console = ControllerConsole::new(&mut context);
console.perform(&container).await?;
Commands::Console { guest } => {
let request = ConsoleStreamRequest { guest };
let Response::ConsoleStream(response) =
client.send(Request::ConsoleStream(request)).await?
else {
return Err(anyhow!("invalid response type"));
};
let stream = client.acquire(response.stream).await?;
let console = XenConsole::new(stream).await?;
console.attach().await?;
}
Commands::List { .. } => {
let containers = context.list().await?;
let request = ListRequest {};
let Response::List(response) = client.send(Request::List(request)).await? else {
return Err(anyhow!("invalid response type"));
};
let mut table = cli_tables::Table::new();
let header = vec!["uuid", "ipv4", "ipv6", "image"];
table.push_row(&header)?;
for container in containers {
let row = vec![
container.uuid.to_string(),
container.ipv4,
container.ipv6,
container.image,
];
table.push_row_string(&row)?;
for guest in response.guests {
table.push_row_string(&vec![
guest.id,
guest.ipv4.unwrap_or("none".to_string()),
guest.ipv6.unwrap_or("none".to_string()),
guest.image,
])?;
}
if table.num_records() == 1 {
println!("no guests have been launched");
} else {
@ -139,28 +137,3 @@ async fn main() -> Result<()> {
}
Ok(())
}
fn map_kernel_path(store: &str, value: String) -> String {
if value == "auto" {
return format!("{}/default/kernel", store);
}
value
}
fn map_initrd_path(store: &str, value: String) -> String {
if value == "auto" {
return format!("{}/default/initrd", store);
}
value
}
fn default_store_path() -> Option<PathBuf> {
let user_dirs = directories::UserDirs::new()?;
let mut path = user_dirs.home_dir().to_path_buf();
if path == PathBuf::from("/root") {
path.push("/var/lib/krata")
} else {
path.push(".krata");
}
Some(path)
}