From d1f9238b2a54bea4f9cac90b2333d34add313f34 Mon Sep 17 00:00:00 2001 From: asteri Date: Sat, 11 Apr 2026 21:11:38 +0200 Subject: [PATCH] add services status --- Cargo.lock | 571 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 + src/app.rs | 76 +++++-- src/bin/tui.rs | 12 +- src/config.rs | 32 +++ src/docker.rs | 47 ++++ src/lib.rs | 2 + src/runner.rs | 4 +- src/server.rs | 2 +- 9 files changed, 730 insertions(+), 20 deletions(-) create mode 100644 src/config.rs create mode 100644 src/docker.rs diff --git a/Cargo.lock b/Cargo.lock index dc0fd84..6b4e9e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,6 +179,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" @@ -319,6 +325,49 @@ dependencies = [ "cipher 0.4.4", ] +[[package]] +name = "bollard" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee04c4c84f1f811b017f2fbb7dd8815c976e7ca98593de9c1e2afad0f636bff4" +dependencies = [ + "base64", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-util", + "hyperlocal", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_urlencoded", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.52.1-rc.29.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0a8ca8799131c1837d1282c3f81f31e76ceb0ce426e04a7fe1ccee3287c066" +dependencies = [ + "serde", + "serde_json", + "serde_repr", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -809,6 +858,17 @@ dependencies = [ "ctutils", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "document-features" version = "0.2.12" @@ -1027,6 +1087,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1271,6 +1340,51 @@ dependencies = [ "digest 0.11.2", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hybrid-array" version = "0.4.10" @@ -1283,6 +1397,77 @@ dependencies = [ "zeroize", ] +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "hyperlocal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -1307,6 +1492,88 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "id-arena" version = "2.3.0" @@ -1319,6 +1586,27 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -1560,6 +1848,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + [[package]] name = "litrs" version = "1.0.0" @@ -1972,6 +2266,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pest" version = "2.8.6" @@ -2161,6 +2461,15 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2674,6 +2983,38 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serdect" version = "0.4.2" @@ -2869,17 +3210,27 @@ name = "ssh-status" version = "0.1.0" dependencies = [ "anyhow", + "bollard", "crossterm", "env_logger", + "futures", "log", "rand 0.10.0", "ratatui", "russh", + "serde", "sysinfo", "termios", "tokio", + "toml", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2941,6 +3292,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "sysinfo" version = "0.38.4" @@ -3079,6 +3441,16 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.51.1" @@ -3105,6 +3477,89 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.19.0" @@ -3178,6 +3633,24 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3211,6 +3684,15 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -3537,6 +4019,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -3625,6 +4113,35 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.48" @@ -3645,12 +4162,66 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index e8c148c..64e0c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,12 +5,16 @@ edition = "2024" [dependencies] anyhow = "1.0.102" +bollard = "0.20.2" crossterm = "0.29.0" env_logger = "0.11.10" +futures = "0.3.32" log = "0.4.29" rand = "0.10.0" ratatui = "0.30.0" russh = "0.60.0" +serde = {version= "1.0.228",features = ["derive"]} sysinfo = "0.38.4" termios = "0.3.3" tokio = "1.51.1" +toml = "1.1.2" diff --git a/src/app.rs b/src/app.rs index 2a7936e..016a306 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,30 +1,43 @@ use ratatui::layout::Rect; use ratatui::prelude::*; -use ratatui::style::{Color, Style}; -use ratatui::widgets::{Block, Borders, Gauge}; +use ratatui::style::Style; +use ratatui::widgets::{Block, Borders, Gauge, Paragraph}; -use sysinfo::{Components, Disk, Disks, Networks, System}; +use sysinfo::{Disks, System}; + +use crate::config::{Config, load}; +use crate::docker; + +use std::path::Path; pub struct App { sys: System, + config: Config, + services: Vec, } impl Default for App { fn default() -> Self { App { sys: System::new_all(), + config: load("server.toml").unwrap(), + services: Default::default(), } } } impl App { - pub fn update_info(&mut self) { + pub async fn update_info(&mut self) { self.sys.refresh_all(); + + self.services = docker::running_services(&self.config.services.clone()) + .await + .unwrap(); } pub fn draw(&self, frame: &mut Frame) { let block = Block::default() - .title_top(Line::from("Server").centered().bold()) + .title_top(Line::from("Server status").centered().bold()) .title_bottom(Line::from("press 'q' to exit").right_aligned().italic()); frame.render_widget(block, frame.area()); @@ -37,6 +50,8 @@ impl App { let block = Block::default().title("Services").borders(Borders::ALL); + self.draw_services(frame, layout[0]); + frame.render_widget(block, layout[0]); let disks_gauges = self.disks(); @@ -55,7 +70,9 @@ impl App { self.draw_gauges(frame, layout_system[2], cpus_gauges, "CPU"); } - fn memory(&self) -> Vec { + fn memory(&self) -> Vec> { + let mut gauges: Vec> = Default::default(); + let mem_total: f64 = ((self.sys.total_memory() >> 20) as f64) / 1024.0; let mem_used: f64 = ((self.sys.used_memory() >> 20) as f64) / 1024.0; @@ -65,28 +82,39 @@ impl App { .label(format!("{mem_used:.2}/{mem_total:.2}Go")) .ratio(mem_used / mem_total); + gauges.push(mem); + let swap_total: f64 = ((self.sys.total_swap() >> 20) as f64) / 1024.0; let swap_used: f64 = ((self.sys.used_swap() >> 20) as f64) / 1024.0; + if swap_total <= 0.0 { + return gauges; + } + let swap = Gauge::default() .block(Block::default().title("SWAP usage:")) .gauge_style(Style::new().white().on_black().italic()) .label(format!("{swap_used:.2}/{swap_total:.2}Go")) .ratio(swap_used / swap_total); - vec![mem, swap] + gauges.push(swap); + gauges } - fn disks(&self) -> Vec { + fn disks(&self) -> Vec> { let mut gauges: Vec = Default::default(); for disk in &Disks::new_with_refreshed_list() { - if disk.mount_point().starts_with("/boot/") || disk.mount_point().starts_with("/tmp/") { + let total = ((disk.total_space() >> 20) as f64) / 1024.0; + let used = total - ((disk.available_space() >> 20) as f64) / 1024.0; + + if self.config.disks.iter().all(|config_disk| { + disk.mount_point() != AsRef::::as_ref(config_disk.mount_point.as_str()) + }) || total <= 0.0 + { continue; } - let total = ((disk.total_space() >> 20) as f64) / 1024.0; - let used = total - ((disk.available_space() >> 20) as f64) / 1024.0; let gauge = Gauge::default() .block(Block::default().title(format!( "{:?} ({:?})", @@ -103,10 +131,10 @@ impl App { gauges } - fn cpus(&self) -> Vec { + fn cpus(&self) -> Vec> { let mut gauges: Vec = Default::default(); - for (idx, cpu) in self.sys.cpus().into_iter().enumerate() { + for cpu in self.sys.cpus() { let usage = cpu.cpu_usage() as f64; let gauge = Gauge::default() .gauge_style(Style::new().white().on_black().italic()) @@ -119,6 +147,28 @@ impl App { gauges } + fn draw_services(&self, frame: &mut Frame, area: Rect) { + let layout = Layout::vertical(vec![Constraint::Length(2); self.config.services.len()]) + .margin(1) + .split(area); + + for (idx, (service, status)) in self + .config + .services + .iter() + .zip(self.services.iter()) + .enumerate() + { + let display = Paragraph::new(service.description.clone()).block( + Block::default() + .title_top(Line::from(service.name.clone()).bold()) + .title_top(Line::from(format!("(running: {status})")).right_aligned()), + ); + + frame.render_widget(display, layout[idx]); + } + } + fn draw_gauges(&self, frame: &mut Frame, area: Rect, gauges: Vec, name: &str) { let block = Block::default().title(name).borders(Borders::ALL); diff --git a/src/bin/tui.rs b/src/bin/tui.rs index 40c81c3..3003041 100644 --- a/src/bin/tui.rs +++ b/src/bin/tui.rs @@ -1,5 +1,8 @@ //extern crate termios; +use ssh_status::config; +use ssh_status::docker; use ssh_status::runner::{Action, Runner}; + use std::io::{self, Write}; use std::time::Duration; @@ -7,7 +10,8 @@ use crossterm::cursor; use crossterm::event::{DisableMouseCapture, EnableMouseCapture, Event, KeyCode}; use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen}; -fn main() { +#[tokio::main] +async fn main() { let mut stdout = io::stdout(); crossterm::terminal::enable_raw_mode().unwrap(); crossterm::execute!( @@ -21,10 +25,10 @@ fn main() { let mut app = Runner::new(stdout).unwrap(); let (w, h) = crossterm::terminal::size().unwrap(); app.resize(w as u32, h as u32).unwrap(); - app.update(); + app.update().await; loop { - if crossterm::event::poll(Duration::from_millis(100)).unwrap() { + if crossterm::event::poll(Duration::from_millis(1000)).unwrap() { match crossterm::event::read().unwrap() { Event::Key(key) => match key.code { KeyCode::Char(c) => { @@ -41,7 +45,7 @@ fn main() { } }; - app.update(); + app.update().await; } let mut stdout = io::stdout(); diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..c2b5897 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,32 @@ +use std::path::Path; +use std::{fs, io}; + +use std::collections::HashMap; + +use serde::Deserialize; +use toml; + +#[derive(Deserialize, Debug, Clone)] +pub struct Config { + pub name: String, + pub disks: Vec, + pub services: Vec, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Service { + pub name: String, + pub description: String, + pub containers: Vec, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Disk { + pub name: String, + pub mount_point: String, +} + +pub fn load>(path: P) -> io::Result { + let config_str = fs::read_to_string(path)?; + Ok(toml::from_str(&config_str).unwrap()) +} diff --git a/src/docker.rs b/src/docker.rs new file mode 100644 index 0000000..bd3b223 --- /dev/null +++ b/src/docker.rs @@ -0,0 +1,47 @@ +use bollard::models::ContainerSummary; +use bollard::{Docker, errors::Error}; + +use crate::config::Service; + +use std::collections::HashMap; +use std::default::Default; + +pub async fn running_containers() -> Result, Error> { + let mut names: Vec = Default::default(); + + let docker = Docker::connect_with_socket_defaults()?; + + let mut list_container_filters = HashMap::new(); + list_container_filters.insert(String::from("status"), vec![String::from("running")]); + + let containers = &docker + .list_containers(Some( + bollard::query_parameters::ListContainersOptionsBuilder::default() + .all(true) + .filters(&list_container_filters) + .build(), + )) + .await?; + + for c in containers { + if let Some(c_names) = &c.names { + for name in c_names { + names.push(name.clone()); + } + } + } + + Ok(names) +} + +pub async fn running_services(services: &Vec) -> Result, Error> { + let running = running_containers().await?; + + let mut status: Vec = Default::default(); + + for service in services.iter() { + let service_status: bool = service.containers.iter().all(|name| running.contains(name)); + status.push(service_status); + } + Ok(status) +} diff --git a/src/lib.rs b/src/lib.rs index 04490e9..4b54ac7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ pub mod app; +pub mod config; +pub mod docker; pub mod runner; pub mod server; diff --git a/src/runner.rs b/src/runner.rs index 8270eeb..7e3c744 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -39,8 +39,8 @@ impl Runner { } } - pub fn update(&mut self) { - self.app.update_info(); + pub async fn update(&mut self) { + self.app.update_info().await; self.terminal.draw(|frame| self.app.draw(frame)).unwrap(); } diff --git a/src/server.rs b/src/server.rs index c7030bd..e48014a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -27,7 +27,7 @@ impl AppServer { let clients = self.clients.clone(); tokio::spawn(async move { loop { - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; for (_, app) in clients.lock().await.iter_mut() { app.update();