From 4d308d523722ff648b3bb49583d9406eeb6cffc9 Mon Sep 17 00:00:00 2001 From: Tamo Date: Thu, 6 Apr 2023 13:38:47 +0200 Subject: [PATCH] Improve the health route by ensuring lmdb is not down And refactorize slightly the auth controller. --- index-scheduler/src/lib.rs | 7 +++++++ meilisearch-auth/src/lib.rs | 6 ++++++ meilisearch-auth/src/store.rs | 7 +++++++ meilisearch/src/analytics/segment_analytics.rs | 12 ++++++++---- .../src/extractors/authentication/mod.rs | 18 ++++++++++++------ meilisearch/src/lib.rs | 7 ++++--- meilisearch/src/main.rs | 3 ++- meilisearch/src/routes/api_key.rs | 11 ++++++----- meilisearch/src/routes/dump.rs | 2 +- meilisearch/src/routes/metrics.rs | 2 +- meilisearch/src/routes/mod.rs | 9 +++++++-- meilisearch/tests/common/server.rs | 2 +- meilisearch/tests/common/service.rs | 4 ++-- 13 files changed, 64 insertions(+), 26 deletions(-) diff --git a/index-scheduler/src/lib.rs b/index-scheduler/src/lib.rs index d70062c97..13c3c6bfe 100644 --- a/index-scheduler/src/lib.rs +++ b/index-scheduler/src/lib.rs @@ -429,6 +429,13 @@ impl IndexScheduler { Ok(this) } + /// Return `Ok(())` if the index scheduler is able to access one of its database. + pub fn health(&self) -> Result<()> { + let rtxn = self.env.read_txn()?; + self.all_tasks.first(&rtxn)?; + Ok(()) + } + fn index_budget( tasks_path: &Path, base_map_size: usize, diff --git a/meilisearch-auth/src/lib.rs b/meilisearch-auth/src/lib.rs index 8d5457766..05f320dfb 100644 --- a/meilisearch-auth/src/lib.rs +++ b/meilisearch-auth/src/lib.rs @@ -34,6 +34,12 @@ impl AuthController { Ok(Self { store: Arc::new(store), master_key: master_key.clone() }) } + /// Return `Ok(())` if the auth controller is able to access one of its database. + pub fn health(&self) -> Result<()> { + self.store.health()?; + Ok(()) + } + /// Return the size of the `AuthController` database in bytes. pub fn size(&self) -> Result { self.store.size() diff --git a/meilisearch-auth/src/store.rs b/meilisearch-auth/src/store.rs index cc5dcdfb5..5c2776154 100644 --- a/meilisearch-auth/src/store.rs +++ b/meilisearch-auth/src/store.rs @@ -61,6 +61,13 @@ impl HeedAuthStore { Ok(Self { env, keys, action_keyid_index_expiration, should_close_on_drop: true }) } + /// Return `Ok(())` if the auth store is able to access one of its database. + pub fn health(&self) -> Result<()> { + let rtxn = self.env.read_txn()?; + self.keys.first(&rtxn)?; + Ok(()) + } + /// Return the size in bytes of database pub fn size(&self) -> Result { Ok(self.env.real_disk_size()?) diff --git a/meilisearch/src/analytics/segment_analytics.rs b/meilisearch/src/analytics/segment_analytics.rs index 2bc79625a..4f02ed60c 100644 --- a/meilisearch/src/analytics/segment_analytics.rs +++ b/meilisearch/src/analytics/segment_analytics.rs @@ -86,7 +86,7 @@ impl SegmentAnalytics { pub async fn new( opt: &Opt, index_scheduler: Arc, - auth_controller: AuthController, + auth_controller: Arc, ) -> Arc { let instance_uid = super::find_user_id(&opt.db_path); let first_time_run = instance_uid.is_none(); @@ -376,7 +376,11 @@ impl Segment { }) } - async fn run(mut self, index_scheduler: Arc, auth_controller: AuthController) { + async fn run( + mut self, + index_scheduler: Arc, + auth_controller: Arc, + ) { const INTERVAL: Duration = Duration::from_secs(60 * 60); // one hour // The first batch must be sent after one hour. let mut interval = @@ -408,10 +412,10 @@ impl Segment { async fn tick( &mut self, index_scheduler: Arc, - auth_controller: AuthController, + auth_controller: Arc, ) { if let Ok(stats) = - create_all_stats(index_scheduler.into(), auth_controller, &AuthFilter::default()) + create_all_stats(index_scheduler.into(), auth_controller.into(), &AuthFilter::default()) { // Replace the version number with the prototype name if any. let version = if let Some(prototype) = crate::prototype_name() { diff --git a/meilisearch/src/extractors/authentication/mod.rs b/meilisearch/src/extractors/authentication/mod.rs index 6373d6a29..007e2be40 100644 --- a/meilisearch/src/extractors/authentication/mod.rs +++ b/meilisearch/src/extractors/authentication/mod.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use std::ops::Deref; use std::pin::Pin; +use actix_web::web::Data; use actix_web::FromRequest; pub use error::AuthenticationError; use futures::future::err; @@ -23,7 +24,7 @@ impl GuardedData { } async fn auth_bearer( - auth: AuthController, + auth: Data, token: String, index: Option, data: Option, @@ -43,7 +44,7 @@ impl GuardedData { } } - async fn auth_token(auth: AuthController, data: Option) -> Result + async fn auth_token(auth: Data, data: Option) -> Result where P: Policy + 'static, { @@ -60,7 +61,7 @@ impl GuardedData { } async fn authenticate( - auth: AuthController, + auth: Data, token: String, index: Option, ) -> Result, ResponseError> @@ -90,7 +91,7 @@ impl FromRequest for GuardedData req: &actix_web::HttpRequest, _payload: &mut actix_web::dev::Payload, ) -> Self::Future { - match req.app_data::().cloned() { + match req.app_data::>().cloned() { Some(auth) => match req .headers() .get("Authorization") @@ -122,10 +123,15 @@ impl FromRequest for GuardedData } pub trait Policy { - fn authenticate(auth: AuthController, token: &str, index: Option<&str>) -> Option; + fn authenticate( + auth: Data, + token: &str, + index: Option<&str>, + ) -> Option; } pub mod policies { + use actix_web::web::Data; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use meilisearch_auth::{AuthController, AuthFilter, SearchRules}; // reexport actions in policies in order to be used in routes configuration. @@ -178,7 +184,7 @@ pub mod policies { /// Otherwise, returns an object containing the generated permissions: the search filters to add to a search, and the list of allowed indexes /// (that may contain more indexes than requested). fn authenticate( - auth: AuthController, + auth: Data, token: &str, index: Option<&str>, ) -> Option { diff --git a/meilisearch/src/lib.rs b/meilisearch/src/lib.rs index 98e754e67..9f85a4c5c 100644 --- a/meilisearch/src/lib.rs +++ b/meilisearch/src/lib.rs @@ -88,7 +88,7 @@ fn is_empty_db(db_path: impl AsRef) -> bool { pub fn create_app( index_scheduler: Data, - auth_controller: AuthController, + auth_controller: Data, opt: Opt, analytics: Arc, enable_dashboard: bool, @@ -136,7 +136,7 @@ enum OnFailure { KeepDb, } -pub fn setup_meilisearch(opt: &Opt) -> anyhow::Result<(Arc, AuthController)> { +pub fn setup_meilisearch(opt: &Opt) -> anyhow::Result<(Arc, Arc)> { let empty_db = is_empty_db(&opt.db_path); let (index_scheduler, auth_controller) = if let Some(ref snapshot_path) = opt.import_snapshot { let snapshot_path_exists = snapshot_path.exists(); @@ -195,6 +195,7 @@ pub fn setup_meilisearch(opt: &Opt) -> anyhow::Result<(Arc, Auth // We create a loop in a thread that registers snapshotCreation tasks let index_scheduler = Arc::new(index_scheduler); + let auth_controller = Arc::new(auth_controller); if let ScheduleSnapshot::Enabled(snapshot_delay) = opt.schedule_snapshot { let snapshot_delay = Duration::from_secs(snapshot_delay); let index_scheduler = index_scheduler.clone(); @@ -380,7 +381,7 @@ fn import_dump( pub fn configure_data( config: &mut web::ServiceConfig, index_scheduler: Data, - auth: AuthController, + auth: Data, opt: &Opt, analytics: Arc, ) { diff --git a/meilisearch/src/main.rs b/meilisearch/src/main.rs index d12539e20..c80245c7e 100644 --- a/meilisearch/src/main.rs +++ b/meilisearch/src/main.rs @@ -74,13 +74,14 @@ async fn main() -> anyhow::Result<()> { async fn run_http( index_scheduler: Arc, - auth_controller: AuthController, + auth_controller: Arc, opt: Opt, analytics: Arc, ) -> anyhow::Result<()> { let enable_dashboard = &opt.env == "development"; let opt_clone = opt.clone(); let index_scheduler = Data::from(index_scheduler); + let auth_controller = Data::from(auth_controller); let http_server = HttpServer::new(move || { create_app( diff --git a/meilisearch/src/routes/api_key.rs b/meilisearch/src/routes/api_key.rs index 7514d01f6..597d04486 100644 --- a/meilisearch/src/routes/api_key.rs +++ b/meilisearch/src/routes/api_key.rs @@ -1,5 +1,6 @@ use std::str; +use actix_web::web::Data; use actix_web::{web, HttpRequest, HttpResponse}; use deserr::actix_web::{AwebJson, AwebQueryParameter}; use deserr::Deserr; @@ -35,7 +36,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) { } pub async fn create_api_key( - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, body: AwebJson, _req: HttpRequest, ) -> Result { @@ -66,7 +67,7 @@ impl ListApiKeys { } pub async fn list_api_keys( - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, list_api_keys: AwebQueryParameter, ) -> Result { let paginate = list_api_keys.into_inner().as_pagination(); @@ -84,7 +85,7 @@ pub async fn list_api_keys( } pub async fn get_api_key( - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, path: web::Path, ) -> Result { let key = path.into_inner().key; @@ -103,7 +104,7 @@ pub async fn get_api_key( } pub async fn patch_api_key( - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, body: AwebJson, path: web::Path, ) -> Result { @@ -123,7 +124,7 @@ pub async fn patch_api_key( } pub async fn delete_api_key( - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, path: web::Path, ) -> Result { let key = path.into_inner().key; diff --git a/meilisearch/src/routes/dump.rs b/meilisearch/src/routes/dump.rs index 8e0e63776..0aabd2aa6 100644 --- a/meilisearch/src/routes/dump.rs +++ b/meilisearch/src/routes/dump.rs @@ -19,7 +19,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) { pub async fn create_dump( index_scheduler: GuardedData, Data>, - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, req: HttpRequest, analytics: web::Data, ) -> Result { diff --git a/meilisearch/src/routes/metrics.rs b/meilisearch/src/routes/metrics.rs index 59cff9b58..874a1a5a5 100644 --- a/meilisearch/src/routes/metrics.rs +++ b/meilisearch/src/routes/metrics.rs @@ -17,7 +17,7 @@ pub fn configure(config: &mut web::ServiceConfig) { pub async fn get_metrics( index_scheduler: GuardedData, Data>, - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, ) -> Result { let auth_filters = index_scheduler.filters(); if !auth_filters.all_indexes_authorized() { diff --git a/meilisearch/src/routes/mod.rs b/meilisearch/src/routes/mod.rs index c824d3f36..51340ac1b 100644 --- a/meilisearch/src/routes/mod.rs +++ b/meilisearch/src/routes/mod.rs @@ -238,7 +238,7 @@ pub struct Stats { async fn get_stats( index_scheduler: GuardedData, Data>, - auth_controller: GuardedData, AuthController>, + auth_controller: GuardedData, Data>, req: HttpRequest, analytics: web::Data, ) -> Result { @@ -253,7 +253,7 @@ async fn get_stats( pub fn create_all_stats( index_scheduler: Data, - auth_controller: AuthController, + auth_controller: Data, filters: &meilisearch_auth::AuthFilter, ) -> Result { let mut last_task: Option = None; @@ -318,9 +318,14 @@ struct KeysResponse { pub async fn get_health( req: HttpRequest, + index_scheduler: Data, + auth_controller: Data, analytics: web::Data, ) -> Result { analytics.health_seen(&req); + index_scheduler.health().unwrap(); + auth_controller.health().unwrap(); + Ok(HttpResponse::Ok().json(serde_json::json!({ "status": "available" }))) } diff --git a/meilisearch/tests/common/server.rs b/meilisearch/tests/common/server.rs index d4cf6184d..66b82eace 100644 --- a/meilisearch/tests/common/server.rs +++ b/meilisearch/tests/common/server.rs @@ -82,7 +82,7 @@ impl Server { > { actix_web::test::init_service(create_app( self.service.index_scheduler.clone().into(), - self.service.auth.clone(), + self.service.auth.clone().into(), self.service.options.clone(), analytics::MockAnalytics::new(&self.service.options), true, diff --git a/meilisearch/tests/common/service.rs b/meilisearch/tests/common/service.rs index c6ac65418..2ff474157 100644 --- a/meilisearch/tests/common/service.rs +++ b/meilisearch/tests/common/service.rs @@ -13,7 +13,7 @@ use crate::common::encoder::Encoder; pub struct Service { pub index_scheduler: Arc, - pub auth: AuthController, + pub auth: Arc, pub options: Opt, pub api_key: Option, } @@ -107,7 +107,7 @@ impl Service { pub async fn request(&self, mut req: test::TestRequest) -> (Value, StatusCode) { let app = test::init_service(create_app( self.index_scheduler.clone().into(), - self.auth.clone(), + self.auth.clone().into(), self.options.clone(), analytics::MockAnalytics::new(&self.options), true,