diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index dec7caa..b349e39 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,9 +1,13 @@ -# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.217.4/containers/rust/.devcontainer/base.Dockerfile - -# [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye -ARG VARIANT="buster" -FROM mcr.microsoft.com/vscode/devcontainers/rust:0-${VARIANT} +ARG VARIANT="bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} RUN printf "alias cls='clear'\nalias ll='ls -l --human-readable --color=auto --group-directories-first --classify --time-style=long-iso -all'" >> /etc/bash.bashrc -# RUN apt-get update && apt-get -y install upx-ucl +ENV PATH="/home/vscode/.cargo/bin:${PATH}" + +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends build-essential pkg-config libssl-dev + +USER vscode +RUN curl --proto '=https' --tlsv1.2 -sSf curl https://sh.rustup.rs | sh -s -- -y + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 41a7ebe..d4f7835 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -34,7 +34,7 @@ "extensions": [ "vadimcn.vscode-lldb", "mutantdino.resourcemonitor", - "matklad.rust-analyzer", + "rust-lang.rust-analyzer", "tamasfe.even-better-toml", "serayuzgur.crates", "christian-kohler.path-intellisense", diff --git a/CHANGELOG.md b/CHANGELOG.md index 602b578..b504e4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +### Chores ++ dependencies updated, [a3168daa3f769a6747dfbe61103073a7e80a1485], [78e59160bb6a978ee80e3a99eb72f051fb64e737] + +### Fixes ++ limit image name to 64 chars max, [b8f7763dd5ac7d0361dd7bfc1dad40f50ee95ae1] ++ devcontainer updated, [3bde4f5629539cab3dbb57556663ab81685f9d7a] + +### Features ++ derive Eq where appropriate, [d7c2601f959bc12a64cd25cef59c837e1e8c2b2a] ++ ignore containers 'oxker' containers, [1be9f52ad4a68f93142784e9df630c59cdec0a79] + +### Refactors ++ improved way to remove leading '/' of container name, [832e9782d7765872cbb84df6b3703fc08cb353c9] + # v0.1.3 ### 2022-08-04 diff --git a/Cargo.toml b/Cargo.toml index 7fb3bb3..af45ac5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,15 +14,15 @@ categories = ["command-line-utilities"] [dependencies] anyhow = "1.0" bollard = "0.13" -cansi = "2.1" +cansi = "2.2" clap={version="3.2", features = ["derive", "unicode"] } -crossterm = "0.24" +crossterm = "0.25" futures-util = "0.3" parking_lot = {version= "0.12"} -tokio = {version = "1.20", features=["full"]} +tokio = {version = "1.21", features=["full"]} tracing = "0.1" tracing-subscriber = "0.3" -tui = "0.18" +tui = "0.19" [dev-dependencies] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..30f795e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,103 @@ +# FROM debian:bullseye-slim +# FROM +FROM alpine:latest +# FROM scratch + +# DOCKER_GUID=1000 \ + # DOCKER_UID=1000 \ + # DOCKER_TIME_CONT=America \ + # DOCKER_TIME_CITY=New_York \ +# ARG DOCKER_APP_USER=oxker \ + # DOCKER_APP_GROUP=docker + +# ENV TZ=${DOCKER_TIME_CONT}/${DOCKER_TIME_CITY} + +# RUN apt-get update \ + # && apt-get install -y ca-certificates wget \ + # && update-ca-certificates \ +# RUN groupadd ${DOCKER_APP_GROUP} +# RUN useradd --no-create-home --no-log-init ${DOCKER_APP_USER} + # && mkdir /healthcheck /logs \ + # && chown ${DOCKER_APP_USER}:${DOCKER_APP_GROUP} /logs + +WORKDIR /app + +# COPY --chown=${DOCKER_APP_USER}:${DOCKER_APP_GROUP} docker/healthcheck/health_api.sh /healthcheck + +# Copy from local release destination +# COPY --chown=${DOCKER_APP_USER} target/release/oxker /app/ +# COPY target/release/oxker . +# RUN mkdir app +COPY /target/x86_64-unknown-linux-musl/release/oxker ./ +COPY ./start_oxker.sh ./ +RUN chmod +x /app/start_oxker.sh + +# Use an unprivileged user +# USER ${DOCKER_APP_USER} +ENV RUST_BACKTRACE=full +# ENTRYPOINT ["./oxker" ] +# CMD [ "./oxker"] +ENTRYPOINT ["/app/start_oxker.sh"] + +# docker run --rm -ti \ +# --name=ctop \ +# --volume /var/run/docker.sock:/var/run/docker.sock:ro \ +# # quay.io/vektorlab/ctop:latest + + +# docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker + +# docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro ghcr.io/mrjackwills/oxker:latest +# could get arch, and then download appropoatley from github? + +# FROM rust:latest as cargo-build + +# WORKDIR /build +# ENV RUSTFLAGS="-C target-feature=+crt-static" + +# COPY Cargo* ./build +# COPY src/ ./build + +# RUN cargo build --release --target x86_64-unknown-linux-gnu + +# ##################################### + + +# ##################################### + +# FROM scratch + +# COPY --from=cargo-build /build/target/x86_64-unknown-linux-gnu/release/oxker /oxker + +# ENTRYPOINT [ "/oxker" ] + +# FROM rust:latest AS build +# WORKDIR /oxker_build + +# # Download the target for static linking. +# RUN rustup target add x86_64-unknown-linux-musl + +# # Create a dummy project and build the app's dependencies. +# # If the Cargo.toml or Cargo.lock files have not changed, +# # we can use the docker build cache and skip these (typically slow) steps. +# RUN USER=root cargo new oxker --bin +# WORKDIR /oxker_build +# COPY Cargo.toml Cargo.lock ./ +# # CMD ["sleep", "6000"] + +# # RUN cargo build --release + +# # Copy the source and build the application. +# COPY src ./src/ +# RUN cargo install --target x86_64-unknown-linux-musl --path . + +# # Copy the statically-linked binary into a scratch container. +# FROM scratch +# COPY --from=build /oxker_build/bin/oxker . +# # USER 1000 +# CMD ["./oxker"] + +# cross build --target x86_64-unknown-linux-musl --release + +# rustup target add x86_64-unknown-linux-musl +# cargo build --release --target=x86_64-unknown-linux-musl \ No newline at end of file diff --git a/README.md b/README.md index 5ec8231..db64461 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@

- +

-

oxker

+

oxker

- A simple tui to view & control docker containers + A simple tui to view & control docker containers

- Built in Rust, making heavy use of tui-rs & Bollard + Built in Rust, making heavy use of tui-rs & Bollard

@@ -25,8 +25,7 @@ Now published on crates.io, so if you have cargo installed, simply run -``` cargo install oxker``` - +```cargo install oxker``` else see the pre-built binaries @@ -107,11 +106,8 @@ using docker-compose.yml; or individually - ```docker run --name redis -d redis:alpine3.16``` ```docker run --name postgres -e POSTGRES_PASSWORD=never_use_this_password_in_production -d postgres:alpine3.16``` -```docker run -d --hostname my-rabbit --name rabbitmq rabbitmq:3``` - - +```docker run -d --hostname my-rabbit --name rabbitmq rabbitmq:3``` \ No newline at end of file diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index 183186c..6da9756 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -78,7 +78,7 @@ impl StatefulList { } /// States of the container -#[derive(Clone, Debug, PartialEq, PartialOrd)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)] pub enum State { Dead, Exited, @@ -113,6 +113,20 @@ impl State { } } +impl From for State { + fn from(input: String) -> Self { + match input.as_ref() { + "dead" => Self::Dead, + "exited" => Self::Exited, + "paused" => Self::Paused, + "removing" => Self::Removing, + "restarting" => Self::Restarting, + "running" => Self::Running, + _ => Self::Unknown, + } + } +} + impl From<&str> for State { fn from(input: &str) -> Self { match input { diff --git a/src/app_data/mod.rs b/src/app_data/mod.rs index 39bc5ee..7e0099c 100644 --- a/src/app_data/mod.rs +++ b/src/app_data/mod.rs @@ -20,7 +20,7 @@ pub struct AppData { sorted_by: Option<(Header, SortedOrder)>, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum SortedOrder { Asc, Desc, @@ -313,7 +313,9 @@ impl AppData { /// So can display nicely and evenly pub fn get_width(&self) -> Columns { let mut output = Columns::new(); + // Think this is causing issues let count = |x: &String| x.chars().count(); + // let count = |_:&String|10; for container in &self.containers.items { let cpu_count = count( @@ -437,51 +439,45 @@ impl AppData { for i in containers.iter() { if let Some(id) = i.id.as_ref() { - let mut name = i - .names - .as_ref() - .unwrap_or(&vec!["".to_owned()]) - .get(0) - .unwrap_or(&String::from("")) - .clone(); - if let Some(c) = name.chars().next() { - if c == '/' { - name.remove(0); - } - } - - let state = State::from(i.state.as_ref().unwrap_or(&"dead".to_owned()).trim()); - let status = i - .status - .as_ref() - .unwrap_or(&"".to_owned()) - .trim() - .to_owned(); - let image = i.image.as_ref().unwrap_or(&"".to_owned()).trim().to_owned(); + // maybe if no name then continue? + let name = i.names.as_ref().map_or("".to_owned(), |f|f.get(0).map_or("".to_owned(), |f|{ + let mut n = f.clone(); + if n.starts_with('/') { + n.remove(0); + } + n + })); + + let state = State::from(i.state.as_ref().map_or("dead".to_owned(), |f|f.trim().to_owned())); + let status = i.status.as_ref().map_or("".to_owned(), |f| f.trim().to_owned()); + let image = i.image.as_ref().map_or("".to_owned(), |f|f.clone()); + if let Some(current_container) = self.get_container_by_id(id) { - if current_container.name != name { - current_container.name = name; + if current_container.name != name { + current_container.name = name; }; if current_container.status != status { - current_container.status = status; + current_container.status = status; }; if current_container.state != state { - current_container.docker_controls.items = DockerControls::gen_vec(&state); - + current_container.docker_controls.items = DockerControls::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 => { - current_container.docker_controls.state.select(None); + State::Removing | State::Restarting | State::Unknown => { + current_container.docker_controls.state.select(None); } _ => current_container.docker_controls.start(), }; current_container.state = state; }; if current_container.image != image { - current_container.image = image; + // current_container.image = image.chars().into_iter().take(64).collect(); + current_container.image = image; }; } else { - let mut container = ContainerItem::new(id.clone(), status, image, state, name); + // let mut container = ContainerItem::new(id.clone(), status, image.chars().into_iter().take(64).collect(), state, name); + let mut container = ContainerItem::new(id.clone(), status, image, state, name); container.logs.end(); self.containers.items.push(container); } diff --git a/src/docker_data/mod.rs b/src/docker_data/mod.rs index 19019ba..c49182c 100644 --- a/src/docker_data/mod.rs +++ b/src/docker_data/mod.rs @@ -1,6 +1,6 @@ use bollard::{ container::{ListContainersOptions, LogsOptions, StartContainerOptions, Stats, StatsOptions}, - Docker, + Docker, service::ContainerSummary, }; use futures_util::StreamExt; use parking_lot::Mutex; @@ -163,6 +163,7 @@ impl DockerData { /// 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 + /// Will ignore any container that uses `oxker` as an entry point pub async fn update_all_containers(&mut self) -> Vec<(bool, String)> { let containers = self .docker @@ -173,12 +174,19 @@ impl DockerData { .await .unwrap_or_default(); - let mut output = vec![]; - // iter over containers, to only send ones which have an id, as use id for identification throughout! - containers + let output = containers .iter() - .filter(|i| i.id.is_some()) - .for_each(|c| output.push(c.clone())); + .filter_map(|f| match f.id { + Some(_) => { + if f.command.as_ref().map_or(false, |c|c.contains("oxker")) { + None + } else { + Some(f.clone()) + } + }, + None => None, + }) + .collect::>(); self.app_data.lock().update_containers(&output); diff --git a/src/main.rs b/src/main.rs index b7fda0e..8015736 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,7 @@ use ui::{create_ui, GuiState}; fn setup_tracing() { tracing_subscriber::fmt().with_max_level(Level::INFO).init(); + // TODO write to file? } #[tokio::main] diff --git a/src/ui/color_match.rs b/src/ui/color_match.rs index 149d74a..a95dc88 100644 --- a/src/ui/color_match.rs +++ b/src/ui/color_match.rs @@ -6,7 +6,7 @@ pub mod log_sanitizer { text::{Span, Spans}, }; - /// Attempt to colorize the given string to tui-rs standars + /// Attempt to colorize the given string to tui-rs standards pub fn colorize_logs(input: &str) -> Vec> { vec![Spans::from( categorise_text(input) diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index 5711148..cf634af 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -169,9 +169,6 @@ impl SelectablePanel { /// Global gui_state, stored in an Arc #[derive(Debug, Clone)] pub struct GuiState { - // Think this should be a BMapTree, so can define order when iterating over potential intersects - // Is an issue if two panels are in the same space, sush as a smaller panel embedded, yet infront of, a larger panel - // If a BMapTree think it would mean have to implement ordering for SelectablePanel panel_map: HashMap, heading_map: HashMap, loading_icon: Loading, diff --git a/start_oxker.sh b/start_oxker.sh new file mode 100755 index 0000000..10a03d1 --- /dev/null +++ b/start_oxker.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e + +# No idea why this is sloving my issue, or even where the issue is originally coming from +sleep 1 + +exec ./oxker "$@" \ No newline at end of file