container stats

This commit is contained in:
beckerinj
2022-12-04 01:59:47 -05:00
parent 38d7126959
commit 34b60c755b
13 changed files with 213 additions and 44 deletions

30
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"

View File

@@ -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)
}
}

View File

@@ -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", "")))
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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())
}

View File

@@ -1,4 +1,4 @@
use std::{fs::File, io::Read, sync::Arc};
use std::sync::Arc;
use axum::Extension;
use dotenv::dotenv;

View File

@@ -1,5 +1,3 @@
use types::PeripheryConfig;
#[macro_export]
macro_rules! response {
($x:expr) => {

View File

@@ -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;