forked from github-starred/komodo
implement get updates list
This commit is contained in:
@@ -4,11 +4,12 @@ use axum::{
|
||||
routing::{delete, get, patch, post},
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use futures_util::future::join_all;
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use types::{
|
||||
traits::Permissioned, BasicContainerInfo, ImageSummary, Network, PermissionLevel, Server,
|
||||
ServerActionState, SystemStats,
|
||||
ServerActionState, ServerStatus, ServerWithStatus, SystemStats,
|
||||
};
|
||||
use typeshare::typeshare;
|
||||
|
||||
@@ -235,8 +236,8 @@ impl State {
|
||||
&self,
|
||||
user: &RequestUser,
|
||||
query: impl Into<Option<Document>>,
|
||||
) -> anyhow::Result<Vec<Server>> {
|
||||
let mut servers: Vec<Server> = self
|
||||
) -> anyhow::Result<Vec<ServerWithStatus>> {
|
||||
let futures = self
|
||||
.db
|
||||
.servers
|
||||
.get_some(query, None)
|
||||
@@ -251,8 +252,26 @@ impl State {
|
||||
permissions != PermissionLevel::None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
servers.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
.map(|server| async {
|
||||
let status = if server.enabled {
|
||||
let res = self.periphery.health_check(&server).await;
|
||||
match res {
|
||||
Ok(_) => ServerStatus::Ok,
|
||||
Err(_) => ServerStatus::NotOk,
|
||||
}
|
||||
} else {
|
||||
ServerStatus::Disabled
|
||||
};
|
||||
|
||||
ServerWithStatus { server, status }
|
||||
});
|
||||
let mut servers: Vec<ServerWithStatus> = join_all(futures).await;
|
||||
servers.sort_by(|a, b| {
|
||||
a.server
|
||||
.name
|
||||
.to_lowercase()
|
||||
.cmp(&b.server.name.to_lowercase())
|
||||
});
|
||||
Ok(servers)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,159 @@
|
||||
use axum::Router;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Query, routing::get, Extension, Json, Router};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, to_bson, ObjectId};
|
||||
use serde_json::Value;
|
||||
use types::{PermissionLevel, Update, UpdateTarget};
|
||||
|
||||
use crate::{
|
||||
auth::{RequestUser, RequestUserExtension},
|
||||
response,
|
||||
state::{State, StateExtension},
|
||||
};
|
||||
|
||||
const NUM_UPDATES_PER_PAGE: usize = 20;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
Router::new().route(
|
||||
"/list",
|
||||
get(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Query(value): Query<Value>| async move {
|
||||
let offset = value
|
||||
.get("offset")
|
||||
.map(|v| v.as_u64().unwrap_or(0))
|
||||
.unwrap_or(0);
|
||||
let target = serde_json::from_str::<UpdateTarget>(&value.to_string()).ok();
|
||||
let updates = state
|
||||
.list_updates(target, offset, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(updates))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn permission_on_update_target(
|
||||
&self,
|
||||
update_target: &UpdateTarget,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<()> {
|
||||
if user.is_admin {
|
||||
Ok(())
|
||||
} else {
|
||||
match update_target {
|
||||
UpdateTarget::System => {
|
||||
if user.is_admin {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("user must be admin to see system updates"))
|
||||
}
|
||||
}
|
||||
UpdateTarget::Build(id) => self
|
||||
.get_build_check_permissions(id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.map(|_| ()),
|
||||
UpdateTarget::Deployment(id) => self
|
||||
.get_deployment_check_permissions(id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.map(|_| ()),
|
||||
UpdateTarget::Server(id) => self
|
||||
.get_server_check_permissions(id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.map(|_| ()),
|
||||
UpdateTarget::Procedure(id) => self
|
||||
.get_procedure_check_permissions(id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.map(|_| ()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_updates(
|
||||
&self,
|
||||
target: Option<UpdateTarget>,
|
||||
offset: u64,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Vec<Update>> {
|
||||
let filter = match target {
|
||||
Some(target) => {
|
||||
self.permission_on_update_target(&target, user).await?;
|
||||
Some(doc! {
|
||||
"target": to_bson(&target).unwrap()
|
||||
})
|
||||
}
|
||||
None => {
|
||||
if user.is_admin {
|
||||
None
|
||||
} else {
|
||||
let permissions_field = format!("permissions.{}", user.id);
|
||||
let target_filter = doc! {
|
||||
"$or": [
|
||||
{ &permissions_field: "update" },
|
||||
{ &permissions_field: "execute" },
|
||||
{ &permissions_field: "read" },
|
||||
]
|
||||
};
|
||||
let build_ids = self
|
||||
.db
|
||||
.builds
|
||||
.get_some(target_filter.clone(), None)
|
||||
.await
|
||||
.context("failed at query to get users builds")?
|
||||
.into_iter()
|
||||
.map(|e| ObjectId::from_str(&e.id).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let deployment_ids = self
|
||||
.db
|
||||
.deployments
|
||||
.get_some(target_filter.clone(), None)
|
||||
.await
|
||||
.context("failed at query to get users deployments")?
|
||||
.into_iter()
|
||||
.map(|e| ObjectId::from_str(&e.id).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let server_ids = self
|
||||
.db
|
||||
.servers
|
||||
.get_some(target_filter.clone(), None)
|
||||
.await
|
||||
.context("failed at query to get users servers")?
|
||||
.into_iter()
|
||||
.map(|e| ObjectId::from_str(&e.id).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let procedure_ids = self
|
||||
.db
|
||||
.procedures
|
||||
.get_some(target_filter, None)
|
||||
.await
|
||||
.context("failed at query to get users procedures")?
|
||||
.into_iter()
|
||||
.map(|e| ObjectId::from_str(&e.id).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let filter = doc! {
|
||||
"$or": [
|
||||
{ "target.type": "Build", "target.id": { "$in": &build_ids } },
|
||||
{ "target.type": "Deployment", "target.id": { "$in": &deployment_ids } },
|
||||
{ "target.type": "Server", "target.id": { "$in": &server_ids } },
|
||||
{ "target.type": "Procedure", "target.id": { "$in": &procedure_ids } }
|
||||
]
|
||||
};
|
||||
Some(filter)
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut updates = self
|
||||
.db
|
||||
.updates
|
||||
.get_most_recent(NUM_UPDATES_PER_PAGE as i64, offset, filter, None)
|
||||
.await
|
||||
.context("mongo get most recent updates query failed")?;
|
||||
updates.reverse();
|
||||
Ok(updates)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export function useServerStats() {
|
||||
if (
|
||||
stat === undefined &&
|
||||
!loading[serverID] &&
|
||||
(serverStatus ? serverStatus === "ok" : true)
|
||||
(serverStatus ? serverStatus === ServerStatus.Ok : true)
|
||||
) {
|
||||
loading[serverID] = true;
|
||||
load(serverID);
|
||||
|
||||
@@ -9,8 +9,10 @@ import {
|
||||
Log,
|
||||
Server,
|
||||
ServerActionState,
|
||||
ServerWithStatus,
|
||||
SystemStats,
|
||||
Update,
|
||||
UpdateTarget,
|
||||
User,
|
||||
UserCredentials,
|
||||
} from "../types";
|
||||
@@ -124,7 +126,7 @@ export class Client {
|
||||
|
||||
// server
|
||||
|
||||
list_servers(query?: QueryObject): Promise<Server[]> {
|
||||
list_servers(query?: QueryObject): Promise<ServerWithStatus[]> {
|
||||
return this.get("/api/server/list" + generateQuery(query));
|
||||
}
|
||||
|
||||
@@ -202,10 +204,7 @@ export class Client {
|
||||
return this.post("/api/build/create_full", build);
|
||||
}
|
||||
|
||||
copy_build(
|
||||
target_id: string,
|
||||
body: CopyBuildBody
|
||||
): Promise<Build> {
|
||||
copy_build(target_id: string, body: CopyBuildBody): Promise<Build> {
|
||||
return this.post(`/api/build/${target_id}/copy`, body);
|
||||
}
|
||||
|
||||
@@ -225,6 +224,18 @@ export class Client {
|
||||
return this.post(`/api/build/${id}/reclone`);
|
||||
}
|
||||
|
||||
// updates
|
||||
|
||||
list_updates(offset: number, target?: UpdateTarget): Promise<Update[]> {
|
||||
return this.get(
|
||||
`/api/update/list${generateQuery({
|
||||
offset,
|
||||
type: target && target.type,
|
||||
id: target && target.id,
|
||||
})}`
|
||||
);
|
||||
}
|
||||
|
||||
// api secrets
|
||||
|
||||
create_api_secret(body: CreateSecretBody): Promise<string> {
|
||||
|
||||
@@ -36,7 +36,7 @@ impl MonitorClient {
|
||||
}
|
||||
|
||||
pub async fn create_full_build(&self, build: &Build) -> anyhow::Result<Build> {
|
||||
self.post("/api/build/create_full", build)
|
||||
self.post::<&Build, _>("/api/build/create_full", build)
|
||||
.await
|
||||
.context(format!("failed at creating full build"))
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ impl MonitorClient {
|
||||
&self,
|
||||
deployment: &Deployment,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
self.post("/api/deployment/create_full", deployment)
|
||||
self.post::<&Deployment, _>("/api/deployment/create_full", deployment)
|
||||
.await
|
||||
.context(format!("failed at creating full deployment"))
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ mod permissions;
|
||||
mod procedure;
|
||||
mod secret;
|
||||
mod server;
|
||||
mod update;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorClient {
|
||||
|
||||
@@ -29,7 +29,7 @@ impl MonitorClient {
|
||||
}
|
||||
|
||||
pub async fn create_full_procedure(&self, procedure: &Procedure) -> anyhow::Result<Procedure> {
|
||||
self.post("/api/procedure/create_full", procedure)
|
||||
self.post::<&Procedure, _>("/api/procedure/create_full", procedure)
|
||||
.await
|
||||
.context(format!("failed at creating full procedure"))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::Context;
|
||||
use monitor_types::{
|
||||
BasicContainerInfo, ImageSummary, Log, Network, Server, ServerActionState, SystemStats,
|
||||
BasicContainerInfo, ImageSummary, Log, Network, Server, ServerActionState, ServerWithStatus,
|
||||
SystemStats,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
@@ -10,7 +11,7 @@ impl MonitorClient {
|
||||
pub async fn list_servers(
|
||||
&self,
|
||||
query: impl Into<Option<Value>>,
|
||||
) -> anyhow::Result<Vec<Server>> {
|
||||
) -> anyhow::Result<Vec<ServerWithStatus>> {
|
||||
self.get("/api/server/list", query.into())
|
||||
.await
|
||||
.context("failed at list servers")
|
||||
@@ -44,7 +45,7 @@ impl MonitorClient {
|
||||
}
|
||||
|
||||
pub async fn create_full_server(&self, server: &Server) -> anyhow::Result<Server> {
|
||||
self.post("/api/server/create_full", server)
|
||||
self.post::<&Server, _>("/api/server/create_full", server)
|
||||
.await
|
||||
.context(format!("failed at creating full server"))
|
||||
}
|
||||
|
||||
30
lib/monitor_client/src/update.rs
Normal file
30
lib/monitor_client/src/update.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use monitor_types::{Update, UpdateTarget};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::MonitorClient;
|
||||
|
||||
impl MonitorClient {
|
||||
pub async fn list_updates(
|
||||
&self,
|
||||
target: impl Into<Option<UpdateTarget>>,
|
||||
offset: u64,
|
||||
) -> anyhow::Result<Vec<Update>> {
|
||||
let mut query = json!({ "offset": offset });
|
||||
if let Some(target) = target.into() {
|
||||
let mut value =
|
||||
serde_json::from_str::<Value>(&serde_json::to_string(&target).unwrap()).unwrap();
|
||||
let value = value.as_object_mut().unwrap();
|
||||
query
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.insert("type".to_string(), value.remove("type").unwrap());
|
||||
if let Some(target_id) = value.remove("id") {
|
||||
query
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.insert("id".to_string(), target_id);
|
||||
}
|
||||
}
|
||||
self.get("/api/update/list", query).await
|
||||
}
|
||||
}
|
||||
@@ -194,8 +194,6 @@ fn default_disk_alert() -> f64 {
|
||||
75.0
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct ServerActionState {
|
||||
@@ -709,6 +707,54 @@ impl Default for UpdateTarget {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Build> for UpdateTarget {
|
||||
fn from(build: &Build) -> Self {
|
||||
Self::Build(build.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Build> for Option<UpdateTarget> {
|
||||
fn from(build: &Build) -> Self {
|
||||
Some(UpdateTarget::Build(build.id.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Deployment> for UpdateTarget {
|
||||
fn from(deployment: &Deployment) -> Self {
|
||||
Self::Deployment(deployment.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Deployment> for Option<UpdateTarget> {
|
||||
fn from(deployment: &Deployment) -> Self {
|
||||
Some(UpdateTarget::Deployment(deployment.id.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Server> for UpdateTarget {
|
||||
fn from(server: &Server) -> Self {
|
||||
Self::Server(server.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Server> for Option<UpdateTarget> {
|
||||
fn from(server: &Server) -> Self {
|
||||
Some(UpdateTarget::Server(server.id.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Procedure> for UpdateTarget {
|
||||
fn from(procedure: &Procedure) -> Self {
|
||||
Self::Procedure(procedure.id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Procedure> for Option<UpdateTarget> {
|
||||
fn from(procedure: &Procedure) -> Self {
|
||||
Some(UpdateTarget::Procedure(procedure.id.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Display, EnumString, PartialEq, Hash, Eq, Clone, Copy)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -867,7 +913,7 @@ pub enum PermissionsTarget {
|
||||
pub enum ServerStatus {
|
||||
Ok,
|
||||
NotOk,
|
||||
Disabled
|
||||
Disabled,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -17,10 +17,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
let start_ts = unix_timestamp_ms();
|
||||
|
||||
let (server, deployment, build) = create_test_setup(&monitor, "test").await?;
|
||||
// let (server, deployment, build) = create_test_setup(&monitor, "test").await?;
|
||||
|
||||
let server_stats = get_server_stats(&monitor).await?;
|
||||
println!("server stats:\n{server_stats:#?}\n");
|
||||
// let server_stats = get_server_stats(&monitor).await?;
|
||||
// println!("server stats:\n{server_stats:#?}\n");
|
||||
|
||||
// let (update, container) = deploy_mongo(&monitor).await?;
|
||||
// println!(
|
||||
@@ -31,6 +31,8 @@ async fn main() -> anyhow::Result<()> {
|
||||
// let update = test_build(&monitor).await?;
|
||||
// println!("build update:\n{update:#?}");
|
||||
|
||||
test_updates(&monitor).await.unwrap();
|
||||
|
||||
let end_ts = unix_timestamp_ms();
|
||||
let finished_in = (end_ts - start_ts) as f64 / 1000.0;
|
||||
println!("\nfinished in {finished_in} s");
|
||||
|
||||
@@ -14,14 +14,11 @@ pub async fn create_test_setup(
|
||||
let mut servers = monitor.list_servers(None).await?;
|
||||
let server = if servers.is_empty() {
|
||||
monitor
|
||||
.create_server(
|
||||
&format!("{group_name}_server"),
|
||||
"http://localhost:8000",
|
||||
)
|
||||
.create_server(&format!("{group_name}_server"), "http://localhost:8000")
|
||||
.await
|
||||
.context("failed at create server")?
|
||||
} else {
|
||||
servers.pop().unwrap()
|
||||
servers.pop().unwrap().server
|
||||
};
|
||||
let mut deployments = monitor.list_deployments(None).await?;
|
||||
let deployment = if deployments.is_empty() {
|
||||
@@ -49,7 +46,7 @@ pub async fn get_server_stats(monitor: &MonitorClient) -> anyhow::Result<SystemS
|
||||
.list_servers(None)
|
||||
.await
|
||||
.context("failed at list servers")?;
|
||||
let server = servers.get(0).ok_or(anyhow!("no servers"))?;
|
||||
let server = &servers.get(0).ok_or(anyhow!("no servers"))?.server;
|
||||
let stats = monitor
|
||||
.get_server_stats(&server.id)
|
||||
.await
|
||||
@@ -64,7 +61,7 @@ pub async fn deploy_mongo(
|
||||
.list_servers(None)
|
||||
.await
|
||||
.context("failed at list servers")?;
|
||||
let server = servers.get(0).ok_or(anyhow!("no servers"))?;
|
||||
let server = &servers.get(0).ok_or(anyhow!("no servers"))?.server;
|
||||
let mut deployment = monitor.create_deployment("mongo_test", &server.id).await?;
|
||||
println!("created deployment");
|
||||
deployment.docker_run_args.image = "mongo".to_string();
|
||||
@@ -84,7 +81,7 @@ pub async fn test_build(monitor: &MonitorClient) -> anyhow::Result<Update> {
|
||||
.list_servers(None)
|
||||
.await
|
||||
.context("failed at list servers")?;
|
||||
let server = servers.get(0).ok_or(anyhow!("no servers"))?;
|
||||
let server = &servers.get(0).ok_or(anyhow!("no servers"))?.server;
|
||||
let mut build = monitor.create_build("old_periphery", &server.id).await?;
|
||||
println!("created build. updating...");
|
||||
build.repo = Some("mbecker20/monitor".to_string());
|
||||
@@ -107,3 +104,13 @@ pub async fn test_build(monitor: &MonitorClient) -> anyhow::Result<Update> {
|
||||
let update = monitor.build(&build.id).await?;
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
pub async fn test_updates(monitor: &MonitorClient) -> anyhow::Result<()> {
|
||||
let updates = monitor.list_updates(None, 0).await?;
|
||||
println!("ALL UPDATES: {updates:#?}");
|
||||
let builds = monitor.list_builds(None).await?;
|
||||
let build = builds.get(0).unwrap();
|
||||
let build_updates = monitor.list_updates(build, 0).await?;
|
||||
println!("{build_updates:#?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user