Skip to content

Commit

Permalink
[FEAT] MySQL DB repository support (#78)
Browse files Browse the repository at this point in the history
* TASK: [gw] Extract ORM repository to be used by repos as a delegate

* FEAT: [gw] Add MySQL support (using diesel ORM)

* TASK: [doc] Update gw invocation doc for new mysql config

* TASK: [example] Add support for using MySQL datasource
  • Loading branch information
chewyfish authored Mar 1, 2024
1 parent 2e806b3 commit 9e54631
Show file tree
Hide file tree
Showing 42 changed files with 5,053 additions and 3,273 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ This is early alpha, use with care.
* Consider gateway-to-gateway service proxy routing (reasons of proximity, security, ...)
* Consider gateway load-balancing, via client redirect (reasons of load, rollout deployment, ...)
* Accommodate integration to well-known identity provider (IdP) systems/protocols for user authentication and 2FA purposes
* Supply more repository DB provider implementations
* Create (K8S, Docker, ...) container resource files

### Change History
Expand Down
2 changes: 2 additions & 0 deletions crates/gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ publish = false

[dependencies]
anyhow = "1.0.76"
chrono = { version = "0.4.34", default-features = false, features = ["clock", "std"] }
clap = { version = "4.4.11", features = [ "derive", "env" ] }
ctrlc = "3.4.2"
diesel = { version = "2.1.4", optional = true }
Expand Down Expand Up @@ -48,3 +49,4 @@ tokio-test = "0.4.3"

[features]
postgres_db = ["diesel/postgres"]
mysql_db = ["diesel/mysql", "diesel/chrono"]
4 changes: 3 additions & 1 deletion crates/gateway/diesel.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# see https://diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/repository/postgres_db/db_schema.rs"
file = "migrations/db_schema_postgres.rs"
#file = "migrations/db_schema_mysql.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]

[migrations_directory]
dir = "migrations/postgres"
#dir = "migrations/mysql"
74 changes: 74 additions & 0 deletions crates/gateway/migrations/db_schema_mysql.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// @generated automatically by Diesel CLI.

diesel::table! {
roles (id) {
id -> Bigint,
#[max_length = 50]
name -> Varchar,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
service_accesses (entity_type, entity_id, service_id) {
#[max_length = 20]
entity_type -> Varchar,
entity_id -> Bigint,
service_id -> Bigint,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
services (id) {
id -> Bigint,
#[max_length = 50]
name -> Varchar,
#[max_length = 20]
transport -> Varchar,
#[max_length = 255]
host -> Varchar,
port -> Integer,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
user_roles (user_id, role_id) {
user_id -> Bigint,
role_id -> Bigint,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
users (id) {
id -> Bigint,
#[max_length = 50]
name -> Varchar,
#[max_length = 20]
status -> Varchar,
#[max_length = 50]
user_name -> Nullable<Varchar>,
#[max_length = 255]
password -> Nullable<Varchar>,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::joinable!(service_accesses -> services (service_id));
diesel::joinable!(user_roles -> roles (role_id));
diesel::joinable!(user_roles -> users (user_id));

diesel::allow_tables_to_appear_in_same_query!(
roles,
service_accesses,
services,
user_roles,
users,
);
74 changes: 74 additions & 0 deletions crates/gateway/migrations/db_schema_postgres.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// @generated automatically by Diesel CLI.

diesel::table! {
roles (id) {
id -> Int8,
#[max_length = 50]
name -> Varchar,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
service_accesses (entity_type, entity_id, service_id) {
#[max_length = 20]
entity_type -> Varchar,
entity_id -> Int8,
service_id -> Int8,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
services (id) {
id -> Int8,
#[max_length = 50]
name -> Varchar,
#[max_length = 20]
transport -> Varchar,
#[max_length = 255]
host -> Varchar,
port -> Int4,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
user_roles (user_id, role_id) {
user_id -> Int8,
role_id -> Int8,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::table! {
users (id) {
id -> Int8,
#[max_length = 50]
name -> Varchar,
#[max_length = 20]
status -> Varchar,
#[max_length = 50]
user_name -> Nullable<Varchar>,
#[max_length = 255]
password -> Nullable<Varchar>,
created_at -> Nullable<Timestamp>,
updated_at -> Nullable<Timestamp>,
}
}

diesel::joinable!(service_accesses -> services (service_id));
diesel::joinable!(user_roles -> roles (role_id));
diesel::joinable!(user_roles -> users (user_id));

diesel::allow_tables_to_appear_in_same_query!(
roles,
service_accesses,
services,
user_roles,
users,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE roles;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE roles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE users;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL CHECK (status IN ('Active', 'Inactive')),
user_name VARCHAR(50) NULL,
password VARCHAR(255) NULL,
created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE user_roles;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TABLE user_roles (
user_id BIGINT NOT NULL REFERENCES users(id),
role_id BIGINT NOT NULL REFERENCES roles(id),
PRIMARY KEY (user_id, role_id),
created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE services;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE services (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
transport VARCHAR(20) NOT NULL CHECK (transport IN ('TCP', 'UDP')),
host VARCHAR(255) NOT NULL,
port INTEGER NOT NULL,
created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE service_accesses;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE service_accesses (
entity_type VARCHAR(20) NOT NULL CHECK (entity_type IN ('Role', 'User')),
entity_id BIGINT NOT NULL,
service_id BIGINT NOT NULL,
created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (entity_type, entity_id, service_id),
FOREIGN KEY(service_id)
REFERENCES services(id)
ON DELETE CASCADE
);
41 changes: 40 additions & 1 deletion crates/gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ use crate::repository::in_memory_db::access_repo::InMemAccessRepo;
use crate::repository::in_memory_db::role_repo::InMemRoleRepo;
use crate::repository::in_memory_db::service_repo::InMemServiceRepo;
use crate::repository::in_memory_db::user_repo::InMemUserRepo;
#[cfg(feature = "mysql_db")]
use crate::repository::mysql_db::access_repo::MysqlServiceAccessRepo;
#[cfg(feature = "mysql_db")]
use crate::repository::mysql_db::role_repo::MysqlRoleRepo;
#[cfg(feature = "mysql_db")]
use crate::repository::mysql_db::service_repo::MysqlServiceRepo;
#[cfg(feature = "mysql_db")]
use crate::repository::mysql_db::user_repo::MysqlUserRepo;
#[cfg(feature = "postgres_db")]
use crate::repository::postgres_db::access_repo::PostgresServiceAccessRepo;
#[cfg(feature = "postgres_db")]
Expand Down Expand Up @@ -119,6 +127,10 @@ pub enum DataSource {
/// In-memory DB, with a simple backing persistence store. Entity store connect string is file path to directory holding JSON record files.
InMemoryDb,

/// MySQL DB
#[cfg(feature = "mysql_db")]
MysqlDb,

/// Postgres DB
#[cfg(feature = "postgres_db")]
PostgresDb,
Expand All @@ -140,6 +152,13 @@ impl DataSource {
Box<dyn Fn() -> Arc<Mutex<dyn UserRepository>>>,
) {
match self {
#[cfg(feature = "mysql_db")]
DataSource::MysqlDb => (
Box::new(|| Arc::new(Mutex::new(MysqlServiceAccessRepo::new()))),
Box::new(|| Arc::new(Mutex::new(MysqlServiceRepo::new()))),
Box::new(|| Arc::new(Mutex::new(MysqlRoleRepo::new()))),
Box::new(|| Arc::new(Mutex::new(MysqlUserRepo::new()))),
),
#[cfg(feature = "postgres_db")]
DataSource::PostgresDb => (
Box::new(|| Arc::new(Mutex::new(PostgresServiceAccessRepo::new()))),
Expand Down Expand Up @@ -293,7 +312,8 @@ pub struct AppConfigArgs {

/// DB entity store connect specifier string. Specification format is dependent on <DATASOURCE> type.
/// For 'in-memory-db' datasource: Directory holding JSON files named 'trust0-db-access.json', 'trust0-db-role.json', 'trust0-db-service.json', 'trust0-db-user.json'
/// For 'postgres-db' datasource: Standard Postgres connect string specification (https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)
/// For 'mysql-db' datasource: Connection URL detailed in diesel documentation - https://docs.rs/diesel/2.1.4/diesel/mysql/struct.MysqlConnection.html
/// For 'postgres-db' datasource: Standard Postgres connect string specification - https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
#[arg(required = false, long = "db-connect", env, verbatim_doc_comment)]
pub db_connect: Option<String>,

Expand Down Expand Up @@ -567,6 +587,25 @@ impl AppConfig {
db_dir.join(INMEMDB_USER_FILENAME).to_str().unwrap(),
)?;
}
#[cfg(feature = "mysql_db")]
DataSource::MysqlDb => {
access_repository
.lock()
.unwrap()
.connect_to_datasource(db_connect_str)?;
role_repository
.lock()
.unwrap()
.connect_to_datasource(db_connect_str)?;
service_repository
.lock()
.unwrap()
.connect_to_datasource(db_connect_str)?;
user_repository
.lock()
.unwrap()
.connect_to_datasource(db_connect_str)?;
}
#[cfg(feature = "postgres_db")]
DataSource::PostgresDb => {
access_repository
Expand Down
5 changes: 5 additions & 0 deletions crates/gateway/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#[cfg(all(feature = "mysql_db", feature = "postgres_db"))]
compile_error!(
"feature \"mysql_db\" and feature \"postgres_db\" cannot be enabled at the same time"
);

use std::time::Duration;
use std::{process, thread};

Expand Down
Loading

0 comments on commit 9e54631

Please sign in to comment.