mirror of
				https://github.com/edera-dev/krata.git
				synced 2025-11-03 23:29:39 +00:00 
			
		
		
		
	krata: rework into daemon / controller structure
This commit is contained in:
		@ -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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user