krata: event-based network backend startup and api enhancements

This commit is contained in:
Alex Zenla
2024-03-27 02:54:39 +00:00
parent 63c0feb053
commit 66465793cd
29 changed files with 346 additions and 229 deletions

View File

@ -1,11 +1,11 @@
use anyhow::Result;
use clap::Parser;
use krata::v1::control::control_service_client::ControlServiceClient;
use krata::{events::EventStream, v1::control::control_service_client::ControlServiceClient};
use tokio::select;
use tonic::transport::Channel;
use crate::{console::StdioConsoleStream, events::EventStream};
use crate::console::StdioConsoleStream;
use super::resolve_guest;

View File

@ -1,17 +1,20 @@
use anyhow::Result;
use clap::Parser;
use krata::v1::{
common::GuestStatus,
control::{
control_service_client::ControlServiceClient, watch_events_reply::Event,
DestroyGuestRequest,
use krata::{
events::EventStream,
v1::{
common::GuestStatus,
control::{
control_service_client::ControlServiceClient, watch_events_reply::Event,
DestroyGuestRequest,
},
},
};
use log::error;
use tonic::{transport::Channel, Request};
use crate::{cli::resolve_guest, events::EventStream};
use crate::cli::resolve_guest;
#[derive(Parser)]
pub struct DestroyCommand {

View File

@ -2,20 +2,24 @@ use std::collections::HashMap;
use anyhow::Result;
use clap::Parser;
use krata::v1::{
common::{
guest_image_spec::Image, GuestEnvVar, GuestImageSpec, GuestOciImageSpec, GuestSpec,
GuestStatus,
},
control::{
control_service_client::ControlServiceClient, watch_events_reply::Event, CreateGuestRequest,
use krata::{
events::EventStream,
v1::{
common::{
guest_image_spec::Image, GuestImageSpec, GuestOciImageSpec, GuestSpec, GuestStatus,
GuestTaskSpec, GuestTaskSpecEnvVar,
},
control::{
control_service_client::ControlServiceClient, watch_events_reply::Event,
CreateGuestRequest,
},
},
};
use log::error;
use tokio::select;
use tonic::{transport::Channel, Request};
use crate::{console::StdioConsoleStream, events::EventStream};
use crate::console::StdioConsoleStream;
#[derive(Parser)]
pub struct LauchCommand {
@ -51,14 +55,17 @@ impl LauchCommand {
}),
vcpus: self.cpus,
mem: self.mem,
env: env_map(&self.env.unwrap_or_default())
.iter()
.map(|(key, value)| GuestEnvVar {
key: key.clone(),
value: value.clone(),
})
.collect(),
run: self.run,
task: Some(GuestTaskSpec {
environment: env_map(&self.env.unwrap_or_default())
.iter()
.map(|(key, value)| GuestTaskSpecEnvVar {
key: key.clone(),
value: value.clone(),
})
.collect(),
command: self.run,
}),
annotations: vec![],
}),
};
let response = client

View File

@ -1,20 +1,20 @@
use anyhow::{anyhow, Result};
use clap::{Parser, ValueEnum};
use cli_tables::Table;
use krata::v1::{
common::{guest_image_spec::Image, Guest},
control::{
control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest,
use krata::{
events::EventStream,
v1::{
common::{guest_image_spec::Image, Guest},
control::{
control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest,
},
},
};
use serde_json::Value;
use tonic::{transport::Channel, Request};
use crate::{
events::EventStream,
format::{guest_state_text, kv2line, proto2dynamic, proto2kv},
};
use crate::format::{guest_state_text, kv2line, proto2dynamic, proto2kv};
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum ListFormat {
@ -106,13 +106,13 @@ impl ListCommand {
.state
.as_ref()
.and_then(|x| x.network.as_ref())
.map(|x| x.ipv4.as_str())
.map(|x| x.guest_ipv4.as_str())
.unwrap_or("unknown");
let ipv6 = guest
.state
.as_ref()
.and_then(|x| x.network.as_ref())
.map(|x| x.ipv6.as_str())
.map(|x| x.guest_ipv6.as_str())
.unwrap_or("unknown");
let Some(spec) = guest.spec else {
continue;

View File

@ -7,13 +7,15 @@ pub mod watch;
use anyhow::{anyhow, Result};
use clap::{Parser, Subcommand};
use krata::v1::control::{
control_service_client::ControlServiceClient, ResolveGuestRequest, WatchEventsRequest,
use krata::{
client::ControlClientProvider,
events::EventStream,
v1::control::{
control_service_client::ControlServiceClient, ResolveGuestRequest, WatchEventsRequest,
},
};
use tonic::{transport::Channel, Request};
use crate::{client::ControlClientProvider, events::EventStream};
use self::{
attach::AttachCommand, destroy::DestroyCommand, launch::LauchCommand, list::ListCommand,
resolve::ResolveCommand, watch::WatchCommand,

View File

@ -1,13 +1,13 @@
use anyhow::Result;
use clap::{Parser, ValueEnum};
use krata::v1::{common::Guest, control::watch_events_reply::Event};
use krata::{
events::EventStream,
v1::{common::Guest, control::watch_events_reply::Event},
};
use prost_reflect::ReflectMessage;
use serde_json::Value;
use crate::{
events::EventStream,
format::{guest_state_text, kv2line, proto2dynamic, proto2kv},
};
use crate::format::{guest_state_text, kv2line, proto2dynamic, proto2kv};
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
enum WatchFormat {

View File

@ -1,61 +0,0 @@
#[cfg(not(unix))]
use anyhow::anyhow;
use anyhow::Result;
use krata::{dial::ControlDialAddress, v1::control::control_service_client::ControlServiceClient};
#[cfg(unix)]
use tokio::net::UnixStream;
#[cfg(unix)]
use tonic::transport::Uri;
use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
#[cfg(unix)]
use tower::service_fn;
pub struct ControlClientProvider {}
impl ControlClientProvider {
pub async fn dial(addr: ControlDialAddress) -> Result<ControlServiceClient<Channel>> {
let channel = match addr {
ControlDialAddress::UnixSocket { path } => {
#[cfg(not(unix))]
return Err(anyhow!(
"unix sockets are not supported on this platform (path {})",
path
));
#[cfg(unix)]
ControlClientProvider::dial_unix_socket(path).await?
}
ControlDialAddress::Tcp { host, port } => {
Endpoint::try_from(format!("http://{}:{}", host, port))?
.connect()
.await?
}
ControlDialAddress::Tls {
host,
port,
insecure: _,
} => {
let tls_config = ClientTlsConfig::new().domain_name(&host);
let address = format!("https://{}:{}", host, port);
Channel::from_shared(address)?
.tls_config(tls_config)?
.connect()
.await?
}
};
Ok(ControlServiceClient::new(channel))
}
#[cfg(unix)]
async fn dial_unix_socket(path: String) -> Result<Channel> {
// This URL is not actually used but is required to be specified.
Ok(Endpoint::try_from(format!("unix://localhost/{}", path))?
.connect_with_connector(service_fn(|uri: Uri| {
let path = uri.path().to_string();
UnixStream::connect(path)
}))
.await?)
}
}

View File

@ -4,9 +4,12 @@ use crossterm::{
terminal::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled},
tty::IsTty,
};
use krata::v1::{
common::GuestStatus,
control::{watch_events_reply::Event, ConsoleDataReply, ConsoleDataRequest},
use krata::{
events::EventStream,
v1::{
common::GuestStatus,
control::{watch_events_reply::Event, ConsoleDataReply, ConsoleDataRequest},
},
};
use log::debug;
use tokio::{
@ -16,8 +19,6 @@ use tokio::{
use tokio_stream::{Stream, StreamExt};
use tonic::Streaming;
use crate::events::EventStream;
pub struct StdioConsoleStream;
impl StdioConsoleStream {

View File

@ -1,57 +0,0 @@
use std::sync::Arc;
use anyhow::Result;
use krata::v1::control::{watch_events_reply::Event, WatchEventsReply};
use log::trace;
use tokio::{sync::broadcast, task::JoinHandle};
use tokio_stream::StreamExt;
use tonic::Streaming;
#[derive(Clone)]
pub struct EventStream {
sender: Arc<broadcast::Sender<Event>>,
task: Arc<JoinHandle<()>>,
}
impl EventStream {
pub async fn open(mut events: Streaming<WatchEventsReply>) -> Result<Self> {
let (sender, _) = broadcast::channel(1000);
let emit = sender.clone();
let task = tokio::task::spawn(async move {
loop {
let Some(result) = events.next().await else {
break;
};
let reply = match result {
Ok(reply) => reply,
Err(error) => {
trace!("event stream processing failed: {}", error);
break;
}
};
let Some(event) = reply.event else {
continue;
};
let _ = emit.send(event);
}
});
Ok(Self {
sender: Arc::new(sender),
task: Arc::new(task),
})
}
pub fn subscribe(&self) -> broadcast::Receiver<Event> {
self.sender.subscribe()
}
}
impl Drop for EventStream {
fn drop(&mut self) {
if Arc::strong_count(&self.task) <= 1 {
self.task.abort();
}
}
}

View File

@ -1,5 +1,3 @@
pub mod cli;
pub mod client;
pub mod console;
pub mod events;
pub mod format;