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
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DockerControls {
pub enum DockerCommand {
Pause,
Restart,
Start,
@@ -342,7 +342,7 @@ pub enum DockerControls {
Delete,
}
impl DockerControls {
impl DockerCommand {
pub const fn get_color(self) -> Color {
match self {
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 {
let disp = match self {
Self::Pause => "pause",
@@ -577,7 +577,7 @@ impl Logs {
pub struct ContainerItem {
pub cpu_stats: VecDeque<CpuStats>,
pub created: u64,
pub docker_controls: StatefulList<DockerControls>,
pub docker_controls: StatefulList<DockerCommand>,
pub id: ContainerId,
pub image: ContainerImage,
pub is_oxker: bool,
@@ -620,7 +620,7 @@ impl ContainerItem {
state: State,
status: ContainerStatus,
) -> Self {
let mut docker_controls = StatefulList::new(DockerControls::gen_vec(state));
let mut docker_controls = StatefulList::new(DockerCommand::gen_vec(state));
docker_controls.start();
Self {
+28 -29
View File
@@ -535,7 +535,7 @@ impl AppData {
/// Get the current selected docker command
/// 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| {
i.docker_controls.state.selected().and_then(|x| {
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> {
self.get_mut_selected_container()
.map(|i| &mut i.docker_controls.state)
}
/// Get mutable Option of the currently selected container DockerControls items
/// TODO command or control, need a uniform name across the application
pub fn get_control_items(&mut self) -> Option<&mut Vec<DockerControls>> {
/// Get mutable Option of the currently selected container DockerConmand items
pub fn get_control_items(&mut self) -> Option<&mut Vec<DockerCommand>> {
self.get_mut_selected_container()
.map(|i| &mut i.docker_controls.items)
}
@@ -855,7 +854,7 @@ impl AppData {
item.status = status;
};
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
match state {
State::Removing | State::Restarting | State::Unknown => {
@@ -1526,7 +1525,7 @@ mod tests {
app_data.docker_controls_start();
let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Pause));
assert_eq!(result, Some(DockerCommand::Pause));
}
#[test]
@@ -1539,7 +1538,7 @@ mod tests {
app_data.docker_controls_next();
let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Restart));
assert_eq!(result, Some(DockerCommand::Restart));
}
#[test]
@@ -1551,12 +1550,12 @@ mod tests {
app_data.docker_controls_end();
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
app_data.docker_controls_next();
let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Delete));
assert_eq!(result, Some(DockerCommand::Delete));
}
#[test]
@@ -1569,19 +1568,19 @@ mod tests {
app_data.docker_controls_previous();
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
app_data.docker_controls_start();
app_data.docker_controls_previous();
let result = app_data.selected_docker_controls();
assert_eq!(result, Some(DockerControls::Pause));
assert_eq!(result, Some(DockerCommand::Pause));
}
#[test]
/// DockerCommands get correct controls dependant on container state
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| {
ContainerItem::new(
1,
@@ -1605,42 +1604,42 @@ mod tests {
test_state(
State::Dead,
&mut vec![
DockerControls::Start,
DockerControls::Restart,
DockerControls::Delete,
DockerCommand::Start,
DockerCommand::Restart,
DockerCommand::Delete,
],
);
test_state(
State::Exited,
&mut vec![
DockerControls::Start,
DockerControls::Restart,
DockerControls::Delete,
DockerCommand::Start,
DockerCommand::Restart,
DockerCommand::Delete,
],
);
test_state(
State::Paused,
&mut vec![
DockerControls::Resume,
DockerControls::Stop,
DockerControls::Delete,
DockerCommand::Resume,
DockerCommand::Stop,
DockerCommand::Delete,
],
);
test_state(State::Removing, &mut vec![DockerControls::Delete]);
test_state(State::Removing, &mut vec![DockerCommand::Delete]);
test_state(
State::Restarting,
&mut vec![DockerControls::Stop, DockerControls::Delete],
&mut vec![DockerCommand::Stop, DockerCommand::Delete],
);
test_state(
State::Running(RunningState::Healthy),
&mut vec![
DockerControls::Pause,
DockerControls::Restart,
DockerControls::Stop,
DockerControls::Delete,
DockerCommand::Pause,
DockerCommand::Restart,
DockerCommand::Stop,
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;
/// app errors to set in global state
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub enum AppError {
DockerCommand(DockerControls),
DockerCommand(DockerCommand),
DockerExec,
DockerLogs,
DockerConnect,
+9 -7
View File
@@ -1,19 +1,21 @@
use std::sync::Arc;
use crate::app_data::ContainerId;
use crate::app_data::{ContainerId, DockerCommand};
use bollard::Docker;
use tokio::sync::oneshot::Sender;
#[derive(Debug)]
pub enum DockerMessage {
ConfirmDelete(ContainerId),
Delete(ContainerId),
Control((DockerCommand, ContainerId)),
// Delete(ContainerId),
Exec(Sender<Arc<Docker>>),
Pause(ContainerId),
// Pause(ContainerId),
Quit,
Restart(ContainerId),
Start(ContainerId),
Stop(ContainerId),
Resume(ContainerId),
// Restart(ContainerId),
// Start(ContainerId),
// Stop(ContainerId),
// Resume(ContainerId),
Update,
}
+35 -74
View File
@@ -22,7 +22,7 @@ use tokio::{
use uuid::Uuid;
use crate::{
app_data::{AppData, ContainerId, ContainerStatus, DockerControls, State},
app_data::{AppData, ContainerId, ContainerStatus, DockerCommand, State},
app_error::AppError,
parse_args::CliArgs,
ui::{GuiState, Status},
@@ -340,7 +340,7 @@ impl DockerData {
/// Set the global error as the docker error, and set gui_state to error
fn set_error(
app_data: &Arc<Mutex<AppData>>,
error: DockerControls,
error: DockerCommand,
gui_state: &Arc<Mutex<GuiState>>,
) {
app_data
@@ -348,78 +348,19 @@ impl DockerData {
.set_error(AppError::DockerCommand(error), gui_state, Status::Error);
}
/// Handle incoming messages, container controls & all container information update
/// Spawn Docker commands off into own thread
#[allow(clippy::too_many_lines)]
async fn message_handler(&mut self) {
while let Some(message) = self.receiver.recv().await {
let docker = Arc::clone(&self.docker);
let gui_state = Arc::clone(&self.gui_state);
let app_data = Arc::clone(&self.app_data);
/// Execute docker comamnds (start, stop etc) on it's own tokio thread
async fn execute_command(&mut self, control: DockerCommand, id: ContainerId) {
let (app_data, docker, gui_state) = (
Arc::clone(&self.app_data),
Arc::clone(&self.docker),
Arc::clone(&self.gui_state),
);
tokio::spawn(async move {
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);
if docker.pause_container(id.get()).await.is_err() {
Self::set_error(&app_data, DockerControls::Pause, &gui_state);
}
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
if match control {
DockerCommand::Delete => {
docker
.remove_container(
id.get(),
Some(RemoveContainerOptions {
@@ -429,18 +370,38 @@ impl DockerData {
}),
)
.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()
{
Self::set_error(&app_data, DockerControls::Stop, &gui_state);
Self::set_error(&app_data, control, &gui_state);
}
gui_state.lock().stop_loading_animation(uuid);
});
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) => {
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::Quit => {
self.spawns
+12 -18
View File
@@ -22,7 +22,7 @@ use uuid::Uuid;
mod message;
use crate::{
app_data::{AppData, DockerControls, Header},
app_data::{AppData, DockerCommand, Header},
app_error::AppError,
docker_data::DockerMessage,
exec::{tty_readable, ExecMode},
@@ -112,7 +112,10 @@ impl InputHandler {
async fn confirm_delete(&self) {
let id = self.gui_state.lock().get_delete_container();
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();
if let Some(id) = option_id {
match command {
DockerControls::Delete => self
DockerCommand::Delete => self
.docker_tx
.send(DockerMessage::ConfirmDelete(id))
.await
.ok(),
DockerControls::Pause => {
self.docker_tx.send(DockerMessage::Pause(id)).await.ok()
}
DockerControls::Resume => {
self.docker_tx.send(DockerMessage::Resume(id)).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()
}
_ => self
.docker_tx
.send(DockerMessage::Control((command, id)))
.await
.ok(),
};
}
}