feature/agent #11
@@ -1,7 +1,7 @@
|
||||
use clap::{Arg, Command};
|
||||
use container::{
|
||||
use container::containers::{
|
||||
ConfigInfoType,
|
||||
db::{DBInfo, sqlite::SQLiteContainer},
|
||||
types::ConfigInfoType,
|
||||
};
|
||||
use migration::{generate_entity, migrate_database};
|
||||
use shared::db_type::DBType;
|
||||
|
||||
40
apps/container/src/containers.rs
Normal file
40
apps/container/src/containers.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
pub mod agent;
|
||||
pub mod db;
|
||||
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
|
||||
use testcontainers::{ContainerAsync, GenericImage, TestcontainersError};
|
||||
|
||||
use crate::containers::{
|
||||
agent::AgentContainerInfo,
|
||||
db::{ContainerizedDBInfo, PreExistingDBInfo},
|
||||
};
|
||||
|
||||
pub type UnStartedContainer =
|
||||
Pin<Box<dyn Future<Output = Result<ContainerAsync<GenericImage>, TestcontainersError>> + Send>>;
|
||||
|
||||
pub type AgentConfigInfoType = ConfigInfoType<AgentContainerInfo, ()>;
|
||||
|
||||
pub type DBConfigInfoType = ConfigInfoType<ContainerizedDBInfo, PreExistingDBInfo>;
|
||||
|
||||
pub trait WithContainer {
|
||||
fn container(&self) -> &Arc<ContainerAsync<GenericImage>>;
|
||||
}
|
||||
|
||||
pub trait WithoutContainer {
|
||||
fn on_delete(&self);
|
||||
}
|
||||
|
||||
impl WithoutContainer for () {
|
||||
fn on_delete(&self) {}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ConfigInfoType<T, U>
|
||||
where
|
||||
T: WithContainer,
|
||||
U: WithoutContainer,
|
||||
{
|
||||
Containerized(T),
|
||||
PreExisting(U),
|
||||
}
|
||||
@@ -5,10 +5,7 @@ use testcontainers::{
|
||||
runners::{AsyncBuilder, AsyncRunner},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::UnStartedContainer,
|
||||
types::{ConfigInfoType, WithContainer},
|
||||
};
|
||||
use crate::{WithContainer, containers::UnStartedContainer};
|
||||
|
||||
pub const SOCK_NAME: &str = "yanpm-agent.sock";
|
||||
const SOCK_FOLDER: &str = "/var/run/yanpm";
|
||||
@@ -25,8 +22,6 @@ pub struct AgentContainerConfig {
|
||||
pub nginx_config: NginxConfig,
|
||||
}
|
||||
|
||||
pub type AgentConfigInfoType = ConfigInfoType<AgentContainerInfo, ()>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AgentContainerInfo {
|
||||
pub container: Arc<ContainerAsync<GenericImage>>,
|
||||
@@ -5,18 +5,15 @@ pub mod sqlite;
|
||||
use async_trait::async_trait;
|
||||
use shared::db_type::DBType;
|
||||
use std::error::Error;
|
||||
use std::future::Future;
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
use url::Host;
|
||||
|
||||
use testcontainers::{ContainerAsync, GenericImage, TestcontainersError};
|
||||
use testcontainers::{ContainerAsync, GenericImage};
|
||||
|
||||
use crate::{ConfigInfoType, WithContainer, WithoutContainer};
|
||||
|
||||
pub type UnStartedContainer =
|
||||
Pin<Box<dyn Future<Output = Result<ContainerAsync<GenericImage>, TestcontainersError>> + Send>>;
|
||||
|
||||
pub type DBConfigInfoType = ConfigInfoType<ContainerizedDBInfo, PreExistingDBInfo>;
|
||||
use crate::{
|
||||
WithContainer, WithoutContainer,
|
||||
containers::{DBConfigInfoType, UnStartedContainer},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PreExistingDBInfo {
|
||||
@@ -9,9 +9,12 @@ use testcontainers::{
|
||||
|
||||
use crate::{
|
||||
ConfigInfoType,
|
||||
db::{
|
||||
ContainerizedDBInfo, DBConfigInfoType, DBInfo, UnStartedContainer,
|
||||
config::{DatabaseContainerConfig, OptionalContainerConfig},
|
||||
containers::{
|
||||
UnStartedContainer,
|
||||
db::{
|
||||
ContainerizedDBInfo, DBConfigInfoType, DBInfo,
|
||||
config::{DatabaseContainerConfig, OptionalContainerConfig},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -53,7 +56,7 @@ impl DBInfo<OptionalContainerConfig> for PostgreSQLContainer {
|
||||
);
|
||||
|
||||
ConfigInfoType::Containerized(ContainerizedDBInfo {
|
||||
db_type: crate::db::DBType::PostgreSQL,
|
||||
db_type: crate::containers::db::DBType::PostgreSQL,
|
||||
container: Arc::new(pg_container),
|
||||
container_name: self.config.container_name.clone(),
|
||||
database_name: self.config.database_name.clone(),
|
||||
@@ -4,7 +4,7 @@ use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
ConfigInfoType,
|
||||
db::{DBConfigInfoType, DBInfo, PreExistingDBInfo, UnStartedContainer},
|
||||
containers::db::{DBConfigInfoType, DBInfo, PreExistingDBInfo, UnStartedContainer},
|
||||
util::to_absolute_path,
|
||||
};
|
||||
|
||||
@@ -69,7 +69,7 @@ impl DBInfo<OptionalContainerConfig> for SQLiteContainer {
|
||||
.expect("Failed to create SQLite database file");
|
||||
//
|
||||
ConfigInfoType::PreExisting(PreExistingDBInfo {
|
||||
db_type: crate::db::DBType::SQLite,
|
||||
db_type: crate::containers::db::DBType::SQLite,
|
||||
url: sqlite_url,
|
||||
on_delete: {
|
||||
let db_path = self.get_db_absolute_path();
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::io::Write;
|
||||
|
||||
use shared::db_type::DBType;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum EnvFileType {
|
||||
DotEnv,
|
||||
@@ -11,25 +9,16 @@ pub enum EnvFileType {
|
||||
#[derive(Clone)]
|
||||
pub struct EnvFile {
|
||||
pub file_type: EnvFileType,
|
||||
pub db_type: DBType,
|
||||
pub db_url: String,
|
||||
//
|
||||
buffer: serde_json::Value,
|
||||
}
|
||||
|
||||
impl EnvFile {
|
||||
pub fn new(file_type: EnvFileType, db_type: DBType, db_url: String) -> Self {
|
||||
let mut env_file = EnvFile {
|
||||
pub fn new(file_type: EnvFileType) -> Self {
|
||||
EnvFile {
|
||||
file_type,
|
||||
db_type,
|
||||
db_url,
|
||||
buffer: serde_json::Value::Object(serde_json::Map::new()),
|
||||
};
|
||||
|
||||
env_file._write_line_buffer("DATABASE__TYPE", &env_file.db_type.to_string());
|
||||
env_file._write_line_buffer("DATABASE__URL", &env_file.db_url.to_string());
|
||||
|
||||
env_file
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_line(&mut self, key: &str, value: &str) {
|
||||
@@ -131,12 +120,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_env_file_write_yaml() {
|
||||
let mut env_file_nested = EnvFile::new(
|
||||
EnvFileType::Yaml,
|
||||
DBType::SQLite,
|
||||
"mysql://user:pass@localhost/db".to_string(),
|
||||
);
|
||||
let mut env_file_nested = EnvFile::new(EnvFileType::Yaml);
|
||||
|
||||
env_file_nested.write_line("DATABASE__TYPE", "SQLite");
|
||||
env_file_nested.write_line("DATABASE__URL", "mysql://user:pass@localhost/db");
|
||||
let mut output_stream = Vec::new();
|
||||
env_file_nested.write(&mut output_stream, false);
|
||||
let output_string = String::from_utf8(output_stream).unwrap();
|
||||
@@ -150,11 +137,9 @@ DATABASE:
|
||||
|
||||
#[test]
|
||||
fn test_env_file_write_env() {
|
||||
let mut env_file_nested = EnvFile::new(
|
||||
EnvFileType::DotEnv,
|
||||
DBType::PostgreSQL,
|
||||
"postgres://user:pass@localhost/db".to_string(),
|
||||
);
|
||||
let mut env_file_nested = EnvFile::new(EnvFileType::DotEnv);
|
||||
env_file_nested.write_line("DATABASE__TYPE", "PostgreSQL");
|
||||
env_file_nested.write_line("DATABASE__URL", "postgres://user:pass@localhost/db");
|
||||
let mut output_stream = Vec::new();
|
||||
env_file_nested.write(&mut output_stream, true);
|
||||
let output_string = String::from_utf8(output_stream).unwrap();
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
pub mod agent;
|
||||
pub mod db;
|
||||
pub mod containers;
|
||||
mod env;
|
||||
pub mod types;
|
||||
mod util;
|
||||
|
||||
use crate::{
|
||||
agent::AgentConfigInfoType,
|
||||
db::DBConfigInfoType,
|
||||
types::{ConfigInfoType, WithContainer, WithoutContainer},
|
||||
containers::{
|
||||
AgentConfigInfoType, ConfigInfoType, DBConfigInfoType, WithContainer, WithoutContainer,
|
||||
},
|
||||
util::{
|
||||
await_termination_signal, remove_file_if_exists, stop_container, to_absolute_path,
|
||||
write_env_files,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use container::agent::{AgentConfig, AgentContainerConfig, AgentContainerInfo, NginxConfig};
|
||||
use container::start_attached;
|
||||
use container::types::ConfigInfoType;
|
||||
use container::{Config, agent};
|
||||
|
||||
use container::db::DBInfo;
|
||||
use container::{
|
||||
Config,
|
||||
containers::{
|
||||
ConfigInfoType,
|
||||
agent::{AgentConfig, AgentContainerConfig, AgentContainerInfo, NginxConfig},
|
||||
db::DBInfo,
|
||||
},
|
||||
start_attached,
|
||||
};
|
||||
|
||||
/// Command line arguments
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -65,7 +68,7 @@ async fn main() {
|
||||
println!("Starting container with database type: {}", args.db_type);
|
||||
let db_config = match args.db_type.to_lowercase().as_str() {
|
||||
"postgres" | "pg" | "pgsql" => {
|
||||
use container::db::postgresql::PostgreSQLContainer;
|
||||
use container::containers::db::postgresql::PostgreSQLContainer;
|
||||
println!("Using PostgreSQL database");
|
||||
PostgreSQLContainer::new(None)
|
||||
.await
|
||||
@@ -74,7 +77,7 @@ async fn main() {
|
||||
}
|
||||
"sqlite" | "sql" => {
|
||||
println!("Using SQLite database");
|
||||
use container::db::sqlite::SQLiteContainer;
|
||||
use container::containers::db::sqlite::SQLiteContainer;
|
||||
SQLiteContainer::new(None)
|
||||
.await
|
||||
.get_db_container_config_info()
|
||||
@@ -89,7 +92,7 @@ async fn main() {
|
||||
|
||||
let agent_container = if let Some(agent_config) = &args.agent_container_config {
|
||||
println!(
|
||||
"Agent container will be used with socket path: {} and nginx config dir: {}",
|
||||
"Agent container will be used with socket folder: {} and nginx config dir: {}",
|
||||
agent_config.agent_config.sock_folder, agent_config.agent_config.nginx_config_dir
|
||||
);
|
||||
Some(agent_config.get_unstarted_container().await)
|
||||
@@ -168,6 +171,7 @@ async fn parse_args() -> ParsedArgs {
|
||||
ParsedArgs {
|
||||
db_type: args.db_type,
|
||||
agent_container_config: Some(AgentContainerConfig {
|
||||
// TODO: allow customization of these fields via CLI args
|
||||
image: "yanpm-agent".to_string(),
|
||||
tag: "latest".to_string(),
|
||||
container_name: format!("yanpm-agent-container-{}", time),
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use testcontainers::{ContainerAsync, GenericImage};
|
||||
|
||||
pub trait WithContainer {
|
||||
fn container(&self) -> &Arc<ContainerAsync<GenericImage>>;
|
||||
}
|
||||
|
||||
pub trait WithoutContainer {
|
||||
fn on_delete(&self);
|
||||
}
|
||||
|
||||
impl WithoutContainer for () {
|
||||
fn on_delete(&self) {}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ConfigInfoType<T, U>
|
||||
where
|
||||
T: WithContainer,
|
||||
U: WithoutContainer,
|
||||
{
|
||||
Containerized(T),
|
||||
PreExisting(U),
|
||||
}
|
||||
@@ -4,10 +4,11 @@ use tokio::signal::unix::{SignalKind, signal};
|
||||
|
||||
use crate::{
|
||||
API_CONFIG_PATH, DB_CONFIG_PATH,
|
||||
agent::{AgentConfigInfoType, AgentContainerInfo, SOCK_NAME},
|
||||
db::DBConfigInfoType,
|
||||
containers::{
|
||||
AgentConfigInfoType, ConfigInfoType, DBConfigInfoType, WithContainer, WithoutContainer,
|
||||
agent::SOCK_NAME,
|
||||
},
|
||||
env::{self, EnvFile},
|
||||
types::{ConfigInfoType, WithContainer, WithoutContainer},
|
||||
};
|
||||
|
||||
// relative to the current working directory
|
||||
@@ -30,7 +31,10 @@ pub fn write_env_files(db_config: &DBConfigInfoType, agent_config: &Option<Agent
|
||||
DBConfigInfoType::PreExisting(config) => (config.db_type.clone(), config.url.clone()),
|
||||
};
|
||||
|
||||
let mut api_env = EnvFile::new(env::EnvFileType::Yaml, db_type, db_url);
|
||||
let mut api_env = EnvFile::new(env::EnvFileType::Yaml);
|
||||
api_env.write_line("DATABASE__TYPE", db_type.to_string().as_str());
|
||||
api_env.write_line("DATABASE__URL", db_url.as_str());
|
||||
|
||||
let mut db_env = api_env.clone();
|
||||
db_env.file_type = env::EnvFileType::DotEnv;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user