diff options
Diffstat (limited to 'common/rust')
| -rw-r--r-- | common/rust/BUILD.bazel | 9 | ||||
| -rw-r--r-- | common/rust/Cargo.toml | 13 | ||||
| -rw-r--r-- | common/rust/cargo/BUILD.bazel | 76 | ||||
| -rw-r--r-- | common/rust/src/config.rs | 41 | ||||
| -rw-r--r-- | common/rust/src/lib.rs | 4 | ||||
| -rw-r--r-- | common/rust/src/monitoring.rs | 60 |
6 files changed, 203 insertions, 0 deletions
diff --git a/common/rust/BUILD.bazel b/common/rust/BUILD.bazel new file mode 100644 index 0000000..0fbd9b2 --- /dev/null +++ b/common/rust/BUILD.bazel @@ -0,0 +1,9 @@ +load("@rules_rust//rust:defs.bzl", "rust_library") +load("//cargo:crates.bzl", "all_crate_deps") + +rust_library( + name = "common", + srcs = glob(["src/**"]), + visibility = ["//visibility:public"], + deps = all_crate_deps(), +) diff --git a/common/rust/Cargo.toml b/common/rust/Cargo.toml new file mode 100644 index 0000000..d631c6c --- /dev/null +++ b/common/rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "common" +version = "0.1.0" +edition = "2018" + +[dependencies] +pretty_env_logger = "0.4" +log = { version = "0.4", features = ["std"] } +serde = { version = "1.0.8", features = ["derive"] } +config = "0.11" +hyper = { version = "0.14", features = ["full"] } +tokio = { version = "1", features = ["full"] } +prometheus = "0.12.0"
\ No newline at end of file diff --git a/common/rust/cargo/BUILD.bazel b/common/rust/cargo/BUILD.bazel new file mode 100644 index 0000000..ff92458 --- /dev/null +++ b/common/rust/cargo/BUILD.bazel @@ -0,0 +1,76 @@ +""" +@generated +cargo-raze generated Bazel file. + +DO NOT EDIT! Replaced on runs of cargo-raze +""" + +package(default_visibility = ["//visibility:public"]) + +licenses([ + "notice", # See individual crates for specific licenses +]) + +# Aliased targets +alias( + name = "config", + actual = "@raze__config__0_11_0//:config", + tags = [ + "cargo-raze", + "manual", + ], +) + +alias( + name = "hyper", + actual = "@raze__hyper__0_14_12//:hyper", + tags = [ + "cargo-raze", + "manual", + ], +) + +alias( + name = "log", + actual = "@raze__log__0_4_14//:log", + tags = [ + "cargo-raze", + "manual", + ], +) + +alias( + name = "pretty_env_logger", + actual = "@raze__pretty_env_logger__0_4_0//:pretty_env_logger", + tags = [ + "cargo-raze", + "manual", + ], +) + +alias( + name = "prometheus", + actual = "@raze__prometheus__0_12_0//:prometheus", + tags = [ + "cargo-raze", + "manual", + ], +) + +alias( + name = "serde", + actual = "@raze__serde__1_0_130//:serde", + tags = [ + "cargo-raze", + "manual", + ], +) + +alias( + name = "tokio", + actual = "@raze__tokio__1_11_0//:tokio", + tags = [ + "cargo-raze", + "manual", + ], +) diff --git a/common/rust/src/config.rs b/common/rust/src/config.rs new file mode 100644 index 0000000..6d8fb33 --- /dev/null +++ b/common/rust/src/config.rs @@ -0,0 +1,41 @@ +use std::env; + +use config::{Config, ConfigError, Environment, File}; +use log::info; +use serde::{Deserialize}; + + +#[derive(Debug, Deserialize, Clone)] +#[serde(bound(deserialize = "T: Deserialize<'de> + std::default::Default + Clone"))] +pub struct Settings<T> { + #[serde(skip_deserializing)] + pub config: T, + pub monitoring: crate::monitoring::MonitoringConfiguration, +} + +impl<T> Settings<T> where T: Deserialize<'static> + std::default::Default + Clone { + pub fn new(service_name: &str) -> Result<Settings<T>, ConfigError> { + let mut default = Config::default(); + // this file my be shared with all the components + default.merge(File::with_name("config/default"))?; + let mode = env::var("ENV").unwrap_or_else(|_| "development".into()); + info!("Configuration Environment: {}", mode); + + default.merge(File::with_name(&format!("config/{}", mode)).required(false))?; + default.merge(File::with_name("config/local").required(false))?; + + // we can configure each component using environment variables + default.merge(Environment::with_prefix(&format!("NOVA_{}", service_name)))?; + let mut config: Settings<T> = default.clone().try_into().unwrap(); + + // try to load the config + config.config = default.get::<T>(&service_name).unwrap(); + + // setup the logger + pretty_env_logger::init_custom_env(&format!("NOVA_{}_LOG", service_name)); + + // start the monitoring system if needed + crate::monitoring::start_monitoring(&config.monitoring); + Ok(config) + } +} diff --git a/common/rust/src/lib.rs b/common/rust/src/lib.rs new file mode 100644 index 0000000..5122334 --- /dev/null +++ b/common/rust/src/lib.rs @@ -0,0 +1,4 @@ +/// This crate contains shared code in all the rust projects +/// It contains utilities such as monitoring, logging and more. +pub mod config; +pub mod monitoring;
\ No newline at end of file diff --git a/common/rust/src/monitoring.rs b/common/rust/src/monitoring.rs new file mode 100644 index 0000000..ded1d95 --- /dev/null +++ b/common/rust/src/monitoring.rs @@ -0,0 +1,60 @@ +use hyper::{ + Response, Body, Request, Server, + header::{CONTENT_TYPE}, + service::{make_service_fn, service_fn}, +}; +use std::net::ToSocketAddrs; +use prometheus::{Encoder, TextEncoder}; +use log::{info,error}; +use serde::Deserialize; + +#[derive(Clone, Debug, Deserialize)] +/// Options for the monitoring service +pub struct MonitoringConfiguration { + enabled: bool, + address: Option<String>, + port: Option<i32>, +} + +/// Handler for the hyper http server +async fn serve_metrics(_request: Request<Body>) -> Result<Response<Body>, hyper::Error> { + let encoder = TextEncoder::new(); + let metrics = prometheus::gather(); + + let mut buffer = vec![]; + encoder.encode(&metrics, &mut buffer).unwrap(); + + let response = Response::builder() + .status(200) + .header(CONTENT_TYPE, encoder.format_type()) + .body(Body::from(buffer)) + .unwrap(); + Ok(response) +} + +/// Starts a monitoring server on the requested port +pub fn start_monitoring(configuration: &MonitoringConfiguration) { + let config = configuration.clone(); + tokio::task::spawn(async move { + if config.enabled { + let address = format!("{}:{}", + config.address.expect("a listening address must be specified for the metrics server"), + config.port.expect("a listening port must be specified for the metrics server") + ); + info!("Starting monitoring server on {}", address); + + let listen_address = address + .to_socket_addrs() + .unwrap() + .next() + .unwrap(); + let server = Server::bind(&listen_address).serve(make_service_fn(|_| async { + Ok::<_, hyper::Error>(service_fn(serve_metrics)) + })); + + if let Err(e) = server.await { + error!("failed to start the monitoring server {}", e); + } + } + }); +}
\ No newline at end of file |
