refactor: DockerData refactors
Use a croner in the docker_data, instead of in the ui thread, as this thread will be paused when in exec mode. is_initilised is again done in docker_data, after stats have been calculated use bollard from git, waiting for new release due to Docker changes
This commit is contained in:
+3
-1
@@ -13,7 +13,9 @@ categories = ["command-line-utilities"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
bollard = "0.15"
|
# bollard = "0.15"
|
||||||
|
bollard = { git = "https://www.github.com/fussybeaver/bollard.git", rev = "cef1cd5" }
|
||||||
|
|
||||||
cansi = "2.2"
|
cansi = "2.2"
|
||||||
clap = { version = "4.4", features = ["derive", "unicode", "color"] }
|
clap = { version = "4.4", features = ["derive", "unicode", "color"] }
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ pub enum State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
pub const fn is_alive(self) -> bool {
|
||||||
|
matches!(self, Self::Running)
|
||||||
|
}
|
||||||
pub const fn get_color(self) -> Color {
|
pub const fn get_color(self) -> Color {
|
||||||
match self {
|
match self {
|
||||||
Self::Paused => Color::Yellow,
|
Self::Paused => Color::Yellow,
|
||||||
@@ -158,6 +161,12 @@ impl From<&str> for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Option<String>> for State {
|
||||||
|
fn from(input: Option<String>) -> Self {
|
||||||
|
input.map_or(Self::Unknown, |input| Self::from(input.as_str()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for State {
|
impl fmt::Display for State {
|
||||||
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 {
|
||||||
@@ -216,7 +225,7 @@ impl fmt::Display for DockerControls {
|
|||||||
Self::Restart => "restart",
|
Self::Restart => "restart",
|
||||||
Self::Start => "start",
|
Self::Start => "start",
|
||||||
Self::Stop => "stop",
|
Self::Stop => "stop",
|
||||||
Self::Unpause => "unpause",
|
Self::Unpause => "resume",
|
||||||
};
|
};
|
||||||
write!(f, "{disp}")
|
write!(f, "{disp}")
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-36
@@ -10,13 +10,19 @@ use futures_util::StreamExt;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{
|
||||||
|
atomic::{AtomicBool, AtomicUsize},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use tokio::{
|
||||||
|
sync::mpsc::{Receiver, Sender},
|
||||||
|
task::JoinHandle,
|
||||||
};
|
};
|
||||||
use tokio::{sync::mpsc::Receiver, task::JoinHandle};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app_data::{AppData, ContainerId, DockerControls},
|
app_data::{AppData, ContainerId, DockerControls, State},
|
||||||
app_error::AppError,
|
app_error::AppError,
|
||||||
parse_args::CliArgs,
|
parse_args::CliArgs,
|
||||||
ui::{GuiState, Status},
|
ui::{GuiState, Status},
|
||||||
@@ -50,6 +56,11 @@ impl Binate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// struct Init {
|
||||||
|
// done: ,
|
||||||
|
// len:
|
||||||
|
// }
|
||||||
|
|
||||||
pub struct DockerData {
|
pub struct DockerData {
|
||||||
app_data: Arc<Mutex<AppData>>,
|
app_data: Arc<Mutex<AppData>>,
|
||||||
args: CliArgs,
|
args: CliArgs,
|
||||||
@@ -57,6 +68,7 @@ pub struct DockerData {
|
|||||||
docker: Arc<Docker>,
|
docker: Arc<Docker>,
|
||||||
gui_state: Arc<Mutex<GuiState>>,
|
gui_state: Arc<Mutex<GuiState>>,
|
||||||
is_running: Arc<AtomicBool>,
|
is_running: Arc<AtomicBool>,
|
||||||
|
init: Option<Arc<AtomicUsize>>,
|
||||||
receiver: Receiver<DockerMessage>,
|
receiver: Receiver<DockerMessage>,
|
||||||
spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>,
|
spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>,
|
||||||
}
|
}
|
||||||
@@ -96,23 +108,49 @@ impl DockerData {
|
|||||||
app_data: Arc<Mutex<AppData>>,
|
app_data: Arc<Mutex<AppData>>,
|
||||||
docker: Arc<Docker>,
|
docker: Arc<Docker>,
|
||||||
id: ContainerId,
|
id: ContainerId,
|
||||||
is_running: bool,
|
init: Option<(Arc<AtomicUsize>, usize)>,
|
||||||
|
state: State,
|
||||||
spawn_id: SpawnId,
|
spawn_id: SpawnId,
|
||||||
spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>,
|
spawns: Arc<Mutex<HashMap<SpawnId, JoinHandle<()>>>>,
|
||||||
) {
|
) {
|
||||||
|
// if dead and !init then inspect!
|
||||||
|
|
||||||
|
if state.is_alive() || init.is_some() {
|
||||||
|
// // if state == State::Paused && init.is_some() {
|
||||||
|
// // app_data.lock().debug_string.push_str("is paused");
|
||||||
|
|
||||||
|
// // if let Ok(result) = docker.inspect_container(id.get(), Some(InspectContainerOptions{size:false})).await {
|
||||||
|
// // let mem_limit = format!("{}", result.host_config.map_or(0, |i|i.memory.unwrap_or_default()));
|
||||||
|
|
||||||
|
// // app_data.lock().debug_string.push_str(&mem_limit);
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // }else if state.is_alive() || init.is_some() {
|
||||||
let mut stream = docker
|
let mut stream = docker
|
||||||
.stats(
|
.stats(
|
||||||
id.get(),
|
id.get(),
|
||||||
Some(StatsOptions {
|
Some(StatsOptions {
|
||||||
stream: false,
|
stream: false,
|
||||||
one_shot: !is_running,
|
one_shot: false,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.take(1);
|
.take(1);
|
||||||
|
|
||||||
|
// let a = stream.next().await;
|
||||||
|
// app_data.lock().debug_string.push_str(&format!("{:?}", a.is_some()));
|
||||||
|
// let bb = a.unwrap().unwrap();
|
||||||
|
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// app_data.lock().debug_string.push_str("jkl");
|
||||||
|
|
||||||
while let Some(Ok(stats)) = stream.next().await {
|
while let Some(Ok(stats)) = stream.next().await {
|
||||||
let mem_stat = stats.memory_stats.usage.unwrap_or_default();
|
let mem_stat = if state.is_alive() {
|
||||||
|
Some(stats.memory_stats.usage.unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mem_limit = stats.memory_stats.limit.unwrap_or_default();
|
let mem_limit = stats.memory_stats.limit.unwrap_or_default();
|
||||||
|
|
||||||
let op_key = stats
|
let op_key = stats
|
||||||
@@ -120,8 +158,11 @@ impl DockerData {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|networks| networks.keys().next().cloned());
|
.and_then(|networks| networks.keys().next().cloned());
|
||||||
|
|
||||||
let cpu_stats = Self::calculate_usage(&stats);
|
let cpu_stats = if state.is_alive() {
|
||||||
|
Some(Self::calculate_usage(&stats))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let (rx, tx) = if let Some(key) = op_key {
|
let (rx, tx) = if let Some(key) = op_key {
|
||||||
stats
|
stats
|
||||||
.networks
|
.networks
|
||||||
@@ -132,31 +173,28 @@ impl DockerData {
|
|||||||
(0, 0)
|
(0, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_running {
|
|
||||||
app_data.lock().update_stats(
|
|
||||||
&id,
|
|
||||||
Some(cpu_stats),
|
|
||||||
Some(mem_stat),
|
|
||||||
mem_limit,
|
|
||||||
rx,
|
|
||||||
tx,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
app_data
|
app_data
|
||||||
.lock()
|
.lock()
|
||||||
.update_stats(&id, None, None, mem_limit, rx, tx);
|
.update_stats(&id, cpu_stats, mem_stat, mem_limit, rx, tx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
spawns.lock().remove(&spawn_id);
|
spawns.lock().remove(&spawn_id);
|
||||||
|
if let Some((target, _)) = init {
|
||||||
|
target.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update all stats, spawn each container into own tokio::spawn thread
|
/// Update all stats, spawn each container into own tokio::spawn thread
|
||||||
fn update_all_container_stats(&mut self, all_ids: &[(bool, ContainerId)]) {
|
fn update_all_container_stats(&mut self, all_ids: &[(State, ContainerId)]) {
|
||||||
for (is_running, id) in all_ids {
|
// let thing =all_ids.len();
|
||||||
|
for (state, id) in all_ids {
|
||||||
|
// let init = self.init.as_ref().map_or_else(|| None, |x| Some((Arc::clone(x), all_ids.len())));
|
||||||
let docker = Arc::clone(&self.docker);
|
let docker = Arc::clone(&self.docker);
|
||||||
let app_data = Arc::clone(&self.app_data);
|
let app_data = Arc::clone(&self.app_data);
|
||||||
let spawns = Arc::clone(&self.spawns);
|
let spawns = Arc::clone(&self.spawns);
|
||||||
let spawn_id = SpawnId::Stats((id.clone(), self.binate));
|
let spawn_id = SpawnId::Stats((id.clone(), self.binate));
|
||||||
|
|
||||||
|
let init = self.init.as_ref().map(|i| (Arc::clone(i), all_ids.len()));
|
||||||
self.spawns
|
self.spawns
|
||||||
.lock()
|
.lock()
|
||||||
.entry(spawn_id.clone())
|
.entry(spawn_id.clone())
|
||||||
@@ -165,7 +203,8 @@ impl DockerData {
|
|||||||
app_data,
|
app_data,
|
||||||
docker,
|
docker,
|
||||||
id.clone(),
|
id.clone(),
|
||||||
*is_running,
|
init,
|
||||||
|
*state,
|
||||||
spawn_id,
|
spawn_id,
|
||||||
spawns,
|
spawns,
|
||||||
))
|
))
|
||||||
@@ -177,7 +216,7 @@ impl DockerData {
|
|||||||
/// Get all current containers, handle into ContainerItem in the app_data struct rather than here
|
/// Get all current containers, handle into ContainerItem in the app_data struct rather than here
|
||||||
/// Just make sure that items sent are guaranteed to have an id
|
/// Just make sure that items sent are guaranteed to have an id
|
||||||
/// If in a containerised runtime, will ignore any container that uses the `/app/oxker` as an entry point, unless the `-s` flag is set
|
/// If in a containerised runtime, will ignore any container that uses the `/app/oxker` as an entry point, unless the `-s` flag is set
|
||||||
pub async fn update_all_containers(&mut self) -> Vec<(bool, ContainerId)> {
|
pub async fn update_all_containers(&mut self) -> Vec<(State, ContainerId)> {
|
||||||
let containers = self
|
let containers = self
|
||||||
.docker
|
.docker
|
||||||
.list_containers(Some(ListContainersOptions::<String> {
|
.list_containers(Some(ListContainersOptions::<String> {
|
||||||
@@ -212,13 +251,7 @@ impl DockerData {
|
|||||||
output
|
output
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|i| {
|
.filter_map(|i| {
|
||||||
i.id.map(|id| {
|
i.id.map(|id| (State::from(i.state), ContainerId::from(id.as_str())))
|
||||||
(
|
|
||||||
i.state == Some("running".to_owned())
|
|
||||||
|| i.state == Some("restarting".to_owned()),
|
|
||||||
ContainerId::from(id.as_str()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
@@ -253,7 +286,7 @@ impl DockerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update all logs, spawn each container into own tokio::spawn thread
|
/// Update all logs, spawn each container into own tokio::spawn thread
|
||||||
fn init_all_logs(&mut self, all_ids: &[(bool, ContainerId)]) {
|
fn init_all_logs(&mut self, all_ids: &[(State, ContainerId)]) {
|
||||||
for (_, id) in all_ids {
|
for (_, id) in all_ids {
|
||||||
let docker = Arc::clone(&self.docker);
|
let docker = Arc::clone(&self.docker);
|
||||||
let app_data = Arc::clone(&self.app_data);
|
let app_data = Arc::clone(&self.app_data);
|
||||||
@@ -275,6 +308,7 @@ impl DockerData {
|
|||||||
.lock()
|
.lock()
|
||||||
.entry(SpawnId::Log(container.id.clone()))
|
.entry(SpawnId::Log(container.id.clone()))
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
|
// TODO make a struct that can create this data
|
||||||
let app_data = Arc::clone(&self.app_data);
|
let app_data = Arc::clone(&self.app_data);
|
||||||
let docker = Arc::clone(&self.docker);
|
let docker = Arc::clone(&self.docker);
|
||||||
let id = container.id.clone();
|
let id = container.id.clone();
|
||||||
@@ -291,17 +325,17 @@ impl DockerData {
|
|||||||
self.gui_state.lock().status_push(Status::Init);
|
self.gui_state.lock().status_push(Status::Init);
|
||||||
let loading_uuid = Uuid::new_v4();
|
let loading_uuid = Uuid::new_v4();
|
||||||
let loading_handle = GuiState::start_loading_animation(&self.gui_state, loading_uuid);
|
let loading_handle = GuiState::start_loading_animation(&self.gui_state, loading_uuid);
|
||||||
// let handle = self.gui_state.lock().st
|
|
||||||
|
|
||||||
let all_ids = self.update_all_containers().await;
|
let all_ids = self.update_all_containers().await;
|
||||||
|
|
||||||
self.update_all_container_stats(&all_ids);
|
self.update_all_container_stats(&all_ids);
|
||||||
|
|
||||||
self.init_all_logs(&all_ids);
|
self.init_all_logs(&all_ids);
|
||||||
|
|
||||||
// wait until all logs have initialised
|
while let Some(x) = self.init.as_ref() {
|
||||||
while !self.app_data.lock().initialised(&all_ids) {
|
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||||
|
if x.load(std::sync::atomic::Ordering::SeqCst) == all_ids.len() {
|
||||||
|
self.init = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.gui_state
|
self.gui_state
|
||||||
.lock()
|
.lock()
|
||||||
@@ -422,11 +456,26 @@ impl DockerData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send an update message every x ms, where x is the args.docker_interval
|
||||||
|
fn croner(args: &CliArgs, docker_tx: Sender<DockerMessage>) {
|
||||||
|
let update_duration = std::time::Duration::from_millis(u64::from(args.docker_interval));
|
||||||
|
let mut now = std::time::Instant::now();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
let to_sleep = update_duration.saturating_sub(now.elapsed());
|
||||||
|
tokio::time::sleep(to_sleep).await;
|
||||||
|
docker_tx.send(DockerMessage::Update).await.ok();
|
||||||
|
now = std::time::Instant::now();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialise self, and start the message receiving loop
|
/// Initialise self, and start the message receiving loop
|
||||||
pub async fn init(
|
pub async fn init(
|
||||||
app_data: Arc<Mutex<AppData>>,
|
app_data: Arc<Mutex<AppData>>,
|
||||||
docker: Docker,
|
docker: Docker,
|
||||||
docker_rx: Receiver<DockerMessage>,
|
docker_rx: Receiver<DockerMessage>,
|
||||||
|
docker_tx: Sender<DockerMessage>,
|
||||||
gui_state: Arc<Mutex<GuiState>>,
|
gui_state: Arc<Mutex<GuiState>>,
|
||||||
is_running: Arc<AtomicBool>,
|
is_running: Arc<AtomicBool>,
|
||||||
) {
|
) {
|
||||||
@@ -434,15 +483,17 @@ impl DockerData {
|
|||||||
if app_data.lock().get_error().is_none() {
|
if app_data.lock().get_error().is_none() {
|
||||||
let mut inner = Self {
|
let mut inner = Self {
|
||||||
app_data,
|
app_data,
|
||||||
args,
|
args: args.clone(),
|
||||||
binate: Binate::One,
|
binate: Binate::One,
|
||||||
docker: Arc::new(docker),
|
docker: Arc::new(docker),
|
||||||
gui_state,
|
gui_state,
|
||||||
|
init: Some(Arc::new(AtomicUsize::new(0))),
|
||||||
is_running,
|
is_running,
|
||||||
receiver: docker_rx,
|
receiver: docker_rx,
|
||||||
spawns: Arc::new(Mutex::new(HashMap::new())),
|
spawns: Arc::new(Mutex::new(HashMap::new())),
|
||||||
};
|
};
|
||||||
inner.initialise_container_data().await;
|
inner.initialise_container_data().await;
|
||||||
|
Self::croner(&args, docker_tx);
|
||||||
inner.message_handler().await;
|
inner.message_handler().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user