From ac22b503a45648d856ac2a3c7ba8a5ba2a2c0566 Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Mon, 24 Apr 2023 13:16:36 +0200 Subject: [PATCH 1/8] feat: controller error handling --- Cargo.lock | 1 + controller/Cargo.toml | 1 + .../src/api/external/routes/instance.rs | 54 ++++++++---- controller/src/api/external/routes/mod.rs | 3 + controller/src/api/external/routes/tenant.rs | 40 ++++++--- .../src/api/external/routes/workload.rs | 26 +++--- controller/src/api/mod.rs | 49 ++++------- controller/src/database/mod.rs | 86 ++++++++++++------- 8 files changed, 147 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e974345..297d30c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -517,6 +517,7 @@ dependencies = [ "rusqlite", "serde", "serde_json", + "thiserror", "tiny_http", "tokio", "tokio-stream", diff --git a/controller/Cargo.toml b/controller/Cargo.toml index 143a0fa0..e44d71ac 100644 --- a/controller/Cargo.toml +++ b/controller/Cargo.toml @@ -34,6 +34,7 @@ rstest = "0.16.0" uuid = { version = "1.3.1", features = ["serde", "v4"] } backoff = { version = "0.4.0", features = ["tokio"]} rand = "0.8.4" +thiserror = "1.0.40" # Instrumentation tracing = { workspace = true } diff --git a/controller/src/api/external/routes/instance.rs b/controller/src/api/external/routes/instance.rs index 746f6e23..f886025e 100644 --- a/controller/src/api/external/routes/instance.rs +++ b/controller/src/api/external/routes/instance.rs @@ -1,32 +1,38 @@ use definition::workload::WorkloadDefinition; use route_recognizer; use rusqlite::Connection; -use std::io; use std::str::FromStr; use std::sync::mpsc::Sender; use tracing::{event, Level}; -use crate::api; use crate::api::external::services::element::elements_set_right_name; use crate::api::external::services::instance::send_create_instance; use crate::api::types::element::OnlyId; use crate::api::types::instance::InstanceDefinition; -use crate::api::{ApiChannel, Crud}; +use crate::api::{ApiChannel, Crud, RikError}; use crate::core::instance::Instance; use crate::database::RikRepository; +use super::HttpResult; + pub fn get( _: &mut tiny_http::Request, _: &route_recognizer::Params, connection: &Connection, _: &Sender, -) -> Result>>, api::RikError> { +) -> HttpResult { if let Ok(mut instances) = RikRepository::find_all(connection, "/instance") { instances = elements_set_right_name(instances.clone()); - let instances_json = serde_json::to_string(&instances).unwrap(); + let instances_json = serde_json::to_string(&instances).map_err(RikError::ParsingError)?; + event!(Level::INFO, "instances.get, instances found"); + Ok(tiny_http::Response::from_string(instances_json) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) + .with_header( + tiny_http::Header::from_str("Content-Type: application/json").map_err(|_| { + RikError::Error("tiny_http::Header failed to add header".to_string()) + })?, + ) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find instances") @@ -39,11 +45,14 @@ pub fn create( _: &route_recognizer::Params, connection: &Connection, internal_sender: &Sender, -) -> Result>>, api::RikError> { +) -> HttpResult { let mut content = String::new(); - req.as_reader().read_to_string(&mut content).unwrap(); + req.as_reader() + .read_to_string(&mut content) + .map_err(RikError::IoError)?; - let mut instance: InstanceDefinition = serde_json::from_str(&content)?; + let mut instance: InstanceDefinition = + serde_json::from_str(&content).map_err(RikError::ParsingError)?; //Workload not found if RikRepository::find_one(connection, &instance.workload_id, "/workload").is_err() { @@ -98,11 +107,14 @@ pub fn create( ); } - Ok( - tiny_http::Response::from_string(serde_json::to_string(&instance_names).unwrap()) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) - .with_status_code(tiny_http::StatusCode::from(201)), + Ok(tiny_http::Response::from_string( + serde_json::to_string(&instance_names).map_err(RikError::ParsingError)?, + ) + .with_header( + tiny_http::Header::from_str("Content-Type: application/json") + .map_err(|_| RikError::Error("tiny_http::Header failed to add header".to_string()))?, ) + .with_status_code(tiny_http::StatusCode::from(201))) } pub fn delete( @@ -110,14 +122,17 @@ pub fn delete( _: &route_recognizer::Params, connection: &Connection, internal_sender: &Sender, -) -> Result>>, api::RikError> { +) -> HttpResult { let mut content = String::new(); - req.as_reader().read_to_string(&mut content).unwrap(); - let OnlyId { id: delete_id } = serde_json::from_str(&content)?; + req.as_reader() + .read_to_string(&mut content) + .map_err(RikError::IoError)?; + let OnlyId { id: delete_id } = + serde_json::from_str(&content).map_err(RikError::ParsingError)?; if let Ok(instance) = RikRepository::find_one(connection, &delete_id, "/instance") { let instance_def: InstanceDefinition = - serde_json::from_value(instance.value.clone()).unwrap(); + serde_json::from_value(instance.value.clone()).map_err(RikError::ParsingError)?; let workload_def_rs = RikRepository::find_one(connection, &instance_def.workload_id, "/workload"); @@ -135,7 +150,8 @@ pub fn delete( .with_status_code(tiny_http::StatusCode::from(404))); } let workload_def: WorkloadDefinition = - serde_json::from_value(workload_def_rs.unwrap().value).unwrap(); + serde_json::from_value(workload_def_rs.map_err(RikError::DataBaseError)?.value) + .map_err(RikError::ParsingError)?; internal_sender .send(ApiChannel { action: Crud::Delete, @@ -143,7 +159,7 @@ pub fn delete( workload_definition: Some(workload_def), instance_id: Some(delete_id), }) - .unwrap(); + .map_err(|e| RikError::InternalCommunicationError(e.to_string()))?; event!( Level::INFO, diff --git a/controller/src/api/external/routes/mod.rs b/controller/src/api/external/routes/mod.rs index 9d27ca5b..d1ddf8d1 100644 --- a/controller/src/api/external/routes/mod.rs +++ b/controller/src/api/external/routes/mod.rs @@ -3,6 +3,7 @@ use rusqlite::Connection; use std::io; use std::sync::mpsc::Sender; use tiny_http::Method; +use tiny_http::Response; use tracing::{event, Level}; use crate::api; @@ -19,6 +20,8 @@ type Handler = fn( &Sender, ) -> Result>>, api::RikError>; +type HttpResult>> = Result, api::RikError>; + pub struct Router { routes: Vec<(tiny_http::Method, route_recognizer::Router)>, } diff --git a/controller/src/api/external/routes/tenant.rs b/controller/src/api/external/routes/tenant.rs index 8467b0b7..0c26f9d8 100644 --- a/controller/src/api/external/routes/tenant.rs +++ b/controller/src/api/external/routes/tenant.rs @@ -1,15 +1,14 @@ use route_recognizer; use rusqlite::Connection; -use std::io; use std::str::FromStr; use std::sync::mpsc::Sender; use tracing::{event, Level}; -use crate::api; +use super::HttpResult; use crate::api::external::services::element::elements_set_right_name; use crate::api::types::element::OnlyId; use crate::api::types::tenant::Tenant; -use crate::api::ApiChannel; +use crate::api::{ApiChannel, RikError}; use crate::database::RikRepository; pub fn get( @@ -17,13 +16,17 @@ pub fn get( _: &route_recognizer::Params, connection: &Connection, _: &Sender, -) -> Result>>, api::RikError> { +) -> HttpResult { if let Ok(mut tenants) = RikRepository::find_all(connection, "/tenant") { tenants = elements_set_right_name(tenants.clone()); - let tenants_json = serde_json::to_string(&tenants).unwrap(); + let tenants_json = serde_json::to_string(&tenants).map_err(RikError::ParsingError)?; event!(Level::INFO, "tenants.get, tenants found"); Ok(tiny_http::Response::from_string(tenants_json) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) + .with_header( + tiny_http::Header::from_str("Content-Type: application/json").map_err(|_| { + RikError::Error("tiny_http::Header failed to add header".to_string()) + })?, + ) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find tenant") @@ -36,15 +39,21 @@ pub fn create( _: &route_recognizer::Params, connection: &Connection, _: &Sender, -) -> Result>>, api::RikError> { +) -> HttpResult { let mut content = String::new(); - req.as_reader().read_to_string(&mut content).unwrap(); - let tenant: Tenant = serde_json::from_str(&content)?; + req.as_reader() + .read_to_string(&mut content) + .map_err(RikError::IoError)?; + let tenant: Tenant = serde_json::from_str(&content).map_err(RikError::ParsingError)?; if RikRepository::insert(connection, &tenant.name, &tenant.value).is_ok() { event!(Level::INFO, "Create tenant"); Ok(tiny_http::Response::from_string(content) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) + .with_header( + tiny_http::Header::from_str("Content-Type: application/json").map_err(|_| { + RikError::Error("tiny_http::Header failed to add header".to_string()) + })?, + ) .with_status_code(tiny_http::StatusCode::from(200))) } else { event!(Level::ERROR, "Cannot create tenant"); @@ -58,13 +67,16 @@ pub fn delete( _: &route_recognizer::Params, connection: &Connection, _: &Sender, -) -> Result>>, api::RikError> { +) -> HttpResult { let mut content = String::new(); - req.as_reader().read_to_string(&mut content).unwrap(); - let OnlyId { id: delete_id } = serde_json::from_str(&content)?; + req.as_reader() + .read_to_string(&mut content) + .map_err(RikError::IoError)?; + let OnlyId { id: delete_id } = + serde_json::from_str(&content).map_err(RikError::ParsingError)?; if let Ok(tenant) = RikRepository::find_one(connection, &delete_id, "/tenant") { - RikRepository::delete(connection, &tenant.id).unwrap(); + RikRepository::delete(connection, &tenant.id).map_err(RikError::DataBaseError)?; event!(Level::INFO, "Delete tenant"); Ok(tiny_http::Response::from_string("").with_status_code(tiny_http::StatusCode::from(204))) } else { diff --git a/controller/src/api/external/routes/workload.rs b/controller/src/api/external/routes/workload.rs index 6e56ae51..1e5b550c 100644 --- a/controller/src/api/external/routes/workload.rs +++ b/controller/src/api/external/routes/workload.rs @@ -1,21 +1,17 @@ -use crate::api; +use super::HttpResult; use crate::api::external::services::element::elements_set_right_name; use crate::api::types::element::OnlyId; -use crate::api::{ApiChannel, Crud}; +use crate::api::{ApiChannel, Crud, RikError}; use crate::core::instance::Instance; use crate::database::RikRepository; use definition::workload::WorkloadDefinition; use route_recognizer; use rusqlite::Connection; use serde_json::json; -use std::io; use std::str::FromStr; use std::sync::mpsc::Sender; -use tiny_http::Response; use tracing::{event, Level}; -type HttpResult>> = Result, api::RikError>; - pub fn get( _: &mut tiny_http::Request, _: &route_recognizer::Params, @@ -24,7 +20,7 @@ pub fn get( ) -> HttpResult { if let Ok(mut workloads) = RikRepository::find_all(connection, "/workload") { workloads = elements_set_right_name(workloads.clone()); - let workloads_json = serde_json::to_string(&workloads).unwrap(); + let workloads_json = serde_json::to_string(&workloads).map_err(RikError::ParsingError)?; event!(Level::INFO, "workloads.get, workloads found"); Ok(tiny_http::Response::from_string(workloads_json) @@ -84,7 +80,8 @@ pub fn create( let mut content = String::new(); req.as_reader().read_to_string(&mut content).unwrap(); - let mut workload: WorkloadDefinition = serde_json::from_str(&content)?; + let mut workload: WorkloadDefinition = + serde_json::from_str(&content).map_err(RikError::ParsingError)?; if workload.replicas.is_none() { workload.replicas = Some(1); } @@ -104,18 +101,18 @@ pub fn create( if let Ok(inserted_id) = RikRepository::insert( connection, &name, - &serde_json::to_string(&workload).unwrap(), + &serde_json::to_string(&workload).map_err(RikError::ParsingError)?, ) { let workload_id: OnlyId = OnlyId { id: inserted_id }; event!( Level::INFO, "workload.create, workload successfully created" ); - Ok( - tiny_http::Response::from_string(serde_json::to_string(&workload_id).unwrap()) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) - .with_status_code(tiny_http::StatusCode::from(200)), + Ok(tiny_http::Response::from_string( + serde_json::to_string(&workload_id).map_err(RikError::ParsingError)?, ) + .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) + .with_status_code(tiny_http::StatusCode::from(200))) } else { event!(Level::ERROR, "workload.create, cannot create workload"); Ok(tiny_http::Response::from_string("Cannot create workload") @@ -131,7 +128,8 @@ pub fn delete( ) -> HttpResult { let mut content = String::new(); req.as_reader().read_to_string(&mut content).unwrap(); - let OnlyId { id: delete_id } = serde_json::from_str(&content)?; + let OnlyId { id: delete_id } = + serde_json::from_str(&content).map_err(RikError::ParsingError)?; if let Ok(workload) = RikRepository::find_one(connection, &delete_id, "/workload") { let definition: WorkloadDefinition = serde_json::from_value(workload.value).unwrap(); diff --git a/controller/src/api/mod.rs b/controller/src/api/mod.rs index 16fb5d2e..07c536d8 100644 --- a/controller/src/api/mod.rs +++ b/controller/src/api/mod.rs @@ -3,6 +3,9 @@ pub mod types; use definition::workload::WorkloadDefinition; use std::fmt::{Debug, Display, Formatter, Result}; +use thiserror::Error; + +use crate::database::DataBaseError; #[derive(Debug)] pub enum Crud { @@ -20,45 +23,25 @@ impl From for Crud { } } -#[derive(Debug)] +#[derive(Debug, Error)] pub enum RikError { + #[error("IO error: {0}")] IoError(std::io::Error), - HttpRequestError(serde_json::Error), + + #[error("HTTP request error: {0}")] + ParsingError(serde_json::Error), + + #[error("Internal communication error: {0}")] InternalCommunicationError(String), - InvalidName(String), -} -impl Display for RikError { - fn fmt(&self, f: &mut Formatter) -> Result { - match *self { - RikError::IoError(ref e) => write!(f, "{}", e), - RikError::HttpRequestError(ref e) => write!(f, "{}", e), - RikError::InternalCommunicationError(ref e) => write!(f, "{}", e), - RikError::InvalidName(ref e) => write!(f, "{}", e), - } - } -} -impl std::error::Error for RikError { - fn cause(&self) -> Option<&dyn std::error::Error> { - match *self { - RikError::IoError(ref e) => Some(e), - RikError::HttpRequestError(ref e) => Some(e), - // TODO: Implement other errors - _ => None, - } - } -} + #[error("Invalid name: {0}")] + InvalidName(String), -impl From for RikError { - fn from(e: std::io::Error) -> RikError { - RikError::IoError(e) - } -} + #[error("Error: {0}")] + Error(String), -impl From for RikError { - fn from(e: serde_json::Error) -> RikError { - RikError::HttpRequestError(e) - } + #[error("Database error: {0}")] + DataBaseError(DataBaseError), } pub struct ApiChannel { diff --git a/controller/src/database/mod.rs b/controller/src/database/mod.rs index 09a78970..67691b61 100644 --- a/controller/src/database/mod.rs +++ b/controller/src/database/mod.rs @@ -1,10 +1,22 @@ use crate::api::types::element::Element; use dotenv::dotenv; -use rusqlite::{params, Connection, Result}; +use rusqlite::{params, Connection}; use std::sync::Arc; +use thiserror::Error; use uuid::Uuid; +#[derive(Debug, Error)] +pub enum DataBaseError { + #[error("Error: {0}")] + SqlError(rusqlite::Error), + + #[error("Io error: {0}")] + IoError(std::io::Error), +} + +type Result = std::result::Result; + #[allow(dead_code)] pub struct RikDataBase { name: String, @@ -19,15 +31,17 @@ impl RikDataBase { pub fn init_tables(&self) -> Result<()> { let connection = self.open()?; // only work with sqlite for now - connection.execute_batch( - "CREATE TABLE IF NOT EXISTS cluster ( + connection + .execute_batch( + "CREATE TABLE IF NOT EXISTS cluster ( id TEXT PRIMARY KEY, name TEXT NOT NULL, value BLOB NOT NULL ); CREATE INDEX IF NOT EXISTS cluster_name_index ON cluster (name); CREATE INDEX IF NOT EXISTS cluster_name_id_index ON cluster (name,id);", - )?; + ) + .map_err(DataBaseError::SqlError)?; Ok(()) } @@ -37,10 +51,10 @@ impl RikDataBase { dotenv().ok(); let file_path = std::env::var("DATABASE_LOCATION").unwrap_or("/var/lib/rik/data/".to_string()); - std::fs::create_dir_all(&file_path).unwrap(); + std::fs::create_dir_all(&file_path).map_err(DataBaseError::IoError)?; let database_path = format!("{}{}.db", file_path, self.name); - Connection::open(database_path) + Connection::open(database_path).map_err(DataBaseError::SqlError) } } @@ -58,34 +72,36 @@ impl RikRepository { } pub fn delete(connection: &Connection, id: &String) -> Result<()> { - connection.execute("DELETE FROM cluster WHERE id = (?1)", params![id])?; + connection + .execute("DELETE FROM cluster WHERE id = (?1)", params![id]) + .map_err(DataBaseError::SqlError)?; Ok(()) } pub fn find_one(connection: &Connection, id: &String, element_type: &str) -> Result { - let mut stmt = connection.prepare(&format!( - "SELECT id, name, value FROM cluster WHERE id = '{}' AND name LIKE '{}%'", - id, element_type - ))?; - match stmt.query_row([], |row| { + let mut stmt = connection + .prepare(&format!( + "SELECT id, name, value FROM cluster WHERE id = '{}' AND name LIKE '{}%'", + id, element_type + )) + .map_err(DataBaseError::SqlError)?; + stmt.query_row([], |row| { Ok(Element::new(row.get(0)?, row.get(1)?, row.get(2)?)) - }) { - Ok(element) => Ok(element), - Err(err) => Err(err), - } + }) + .map_err(DataBaseError::SqlError) } pub fn check_duplicate_name(connection: &Connection, name: &str) -> Result { - let mut stmt = connection.prepare(&format!( - "SELECT id, name, value FROM cluster WHERE name LIKE '{}%'", - name - ))?; - match stmt.query_row([], |row| { + let mut stmt = connection + .prepare(&format!( + "SELECT id, name, value FROM cluster WHERE name LIKE '{}%'", + name + )) + .map_err(DataBaseError::SqlError)?; + stmt.query_row([], |row| { Ok(Element::new(row.get(0)?, row.get(1)?, row.get(2)?)) - }) { - Ok(element) => Ok(element), - Err(err) => Err(err), - } + }) + .map_err(DataBaseError::SqlError) } // TODO: add pagination @@ -104,16 +120,18 @@ impl RikRepository { let mut elements: Vec = Vec::new(); for element in elements_iter { - elements.push(element?); + elements.push(element.map_err(DataBaseError::SqlError)?); } Ok(elements) } pub fn update(connection: &Connection, id: &String, value: &String) -> Result<()> { - connection.execute( - "UPDATE cluster SET value=(?1) WHERE id = (?2)", - params![value, id], - )?; + connection + .execute( + "UPDATE cluster SET value=(?1) WHERE id = (?2)", + params![value, id], + ) + .map_err(DataBaseError::SqlError)?; Ok(()) } @@ -133,7 +151,7 @@ impl RikRepository { "INSERT INTO cluster (id, name, value) VALUES (?1, ?2, ?3)", params![id, name, value], ) - .unwrap(); + .map_err(DataBaseError::SqlError)?; Ok(id.to_string()) } } @@ -141,8 +159,10 @@ impl RikRepository { #[cfg(test)] mod test { - use crate::database::{RikDataBase, RikRepository}; - use crate::tests::fixtures::db_connection; + use crate::{ + database::{RikDataBase, RikRepository}, + tests::fixtures::db_connection, + }; use rstest::rstest; use uuid::Uuid; From 159b4ec27f43d8cd1adbd702eef78eb73c2997a5 Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Sun, 7 May 2023 15:59:06 +0200 Subject: [PATCH 2/8] fix: rename DataBaseError to DatabaseError Signed-off-by: Malo Polese --- controller/src/api/mod.rs | 4 ++-- controller/src/database/mod.rs | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/controller/src/api/mod.rs b/controller/src/api/mod.rs index 07c536d8..448c27cb 100644 --- a/controller/src/api/mod.rs +++ b/controller/src/api/mod.rs @@ -5,7 +5,7 @@ use definition::workload::WorkloadDefinition; use std::fmt::{Debug, Display, Formatter, Result}; use thiserror::Error; -use crate::database::DataBaseError; +use crate::database::DatabaseError; #[derive(Debug)] pub enum Crud { @@ -41,7 +41,7 @@ pub enum RikError { Error(String), #[error("Database error: {0}")] - DataBaseError(DataBaseError), + DataBaseError(DatabaseError), } pub struct ApiChannel { diff --git a/controller/src/database/mod.rs b/controller/src/database/mod.rs index 67691b61..f026d520 100644 --- a/controller/src/database/mod.rs +++ b/controller/src/database/mod.rs @@ -7,7 +7,7 @@ use thiserror::Error; use uuid::Uuid; #[derive(Debug, Error)] -pub enum DataBaseError { +pub enum DatabaseError { #[error("Error: {0}")] SqlError(rusqlite::Error), @@ -15,7 +15,7 @@ pub enum DataBaseError { IoError(std::io::Error), } -type Result = std::result::Result; +type Result = std::result::Result; #[allow(dead_code)] pub struct RikDataBase { @@ -41,7 +41,7 @@ impl RikDataBase { CREATE INDEX IF NOT EXISTS cluster_name_index ON cluster (name); CREATE INDEX IF NOT EXISTS cluster_name_id_index ON cluster (name,id);", ) - .map_err(DataBaseError::SqlError)?; + .map_err(DatabaseError::SqlError)?; Ok(()) } @@ -51,10 +51,10 @@ impl RikDataBase { dotenv().ok(); let file_path = std::env::var("DATABASE_LOCATION").unwrap_or("/var/lib/rik/data/".to_string()); - std::fs::create_dir_all(&file_path).map_err(DataBaseError::IoError)?; + std::fs::create_dir_all(&file_path).map_err(DatabaseError::IoError)?; let database_path = format!("{}{}.db", file_path, self.name); - Connection::open(database_path).map_err(DataBaseError::SqlError) + Connection::open(database_path).map_err(DatabaseError::SqlError) } } @@ -74,7 +74,7 @@ impl RikRepository { pub fn delete(connection: &Connection, id: &String) -> Result<()> { connection .execute("DELETE FROM cluster WHERE id = (?1)", params![id]) - .map_err(DataBaseError::SqlError)?; + .map_err(DatabaseError::SqlError)?; Ok(()) } @@ -84,11 +84,11 @@ impl RikRepository { "SELECT id, name, value FROM cluster WHERE id = '{}' AND name LIKE '{}%'", id, element_type )) - .map_err(DataBaseError::SqlError)?; + .map_err(DatabaseError::SqlError)?; stmt.query_row([], |row| { Ok(Element::new(row.get(0)?, row.get(1)?, row.get(2)?)) }) - .map_err(DataBaseError::SqlError) + .map_err(DatabaseError::SqlError) } pub fn check_duplicate_name(connection: &Connection, name: &str) -> Result { @@ -97,11 +97,11 @@ impl RikRepository { "SELECT id, name, value FROM cluster WHERE name LIKE '{}%'", name )) - .map_err(DataBaseError::SqlError)?; + .map_err(DatabaseError::SqlError)?; stmt.query_row([], |row| { Ok(Element::new(row.get(0)?, row.get(1)?, row.get(2)?)) }) - .map_err(DataBaseError::SqlError) + .map_err(DatabaseError::SqlError) } // TODO: add pagination @@ -120,7 +120,7 @@ impl RikRepository { let mut elements: Vec = Vec::new(); for element in elements_iter { - elements.push(element.map_err(DataBaseError::SqlError)?); + elements.push(element.map_err(DatabaseError::SqlError)?); } Ok(elements) } @@ -131,7 +131,7 @@ impl RikRepository { "UPDATE cluster SET value=(?1) WHERE id = (?2)", params![value, id], ) - .map_err(DataBaseError::SqlError)?; + .map_err(DatabaseError::SqlError)?; Ok(()) } @@ -151,7 +151,7 @@ impl RikRepository { "INSERT INTO cluster (id, name, value) VALUES (?1, ?2, ?3)", params![id, name, value], ) - .map_err(DataBaseError::SqlError)?; + .map_err(DatabaseError::SqlError)?; Ok(id.to_string()) } } From 9828b2feb1f0d1793554384e421a1cbf8a36886e Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Sun, 7 May 2023 16:00:06 +0200 Subject: [PATCH 3/8] feat: add enum ContentType --- controller/src/api/external/routes/instance.rs | 13 +++---------- controller/src/api/external/routes/mod.rs | 12 ++++++++++++ controller/src/api/external/routes/tenant.rs | 13 +++---------- controller/src/api/external/routes/workload.rs | 7 ++++--- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/controller/src/api/external/routes/instance.rs b/controller/src/api/external/routes/instance.rs index f886025e..0429e8eb 100644 --- a/controller/src/api/external/routes/instance.rs +++ b/controller/src/api/external/routes/instance.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use std::sync::mpsc::Sender; use tracing::{event, Level}; +use crate::api::external::routes::ContentType; use crate::api::external::services::element::elements_set_right_name; use crate::api::external::services::instance::send_create_instance; use crate::api::types::element::OnlyId; @@ -26,13 +27,8 @@ pub fn get( let instances_json = serde_json::to_string(&instances).map_err(RikError::ParsingError)?; event!(Level::INFO, "instances.get, instances found"); - Ok(tiny_http::Response::from_string(instances_json) - .with_header( - tiny_http::Header::from_str("Content-Type: application/json").map_err(|_| { - RikError::Error("tiny_http::Header failed to add header".to_string()) - })?, - ) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find instances") @@ -110,10 +106,7 @@ pub fn create( Ok(tiny_http::Response::from_string( serde_json::to_string(&instance_names).map_err(RikError::ParsingError)?, ) - .with_header( - tiny_http::Header::from_str("Content-Type: application/json") - .map_err(|_| RikError::Error("tiny_http::Header failed to add header".to_string()))?, - ) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) .with_status_code(tiny_http::StatusCode::from(201))) } diff --git a/controller/src/api/external/routes/mod.rs b/controller/src/api/external/routes/mod.rs index d1ddf8d1..09da50fd 100644 --- a/controller/src/api/external/routes/mod.rs +++ b/controller/src/api/external/routes/mod.rs @@ -22,6 +22,18 @@ type Handler = fn( type HttpResult>> = Result, api::RikError>; +pub enum ContentType { + JSON, +} + +impl Into<&str> for ContentType { + fn into(self) -> &'static str { + match self { + ContentType::JSON => "Content-Type: application/json", + } + } +} + pub struct Router { routes: Vec<(tiny_http::Method, route_recognizer::Router)>, } diff --git a/controller/src/api/external/routes/tenant.rs b/controller/src/api/external/routes/tenant.rs index 0c26f9d8..9b2b6590 100644 --- a/controller/src/api/external/routes/tenant.rs +++ b/controller/src/api/external/routes/tenant.rs @@ -5,6 +5,7 @@ use std::sync::mpsc::Sender; use tracing::{event, Level}; use super::HttpResult; +use crate::api::external::routes::ContentType; use crate::api::external::services::element::elements_set_right_name; use crate::api::types::element::OnlyId; use crate::api::types::tenant::Tenant; @@ -22,11 +23,7 @@ pub fn get( let tenants_json = serde_json::to_string(&tenants).map_err(RikError::ParsingError)?; event!(Level::INFO, "tenants.get, tenants found"); Ok(tiny_http::Response::from_string(tenants_json) - .with_header( - tiny_http::Header::from_str("Content-Type: application/json").map_err(|_| { - RikError::Error("tiny_http::Header failed to add header".to_string()) - })?, - ) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find tenant") @@ -49,11 +46,7 @@ pub fn create( if RikRepository::insert(connection, &tenant.name, &tenant.value).is_ok() { event!(Level::INFO, "Create tenant"); Ok(tiny_http::Response::from_string(content) - .with_header( - tiny_http::Header::from_str("Content-Type: application/json").map_err(|_| { - RikError::Error("tiny_http::Header failed to add header".to_string()) - })?, - ) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) .with_status_code(tiny_http::StatusCode::from(200))) } else { event!(Level::ERROR, "Cannot create tenant"); diff --git a/controller/src/api/external/routes/workload.rs b/controller/src/api/external/routes/workload.rs index 1e5b550c..5c06f970 100644 --- a/controller/src/api/external/routes/workload.rs +++ b/controller/src/api/external/routes/workload.rs @@ -1,4 +1,5 @@ use super::HttpResult; +use crate::api::external::routes::ContentType; use crate::api::external::services::element::elements_set_right_name; use crate::api::types::element::OnlyId; use crate::api::{ApiChannel, Crud, RikError}; @@ -24,7 +25,7 @@ pub fn get( event!(Level::INFO, "workloads.get, workloads found"); Ok(tiny_http::Response::from_string(workloads_json) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find workloads") @@ -61,7 +62,7 @@ pub fn get_instances( let instances_json = json!({ "instances": instances }).to_string(); return Ok(tiny_http::Response::from_string(instances_json) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) .with_status_code(tiny_http::StatusCode::from(200))); } @@ -111,7 +112,7 @@ pub fn create( Ok(tiny_http::Response::from_string( serde_json::to_string(&workload_id).map_err(RikError::ParsingError)?, ) - .with_header(tiny_http::Header::from_str("Content-Type: application/json").unwrap()) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) .with_status_code(tiny_http::StatusCode::from(200))) } else { event!(Level::ERROR, "workload.create, cannot create workload"); From 531e9fbba41c40a3b832f979b111892bd04606a9 Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Sun, 7 May 2023 16:01:18 +0200 Subject: [PATCH 4/8] fix: add more context on SQL error Signed-off-by: Malo Polese --- controller/src/database/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/src/database/mod.rs b/controller/src/database/mod.rs index f026d520..aae8771c 100644 --- a/controller/src/database/mod.rs +++ b/controller/src/database/mod.rs @@ -8,7 +8,7 @@ use uuid::Uuid; #[derive(Debug, Error)] pub enum DatabaseError { - #[error("Error: {0}")] + #[error("Sql Error: {0}")] SqlError(rusqlite::Error), #[error("Io error: {0}")] From 2e93c4d9f563054dd5227cccd16041313cd9a6a4 Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Sun, 7 May 2023 16:01:58 +0200 Subject: [PATCH 5/8] remove generic error Signed-off-by: Malo Polese --- controller/src/api/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/controller/src/api/mod.rs b/controller/src/api/mod.rs index 448c27cb..1c474052 100644 --- a/controller/src/api/mod.rs +++ b/controller/src/api/mod.rs @@ -37,9 +37,6 @@ pub enum RikError { #[error("Invalid name: {0}")] InvalidName(String), - #[error("Error: {0}")] - Error(String), - #[error("Database error: {0}")] DataBaseError(DatabaseError), } From e4da759cae4c8344cad2bd574e8ca8bc9963af8b Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Sun, 7 May 2023 16:17:15 +0200 Subject: [PATCH 6/8] feat: repalce thiserror by anyhow for the external api Signed-off-by: Malo Polese --- Cargo.lock | 1 + controller/Cargo.toml | 1 + .../src/api/external/routes/instance.rs | 47 +++++++------------ controller/src/api/external/routes/mod.rs | 5 +- controller/src/api/external/routes/tenant.rs | 19 +++----- .../src/api/external/routes/workload.rs | 26 +++++----- controller/src/api/mod.rs | 11 ----- 7 files changed, 40 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 297d30c6..9d780019 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -501,6 +501,7 @@ dependencies = [ name = "controller" version = "1.0.0" dependencies = [ + "anyhow", "async-trait", "backoff", "chrono", diff --git a/controller/Cargo.toml b/controller/Cargo.toml index e44d71ac..48532e75 100644 --- a/controller/Cargo.toml +++ b/controller/Cargo.toml @@ -35,6 +35,7 @@ uuid = { version = "1.3.1", features = ["serde", "v4"] } backoff = { version = "0.4.0", features = ["tokio"]} rand = "0.8.4" thiserror = "1.0.40" +anyhow = "1.0.71" # Instrumentation tracing = { workspace = true } diff --git a/controller/src/api/external/routes/instance.rs b/controller/src/api/external/routes/instance.rs index 0429e8eb..a3d742c8 100644 --- a/controller/src/api/external/routes/instance.rs +++ b/controller/src/api/external/routes/instance.rs @@ -10,7 +10,7 @@ use crate::api::external::services::element::elements_set_right_name; use crate::api::external::services::instance::send_create_instance; use crate::api::types::element::OnlyId; use crate::api::types::instance::InstanceDefinition; -use crate::api::{ApiChannel, Crud, RikError}; +use crate::api::{ApiChannel, Crud}; use crate::core::instance::Instance; use crate::database::RikRepository; @@ -24,7 +24,7 @@ pub fn get( ) -> HttpResult { if let Ok(mut instances) = RikRepository::find_all(connection, "/instance") { instances = elements_set_right_name(instances.clone()); - let instances_json = serde_json::to_string(&instances).map_err(RikError::ParsingError)?; + let instances_json = serde_json::to_string(&instances)?; event!(Level::INFO, "instances.get, instances found"); Ok(tiny_http::Response::from_string(instances_json) @@ -43,12 +43,9 @@ pub fn create( internal_sender: &Sender, ) -> HttpResult { let mut content = String::new(); - req.as_reader() - .read_to_string(&mut content) - .map_err(RikError::IoError)?; + req.as_reader().read_to_string(&mut content)?; - let mut instance: InstanceDefinition = - serde_json::from_str(&content).map_err(RikError::ParsingError)?; + let mut instance: InstanceDefinition = serde_json::from_str(&content)?; //Workload not found if RikRepository::find_one(connection, &instance.workload_id, "/workload").is_err() { @@ -103,11 +100,11 @@ pub fn create( ); } - Ok(tiny_http::Response::from_string( - serde_json::to_string(&instance_names).map_err(RikError::ParsingError)?, + Ok( + tiny_http::Response::from_string(serde_json::to_string(&instance_names)?) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_status_code(tiny_http::StatusCode::from(201)), ) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) - .with_status_code(tiny_http::StatusCode::from(201))) } pub fn delete( @@ -117,15 +114,11 @@ pub fn delete( internal_sender: &Sender, ) -> HttpResult { let mut content = String::new(); - req.as_reader() - .read_to_string(&mut content) - .map_err(RikError::IoError)?; - let OnlyId { id: delete_id } = - serde_json::from_str(&content).map_err(RikError::ParsingError)?; + req.as_reader().read_to_string(&mut content)?; + let OnlyId { id: delete_id } = serde_json::from_str(&content)?; if let Ok(instance) = RikRepository::find_one(connection, &delete_id, "/instance") { - let instance_def: InstanceDefinition = - serde_json::from_value(instance.value.clone()).map_err(RikError::ParsingError)?; + let instance_def: InstanceDefinition = serde_json::from_value(instance.value.clone())?; let workload_def_rs = RikRepository::find_one(connection, &instance_def.workload_id, "/workload"); @@ -142,17 +135,13 @@ pub fn delete( )) .with_status_code(tiny_http::StatusCode::from(404))); } - let workload_def: WorkloadDefinition = - serde_json::from_value(workload_def_rs.map_err(RikError::DataBaseError)?.value) - .map_err(RikError::ParsingError)?; - internal_sender - .send(ApiChannel { - action: Crud::Delete, - workload_id: Some(instance_def.workload_id), - workload_definition: Some(workload_def), - instance_id: Some(delete_id), - }) - .map_err(|e| RikError::InternalCommunicationError(e.to_string()))?; + let workload_def: WorkloadDefinition = serde_json::from_value(workload_def_rs?.value)?; + internal_sender.send(ApiChannel { + action: Crud::Delete, + workload_id: Some(instance_def.workload_id), + workload_definition: Some(workload_def), + instance_id: Some(delete_id), + })?; event!( Level::INFO, diff --git a/controller/src/api/external/routes/mod.rs b/controller/src/api/external/routes/mod.rs index 09da50fd..aef8c5e6 100644 --- a/controller/src/api/external/routes/mod.rs +++ b/controller/src/api/external/routes/mod.rs @@ -6,7 +6,6 @@ use tiny_http::Method; use tiny_http::Response; use tracing::{event, Level}; -use crate::api; use crate::api::ApiChannel; mod instance; @@ -18,9 +17,9 @@ type Handler = fn( &route_recognizer::Params, &Connection, &Sender, -) -> Result>>, api::RikError>; +) -> Result>>, anyhow::Error>; -type HttpResult>> = Result, api::RikError>; +type HttpResult>> = Result, anyhow::Error>; pub enum ContentType { JSON, diff --git a/controller/src/api/external/routes/tenant.rs b/controller/src/api/external/routes/tenant.rs index 9b2b6590..9a1e7341 100644 --- a/controller/src/api/external/routes/tenant.rs +++ b/controller/src/api/external/routes/tenant.rs @@ -9,7 +9,7 @@ use crate::api::external::routes::ContentType; use crate::api::external::services::element::elements_set_right_name; use crate::api::types::element::OnlyId; use crate::api::types::tenant::Tenant; -use crate::api::{ApiChannel, RikError}; +use crate::api::ApiChannel; use crate::database::RikRepository; pub fn get( @@ -20,7 +20,7 @@ pub fn get( ) -> HttpResult { if let Ok(mut tenants) = RikRepository::find_all(connection, "/tenant") { tenants = elements_set_right_name(tenants.clone()); - let tenants_json = serde_json::to_string(&tenants).map_err(RikError::ParsingError)?; + let tenants_json = serde_json::to_string(&tenants)?; event!(Level::INFO, "tenants.get, tenants found"); Ok(tiny_http::Response::from_string(tenants_json) .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) @@ -38,10 +38,8 @@ pub fn create( _: &Sender, ) -> HttpResult { let mut content = String::new(); - req.as_reader() - .read_to_string(&mut content) - .map_err(RikError::IoError)?; - let tenant: Tenant = serde_json::from_str(&content).map_err(RikError::ParsingError)?; + req.as_reader().read_to_string(&mut content)?; + let tenant: Tenant = serde_json::from_str(&content)?; if RikRepository::insert(connection, &tenant.name, &tenant.value).is_ok() { event!(Level::INFO, "Create tenant"); @@ -62,14 +60,11 @@ pub fn delete( _: &Sender, ) -> HttpResult { let mut content = String::new(); - req.as_reader() - .read_to_string(&mut content) - .map_err(RikError::IoError)?; - let OnlyId { id: delete_id } = - serde_json::from_str(&content).map_err(RikError::ParsingError)?; + req.as_reader().read_to_string(&mut content)?; + let OnlyId { id: delete_id } = serde_json::from_str(&content)?; if let Ok(tenant) = RikRepository::find_one(connection, &delete_id, "/tenant") { - RikRepository::delete(connection, &tenant.id).map_err(RikError::DataBaseError)?; + RikRepository::delete(connection, &tenant.id)?; event!(Level::INFO, "Delete tenant"); Ok(tiny_http::Response::from_string("").with_status_code(tiny_http::StatusCode::from(204))) } else { diff --git a/controller/src/api/external/routes/workload.rs b/controller/src/api/external/routes/workload.rs index 5c06f970..59b516d1 100644 --- a/controller/src/api/external/routes/workload.rs +++ b/controller/src/api/external/routes/workload.rs @@ -2,7 +2,7 @@ use super::HttpResult; use crate::api::external::routes::ContentType; use crate::api::external::services::element::elements_set_right_name; use crate::api::types::element::OnlyId; -use crate::api::{ApiChannel, Crud, RikError}; +use crate::api::{ApiChannel, Crud}; use crate::core::instance::Instance; use crate::database::RikRepository; use definition::workload::WorkloadDefinition; @@ -21,7 +21,7 @@ pub fn get( ) -> HttpResult { if let Ok(mut workloads) = RikRepository::find_all(connection, "/workload") { workloads = elements_set_right_name(workloads.clone()); - let workloads_json = serde_json::to_string(&workloads).map_err(RikError::ParsingError)?; + let workloads_json = serde_json::to_string(&workloads)?; event!(Level::INFO, "workloads.get, workloads found"); Ok(tiny_http::Response::from_string(workloads_json) @@ -81,8 +81,7 @@ pub fn create( let mut content = String::new(); req.as_reader().read_to_string(&mut content).unwrap(); - let mut workload: WorkloadDefinition = - serde_json::from_str(&content).map_err(RikError::ParsingError)?; + let mut workload: WorkloadDefinition = serde_json::from_str(&content)?; if workload.replicas.is_none() { workload.replicas = Some(1); } @@ -99,21 +98,19 @@ pub fn create( .with_status_code(tiny_http::StatusCode::from(404))); } - if let Ok(inserted_id) = RikRepository::insert( - connection, - &name, - &serde_json::to_string(&workload).map_err(RikError::ParsingError)?, - ) { + if let Ok(inserted_id) = + RikRepository::insert(connection, &name, &serde_json::to_string(&workload)?) + { let workload_id: OnlyId = OnlyId { id: inserted_id }; event!( Level::INFO, "workload.create, workload successfully created" ); - Ok(tiny_http::Response::from_string( - serde_json::to_string(&workload_id).map_err(RikError::ParsingError)?, + Ok( + tiny_http::Response::from_string(serde_json::to_string(&workload_id)?) + .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_status_code(tiny_http::StatusCode::from(200)), ) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) - .with_status_code(tiny_http::StatusCode::from(200))) } else { event!(Level::ERROR, "workload.create, cannot create workload"); Ok(tiny_http::Response::from_string("Cannot create workload") @@ -129,8 +126,7 @@ pub fn delete( ) -> HttpResult { let mut content = String::new(); req.as_reader().read_to_string(&mut content).unwrap(); - let OnlyId { id: delete_id } = - serde_json::from_str(&content).map_err(RikError::ParsingError)?; + let OnlyId { id: delete_id } = serde_json::from_str(&content)?; if let Ok(workload) = RikRepository::find_one(connection, &delete_id, "/workload") { let definition: WorkloadDefinition = serde_json::from_value(workload.value).unwrap(); diff --git a/controller/src/api/mod.rs b/controller/src/api/mod.rs index 1c474052..11ac28a0 100644 --- a/controller/src/api/mod.rs +++ b/controller/src/api/mod.rs @@ -5,8 +5,6 @@ use definition::workload::WorkloadDefinition; use std::fmt::{Debug, Display, Formatter, Result}; use thiserror::Error; -use crate::database::DatabaseError; - #[derive(Debug)] pub enum Crud { Create = 0, @@ -25,20 +23,11 @@ impl From for Crud { #[derive(Debug, Error)] pub enum RikError { - #[error("IO error: {0}")] - IoError(std::io::Error), - - #[error("HTTP request error: {0}")] - ParsingError(serde_json::Error), - #[error("Internal communication error: {0}")] InternalCommunicationError(String), #[error("Invalid name: {0}")] InvalidName(String), - - #[error("Database error: {0}")] - DataBaseError(DatabaseError), } pub struct ApiChannel { From 8acf04ac3e74585b515fee568341a86f6cc25e49 Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Sun, 7 May 2023 16:41:24 +0200 Subject: [PATCH 7/8] fix: error handling in thread Signed-off-by: Malo Polese --- controller/src/api/external/mod.rs | 48 +++++++++++++++++++----------- controller/src/api/mod.rs | 5 ++++ controller/src/main.rs | 24 ++++++++++----- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/controller/src/api/external/mod.rs b/controller/src/api/external/mod.rs index 8e526af5..03c39da1 100644 --- a/controller/src/api/external/mod.rs +++ b/controller/src/api/external/mod.rs @@ -11,6 +11,8 @@ use tiny_http::{Request, Server as TinyServer}; use tracing::{event, Level}; +use super::RikError; + pub struct Server { internal_sender: Sender, } @@ -20,11 +22,11 @@ impl Server { Server { internal_sender } } - pub fn run(&self, db: Arc) { - self.run_server(db); + pub fn run(&self, db: Arc) -> Result<(), RikError> { + self.run_server(db) } - fn run_server(&self, db: Arc) { + fn run_server(&self, db: Arc) -> Result<(), RikError> { let host = String::from("0.0.0.0"); dotenv().ok(); let port: usize = match std::env::var("PORT") { @@ -41,28 +43,38 @@ impl Server { let db = db.clone(); let internal_sender = self.internal_sender.clone(); - let guard = thread::spawn(move || loop { - let router = routes::Router::new(); - let connection = db.open().unwrap(); + let guard = thread::spawn(move || -> Result<(), RikError> { + loop { + let router = routes::Router::new(); + let connection = db.open().map_err(RikError::DatabaseError)?; - let mut req: Request = server.recv().unwrap(); + let mut req: Request = server.recv().unwrap(); - if let Some(res) = router.handle(&mut req, &connection, &internal_sender) { - req.respond(res).unwrap(); - continue; + if let Some(res) = router.handle(&mut req, &connection, &internal_sender) { + req.respond(res).unwrap(); + continue; + } + event!( + Level::INFO, + "Route {} ({}) could not be found", + req.url(), + req.method() + ); + req.respond(tiny_http::Response::empty(tiny_http::StatusCode::from(404))) + .unwrap(); } - event!( - Level::INFO, - "Route {} ({}) could not be found", - req.url(), - req.method() - ); - req.respond(tiny_http::Response::empty(tiny_http::StatusCode::from(404))) - .unwrap(); }); guards.push(guard); } + + for guard in guards { + guard + .join() + .expect("Couldn't join on the associated thread")? + } + event!(Level::INFO, "Server running on http://{}:{}", host, port); + Ok(()) } } diff --git a/controller/src/api/mod.rs b/controller/src/api/mod.rs index 11ac28a0..61799ccb 100644 --- a/controller/src/api/mod.rs +++ b/controller/src/api/mod.rs @@ -5,6 +5,8 @@ use definition::workload::WorkloadDefinition; use std::fmt::{Debug, Display, Formatter, Result}; use thiserror::Error; +use crate::database::DatabaseError; + #[derive(Debug)] pub enum Crud { Create = 0, @@ -23,6 +25,9 @@ impl From for Crud { #[derive(Debug, Error)] pub enum RikError { + #[error("Database error {0}")] + DatabaseError(DatabaseError), + #[error("Internal communication error: {0}")] InternalCommunicationError(String), diff --git a/controller/src/main.rs b/controller/src/main.rs index 8ba0b626..074d10fc 100644 --- a/controller/src/main.rs +++ b/controller/src/main.rs @@ -6,9 +6,9 @@ mod tests; use std::sync::mpsc::channel; use std::thread; -use crate::database::RikDataBase; +use crate::{api::RikError, database::RikDataBase}; use api::{external, ApiChannel}; -use tracing::{event, metadata::LevelFilter, Level}; +use tracing::{error, event, metadata::LevelFilter, Level}; use tracing_subscriber::{ fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter, }; @@ -32,7 +32,9 @@ async fn main() { logger_setup(); event!(Level::INFO, "Starting Rik"); let db = RikDataBase::new(String::from("rik")); - db.init_tables().unwrap(); + if let Err(e) = db.init_tables() { + error!("Error while table initialization {}", e) + } let (legacy_sender, legacy_receiver) = channel::(); @@ -42,18 +44,26 @@ async fn main() { let external_api = external::Server::new(legacy_sender); let mut threads = Vec::new(); - threads.push(thread::spawn(move || { + threads.push(thread::spawn(move || -> Result<(), RikError> { let future = async move { internal_api.listen_notification(legacy_receiver).await }; Builder::new_multi_thread() .enable_all() .build() .unwrap() - .block_on(future) + .block_on(future); + Ok(()) })); - threads.push(thread::spawn(move || external_api.run(db))); + threads.push(thread::spawn(move || -> Result<(), RikError> { + external_api.run(db) + })); for thread in threads { - thread.join().unwrap(); + if let Err(e) = thread + .join() + .expect("Couldn't join on the associated thread") + { + error!("An error occured {}", e) + } } } From f91d07e09aaadfbb1e75e4b474d0bf815b73bb0a Mon Sep 17 00:00:00 2001 From: Malo Polese Date: Wed, 10 May 2023 20:03:06 +0200 Subject: [PATCH 8/8] feat: add Into implementation for ContentType Signed-off-by: Malo Polese --- controller/src/api/external/routes/instance.rs | 6 +++--- controller/src/api/external/routes/mod.rs | 9 ++++++--- controller/src/api/external/routes/tenant.rs | 6 +++--- controller/src/api/external/routes/workload.rs | 8 ++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/controller/src/api/external/routes/instance.rs b/controller/src/api/external/routes/instance.rs index a3d742c8..ddd6b0c9 100644 --- a/controller/src/api/external/routes/instance.rs +++ b/controller/src/api/external/routes/instance.rs @@ -1,7 +1,6 @@ use definition::workload::WorkloadDefinition; use route_recognizer; use rusqlite::Connection; -use std::str::FromStr; use std::sync::mpsc::Sender; use tracing::{event, Level}; @@ -13,6 +12,7 @@ use crate::api::types::instance::InstanceDefinition; use crate::api::{ApiChannel, Crud}; use crate::core::instance::Instance; use crate::database::RikRepository; +use tiny_http::Header; use super::HttpResult; @@ -28,7 +28,7 @@ pub fn get( event!(Level::INFO, "instances.get, instances found"); Ok(tiny_http::Response::from_string(instances_json) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_header::
(ContentType::JSON.into()) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find instances") @@ -102,7 +102,7 @@ pub fn create( Ok( tiny_http::Response::from_string(serde_json::to_string(&instance_names)?) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_header::
(ContentType::JSON.into()) .with_status_code(tiny_http::StatusCode::from(201)), ) } diff --git a/controller/src/api/external/routes/mod.rs b/controller/src/api/external/routes/mod.rs index aef8c5e6..0a1c7eeb 100644 --- a/controller/src/api/external/routes/mod.rs +++ b/controller/src/api/external/routes/mod.rs @@ -1,6 +1,7 @@ use route_recognizer; use rusqlite::Connection; use std::io; +use std::str::FromStr; use std::sync::mpsc::Sender; use tiny_http::Method; use tiny_http::Response; @@ -25,10 +26,12 @@ pub enum ContentType { JSON, } -impl Into<&str> for ContentType { - fn into(self) -> &'static str { +impl Into for ContentType { + fn into(self) -> tiny_http::Header { match self { - ContentType::JSON => "Content-Type: application/json", + ContentType::JSON => { + tiny_http::Header::from_str("Content-Type: application/json").unwrap() + } } } } diff --git a/controller/src/api/external/routes/tenant.rs b/controller/src/api/external/routes/tenant.rs index 9a1e7341..183b0985 100644 --- a/controller/src/api/external/routes/tenant.rs +++ b/controller/src/api/external/routes/tenant.rs @@ -1,7 +1,7 @@ use route_recognizer; use rusqlite::Connection; -use std::str::FromStr; use std::sync::mpsc::Sender; +use tiny_http::Header; use tracing::{event, Level}; use super::HttpResult; @@ -23,7 +23,7 @@ pub fn get( let tenants_json = serde_json::to_string(&tenants)?; event!(Level::INFO, "tenants.get, tenants found"); Ok(tiny_http::Response::from_string(tenants_json) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_header::
(ContentType::JSON.into()) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find tenant") @@ -44,7 +44,7 @@ pub fn create( if RikRepository::insert(connection, &tenant.name, &tenant.value).is_ok() { event!(Level::INFO, "Create tenant"); Ok(tiny_http::Response::from_string(content) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_header::
(ContentType::JSON.into()) .with_status_code(tiny_http::StatusCode::from(200))) } else { event!(Level::ERROR, "Cannot create tenant"); diff --git a/controller/src/api/external/routes/workload.rs b/controller/src/api/external/routes/workload.rs index 59b516d1..aad47a40 100644 --- a/controller/src/api/external/routes/workload.rs +++ b/controller/src/api/external/routes/workload.rs @@ -9,8 +9,8 @@ use definition::workload::WorkloadDefinition; use route_recognizer; use rusqlite::Connection; use serde_json::json; -use std::str::FromStr; use std::sync::mpsc::Sender; +use tiny_http::Header; use tracing::{event, Level}; pub fn get( @@ -25,7 +25,7 @@ pub fn get( event!(Level::INFO, "workloads.get, workloads found"); Ok(tiny_http::Response::from_string(workloads_json) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_header::
(ContentType::JSON.into()) .with_status_code(tiny_http::StatusCode::from(200))) } else { Ok(tiny_http::Response::from_string("Cannot find workloads") @@ -62,7 +62,7 @@ pub fn get_instances( let instances_json = json!({ "instances": instances }).to_string(); return Ok(tiny_http::Response::from_string(instances_json) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_header::
(ContentType::JSON.into()) .with_status_code(tiny_http::StatusCode::from(200))); } @@ -108,7 +108,7 @@ pub fn create( ); Ok( tiny_http::Response::from_string(serde_json::to_string(&workload_id)?) - .with_header(tiny_http::Header::from_str(ContentType::JSON.into()).unwrap()) + .with_header::
(ContentType::JSON.into()) .with_status_code(tiny_http::StatusCode::from(200)), ) } else {