start working on core

This commit is contained in:
mbecker20
2023-06-16 06:20:26 +00:00
parent b5ef174312
commit 76ddbe1869
7 changed files with 315 additions and 6 deletions

58
Cargo.lock generated
View File

@@ -980,6 +980,12 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "http-range-header"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
[[package]]
name = "httparse"
version = "1.8.0"
@@ -1292,6 +1298,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
@@ -1376,6 +1392,10 @@ version = "1.0.0"
dependencies = [
"anyhow",
"async_timing_util",
"axum",
"dotenv",
"envy",
"log",
"merge_config_files",
"monitor_helpers",
"monitor_types",
@@ -1383,8 +1403,12 @@ dependencies = [
"periphery_client",
"resolver_api",
"serde",
"simple_logger",
"termination_signal",
"tokio",
"tower",
"tower-http",
"uuid",
]
[[package]]
@@ -2601,6 +2625,31 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-http"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
dependencies = [
"bitflags",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
@@ -2743,6 +2792,15 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"

View File

@@ -14,8 +14,16 @@ monitor_helpers.workspace = true
periphery_client.workspace = true
# external
tokio.workspace = true
anyhow.workspace = true
axum.workspace = true
tower.workspace = true
tower-http.workspace = true
serde.workspace = true
uuid.workspace = true
anyhow.workspace = true
log.workspace = true
simple_logger.workspace = true
dotenv.workspace = true
envy.workspace = true
# mogh
async_timing_util.workspace = true
merge_config_files.workspace = true

View File

@@ -1 +1,172 @@
use anyhow::Context;
use merge_config_files::parse_config_file;
use monitor_types::entities::Timelength;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Debug)]
pub struct Env {
#[serde(default = "default_config_path")]
pub config_path: String,
#[serde(default = "default_frontend_path")]
pub frontend_path: String,
pub port: Option<u16>,
}
fn default_config_path() -> String {
"/config/config.toml".to_string()
}
fn default_frontend_path() -> String {
"/frontend".to_string()
}
impl Env {
pub fn load() -> anyhow::Result<Env> {
dotenv::dotenv().ok();
envy::from_env().context("failed to parse environment")
}
}
#[derive(Deserialize, Debug, Clone)]
pub struct CoreConfig {
#[serde(default = "default_title")]
pub title: String,
// the host to use with oauth redirect url, whatever host the user hits to access monitor. eg 'https://monitor.mogh.tech'
pub host: String,
// port the core web server runs on
#[serde(default = "default_core_port")]
pub port: u16,
pub jwt_secret: String,
#[serde(default = "default_jwt_valid_for")]
pub jwt_valid_for: Timelength,
// interval at which to collect server stats and alert for out of bounds
pub monitoring_interval: Timelength,
// daily utc offset in hours to run daily update. eg 8:00 eastern time is 13:00 UTC, so offset should be 13. default of 0 runs at UTC midnight.
#[serde(default)]
pub daily_offset_hours: u8,
// number of days to keep stats, or 0 to disable pruning. stats older than this number of days are deleted on a daily cycle
#[serde(default)]
pub keep_stats_for_days: u64,
// used to verify validity from github webhooks
pub github_webhook_secret: String,
// used to form the frontend listener url, if None will use 'host'.
pub github_webhook_base_url: Option<String>,
// sent in auth header with req to periphery
pub passkey: String,
// integration with slack app
pub slack_url: Option<String>,
// enable login with local auth
pub local_auth: bool,
#[serde(default = "default_log_level")]
pub log_level: LogLevel,
// allowed docker orgs used with monitor. first in this list will be default for build
#[serde(default)]
pub docker_organizations: Vec<String>,
pub mongo: MongoConfig,
#[serde(default)]
pub github_oauth: OauthCredentials,
#[serde(default)]
pub google_oauth: OauthCredentials,
#[serde(default)]
pub aws: AwsCredentials,
}
fn default_title() -> String {
String::from("monitor")
}
fn default_core_port() -> u16 {
9000
}
fn default_jwt_valid_for() -> Timelength {
Timelength::OneWeek
}
fn default_log_level() -> LogLevel {
LogLevel::Info
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct OauthCredentials {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub id: String,
#[serde(default)]
pub secret: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MongoConfig {
pub uri: String,
#[serde(default = "default_core_mongo_app_name")]
pub app_name: String,
#[serde(default = "default_core_mongo_db_name")]
pub db_name: String,
}
fn default_core_mongo_app_name() -> String {
"monitor_core".to_string()
}
fn default_core_mongo_db_name() -> String {
"monitor".to_string()
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct AwsCredentials {
#[serde(skip_serializing)]
pub access_key_id: String,
#[serde(skip_serializing)]
pub secret_access_key: String,
}
impl CoreConfig {
pub fn load(config_path: &str) -> CoreConfig {
parse_config_file::<CoreConfig>(config_path)
.unwrap_or_else(|e| panic!("failed at parsing config at {config_path} | {e:#?}"))
}
}
#[derive(Deserialize, Debug, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
Off,
Error,
Warn,
Info,
Debug,
Trace,
}
impl From<LogLevel> for log::LevelFilter {
fn from(value: LogLevel) -> Self {
match value {
LogLevel::Off => log::LevelFilter::Off,
LogLevel::Error => log::LevelFilter::Error,
LogLevel::Warn => log::LevelFilter::Warn,
LogLevel::Info => log::LevelFilter::Info,
LogLevel::Debug => log::LevelFilter::Debug,
LogLevel::Trace => log::LevelFilter::Trace,
}
}
}

View File

@@ -1,10 +1,58 @@
#[macro_use]
extern crate log;
use std::{sync::Arc, time::Instant};
use axum::{
headers::ContentType, http::StatusCode, routing::post, Extension, Json, Router, TypedHeader,
};
use termination_signal::tokio::immediate_term_handle;
use uuid::Uuid;
use crate::requests::CoreRequest;
mod config;
mod resolvers;
mod requests;
mod state;
async fn app() -> anyhow::Result<()> {
let state = state::State::load().await?;
info!("version: v{}", env!("CARGO_PKG_VERSION"));
let socket_addr = state.socket_addr()?;
let app = Router::new()
.route(
"/api",
post(
|state: Extension<Arc<state::State>>, Json(request): Json<CoreRequest>| async move {
let timer = Instant::now();
let req_id = Uuid::new_v4();
info!("request {req_id} | {request:?}");
let res = state
.resolve_request(request)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")));
if let Err(e) = &res {
debug!("request {req_id} ERROR: {e:?}");
}
let res = res?;
let elapsed = timer.elapsed();
info!("request {req_id} | resolve time: {elapsed:?}");
debug!("request {req_id} RESPONSE: {res}");
Result::<_, (StatusCode, String)>::Ok((TypedHeader(ContentType::json()), res))
},
),
)
.layer(Extension(state));
info!("starting server on {}", socket_addr);
axum::Server::bind(&socket_addr)
.serve(app.into_make_service())
.await?;
Ok(())
}

View File

@@ -6,4 +6,5 @@ use crate::state::State;
#[derive(Serialize, Deserialize, Debug, Clone, Resolver)]
#[serde(tag = "type", content = "params")]
#[resolver_target(State)]
#[allow(clippy::enum_variant_names, clippy::large_enum_variant)]
pub enum CoreRequest {}

View File

@@ -1,11 +1,35 @@
use std::sync::Arc;
use std::{sync::Arc, net::SocketAddr, str::FromStr};
pub struct State {}
use anyhow::Context;
use simple_logger::SimpleLogger;
use crate::config::{CoreConfig, Env};
pub struct State {
pub env: Env,
pub config: CoreConfig,
}
impl State {
pub async fn load() -> anyhow::Result<Arc<State>> {
let state = State {};
let env = Env::load()?;
let config = CoreConfig::load(&env.config_path);
SimpleLogger::new()
.with_level(config.log_level.into())
.env()
.with_colors(true)
.with_utc_timestamps()
.init()
.context("failed to configure logger")?;
let state = State { env, config };
Ok(state.into())
}
pub fn socket_addr(&self) -> anyhow::Result<SocketAddr> {
SocketAddr::from_str(&format!("0.0.0.0:{}", self.config.port))
.context("failed to parse socket addr")
}
}

View File

@@ -1 +0,0 @@