feat: exec mode improvements
Use Bollard library to exec in pure Rust. `--use-cli` cli arg, will then only try to exec into containers using Docker. Only try to exec into a container if the state == Running.
This commit is contained in:
@@ -279,6 +279,7 @@ pub fn chart(f: &mut Frame, area: Rect, app_data: &Arc<Mutex<AppData>>) {
|
||||
.data(&mem.0)];
|
||||
|
||||
let cpu_stats = CpuStats::new(cpu.0.last().map_or(0.00, |f| f.1));
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
let mem_stats = ByteStats::new(mem.0.last().map_or(0, |f| f.1 as u64));
|
||||
let cpu_chart = make_chart(cpu.2, "cpu", cpu_dataset, &cpu_stats, &cpu.1);
|
||||
let mem_chart = make_chart(mem.2, "memory", mem_dataset, &mem_stats, &mem.1);
|
||||
@@ -359,8 +360,8 @@ pub fn heading_bar(
|
||||
if let Some((a, b)) = data.sorted_by.as_ref() {
|
||||
if x == a {
|
||||
match b {
|
||||
SortedOrder::Asc => suffix = " ⌃",
|
||||
SortedOrder::Desc => suffix = " ⌄",
|
||||
SortedOrder::Asc => suffix = " ▲",
|
||||
SortedOrder::Desc => suffix = " ▼",
|
||||
}
|
||||
suffix_margin = 2;
|
||||
color = Color::White;
|
||||
|
||||
+33
-4
@@ -7,7 +7,10 @@ use std::{
|
||||
use tokio::task::JoinHandle;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::app_data::{ContainerId, Header};
|
||||
use crate::{
|
||||
app_data::{ContainerId, Header},
|
||||
exec::ExecMode,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum SelectablePanel {
|
||||
@@ -174,6 +177,7 @@ pub struct GuiState {
|
||||
panel_map: HashMap<SelectablePanel, Rect>,
|
||||
selected_panel: SelectablePanel,
|
||||
status: HashSet<Status>,
|
||||
exec_mode: Option<ExecMode>,
|
||||
pub info_box_text: Option<String>,
|
||||
}
|
||||
impl GuiState {
|
||||
@@ -265,16 +269,41 @@ impl GuiState {
|
||||
}
|
||||
|
||||
/// Remove a gui_status into the current gui_status HashSet
|
||||
/// Remove exec mode & deleteConfirm is required
|
||||
pub fn status_del(&mut self, status: Status) {
|
||||
self.status.remove(&status);
|
||||
if status == Status::DeleteConfirm {
|
||||
self.status.remove(&Status::DeleteConfirm);
|
||||
match status {
|
||||
Status::DeleteConfirm => {
|
||||
self.status.remove(&Status::DeleteConfirm);
|
||||
}
|
||||
Status::Exec => {
|
||||
self.exec_mode = None;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inset the ExecMode into self, and set the Status as exec
|
||||
/// Using StatusPush with Status::Exec won't insert into the hash map
|
||||
/// To force self.exec_mode to be set
|
||||
pub fn set_exec_mode(&mut self, mode: ExecMode) {
|
||||
self.exec_mode = Some(mode);
|
||||
self.status.insert(Status::Exec);
|
||||
}
|
||||
|
||||
pub fn get_exec_mode(&mut self) -> Option<ExecMode> {
|
||||
self.exec_mode.clone()
|
||||
}
|
||||
|
||||
/// Insert a gui_status into the current gui_status HashSet
|
||||
/// If the status is Exec, it won't get inserted, set_exec_mode() should be used instead
|
||||
pub fn status_push(&mut self, status: Status) {
|
||||
self.status.insert(status);
|
||||
match status {
|
||||
Status::Exec => (),
|
||||
_ => {
|
||||
self.status.insert(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Change to next selectable panel
|
||||
|
||||
+23
-31
@@ -1,4 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use bollard::Docker;
|
||||
use crossterm::{
|
||||
event::{self, DisableMouseCapture, Event},
|
||||
execute,
|
||||
@@ -28,20 +29,20 @@ pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status};
|
||||
use crate::{
|
||||
app_data::{AppData, Columns, ContainerId, Header, SortedOrder},
|
||||
app_error::AppError,
|
||||
docker_data::DockerMessage,
|
||||
input_handler::InputMessages,
|
||||
};
|
||||
|
||||
pub const DOCKER_COMMAND: &str = "docker";
|
||||
|
||||
pub struct Ui {
|
||||
// args: CliArgs,
|
||||
app_data: Arc<Mutex<AppData>>,
|
||||
docker_sx: Sender<DockerMessage>,
|
||||
gui_state: Arc<Mutex<GuiState>>,
|
||||
input_poll_rate: Duration,
|
||||
is_running: Arc<AtomicBool>,
|
||||
now: Instant,
|
||||
sender: Sender<InputMessages>,
|
||||
terminal: Terminal<CrosstermBackend<Stdout>>,
|
||||
cursor_position: (u16, u16),
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
@@ -60,20 +61,24 @@ impl Ui {
|
||||
/// Create a new Ui struct, and execute the drawing loop
|
||||
pub async fn create(
|
||||
app_data: Arc<Mutex<AppData>>,
|
||||
docker_sx: Sender<DockerMessage>,
|
||||
gui_state: Arc<Mutex<GuiState>>,
|
||||
is_running: Arc<AtomicBool>,
|
||||
sender: Sender<InputMessages>,
|
||||
) {
|
||||
if let Ok(terminal) = Self::setup_terminal() {
|
||||
if let Ok(mut terminal) = Self::setup_terminal() {
|
||||
// let args = app_data.lock().args.clone();
|
||||
let cursor_position = terminal.get_cursor().unwrap_or_default();
|
||||
let mut ui = Self {
|
||||
app_data,
|
||||
docker_sx,
|
||||
gui_state,
|
||||
input_poll_rate: std::time::Duration::from_millis(100),
|
||||
is_running,
|
||||
now: Instant::now(),
|
||||
sender,
|
||||
terminal,
|
||||
cursor_position,
|
||||
};
|
||||
if let Err(e) = ui.draw_ui().await {
|
||||
error!("{e}");
|
||||
@@ -111,6 +116,9 @@ impl Ui {
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
disable_raw_mode()?;
|
||||
self.terminal.clear().ok();
|
||||
self.terminal
|
||||
.set_cursor(self.cursor_position.0, self.cursor_position.1)?;
|
||||
Ok(self.terminal.show_cursor()?)
|
||||
}
|
||||
|
||||
@@ -138,24 +146,17 @@ impl Ui {
|
||||
}
|
||||
|
||||
/// Use exeternal docker cli to exec into a container
|
||||
fn exec(&mut self) {
|
||||
let id = self.app_data.lock().get_selected_container_id();
|
||||
async fn exec(&mut self) {
|
||||
let mut exec_mode = self.gui_state.lock().get_exec_mode();
|
||||
|
||||
if let Some(id) = id {
|
||||
// if Self::can_exec(&id).is_some() {
|
||||
if let Ok(mut child) = std::process::Command::new(DOCKER_COMMAND)
|
||||
.args(["exec", "-it", id.get(), "sh"])
|
||||
.stdin(std::process::Stdio::inherit())
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.spawn()
|
||||
{
|
||||
self.reset_terminal().ok();
|
||||
child.wait().ok();
|
||||
if child.kill().is_err() {
|
||||
std::process::exit(1)
|
||||
}
|
||||
}
|
||||
if let Some(mode) = exec_mode {
|
||||
self.reset_terminal().ok();
|
||||
self.terminal.clear().ok();
|
||||
if let Err(e) = mode.run(&self.app_data, &self.gui_state).await {
|
||||
self.app_data
|
||||
.lock()
|
||||
.set_error(e, &self.gui_state, Status::Error);
|
||||
};
|
||||
}
|
||||
self.terminal.clear().ok();
|
||||
self.reset_terminal().ok();
|
||||
@@ -168,7 +169,7 @@ impl Ui {
|
||||
while self.is_running.load(Ordering::SeqCst) {
|
||||
let exec = self.gui_state.lock().status_contains(&[Status::Exec]);
|
||||
if exec {
|
||||
self.exec();
|
||||
self.exec().await;
|
||||
}
|
||||
|
||||
if self
|
||||
@@ -220,15 +221,6 @@ impl Ui {
|
||||
}
|
||||
}
|
||||
|
||||
// #[macro_export]
|
||||
// /// This macro simplifies the definition and evaluation of variables by capturing and immediately evaluating an expression.
|
||||
// macro_rules! value_capture {
|
||||
// ($name:ident, $lock_expr:expr) => {
|
||||
// let $name = || $lock_expr;
|
||||
// let $name = $name();
|
||||
// };
|
||||
// }
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn get_wholelayout(f: &Frame) -> std::rc::Rc<[ratatui::layout::Rect]> {
|
||||
Layout::default()
|
||||
|
||||
Reference in New Issue
Block a user