implement list resources with ListItem on Resource trait

This commit is contained in:
mbecker20
2023-07-29 14:35:36 -04:00
parent 26c692978a
commit a279982187
10 changed files with 161 additions and 94 deletions

View File

@@ -12,7 +12,7 @@ pub async fn send_alert(alerter: &Alerter, alert: &Alert) -> anyhow::Result<()>
pub async fn send_slack_alert(url: &str, alert: &Alert) -> anyhow::Result<()> {
let (text, blocks) = match alert {
Alert::ServerUnreachable { id, name, region } => {
Alert::ServerUnreachable { name, region, .. } => {
let region = fmt_region(region);
let text = format!("CRITICAL 🚨 | *{name}*{region} is *unreachable* ❌");
let blocks = vec![
@@ -22,12 +22,12 @@ pub async fn send_slack_alert(url: &str, alert: &Alert) -> anyhow::Result<()> {
(text, blocks.into())
}
Alert::ServerCpu {
id,
name,
region,
state,
percentage,
top_procs,
..
} => {
let region = fmt_region(region);
let text =
@@ -42,13 +42,13 @@ pub async fn send_slack_alert(url: &str, alert: &Alert) -> anyhow::Result<()> {
(text, blocks.into())
}
Alert::ServerMem {
id,
name,
region,
state,
used_gb,
total_gb,
top_procs,
..
} => {
let region = fmt_region(region);
let percentage = 100.0 * used_gb / total_gb;
@@ -65,13 +65,13 @@ pub async fn send_slack_alert(url: &str, alert: &Alert) -> anyhow::Result<()> {
(text, blocks.into())
}
Alert::ServerDisk {
id,
name,
region,
state,
path,
used_gb,
total_gb,
..
} => {
let region = fmt_region(region);
let percentage = 100.0 * used_gb / total_gb;

View File

@@ -28,7 +28,7 @@ impl Resolve<ListAlerters, RequestUser> for State {
ListAlerters { query }: ListAlerters,
user: RequestUser,
) -> anyhow::Result<Vec<Alerter>> {
self.list_resources_for_user(&user, query).await
<State as Resource<Alerter>>::list_resources_for_user(self, &user, query).await
}
}

View File

@@ -31,20 +31,7 @@ impl Resolve<ListBuilds, RequestUser> for State {
ListBuilds { query }: ListBuilds,
user: RequestUser,
) -> anyhow::Result<Vec<BuildListItem>> {
let builds: Vec<Build> = self.list_resources_for_user(&user, query).await?;
let builds = builds
.into_iter()
.map(|build| BuildListItem {
id: build.id,
name: build.name,
last_built_at: build.last_built_at,
version: build.config.version,
tags: build.tags,
})
.collect();
Ok(builds)
<State as Resource<Build>>::list_resources_for_user(self, &user, query).await
}
}

View File

@@ -28,7 +28,7 @@ impl Resolve<ListBuilders, RequestUser> for State {
ListBuilders { query }: ListBuilders,
user: RequestUser,
) -> anyhow::Result<Vec<Builder>> {
self.list_resources_for_user(&user, query).await
<State as Resource<Builder>>::list_resources_for_user(self, &user, query).await
}
}

View File

@@ -2,7 +2,6 @@ use std::cmp;
use anyhow::{anyhow, Context};
use async_trait::async_trait;
use futures::future::join_all;
use monitor_types::{
entities::{
deployment::{
@@ -40,26 +39,7 @@ impl Resolve<ListDeployments, RequestUser> for State {
ListDeployments { query }: ListDeployments,
user: RequestUser,
) -> anyhow::Result<Vec<DeploymentListItem>> {
let deployments: Vec<Deployment> = self.list_resources_for_user(&user, query).await?;
let deployments = deployments.into_iter().map(|deployment| async {
let status = self.deployment_status_cache.get(&deployment.id).await;
DeploymentListItem {
id: deployment.id,
name: deployment.name,
tags: deployment.tags,
state: status.as_ref().map(|s| s.state).unwrap_or_default(),
status: status
.as_ref()
.and_then(|s| s.container.as_ref().and_then(|c| c.status.to_owned())),
image: String::new(),
version: String::new(),
}
});
let deployments = join_all(deployments).await;
Ok(deployments)
<State as Resource<Deployment>>::list_resources_for_user(self, &user, query).await
}
}

View File

@@ -27,19 +27,7 @@ impl Resolve<ListRepos, RequestUser> for State {
ListRepos { query }: ListRepos,
user: RequestUser,
) -> anyhow::Result<Vec<RepoListItem>> {
let repos: Vec<Repo> = self.list_resources_for_user(&user, query).await?;
let repos = repos
.into_iter()
.map(|repo| RepoListItem {
id: repo.id,
name: repo.name,
last_pulled_at: repo.last_pulled_at,
tags: repo.tags,
})
.collect();
Ok(repos)
<State as Resource<Repo>>::list_resources_for_user(self, &user, query).await
}
}

View File

@@ -4,22 +4,63 @@ use anyhow::{anyhow, Context};
use async_trait::async_trait;
use monitor_types::{
entities::{
server,
tag::Tag,
update::ResourceTargetVariant::{self, *},
PermissionLevel,
},
permissioned::Permissioned,
requests::read::{
BuildListItem, DeploymentListItem, FindResources, FindResourcesResponse, RepoListItem,
ServerListItem,
BuildListItem, DeploymentListItem, FindResources, FindResources2, FindResourcesResponse,
RepoListItem, ServerListItem,
},
};
use mungos::mongodb::bson::{doc, oid::ObjectId};
use resolver_api::Resolve;
use crate::{auth::RequestUser, state::State};
use crate::{auth::RequestUser, resource::Resource, state::State};
const ALL_RESOURCE_TYPES: [ResourceTargetVariant; 4] = [Server, Build, Deployment, Repo];
const FIND_RESOURCE_TYPES: [ResourceTargetVariant; 4] = [Server, Build, Deployment, Repo];
#[async_trait]
impl Resolve<FindResources2, RequestUser> for State {
async fn resolve(
&self,
FindResources2 { query, resources }: FindResources2,
user: RequestUser,
) -> anyhow::Result<FindResourcesResponse> {
let mut res = FindResourcesResponse::default();
let resource_types = resources
.map(|rs| {
rs.into_iter()
.filter(|r| !matches!(r, System | Builder | Alerter))
.collect()
})
.unwrap_or(FIND_RESOURCE_TYPES.to_vec());
for resource_type in resource_types {
match resource_type {
Server => {
// let servers: Vec<server::Server> =
// self.list_resources_for_user(&user, query.clone()).await?;
// res.servers = servers.into_iter().
todo!()
}
Deployment => {
todo!()
}
Build => {
todo!()
}
Repo => {
todo!()
}
_ => unreachable!(),
}
}
todo!()
}
}
#[async_trait]
impl Resolve<FindResources, RequestUser> for State {
@@ -202,7 +243,7 @@ fn seperate_tags(tags: Vec<Tag>) -> SeperateTags {
}
if seperated.resource_types.is_empty() {
seperated.resource_types = ALL_RESOURCE_TYPES.to_vec();
seperated.resource_types = FIND_RESOURCE_TYPES.to_vec();
}
seperated

View File

@@ -1,6 +1,5 @@
use anyhow::anyhow;
use async_trait::async_trait;
use futures::future::join_all;
use monitor_types::{
entities::{
deployment::ContainerSummary,
@@ -52,21 +51,7 @@ impl Resolve<ListServers, RequestUser> for State {
ListServers { query }: ListServers,
user: RequestUser,
) -> anyhow::Result<Vec<ServerListItem>> {
let servers = self.list_resources_for_user(&user, query).await?;
let servers = servers.into_iter().map(|server: Server| async {
let status = self.server_status_cache.get(&server.id).await;
ServerListItem {
id: server.id,
name: server.name,
tags: server.tags,
status: status.map(|s| s.status).unwrap_or_default(),
}
});
let servers = join_all(servers).await;
Ok(servers)
<State as Resource<Server>>::list_resources_for_user(self, &user, query).await
}
}
@@ -346,16 +331,11 @@ impl Resolve<GetServersSummary, RequestUser> for State {
GetServersSummary {}: GetServersSummary,
user: RequestUser,
) -> anyhow::Result<GetServersSummaryResponse> {
let servers: Vec<Server> = self.list_resources_for_user(&user, None).await?;
let servers = <State as Resource<Server>>::list_resources_for_user(self, &user, None).await?;
let mut res = GetServersSummaryResponse::default();
for server in servers {
res.total += 1;
let status = self
.server_status_cache
.get(&server.id)
.await
.unwrap_or_default();
match status.status {
match server.status {
ServerStatus::Ok => {
res.healthy += 1;
}

View File

@@ -1,24 +1,30 @@
use anyhow::{anyhow, Context};
use async_trait::async_trait;
use futures::future::join_all;
use monitor_types::{
entities::{
alerter::Alerter, build::Build, builder::Builder, deployment::Deployment, repo::Repo,
server::Server, PermissionLevel,
},
permissioned::Permissioned,
requests::read::{BuildListItem, DeploymentListItem, RepoListItem, ServerListItem},
};
use mungos::{
mongodb::bson::{doc, Document},
AggStage::*,
Collection, Indexed,
};
use serde::Serialize;
use crate::{auth::RequestUser, state::State};
#[async_trait]
pub trait Resource<T: Indexed + Send + Unpin + Permissioned> {
type ListItem: Serialize + Send;
fn name() -> &'static str;
fn coll(&self) -> &Collection<T>;
async fn into_list_item(&self, resource: T) -> Self::ListItem;
async fn get_resource(&self, id: &str) -> anyhow::Result<T> {
self.coll()
@@ -92,7 +98,7 @@ pub trait Resource<T: Indexed + Send + Unpin + Permissioned> {
&self,
user: &RequestUser,
query: Option<Document>,
) -> anyhow::Result<Vec<T>> {
) -> anyhow::Result<Vec<Self::ListItem>> {
let mut query = query.unwrap_or_default();
if !user.is_admin {
query.insert(
@@ -100,14 +106,24 @@ pub trait Resource<T: Indexed + Send + Unpin + Permissioned> {
doc! { "$in": ["read", "execute", "update"] },
);
}
self.coll()
let list = self
.coll()
.get_some(query, None)
.await
.context(format!("failed to pull {}s from mongo", Self::name()))
.context(format!("failed to pull {}s from mongo", Self::name()))?
.into_iter()
.map(|resource| self.into_list_item(resource));
let list = join_all(list).await;
Ok(list)
}
}
#[async_trait]
impl Resource<Server> for State {
type ListItem = ServerListItem;
fn name() -> &'static str {
"server"
}
@@ -115,9 +131,22 @@ impl Resource<Server> for State {
fn coll(&self) -> &Collection<Server> {
&self.db.servers
}
async fn into_list_item(&self, server: Server) -> ServerListItem {
let status = self.server_status_cache.get(&server.id).await;
ServerListItem {
id: server.id,
name: server.name,
tags: server.tags,
status: status.map(|s| s.status).unwrap_or_default(),
}
}
}
#[async_trait]
impl Resource<Deployment> for State {
type ListItem = DeploymentListItem;
fn name() -> &'static str {
"deployment"
}
@@ -125,9 +154,27 @@ impl Resource<Deployment> for State {
fn coll(&self) -> &Collection<Deployment> {
&self.db.deployments
}
async fn into_list_item(&self, deployment: Deployment) -> DeploymentListItem {
let status = self.deployment_status_cache.get(&deployment.id).await;
DeploymentListItem {
id: deployment.id,
name: deployment.name,
tags: deployment.tags,
state: status.as_ref().map(|s| s.state).unwrap_or_default(),
status: status
.as_ref()
.and_then(|s| s.container.as_ref().and_then(|c| c.status.to_owned())),
image: String::new(),
version: String::new(),
}
}
}
#[async_trait]
impl Resource<Build> for State {
type ListItem = BuildListItem;
fn name() -> &'static str {
"build"
}
@@ -135,19 +182,22 @@ impl Resource<Build> for State {
fn coll(&self) -> &Collection<Build> {
&self.db.builds
}
}
impl Resource<Builder> for State {
fn name() -> &'static str {
"builder"
}
fn coll(&self) -> &Collection<Builder> {
&self.db.builders
async fn into_list_item(&self, build: Build) -> BuildListItem {
BuildListItem {
id: build.id,
name: build.name,
last_built_at: build.last_built_at,
version: build.config.version,
tags: build.tags,
}
}
}
#[async_trait]
impl Resource<Repo> for State {
type ListItem = RepoListItem;
fn name() -> &'static str {
"repo"
}
@@ -155,9 +205,38 @@ impl Resource<Repo> for State {
fn coll(&self) -> &Collection<Repo> {
&self.db.repos
}
async fn into_list_item(&self, repo: Repo) -> RepoListItem {
RepoListItem {
id: repo.id,
name: repo.name,
last_pulled_at: repo.last_pulled_at,
tags: repo.tags,
}
}
}
#[async_trait]
impl Resource<Builder> for State {
type ListItem = Builder;
fn name() -> &'static str {
"builder"
}
fn coll(&self) -> &Collection<Builder> {
&self.db.builders
}
async fn into_list_item(&self, builder: Builder) -> Builder {
builder
}
}
#[async_trait]
impl Resource<Alerter> for State {
type ListItem = Alerter;
fn name() -> &'static str {
"alerter"
}
@@ -165,4 +244,8 @@ impl Resource<Alerter> for State {
fn coll(&self) -> &Collection<Alerter> {
&self.db.alerters
}
async fn into_list_item(&self, alerter: Alerter) -> Alerter {
alerter
}
}