2024-03-23 10:09:00 +00:00
|
|
|
use anyhow::{anyhow, Result};
|
2024-03-23 04:07:48 +00:00
|
|
|
use clap::{Parser, ValueEnum};
|
|
|
|
use cli_tables::Table;
|
2024-03-24 05:52:25 +00:00
|
|
|
use krata::v1::{
|
2024-03-23 04:07:48 +00:00
|
|
|
common::{guest_image_spec::Image, Guest},
|
2024-03-23 10:09:00 +00:00
|
|
|
control::{
|
|
|
|
control_service_client::ControlServiceClient, ListGuestsRequest, ResolveGuestRequest,
|
|
|
|
},
|
2024-03-15 15:59:18 +00:00
|
|
|
};
|
|
|
|
|
2024-03-23 04:07:48 +00:00
|
|
|
use serde_json::Value;
|
2024-03-15 15:59:18 +00:00
|
|
|
use tonic::{transport::Channel, Request};
|
|
|
|
|
2024-03-23 04:07:48 +00:00
|
|
|
use crate::{
|
|
|
|
events::EventStream,
|
2024-03-23 09:48:53 +00:00
|
|
|
format::{guest_state_text, kv2line, proto2dynamic, proto2kv},
|
2024-03-23 04:07:48 +00:00
|
|
|
};
|
2024-03-15 15:59:18 +00:00
|
|
|
|
2024-03-23 07:00:12 +00:00
|
|
|
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
2024-03-23 04:07:48 +00:00
|
|
|
enum ListFormat {
|
|
|
|
CliTable,
|
|
|
|
Json,
|
|
|
|
JsonPretty,
|
|
|
|
Jsonl,
|
|
|
|
Yaml,
|
|
|
|
KeyValue,
|
|
|
|
}
|
|
|
|
|
2024-03-15 15:59:18 +00:00
|
|
|
#[derive(Parser)]
|
2024-03-23 04:07:48 +00:00
|
|
|
pub struct ListCommand {
|
2024-03-23 07:00:12 +00:00
|
|
|
#[arg(short, long, default_value = "cli-table")]
|
2024-03-23 04:07:48 +00:00
|
|
|
format: ListFormat,
|
2024-03-23 10:09:00 +00:00
|
|
|
#[arg()]
|
|
|
|
guest: Option<String>,
|
2024-03-23 04:07:48 +00:00
|
|
|
}
|
2024-03-15 15:59:18 +00:00
|
|
|
|
|
|
|
impl ListCommand {
|
|
|
|
pub async fn run(
|
|
|
|
self,
|
|
|
|
mut client: ControlServiceClient<Channel>,
|
|
|
|
_events: EventStream,
|
|
|
|
) -> Result<()> {
|
2024-03-23 10:09:00 +00:00
|
|
|
let guests = if let Some(ref guest) = self.guest {
|
|
|
|
let reply = client
|
|
|
|
.resolve_guest(Request::new(ResolveGuestRequest {
|
|
|
|
name: guest.clone(),
|
|
|
|
}))
|
|
|
|
.await?
|
|
|
|
.into_inner();
|
|
|
|
if let Some(guest) = reply.guest {
|
|
|
|
vec![guest]
|
|
|
|
} else {
|
|
|
|
return Err(anyhow!("unable to resolve guest '{}'", guest));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
client
|
|
|
|
.list_guests(Request::new(ListGuestsRequest {}))
|
|
|
|
.await?
|
|
|
|
.into_inner()
|
|
|
|
.guests
|
|
|
|
};
|
2024-03-23 04:07:48 +00:00
|
|
|
|
|
|
|
match self.format {
|
|
|
|
ListFormat::CliTable => {
|
2024-03-23 10:09:00 +00:00
|
|
|
self.print_guest_table(guests)?;
|
2024-03-23 04:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ListFormat::Json | ListFormat::JsonPretty | ListFormat::Yaml => {
|
|
|
|
let mut values = Vec::new();
|
2024-03-23 10:09:00 +00:00
|
|
|
for guest in guests {
|
2024-03-23 04:07:48 +00:00
|
|
|
let message = proto2dynamic(guest)?;
|
|
|
|
values.push(serde_json::to_value(message)?);
|
|
|
|
}
|
|
|
|
let value = Value::Array(values);
|
|
|
|
let encoded = if self.format == ListFormat::JsonPretty {
|
|
|
|
serde_json::to_string_pretty(&value)?
|
|
|
|
} else if self.format == ListFormat::Yaml {
|
|
|
|
serde_yaml::to_string(&value)?
|
|
|
|
} else {
|
|
|
|
serde_json::to_string(&value)?
|
|
|
|
};
|
|
|
|
println!("{}", encoded.trim());
|
|
|
|
}
|
|
|
|
|
|
|
|
ListFormat::Jsonl => {
|
2024-03-23 10:09:00 +00:00
|
|
|
for guest in guests {
|
2024-03-23 04:07:48 +00:00
|
|
|
let message = proto2dynamic(guest)?;
|
|
|
|
println!("{}", serde_json::to_string(&message)?);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ListFormat::KeyValue => {
|
2024-03-23 10:09:00 +00:00
|
|
|
self.print_key_value(guests)?;
|
2024-03-23 04:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn print_guest_table(&self, guests: Vec<Guest>) -> Result<()> {
|
|
|
|
let mut table = Table::new();
|
2024-03-15 15:59:18 +00:00
|
|
|
let header = vec!["name", "uuid", "state", "ipv4", "ipv6", "image"];
|
|
|
|
table.push_row(&header)?;
|
2024-03-23 04:07:48 +00:00
|
|
|
for guest in guests {
|
2024-03-15 15:59:18 +00:00
|
|
|
let ipv4 = guest
|
2024-03-23 07:00:12 +00:00
|
|
|
.state
|
2024-03-15 15:59:18 +00:00
|
|
|
.as_ref()
|
2024-03-23 07:00:12 +00:00
|
|
|
.and_then(|x| x.network.as_ref())
|
2024-03-15 15:59:18 +00:00
|
|
|
.map(|x| x.ipv4.as_str())
|
|
|
|
.unwrap_or("unknown");
|
|
|
|
let ipv6 = guest
|
2024-03-23 07:00:12 +00:00
|
|
|
.state
|
2024-03-15 15:59:18 +00:00
|
|
|
.as_ref()
|
2024-03-23 07:00:12 +00:00
|
|
|
.and_then(|x| x.network.as_ref())
|
2024-03-15 15:59:18 +00:00
|
|
|
.map(|x| x.ipv6.as_str())
|
|
|
|
.unwrap_or("unknown");
|
|
|
|
let Some(spec) = guest.spec else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
let image = spec
|
|
|
|
.image
|
|
|
|
.map(|x| {
|
|
|
|
x.image
|
|
|
|
.map(|y| match y {
|
|
|
|
Image::Oci(oci) => oci.image,
|
|
|
|
})
|
|
|
|
.unwrap_or("unknown".to_string())
|
|
|
|
})
|
|
|
|
.unwrap_or("unknown".to_string());
|
|
|
|
table.push_row_string(&vec![
|
|
|
|
spec.name,
|
|
|
|
guest.id,
|
2024-03-23 07:00:12 +00:00
|
|
|
format!("{}", guest_state_text(guest.state.as_ref())),
|
2024-03-15 15:59:18 +00:00
|
|
|
ipv4.to_string(),
|
|
|
|
ipv6.to_string(),
|
|
|
|
image,
|
|
|
|
])?;
|
|
|
|
}
|
|
|
|
if table.num_records() == 1 {
|
2024-03-23 10:09:00 +00:00
|
|
|
if self.guest.is_none() {
|
|
|
|
println!("no guests have been launched");
|
|
|
|
}
|
2024-03-15 15:59:18 +00:00
|
|
|
} else {
|
|
|
|
println!("{}", table.to_string());
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-03-23 04:07:48 +00:00
|
|
|
|
|
|
|
fn print_key_value(&self, guests: Vec<Guest>) -> Result<()> {
|
|
|
|
for guest in guests {
|
|
|
|
let kvs = proto2kv(guest)?;
|
2024-03-23 07:00:12 +00:00
|
|
|
println!("{}", kv2line(kvs),);
|
2024-03-23 04:07:48 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-03-15 15:59:18 +00:00
|
|
|
}
|