Merge branch 'refactor/docker' into dev

This commit is contained in:
Jack Wills
2024-11-15 15:39:26 +00:00
6 changed files with 104 additions and 148 deletions
+5 -5
View File
@@ -333,7 +333,7 @@ impl fmt::Display for State {
/// Items for the container control list /// Items for the container control list
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DockerControls { pub enum DockerCommand {
Pause, Pause,
Restart, Restart,
Start, Start,
@@ -342,7 +342,7 @@ pub enum DockerControls {
Delete, Delete,
} }
impl DockerControls { impl DockerCommand {
pub const fn get_color(self) -> Color { pub const fn get_color(self) -> Color {
match self { match self {
Self::Pause => Color::Yellow, Self::Pause => Color::Yellow,
@@ -366,7 +366,7 @@ impl DockerControls {
} }
} }
impl fmt::Display for DockerControls { impl fmt::Display for DockerCommand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let disp = match self { let disp = match self {
Self::Pause => "pause", Self::Pause => "pause",
@@ -577,7 +577,7 @@ impl Logs {
pub struct ContainerItem { pub struct ContainerItem {
pub cpu_stats: VecDeque<CpuStats>, pub cpu_stats: VecDeque<CpuStats>,
pub created: u64, pub created: u64,
pub docker_controls: StatefulList<DockerControls>, pub docker_controls: StatefulList<DockerCommand>,
pub id: ContainerId, pub id: ContainerId,
pub image: ContainerImage, pub image: ContainerImage,
pub is_oxker: bool, pub is_oxker: bool,
@@ -620,7 +620,7 @@ impl ContainerItem {
state: State, state: State,
status: ContainerStatus, status: ContainerStatus,
) -> Self { ) -> Self {
let mut docker_controls = StatefulList::new(DockerControls::gen_vec(state)); let mut docker_controls = StatefulList::new(DockerCommand::gen_vec(state));
docker_controls.start(); docker_controls.start();
Self { Self {
+28 -29
View File
@@ -535,7 +535,7 @@ impl AppData {
/// Get the current selected docker command /// Get the current selected docker command
/// So know which command to execute /// So know which command to execute
pub fn selected_docker_controls(&self) -> Option<DockerControls> { pub fn selected_docker_controls(&self) -> Option<DockerCommand> {
self.get_selected_container().and_then(|i| { self.get_selected_container().and_then(|i| {
i.docker_controls.state.selected().and_then(|x| { i.docker_controls.state.selected().and_then(|x| {
i.docker_controls i.docker_controls
@@ -574,15 +574,14 @@ impl AppData {
} }
} }
/// Get mutable Option of the currently selected container DockerControls state /// Get mutable Option of the currently selected container DockerCommand state
pub fn get_control_state(&mut self) -> Option<&mut ListState> { pub fn get_control_state(&mut self) -> Option<&mut ListState> {
self.get_mut_selected_container() self.get_mut_selected_container()
.map(|i| &mut i.docker_controls.state) .map(|i| &mut i.docker_controls.state)
} }
/// Get mutable Option of the currently selected container DockerControls items /// Get mutable Option of the currently selected container DockerConmand items
/// TODO command or control, need a uniform name across the application pub fn get_control_items(&mut self) -> Option<&mut Vec<DockerCommand>> {
pub fn get_control_items(&mut self) -> Option<&mut Vec<DockerControls>> {
self.get_mut_selected_container() self.get_mut_selected_container()
.map(|i| &mut i.docker_controls.items) .map(|i| &mut i.docker_controls.items)
} }
@@ -855,7 +854,7 @@ impl AppData {
item.status = status; item.status = status;
}; };
if item.state != state { if item.state != state {
item.docker_controls.items = DockerControls::gen_vec(state); item.docker_controls.items = DockerCommand::gen_vec(state);
// Update the list state, needs to be None if the gen_vec returns an empty vec // Update the list state, needs to be None if the gen_vec returns an empty vec
match state { match state {
State::Removing | State::Restarting | State::Unknown => { State::Removing | State::Restarting | State::Unknown => {
@@ -1526,7 +1525,7 @@ mod tests {
app_data.docker_controls_start(); app_data.docker_controls_start();
let result = app_data.selected_docker_controls(); let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Pause)); assert_eq!(result, Some(DockerCommand::Pause));
} }
#[test] #[test]
@@ -1539,7 +1538,7 @@ mod tests {
app_data.docker_controls_next(); app_data.docker_controls_next();
let result = app_data.selected_docker_controls(); let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Restart)); assert_eq!(result, Some(DockerCommand::Restart));
} }
#[test] #[test]
@@ -1551,12 +1550,12 @@ mod tests {
app_data.docker_controls_end(); app_data.docker_controls_end();
let result = app_data.selected_docker_controls(); let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Delete)); assert_eq!(result, Some(DockerCommand::Delete));
// Next has no effect when at end // Next has no effect when at end
app_data.docker_controls_next(); app_data.docker_controls_next();
let result = app_data.selected_docker_controls(); let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Delete)); assert_eq!(result, Some(DockerCommand::Delete));
} }
#[test] #[test]
@@ -1569,19 +1568,19 @@ mod tests {
app_data.docker_controls_previous(); app_data.docker_controls_previous();
let result = app_data.selected_docker_controls(); let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Stop)); assert_eq!(result, Some(DockerCommand::Stop));
// previous has no effect when at start // previous has no effect when at start
app_data.docker_controls_start(); app_data.docker_controls_start();
app_data.docker_controls_previous(); app_data.docker_controls_previous();
let result = app_data.selected_docker_controls(); let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Pause)); assert_eq!(result, Some(DockerCommand::Pause));
} }
#[test] #[test]
/// DockerCommands get correct controls dependant on container state /// DockerCommands get correct controls dependant on container state
fn test_app_data_get_control_items() { fn test_app_data_get_control_items() {
let test_state = |state: State, expected: &mut Vec<DockerControls>| { let test_state = |state: State, expected: &mut Vec<DockerCommand>| {
let gen_item_state = |state: State| { let gen_item_state = |state: State| {
ContainerItem::new( ContainerItem::new(
1, 1,
@@ -1605,42 +1604,42 @@ mod tests {
test_state( test_state(
State::Dead, State::Dead,
&mut vec![ &mut vec![
DockerControls::Start, DockerCommand::Start,
DockerControls::Restart, DockerCommand::Restart,
DockerControls::Delete, DockerCommand::Delete,
], ],
); );
test_state( test_state(
State::Exited, State::Exited,
&mut vec![ &mut vec![
DockerControls::Start, DockerCommand::Start,
DockerControls::Restart, DockerCommand::Restart,
DockerControls::Delete, DockerCommand::Delete,
], ],
); );
test_state( test_state(
State::Paused, State::Paused,
&mut vec![ &mut vec![
DockerControls::Resume, DockerCommand::Resume,
DockerControls::Stop, DockerCommand::Stop,
DockerControls::Delete, DockerCommand::Delete,
], ],
); );
test_state(State::Removing, &mut vec![DockerControls::Delete]); test_state(State::Removing, &mut vec![DockerCommand::Delete]);
test_state( test_state(
State::Restarting, State::Restarting,
&mut vec![DockerControls::Stop, DockerControls::Delete], &mut vec![DockerCommand::Stop, DockerCommand::Delete],
); );
test_state( test_state(
State::Running(RunningState::Healthy), State::Running(RunningState::Healthy),
&mut vec![ &mut vec![
DockerControls::Pause, DockerCommand::Pause,
DockerControls::Restart, DockerCommand::Restart,
DockerControls::Stop, DockerCommand::Stop,
DockerControls::Delete, DockerCommand::Delete,
], ],
); );
test_state(State::Unknown, &mut vec![DockerControls::Delete]); test_state(State::Unknown, &mut vec![DockerCommand::Delete]);
} }
// ****** // // ****** //
+2 -2
View File
@@ -1,11 +1,11 @@
use crate::app_data::DockerControls; use crate::app_data::DockerCommand;
use std::fmt; use std::fmt;
/// app errors to set in global state /// app errors to set in global state
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum AppError { pub enum AppError {
DockerCommand(DockerControls), DockerCommand(DockerCommand),
DockerExec, DockerExec,
DockerLogs, DockerLogs,
DockerConnect, DockerConnect,
+9 -7
View File
@@ -1,19 +1,21 @@
use std::sync::Arc; use std::sync::Arc;
use crate::app_data::ContainerId; use crate::app_data::{ContainerId, DockerCommand};
use bollard::Docker; use bollard::Docker;
use tokio::sync::oneshot::Sender; use tokio::sync::oneshot::Sender;
#[derive(Debug)] #[derive(Debug)]
pub enum DockerMessage { pub enum DockerMessage {
ConfirmDelete(ContainerId), ConfirmDelete(ContainerId),
Delete(ContainerId), Control((DockerCommand, ContainerId)),
// Delete(ContainerId),
Exec(Sender<Arc<Docker>>), Exec(Sender<Arc<Docker>>),
Pause(ContainerId), // Pause(ContainerId),
Quit, Quit,
Restart(ContainerId), // Restart(ContainerId),
Start(ContainerId), // Start(ContainerId),
Stop(ContainerId), // Stop(ContainerId),
Resume(ContainerId), // Resume(ContainerId),
Update, Update,
} }
+35 -74
View File
@@ -22,7 +22,7 @@ use tokio::{
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
app_data::{AppData, ContainerId, ContainerStatus, DockerControls, State}, app_data::{AppData, ContainerId, ContainerStatus, DockerCommand, State},
app_error::AppError, app_error::AppError,
parse_args::CliArgs, parse_args::CliArgs,
ui::{GuiState, Status}, ui::{GuiState, Status},
@@ -340,7 +340,7 @@ impl DockerData {
/// Set the global error as the docker error, and set gui_state to error /// Set the global error as the docker error, and set gui_state to error
fn set_error( fn set_error(
app_data: &Arc<Mutex<AppData>>, app_data: &Arc<Mutex<AppData>>,
error: DockerControls, error: DockerCommand,
gui_state: &Arc<Mutex<GuiState>>, gui_state: &Arc<Mutex<GuiState>>,
) { ) {
app_data app_data
@@ -348,78 +348,19 @@ impl DockerData {
.set_error(AppError::DockerCommand(error), gui_state, Status::Error); .set_error(AppError::DockerCommand(error), gui_state, Status::Error);
} }
/// Handle incoming messages, container controls & all container information update /// Execute docker comamnds (start, stop etc) on it's own tokio thread
/// Spawn Docker commands off into own thread async fn execute_command(&mut self, control: DockerCommand, id: ContainerId) {
#[allow(clippy::too_many_lines)] let (app_data, docker, gui_state) = (
async fn message_handler(&mut self) { Arc::clone(&self.app_data),
while let Some(message) = self.receiver.recv().await { Arc::clone(&self.docker),
let docker = Arc::clone(&self.docker); Arc::clone(&self.gui_state),
let gui_state = Arc::clone(&self.gui_state); );
let app_data = Arc::clone(&self.app_data); tokio::spawn(async move {
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
// TODO need to refactor these
match message {
DockerMessage::Exec(docker_tx) => {
docker_tx.send(Arc::clone(&self.docker)).ok();
}
DockerMessage::Pause(id) => {
tokio::spawn(async move {
GuiState::start_loading_animation(&gui_state, uuid); GuiState::start_loading_animation(&gui_state, uuid);
if docker.pause_container(id.get()).await.is_err() { if match control {
Self::set_error(&app_data, DockerControls::Pause, &gui_state); DockerCommand::Delete => {
} docker
gui_state.lock().stop_loading_animation(uuid);
});
self.update_everything().await;
}
DockerMessage::Restart(id) => {
tokio::spawn(async move {
GuiState::start_loading_animation(&gui_state, uuid);
if docker.restart_container(id.get(), None).await.is_err() {
Self::set_error(&app_data, DockerControls::Restart, &gui_state);
}
gui_state.lock().stop_loading_animation(uuid);
});
self.update_everything().await;
}
DockerMessage::Start(id) => {
tokio::spawn(async move {
GuiState::start_loading_animation(&gui_state, uuid);
if docker
.start_container(id.get(), None::<StartContainerOptions<String>>)
.await
.is_err()
{
Self::set_error(&app_data, DockerControls::Start, &gui_state);
}
gui_state.lock().stop_loading_animation(uuid);
});
self.update_everything().await;
}
DockerMessage::Stop(id) => {
tokio::spawn(async move {
GuiState::start_loading_animation(&gui_state, uuid);
if docker.stop_container(id.get(), None).await.is_err() {
Self::set_error(&app_data, DockerControls::Stop, &gui_state);
}
gui_state.lock().stop_loading_animation(uuid);
});
self.update_everything().await;
}
DockerMessage::Resume(id) => {
tokio::spawn(async move {
GuiState::start_loading_animation(&gui_state, uuid);
if docker.unpause_container(id.get()).await.is_err() {
Self::set_error(&app_data, DockerControls::Resume, &gui_state);
}
gui_state.lock().stop_loading_animation(uuid);
});
self.update_everything().await;
}
DockerMessage::Delete(id) => {
tokio::spawn(async move {
GuiState::start_loading_animation(&gui_state, uuid);
if docker
.remove_container( .remove_container(
id.get(), id.get(),
Some(RemoveContainerOptions { Some(RemoveContainerOptions {
@@ -429,18 +370,38 @@ impl DockerData {
}), }),
) )
.await .await
}
DockerCommand::Pause => docker.pause_container(id.get()).await,
DockerCommand::Restart => docker.restart_container(id.get(), None).await,
DockerCommand::Resume => docker.unpause_container(id.get()).await,
DockerCommand::Start => {
docker
.start_container(id.get(), None::<StartContainerOptions<String>>)
.await
}
DockerCommand::Stop => docker.stop_container(id.get(), None).await,
}
.is_err() .is_err()
{ {
Self::set_error(&app_data, DockerControls::Stop, &gui_state); Self::set_error(&app_data, control, &gui_state);
} }
gui_state.lock().stop_loading_animation(uuid); gui_state.lock().stop_loading_animation(uuid);
}); });
self.update_everything().await; self.update_everything().await;
self.gui_state.lock().set_delete_container(None);
} }
/// Handle incoming messages, container controls & all container information update
/// Spawn Docker commands off into own thread
async fn message_handler(&mut self) {
while let Some(message) = self.receiver.recv().await {
match message {
DockerMessage::ConfirmDelete(id) => { DockerMessage::ConfirmDelete(id) => {
self.gui_state.lock().set_delete_container(Some(id)); self.gui_state.lock().set_delete_container(Some(id));
} }
DockerMessage::Control((command, id)) => self.execute_command(command, id).await,
DockerMessage::Exec(docker_tx) => {
docker_tx.send(Arc::clone(&self.docker)).ok();
}
DockerMessage::Update => self.update_everything().await, DockerMessage::Update => self.update_everything().await,
DockerMessage::Quit => { DockerMessage::Quit => {
self.spawns self.spawns
+12 -18
View File
@@ -22,7 +22,7 @@ use uuid::Uuid;
mod message; mod message;
use crate::{ use crate::{
app_data::{AppData, DockerControls, Header}, app_data::{AppData, DockerCommand, Header},
app_error::AppError, app_error::AppError,
docker_data::DockerMessage, docker_data::DockerMessage,
exec::{tty_readable, ExecMode}, exec::{tty_readable, ExecMode},
@@ -112,7 +112,10 @@ impl InputHandler {
async fn confirm_delete(&self) { async fn confirm_delete(&self) {
let id = self.gui_state.lock().get_delete_container(); let id = self.gui_state.lock().get_delete_container();
if let Some(id) = id { if let Some(id) = id {
self.docker_tx.send(DockerMessage::Delete(id)).await.ok(); self.docker_tx
.send(DockerMessage::Control((DockerCommand::Delete, id)))
.await
.ok();
} }
} }
@@ -281,26 +284,17 @@ impl InputHandler {
let option_id = self.app_data.lock().get_selected_container_id(); let option_id = self.app_data.lock().get_selected_container_id();
if let Some(id) = option_id { if let Some(id) = option_id {
match command { match command {
DockerControls::Delete => self DockerCommand::Delete => self
.docker_tx .docker_tx
.send(DockerMessage::ConfirmDelete(id)) .send(DockerMessage::ConfirmDelete(id))
.await .await
.ok(), .ok(),
DockerControls::Pause => {
self.docker_tx.send(DockerMessage::Pause(id)).await.ok() _ => self
} .docker_tx
DockerControls::Resume => { .send(DockerMessage::Control((command, id)))
self.docker_tx.send(DockerMessage::Resume(id)).await.ok() .await
} .ok(),
DockerControls::Start => {
self.docker_tx.send(DockerMessage::Start(id)).await.ok()
}
DockerControls::Stop => {
self.docker_tx.send(DockerMessage::Stop(id)).await.ok()
}
DockerControls::Restart => {
self.docker_tx.send(DockerMessage::Restart(id)).await.ok()
}
}; };
} }
} }