2024-03-06 12:05:01 +00:00
|
|
|
use async_stream::try_stream;
|
|
|
|
use futures::Stream;
|
2024-04-12 00:34:46 -07:00
|
|
|
use krata::{
|
2024-04-21 21:00:32 -07:00
|
|
|
idm::internal::{
|
2024-04-22 13:13:43 -07:00
|
|
|
exec_stream_request_update::Update, request::Request as IdmRequestType,
|
|
|
|
response::Response as IdmResponseType, ExecEnvVar, ExecStreamRequestStart,
|
|
|
|
ExecStreamRequestStdin, ExecStreamRequestUpdate, MetricsRequest, Request as IdmRequest,
|
2024-04-12 00:34:46 -07:00
|
|
|
},
|
|
|
|
v1::{
|
2024-04-16 01:53:44 -07:00
|
|
|
common::{Guest, GuestState, GuestStatus, OciImageFormat},
|
2024-04-12 00:34:46 -07:00
|
|
|
control::{
|
|
|
|
control_service_server::ControlService, ConsoleDataReply, ConsoleDataRequest,
|
|
|
|
CreateGuestReply, CreateGuestRequest, DestroyGuestReply, DestroyGuestRequest,
|
2024-04-29 10:02:20 -07:00
|
|
|
DeviceInfo, ExecGuestReply, ExecGuestRequest, IdentifyHostReply, IdentifyHostRequest,
|
|
|
|
ListDevicesReply, ListDevicesRequest, ListGuestsReply, ListGuestsRequest,
|
|
|
|
PullImageReply, PullImageRequest, ReadGuestMetricsReply, ReadGuestMetricsRequest,
|
|
|
|
ResolveGuestReply, ResolveGuestRequest, SnoopIdmReply, SnoopIdmRequest,
|
|
|
|
WatchEventsReply, WatchEventsRequest,
|
2024-04-12 00:34:46 -07:00
|
|
|
},
|
2024-03-14 14:03:11 +00:00
|
|
|
},
|
2024-03-06 12:05:01 +00:00
|
|
|
};
|
2024-04-15 10:24:14 -07:00
|
|
|
use krataoci::{
|
|
|
|
name::ImageName,
|
2024-04-16 01:53:44 -07:00
|
|
|
packer::{service::OciPackerService, OciPackedFormat, OciPackedImage},
|
2024-04-15 10:24:14 -07:00
|
|
|
progress::{OciProgress, OciProgressContext},
|
|
|
|
};
|
|
|
|
use std::{pin::Pin, str::FromStr};
|
2024-03-06 12:05:01 +00:00
|
|
|
use tokio::{
|
|
|
|
select,
|
2024-04-02 08:57:34 +00:00
|
|
|
sync::mpsc::{channel, Sender},
|
2024-04-15 10:24:14 -07:00
|
|
|
task::JoinError,
|
2024-03-06 12:05:01 +00:00
|
|
|
};
|
|
|
|
use tokio_stream::StreamExt;
|
|
|
|
use tonic::{Request, Response, Status, Streaming};
|
2024-03-14 14:03:11 +00:00
|
|
|
use uuid::Uuid;
|
2024-03-06 12:05:01 +00:00
|
|
|
|
2024-04-12 00:34:46 -07:00
|
|
|
use crate::{
|
2024-04-21 21:00:32 -07:00
|
|
|
command::DaemonCommand, console::DaemonConsoleHandle, db::GuestStore,
|
2024-04-29 10:02:20 -07:00
|
|
|
devices::DaemonDeviceManager, event::DaemonEventContext, glt::GuestLookupTable,
|
|
|
|
idm::DaemonIdmHandle, metrics::idm_metric_to_api, oci::convert_oci_progress,
|
2024-04-12 00:34:46 -07:00
|
|
|
};
|
2024-03-06 12:05:01 +00:00
|
|
|
|
|
|
|
pub struct ApiError {
|
|
|
|
message: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<anyhow::Error> for ApiError {
|
|
|
|
fn from(value: anyhow::Error) -> Self {
|
|
|
|
ApiError {
|
|
|
|
message: value.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ApiError> for Status {
|
|
|
|
fn from(value: ApiError) -> Self {
|
|
|
|
Status::unknown(value.message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2024-04-15 10:24:14 -07:00
|
|
|
pub struct DaemonControlService {
|
2024-04-21 21:00:32 -07:00
|
|
|
glt: GuestLookupTable,
|
2024-04-29 10:02:20 -07:00
|
|
|
devices: DaemonDeviceManager,
|
2024-03-06 15:57:56 +00:00
|
|
|
events: DaemonEventContext,
|
2024-04-02 08:57:34 +00:00
|
|
|
console: DaemonConsoleHandle,
|
2024-04-12 00:34:46 -07:00
|
|
|
idm: DaemonIdmHandle,
|
2024-03-14 14:03:11 +00:00
|
|
|
guests: GuestStore,
|
|
|
|
guest_reconciler_notify: Sender<Uuid>,
|
2024-04-15 10:24:14 -07:00
|
|
|
packer: OciPackerService,
|
2024-03-06 12:05:01 +00:00
|
|
|
}
|
|
|
|
|
2024-04-15 10:24:14 -07:00
|
|
|
impl DaemonControlService {
|
2024-04-29 10:02:20 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2024-03-14 14:03:11 +00:00
|
|
|
pub fn new(
|
2024-04-21 21:00:32 -07:00
|
|
|
glt: GuestLookupTable,
|
2024-04-29 10:02:20 -07:00
|
|
|
devices: DaemonDeviceManager,
|
2024-03-14 14:03:11 +00:00
|
|
|
events: DaemonEventContext,
|
2024-04-02 08:57:34 +00:00
|
|
|
console: DaemonConsoleHandle,
|
2024-04-12 00:34:46 -07:00
|
|
|
idm: DaemonIdmHandle,
|
2024-03-14 14:03:11 +00:00
|
|
|
guests: GuestStore,
|
|
|
|
guest_reconciler_notify: Sender<Uuid>,
|
2024-04-15 10:24:14 -07:00
|
|
|
packer: OciPackerService,
|
2024-03-14 14:03:11 +00:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
2024-04-21 21:00:32 -07:00
|
|
|
glt,
|
2024-04-29 10:02:20 -07:00
|
|
|
devices,
|
2024-03-14 14:03:11 +00:00
|
|
|
events,
|
2024-04-02 08:57:34 +00:00
|
|
|
console,
|
2024-04-12 00:34:46 -07:00
|
|
|
idm,
|
2024-03-14 14:03:11 +00:00
|
|
|
guests,
|
|
|
|
guest_reconciler_notify,
|
2024-04-15 10:24:14 -07:00
|
|
|
packer,
|
2024-03-14 14:03:11 +00:00
|
|
|
}
|
2024-03-06 12:05:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum ConsoleDataSelect {
|
2024-04-02 08:57:34 +00:00
|
|
|
Read(Option<Vec<u8>>),
|
2024-03-06 12:05:01 +00:00
|
|
|
Write(Option<Result<ConsoleDataRequest, tonic::Status>>),
|
|
|
|
}
|
|
|
|
|
2024-04-15 10:24:14 -07:00
|
|
|
enum PullImageSelect {
|
2024-04-16 01:53:44 -07:00
|
|
|
Progress(Option<OciProgress>),
|
|
|
|
Completed(Result<Result<OciPackedImage, anyhow::Error>, JoinError>),
|
2024-04-15 10:24:14 -07:00
|
|
|
}
|
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
#[tonic::async_trait]
|
2024-04-15 10:24:14 -07:00
|
|
|
impl ControlService for DaemonControlService {
|
2024-04-22 13:13:43 -07:00
|
|
|
type ExecGuestStream =
|
|
|
|
Pin<Box<dyn Stream<Item = Result<ExecGuestReply, Status>> + Send + 'static>>;
|
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
type ConsoleDataStream =
|
|
|
|
Pin<Box<dyn Stream<Item = Result<ConsoleDataReply, Status>> + Send + 'static>>;
|
|
|
|
|
2024-04-15 10:24:14 -07:00
|
|
|
type PullImageStream =
|
|
|
|
Pin<Box<dyn Stream<Item = Result<PullImageReply, Status>> + Send + 'static>>;
|
|
|
|
|
2024-03-06 15:57:56 +00:00
|
|
|
type WatchEventsStream =
|
|
|
|
Pin<Box<dyn Stream<Item = Result<WatchEventsReply, Status>> + Send + 'static>>;
|
|
|
|
|
2024-04-14 04:54:21 -07:00
|
|
|
type SnoopIdmStream =
|
|
|
|
Pin<Box<dyn Stream<Item = Result<SnoopIdmReply, Status>> + Send + 'static>>;
|
|
|
|
|
2024-04-21 21:00:32 -07:00
|
|
|
async fn identify_host(
|
|
|
|
&self,
|
|
|
|
request: Request<IdentifyHostRequest>,
|
|
|
|
) -> Result<Response<IdentifyHostReply>, Status> {
|
|
|
|
let _ = request.into_inner();
|
|
|
|
Ok(Response::new(IdentifyHostReply {
|
|
|
|
host_domid: self.glt.host_domid(),
|
|
|
|
host_uuid: self.glt.host_uuid().to_string(),
|
|
|
|
krata_version: DaemonCommand::version(),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2024-03-14 14:03:11 +00:00
|
|
|
async fn create_guest(
|
2024-03-06 12:05:01 +00:00
|
|
|
&self,
|
2024-03-14 14:03:11 +00:00
|
|
|
request: Request<CreateGuestRequest>,
|
|
|
|
) -> Result<Response<CreateGuestReply>, Status> {
|
2024-03-06 12:05:01 +00:00
|
|
|
let request = request.into_inner();
|
2024-03-14 14:03:11 +00:00
|
|
|
let Some(spec) = request.spec else {
|
2024-03-08 08:47:18 +00:00
|
|
|
return Err(ApiError {
|
2024-03-14 14:03:11 +00:00
|
|
|
message: "guest spec not provided".to_string(),
|
2024-03-08 08:47:18 +00:00
|
|
|
}
|
|
|
|
.into());
|
|
|
|
};
|
2024-03-14 14:03:11 +00:00
|
|
|
let uuid = Uuid::new_v4();
|
|
|
|
self.guests
|
|
|
|
.update(
|
|
|
|
uuid,
|
2024-03-30 09:29:03 +00:00
|
|
|
Guest {
|
2024-03-14 14:03:11 +00:00
|
|
|
id: uuid.to_string(),
|
2024-03-30 09:29:03 +00:00
|
|
|
state: Some(GuestState {
|
|
|
|
status: GuestStatus::Starting.into(),
|
|
|
|
network: None,
|
|
|
|
exit_info: None,
|
|
|
|
error_info: None,
|
2024-04-21 21:00:32 -07:00
|
|
|
host: self.glt.host_uuid().to_string(),
|
2024-03-30 09:29:03 +00:00
|
|
|
domid: u32::MAX,
|
2024-03-14 14:03:11 +00:00
|
|
|
}),
|
2024-03-30 09:29:03 +00:00
|
|
|
spec: Some(spec),
|
2024-03-14 14:03:11 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.map_err(ApiError::from)?;
|
|
|
|
self.guest_reconciler_notify
|
|
|
|
.send(uuid)
|
|
|
|
.await
|
|
|
|
.map_err(|x| ApiError {
|
|
|
|
message: x.to_string(),
|
|
|
|
})?;
|
|
|
|
Ok(Response::new(CreateGuestReply {
|
|
|
|
guest_id: uuid.to_string(),
|
|
|
|
}))
|
2024-03-06 12:05:01 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 13:13:43 -07:00
|
|
|
async fn exec_guest(
|
|
|
|
&self,
|
|
|
|
request: Request<Streaming<ExecGuestRequest>>,
|
|
|
|
) -> Result<Response<Self::ExecGuestStream>, Status> {
|
|
|
|
let mut input = request.into_inner();
|
|
|
|
let Some(request) = input.next().await else {
|
|
|
|
return Err(ApiError {
|
|
|
|
message: "expected to have at least one request".to_string(),
|
|
|
|
}
|
|
|
|
.into());
|
|
|
|
};
|
|
|
|
let request = request?;
|
|
|
|
|
|
|
|
let Some(task) = request.task else {
|
|
|
|
return Err(ApiError {
|
|
|
|
message: "task is missing".to_string(),
|
|
|
|
}
|
|
|
|
.into());
|
|
|
|
};
|
|
|
|
|
|
|
|
let uuid = Uuid::from_str(&request.guest_id).map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
|
|
|
let idm = self.idm.client(uuid).await.map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let idm_request = IdmRequest {
|
|
|
|
request: Some(IdmRequestType::ExecStream(ExecStreamRequestUpdate {
|
|
|
|
update: Some(Update::Start(ExecStreamRequestStart {
|
|
|
|
environment: task
|
|
|
|
.environment
|
|
|
|
.into_iter()
|
|
|
|
.map(|x| ExecEnvVar {
|
|
|
|
key: x.key,
|
|
|
|
value: x.value,
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
command: task.command,
|
|
|
|
working_directory: task.working_directory,
|
|
|
|
})),
|
|
|
|
})),
|
|
|
|
};
|
|
|
|
|
|
|
|
let output = try_stream! {
|
|
|
|
let mut handle = idm.send_stream(idm_request).await.map_err(|x| ApiError {
|
|
|
|
message: x.to_string(),
|
|
|
|
})?;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
select! {
|
|
|
|
x = input.next() => if let Some(update) = x {
|
|
|
|
let update: Result<ExecGuestRequest, Status> = update.map_err(|error| ApiError {
|
|
|
|
message: error.to_string()
|
|
|
|
}.into());
|
|
|
|
|
|
|
|
if let Ok(update) = update {
|
|
|
|
if !update.data.is_empty() {
|
|
|
|
let _ = handle.update(IdmRequest {
|
|
|
|
request: Some(IdmRequestType::ExecStream(ExecStreamRequestUpdate {
|
|
|
|
update: Some(Update::Stdin(ExecStreamRequestStdin {
|
|
|
|
data: update.data,
|
|
|
|
})),
|
|
|
|
}))}).await;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
x = handle.receiver.recv() => match x {
|
|
|
|
Some(response) => {
|
|
|
|
let Some(IdmResponseType::ExecStream(update)) = response.response else {
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
let reply = ExecGuestReply {
|
|
|
|
exited: update.exited,
|
|
|
|
error: update.error,
|
|
|
|
exit_code: update.exit_code,
|
|
|
|
stdout: update.stdout,
|
|
|
|
stderr: update.stderr
|
|
|
|
};
|
|
|
|
yield reply;
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Response::new(Box::pin(output) as Self::ExecGuestStream))
|
|
|
|
}
|
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
async fn destroy_guest(
|
|
|
|
&self,
|
|
|
|
request: Request<DestroyGuestRequest>,
|
|
|
|
) -> Result<Response<DestroyGuestReply>, Status> {
|
|
|
|
let request = request.into_inner();
|
2024-03-14 14:03:11 +00:00
|
|
|
let uuid = Uuid::from_str(&request.guest_id).map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
2024-03-30 09:29:03 +00:00
|
|
|
let Some(mut guest) = self.guests.read(uuid).await.map_err(ApiError::from)? else {
|
2024-03-14 14:03:11 +00:00
|
|
|
return Err(ApiError {
|
|
|
|
message: "guest not found".to_string(),
|
|
|
|
}
|
|
|
|
.into());
|
|
|
|
};
|
|
|
|
|
|
|
|
guest.state = Some(guest.state.as_mut().cloned().unwrap_or_default());
|
|
|
|
|
|
|
|
if guest.state.as_ref().unwrap().status() == GuestStatus::Destroyed {
|
|
|
|
return Err(ApiError {
|
|
|
|
message: "guest already destroyed".to_string(),
|
|
|
|
}
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
2024-03-23 07:00:12 +00:00
|
|
|
guest.state.as_mut().unwrap().status = GuestStatus::Destroying.into();
|
2024-03-14 14:03:11 +00:00
|
|
|
self.guests
|
2024-03-30 09:29:03 +00:00
|
|
|
.update(uuid, guest)
|
2024-03-06 12:05:01 +00:00
|
|
|
.await
|
|
|
|
.map_err(ApiError::from)?;
|
2024-03-14 14:03:11 +00:00
|
|
|
self.guest_reconciler_notify
|
|
|
|
.send(uuid)
|
|
|
|
.await
|
|
|
|
.map_err(|x| ApiError {
|
|
|
|
message: x.to_string(),
|
|
|
|
})?;
|
2024-03-06 12:05:01 +00:00
|
|
|
Ok(Response::new(DestroyGuestReply {}))
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn list_guests(
|
|
|
|
&self,
|
|
|
|
request: Request<ListGuestsRequest>,
|
|
|
|
) -> Result<Response<ListGuestsReply>, Status> {
|
|
|
|
let _ = request.into_inner();
|
2024-03-14 14:03:11 +00:00
|
|
|
let guests = self.guests.list().await.map_err(ApiError::from)?;
|
2024-03-30 09:29:03 +00:00
|
|
|
let guests = guests.into_values().collect::<Vec<Guest>>();
|
2024-03-06 12:05:01 +00:00
|
|
|
Ok(Response::new(ListGuestsReply { guests }))
|
|
|
|
}
|
|
|
|
|
2024-03-23 09:48:53 +00:00
|
|
|
async fn resolve_guest(
|
|
|
|
&self,
|
|
|
|
request: Request<ResolveGuestRequest>,
|
|
|
|
) -> Result<Response<ResolveGuestReply>, Status> {
|
|
|
|
let request = request.into_inner();
|
|
|
|
let guests = self.guests.list().await.map_err(ApiError::from)?;
|
|
|
|
let guests = guests
|
|
|
|
.into_values()
|
|
|
|
.filter(|x| {
|
|
|
|
let comparison_spec = x.spec.as_ref().cloned().unwrap_or_default();
|
|
|
|
(!request.name.is_empty() && comparison_spec.name == request.name)
|
|
|
|
|| x.id == request.name
|
|
|
|
})
|
|
|
|
.collect::<Vec<Guest>>();
|
|
|
|
Ok(Response::new(ResolveGuestReply {
|
|
|
|
guest: guests.first().cloned(),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
async fn console_data(
|
|
|
|
&self,
|
|
|
|
request: Request<Streaming<ConsoleDataRequest>>,
|
|
|
|
) -> Result<Response<Self::ConsoleDataStream>, Status> {
|
|
|
|
let mut input = request.into_inner();
|
|
|
|
let Some(request) = input.next().await else {
|
|
|
|
return Err(ApiError {
|
|
|
|
message: "expected to have at least one request".to_string(),
|
|
|
|
}
|
|
|
|
.into());
|
|
|
|
};
|
|
|
|
let request = request?;
|
2024-03-14 23:29:07 +00:00
|
|
|
let uuid = Uuid::from_str(&request.guest_id).map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
2024-04-02 08:57:34 +00:00
|
|
|
let (sender, mut receiver) = channel(100);
|
|
|
|
let console = self
|
|
|
|
.console
|
2024-04-21 21:00:32 -07:00
|
|
|
.attach(uuid, sender)
|
2024-04-02 08:57:34 +00:00
|
|
|
.await
|
|
|
|
.map_err(|error| ApiError {
|
|
|
|
message: format!("failed to attach to console: {}", error),
|
|
|
|
})?;
|
2024-03-06 12:05:01 +00:00
|
|
|
|
|
|
|
let output = try_stream! {
|
2024-04-02 08:57:34 +00:00
|
|
|
yield ConsoleDataReply { data: console.initial.clone(), };
|
2024-03-06 12:05:01 +00:00
|
|
|
loop {
|
|
|
|
let what = select! {
|
2024-04-02 08:57:34 +00:00
|
|
|
x = receiver.recv() => ConsoleDataSelect::Read(x),
|
2024-03-06 12:05:01 +00:00
|
|
|
x = input.next() => ConsoleDataSelect::Write(x),
|
|
|
|
};
|
|
|
|
|
|
|
|
match what {
|
2024-04-02 08:57:34 +00:00
|
|
|
ConsoleDataSelect::Read(Some(data)) => {
|
2024-03-06 12:05:01 +00:00
|
|
|
yield ConsoleDataReply { data, };
|
|
|
|
},
|
|
|
|
|
2024-04-02 08:57:34 +00:00
|
|
|
ConsoleDataSelect::Read(None) => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-03-06 12:05:01 +00:00
|
|
|
ConsoleDataSelect::Write(Some(request)) => {
|
|
|
|
let request = request?;
|
|
|
|
if !request.data.is_empty() {
|
2024-04-02 08:57:34 +00:00
|
|
|
console.send(request.data).await.map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
2024-03-06 12:05:01 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
ConsoleDataSelect::Write(None) => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Response::new(Box::pin(output) as Self::ConsoleDataStream))
|
|
|
|
}
|
2024-03-06 15:57:56 +00:00
|
|
|
|
2024-04-12 00:34:46 -07:00
|
|
|
async fn read_guest_metrics(
|
|
|
|
&self,
|
|
|
|
request: Request<ReadGuestMetricsRequest>,
|
|
|
|
) -> Result<Response<ReadGuestMetricsReply>, Status> {
|
|
|
|
let request = request.into_inner();
|
|
|
|
let uuid = Uuid::from_str(&request.guest_id).map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
2024-04-21 21:00:32 -07:00
|
|
|
let client = self.idm.client(uuid).await.map_err(|error| ApiError {
|
2024-04-12 00:34:46 -07:00
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let response = client
|
2024-04-21 21:00:32 -07:00
|
|
|
.send(IdmRequest {
|
|
|
|
request: Some(IdmRequestType::Metrics(MetricsRequest {})),
|
|
|
|
})
|
2024-04-12 00:34:46 -07:00
|
|
|
.await
|
|
|
|
.map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let mut reply = ReadGuestMetricsReply::default();
|
2024-04-21 21:00:32 -07:00
|
|
|
if let Some(IdmResponseType::Metrics(metrics)) = response.response {
|
2024-04-12 00:34:46 -07:00
|
|
|
reply.root = metrics.root.map(idm_metric_to_api);
|
|
|
|
}
|
|
|
|
Ok(Response::new(reply))
|
|
|
|
}
|
|
|
|
|
2024-04-15 10:24:14 -07:00
|
|
|
async fn pull_image(
|
|
|
|
&self,
|
|
|
|
request: Request<PullImageRequest>,
|
|
|
|
) -> Result<Response<Self::PullImageStream>, Status> {
|
|
|
|
let request = request.into_inner();
|
|
|
|
let name = ImageName::parse(&request.image).map_err(|err| ApiError {
|
|
|
|
message: err.to_string(),
|
|
|
|
})?;
|
|
|
|
let format = match request.format() {
|
2024-04-16 01:53:44 -07:00
|
|
|
OciImageFormat::Unknown => OciPackedFormat::Squashfs,
|
|
|
|
OciImageFormat::Squashfs => OciPackedFormat::Squashfs,
|
|
|
|
OciImageFormat::Erofs => OciPackedFormat::Erofs,
|
|
|
|
OciImageFormat::Tar => OciPackedFormat::Tar,
|
2024-04-15 10:24:14 -07:00
|
|
|
};
|
2024-04-16 01:53:44 -07:00
|
|
|
let (context, mut receiver) = OciProgressContext::create();
|
2024-04-15 10:24:14 -07:00
|
|
|
let our_packer = self.packer.clone();
|
|
|
|
|
|
|
|
let output = try_stream! {
|
|
|
|
let mut task = tokio::task::spawn(async move {
|
2024-04-16 09:29:54 -07:00
|
|
|
our_packer.request(name, format, request.overwrite_cache, context).await
|
2024-04-15 10:24:14 -07:00
|
|
|
});
|
2024-04-16 01:53:44 -07:00
|
|
|
let abort_handle = task.abort_handle();
|
|
|
|
let _task_cancel_guard = scopeguard::guard(abort_handle, |handle| {
|
|
|
|
handle.abort();
|
|
|
|
});
|
|
|
|
|
2024-04-15 10:24:14 -07:00
|
|
|
loop {
|
|
|
|
let what = select! {
|
2024-04-16 09:29:54 -07:00
|
|
|
x = receiver.changed() => match x {
|
|
|
|
Ok(_) => PullImageSelect::Progress(Some(receiver.borrow_and_update().clone())),
|
|
|
|
Err(_) => PullImageSelect::Progress(None),
|
|
|
|
},
|
2024-04-15 10:24:14 -07:00
|
|
|
x = &mut task => PullImageSelect::Completed(x),
|
|
|
|
};
|
|
|
|
match what {
|
2024-04-16 09:29:54 -07:00
|
|
|
PullImageSelect::Progress(Some(progress)) => {
|
2024-04-16 01:53:44 -07:00
|
|
|
let reply = PullImageReply {
|
|
|
|
progress: Some(convert_oci_progress(progress)),
|
|
|
|
digest: String::new(),
|
|
|
|
format: OciImageFormat::Unknown.into(),
|
|
|
|
};
|
|
|
|
yield reply;
|
2024-04-15 10:24:14 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
PullImageSelect::Completed(result) => {
|
|
|
|
let result = result.map_err(|err| ApiError {
|
|
|
|
message: err.to_string(),
|
|
|
|
})?;
|
|
|
|
let packed = result.map_err(|err| ApiError {
|
|
|
|
message: err.to_string(),
|
|
|
|
})?;
|
|
|
|
let reply = PullImageReply {
|
|
|
|
progress: None,
|
|
|
|
digest: packed.digest,
|
|
|
|
format: match packed.format {
|
2024-04-16 01:53:44 -07:00
|
|
|
OciPackedFormat::Squashfs => OciImageFormat::Squashfs.into(),
|
|
|
|
OciPackedFormat::Erofs => OciImageFormat::Erofs.into(),
|
2024-04-22 12:48:45 -07:00
|
|
|
OciPackedFormat::Tar => OciImageFormat::Tar.into(),
|
2024-04-15 10:24:14 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
yield reply;
|
|
|
|
break;
|
|
|
|
},
|
2024-04-16 01:53:44 -07:00
|
|
|
|
|
|
|
_ => {
|
|
|
|
continue;
|
|
|
|
}
|
2024-04-15 10:24:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(Response::new(Box::pin(output) as Self::PullImageStream))
|
|
|
|
}
|
|
|
|
|
2024-03-06 15:57:56 +00:00
|
|
|
async fn watch_events(
|
|
|
|
&self,
|
|
|
|
request: Request<WatchEventsRequest>,
|
|
|
|
) -> Result<Response<Self::WatchEventsStream>, Status> {
|
|
|
|
let _ = request.into_inner();
|
|
|
|
let mut events = self.events.subscribe();
|
|
|
|
let output = try_stream! {
|
|
|
|
while let Ok(event) = events.recv().await {
|
|
|
|
yield WatchEventsReply { event: Some(event), };
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(Response::new(Box::pin(output) as Self::WatchEventsStream))
|
|
|
|
}
|
2024-04-14 04:54:21 -07:00
|
|
|
|
|
|
|
async fn snoop_idm(
|
|
|
|
&self,
|
|
|
|
request: Request<SnoopIdmRequest>,
|
|
|
|
) -> Result<Response<Self::SnoopIdmStream>, Status> {
|
|
|
|
let _ = request.into_inner();
|
|
|
|
let mut messages = self.idm.snoop();
|
2024-04-21 21:00:32 -07:00
|
|
|
let glt = self.glt.clone();
|
2024-04-14 04:54:21 -07:00
|
|
|
let output = try_stream! {
|
|
|
|
while let Ok(event) = messages.recv().await {
|
2024-04-21 21:00:32 -07:00
|
|
|
let Some(from_uuid) = glt.lookup_uuid_by_domid(event.from).await else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
let Some(to_uuid) = glt.lookup_uuid_by_domid(event.to).await else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
yield SnoopIdmReply { from: from_uuid.to_string(), to: to_uuid.to_string(), packet: Some(event.packet) };
|
2024-04-14 04:54:21 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(Response::new(Box::pin(output) as Self::SnoopIdmStream))
|
|
|
|
}
|
2024-04-29 10:02:20 -07:00
|
|
|
|
|
|
|
async fn list_devices(
|
|
|
|
&self,
|
|
|
|
request: Request<ListDevicesRequest>,
|
|
|
|
) -> Result<Response<ListDevicesReply>, Status> {
|
|
|
|
let _ = request.into_inner();
|
|
|
|
let mut devices = Vec::new();
|
|
|
|
let state = self.devices.copy().await.map_err(|error| ApiError {
|
|
|
|
message: error.to_string(),
|
|
|
|
})?;
|
|
|
|
for (name, state) in state {
|
|
|
|
devices.push(DeviceInfo {
|
|
|
|
name,
|
|
|
|
claimed: state.owner.is_some(),
|
|
|
|
owner: state.owner.map(|x| x.to_string()).unwrap_or_default(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Ok(Response::new(ListDevicesReply { devices }))
|
|
|
|
}
|
2024-03-06 12:05:01 +00:00
|
|
|
}
|