mirror of
https://github.com/moghtech/komodo.git
synced 2026-03-11 17:44:19 -05:00
container stats
This commit is contained in:
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -650,9 +650,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -660,9 +660,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
@@ -677,15 +677,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -694,21 +694,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -794,6 +794,8 @@ dependencies = [
|
||||
"async_timing_util",
|
||||
"axum",
|
||||
"bollard",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"monitor_types",
|
||||
"rand",
|
||||
"run_command",
|
||||
|
||||
@@ -10,10 +10,12 @@ types = { package = "monitor_types", path = "../types" }
|
||||
async_timing_util = "0.1.11"
|
||||
bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
axum = "0.6"
|
||||
axum = { version = "0.6", features = ["ws", "json"] }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
toml = "0.5"
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
rand = "0.8"
|
||||
rand = "0.8"
|
||||
futures = "0.3"
|
||||
futures-util = "0.3.25"
|
||||
@@ -2,8 +2,15 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use axum::Extension;
|
||||
use bollard::{container::ListContainersOptions, Docker};
|
||||
use types::BasicContainerInfo;
|
||||
use bollard::{
|
||||
container::{ListContainersOptions, StatsOptions},
|
||||
Docker,
|
||||
};
|
||||
use futures::{future::join_all, Stream};
|
||||
use futures_util::stream::StreamExt;
|
||||
use types::{BasicContainerInfo, DockerContainerState};
|
||||
|
||||
pub use bollard::container::Stats;
|
||||
|
||||
pub type DockerExtension = Extension<Arc<DockerClient>>;
|
||||
|
||||
@@ -46,4 +53,65 @@ impl DockerClient {
|
||||
.collect::<anyhow::Result<Vec<BasicContainerInfo>>>()?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn container_stats_stream(
|
||||
&self,
|
||||
container_name: &str,
|
||||
) -> impl Stream<Item = Result<Stats, bollard::errors::Error>> {
|
||||
self.docker.stats(
|
||||
container_name,
|
||||
Some(StatsOptions {
|
||||
stream: true,
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_container_stats(&self, container_name: &str) -> anyhow::Result<Stats> {
|
||||
let mut stats = self
|
||||
.docker
|
||||
.stats(
|
||||
container_name,
|
||||
Some(StatsOptions {
|
||||
stream: false,
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.take(1)
|
||||
.next()
|
||||
.await
|
||||
.ok_or(anyhow!("got no stats for {container_name}"))??;
|
||||
stats.name = stats.name.replace("/", "");
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
pub async fn get_container_stats_list(&self) -> anyhow::Result<Vec<Stats>> {
|
||||
let futures = self
|
||||
.list_containers()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|c| c.state == DockerContainerState::Running)
|
||||
.map(|c| async move {
|
||||
let mut stats = self
|
||||
.docker
|
||||
.stats(
|
||||
&c.name,
|
||||
Some(StatsOptions {
|
||||
stream: false,
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.take(1)
|
||||
.next()
|
||||
.await
|
||||
.ok_or(anyhow!("got no stats for {}", c.name))??;
|
||||
stats.name = stats.name.replace("/", "");
|
||||
Ok::<_, anyhow::Error>(stats)
|
||||
});
|
||||
let stats = join_all(futures)
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<anyhow::Result<_>>()?;
|
||||
Ok(stats)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,16 @@ pub async fn container_log(container_name: &str, tail: Option<u64>) -> Log {
|
||||
run_monitor_command("get container log", command).await
|
||||
}
|
||||
|
||||
pub async fn container_stats() -> anyhow::Result<Vec<DockerContainerStats>> {
|
||||
let command = "docker stats --no-stream --format \"{{json .}}\"";
|
||||
let output = async_run_command(command).await;
|
||||
pub async fn container_stats(
|
||||
container_name: Option<String>,
|
||||
) -> anyhow::Result<Vec<DockerContainerStats>> {
|
||||
let format = "--format \"{{ json . }}\"";
|
||||
let container_name = match container_name {
|
||||
Some(name) => format!(" {name}"),
|
||||
None => "".to_string(),
|
||||
};
|
||||
let command = format!("docker stats{container_name} --no-stream {format}");
|
||||
let output = async_run_command(&command).await;
|
||||
if output.success() {
|
||||
let res = output
|
||||
.stdout
|
||||
@@ -31,7 +38,7 @@ pub async fn container_stats() -> anyhow::Result<Vec<DockerContainerStats>> {
|
||||
.collect::<anyhow::Result<Vec<DockerContainerStats>>>()?;
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(anyhow!("failed to get docker logs"))
|
||||
Err(anyhow!("{}", output.stderr.replace("\n", "")))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1,23 @@
|
||||
use monitor_types::{Build, SystemStats};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::MonitorClient;
|
||||
|
||||
impl MonitorClient {
|
||||
pub async fn list_builds(&self) -> anyhow::Result<Vec<Build>> {
|
||||
self.get("/api/build/list").await
|
||||
}
|
||||
|
||||
pub async fn create_build(&self, name: &str, address: &str) -> anyhow::Result<()> {
|
||||
self.post(
|
||||
"/api/build/create",
|
||||
json!({ "name": name, "address": address }),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_build(&self, id: &str) -> anyhow::Result<()> {
|
||||
self.delete::<(), _>(&format!("/api/build/delete/{id}"), None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,23 @@
|
||||
use monitor_types::{Deployment, SystemStats};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::MonitorClient;
|
||||
|
||||
impl MonitorClient {
|
||||
pub async fn list_deployments(&self) -> anyhow::Result<Vec<Deployment>> {
|
||||
self.get("/api/deployment/list").await
|
||||
}
|
||||
|
||||
pub async fn create_deployment(&self, name: &str, address: &str) -> anyhow::Result<()> {
|
||||
self.post(
|
||||
"/api/deployment/create",
|
||||
json!({ "name": name, "address": address }),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_deployment(&self, id: &str) -> anyhow::Result<()> {
|
||||
self.delete::<(), _>(&format!("/api/deployment/delete/{id}"), None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use serde_json::json;
|
||||
use types::{BasicContainerInfo, Deployment, Log, Server};
|
||||
use types::{BasicContainerInfo, Deployment, DockerContainerStats, Log, Server};
|
||||
|
||||
use crate::PeripheryClient;
|
||||
|
||||
@@ -53,6 +53,22 @@ impl PeripheryClient {
|
||||
}
|
||||
|
||||
pub async fn container_prune(&self, server: &Server) -> anyhow::Result<Log> {
|
||||
self.post_json(server, "container/prune", &json!({})).await
|
||||
self.post_json(server, "/container/prune", &json!({})).await
|
||||
}
|
||||
|
||||
pub async fn container_stats(
|
||||
&self,
|
||||
server: &Server,
|
||||
container_name: &str,
|
||||
) -> anyhow::Result<Vec<DockerContainerStats>> {
|
||||
self.get_json(server, &format!("/container/stats/{container_name}"))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn container_stats_list(
|
||||
&self,
|
||||
server: &Server,
|
||||
) -> anyhow::Result<Vec<DockerContainerStats>> {
|
||||
self.get_json(server, "/container/stats/list").await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::anyhow;
|
||||
use axum::{
|
||||
extract::Path,
|
||||
http::StatusCode,
|
||||
routing::{get, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
@@ -27,6 +27,51 @@ pub fn router() -> Router {
|
||||
response!(Json(containers))
|
||||
}),
|
||||
)
|
||||
// .route(
|
||||
// "/stats/:name",
|
||||
// get(
|
||||
// |Extension(dc): DockerExtension, Path(Container { name }): Path<Container>| async move {
|
||||
// let stats = dc
|
||||
// .get_container_stats(&name)
|
||||
// .await
|
||||
// .map_err(handle_anyhow_error)?;
|
||||
// response!(Json(stats))
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
// .route(
|
||||
// "/stats/list",
|
||||
// get(
|
||||
// |Extension(dc): DockerExtension| async move {
|
||||
// let stats_list = dc
|
||||
// .get_container_stats_list()
|
||||
// .await
|
||||
// .map_err(handle_anyhow_error)?;
|
||||
// response!(Json(stats_list))
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
.route(
|
||||
"/stats/:name",
|
||||
get(|Path(c): Path<Container>| async move {
|
||||
let stats = docker::container_stats(Some(c.name.clone()))
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?
|
||||
.pop()
|
||||
.ok_or(anyhow!("no stats for container {}", c.name))
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(stats))
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/stats/list",
|
||||
get(|| async {
|
||||
let stats = docker::container_stats(None)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(stats))
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/start",
|
||||
post(|Json(container): Json<Container>| async move {
|
||||
|
||||
@@ -4,7 +4,7 @@ use helpers::{
|
||||
git::{self, CloneArgs},
|
||||
handle_anyhow_error,
|
||||
};
|
||||
use types::{Build, Deployment, GithubToken, Log, PeripheryConfig};
|
||||
use types::{GithubToken, Log, PeripheryConfig};
|
||||
|
||||
use crate::PeripheryConfigExtension;
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
use std::{
|
||||
path::Path,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use axum::{routing::get, Extension, Json, Router};
|
||||
use helpers::{docker, handle_anyhow_error};
|
||||
use sysinfo::{CpuExt, DiskExt, NetworkExt, ProcessExt, ProcessRefreshKind, SystemExt};
|
||||
use types::{DiskUsage, SingleDiskUsage, SystemNetwork, SystemStats};
|
||||
|
||||
use crate::response;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
@@ -19,15 +15,6 @@ pub fn router() -> Router {
|
||||
Json(stats)
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/docker",
|
||||
get(|| async {
|
||||
let stats = docker::container_stats()
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(stats))
|
||||
}),
|
||||
)
|
||||
.layer(StatsClient::extension())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{fs::File, io::Read, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::Extension;
|
||||
use dotenv::dotenv;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use types::PeripheryConfig;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! response {
|
||||
($x:expr) => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#![allow(unused)]
|
||||
// #![allow(unused)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use ::helpers::get_socket_addr;
|
||||
use axum::{extract::Path, http::StatusCode, routing::get, Extension, Json, Router};
|
||||
use axum::Extension;
|
||||
use types::PeripheryConfig;
|
||||
|
||||
mod api;
|
||||
|
||||
Reference in New Issue
Block a user