Refactor container definitions

This commit is contained in:
GW_MC
2025-12-22 14:32:57 +08:00
parent 7db23b01df
commit 6e85bda13f
12 changed files with 92 additions and 91 deletions

View File

@@ -1,7 +1,7 @@
use clap::{Arg, Command}; use clap::{Arg, Command};
use container::{ use container::containers::{
ConfigInfoType,
db::{DBInfo, sqlite::SQLiteContainer}, db::{DBInfo, sqlite::SQLiteContainer},
types::ConfigInfoType,
}; };
use migration::{generate_entity, migrate_database}; use migration::{generate_entity, migrate_database};
use shared::db_type::DBType; use shared::db_type::DBType;

View 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),
}

View File

@@ -5,10 +5,7 @@ use testcontainers::{
runners::{AsyncBuilder, AsyncRunner}, runners::{AsyncBuilder, AsyncRunner},
}; };
use crate::{ use crate::{WithContainer, containers::UnStartedContainer};
db::UnStartedContainer,
types::{ConfigInfoType, WithContainer},
};
pub const SOCK_NAME: &str = "yanpm-agent.sock"; pub const SOCK_NAME: &str = "yanpm-agent.sock";
const SOCK_FOLDER: &str = "/var/run/yanpm"; const SOCK_FOLDER: &str = "/var/run/yanpm";
@@ -25,8 +22,6 @@ pub struct AgentContainerConfig {
pub nginx_config: NginxConfig, pub nginx_config: NginxConfig,
} }
pub type AgentConfigInfoType = ConfigInfoType<AgentContainerInfo, ()>;
#[derive(Clone)] #[derive(Clone)]
pub struct AgentContainerInfo { pub struct AgentContainerInfo {
pub container: Arc<ContainerAsync<GenericImage>>, pub container: Arc<ContainerAsync<GenericImage>>,

View File

@@ -5,18 +5,15 @@ pub mod sqlite;
use async_trait::async_trait; use async_trait::async_trait;
use shared::db_type::DBType; use shared::db_type::DBType;
use std::error::Error; use std::error::Error;
use std::future::Future; use std::sync::Arc;
use std::{pin::Pin, sync::Arc};
use url::Host; use url::Host;
use testcontainers::{ContainerAsync, GenericImage, TestcontainersError}; use testcontainers::{ContainerAsync, GenericImage};
use crate::{ConfigInfoType, WithContainer, WithoutContainer}; use crate::{
WithContainer, WithoutContainer,
pub type UnStartedContainer = containers::{DBConfigInfoType, UnStartedContainer},
Pin<Box<dyn Future<Output = Result<ContainerAsync<GenericImage>, TestcontainersError>> + Send>>; };
pub type DBConfigInfoType = ConfigInfoType<ContainerizedDBInfo, PreExistingDBInfo>;
#[derive(Clone)] #[derive(Clone)]
pub struct PreExistingDBInfo { pub struct PreExistingDBInfo {

View File

@@ -9,9 +9,12 @@ use testcontainers::{
use crate::{ use crate::{
ConfigInfoType, ConfigInfoType,
db::{ containers::{
ContainerizedDBInfo, DBConfigInfoType, DBInfo, UnStartedContainer, UnStartedContainer,
config::{DatabaseContainerConfig, OptionalContainerConfig}, db::{
ContainerizedDBInfo, DBConfigInfoType, DBInfo,
config::{DatabaseContainerConfig, OptionalContainerConfig},
},
}, },
}; };
@@ -53,7 +56,7 @@ impl DBInfo<OptionalContainerConfig> for PostgreSQLContainer {
); );
ConfigInfoType::Containerized(ContainerizedDBInfo { ConfigInfoType::Containerized(ContainerizedDBInfo {
db_type: crate::db::DBType::PostgreSQL, db_type: crate::containers::db::DBType::PostgreSQL,
container: Arc::new(pg_container), container: Arc::new(pg_container),
container_name: self.config.container_name.clone(), container_name: self.config.container_name.clone(),
database_name: self.config.database_name.clone(), database_name: self.config.database_name.clone(),

View File

@@ -4,7 +4,7 @@ use async_trait::async_trait;
use crate::{ use crate::{
ConfigInfoType, ConfigInfoType,
db::{DBConfigInfoType, DBInfo, PreExistingDBInfo, UnStartedContainer}, containers::db::{DBConfigInfoType, DBInfo, PreExistingDBInfo, UnStartedContainer},
util::to_absolute_path, util::to_absolute_path,
}; };
@@ -69,7 +69,7 @@ impl DBInfo<OptionalContainerConfig> for SQLiteContainer {
.expect("Failed to create SQLite database file"); .expect("Failed to create SQLite database file");
// //
ConfigInfoType::PreExisting(PreExistingDBInfo { ConfigInfoType::PreExisting(PreExistingDBInfo {
db_type: crate::db::DBType::SQLite, db_type: crate::containers::db::DBType::SQLite,
url: sqlite_url, url: sqlite_url,
on_delete: { on_delete: {
let db_path = self.get_db_absolute_path(); let db_path = self.get_db_absolute_path();

View File

@@ -1,7 +1,5 @@
use std::io::Write; use std::io::Write;
use shared::db_type::DBType;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum EnvFileType { pub enum EnvFileType {
DotEnv, DotEnv,
@@ -11,25 +9,16 @@ pub enum EnvFileType {
#[derive(Clone)] #[derive(Clone)]
pub struct EnvFile { pub struct EnvFile {
pub file_type: EnvFileType, pub file_type: EnvFileType,
pub db_type: DBType,
pub db_url: String,
// //
buffer: serde_json::Value, buffer: serde_json::Value,
} }
impl EnvFile { impl EnvFile {
pub fn new(file_type: EnvFileType, db_type: DBType, db_url: String) -> Self { pub fn new(file_type: EnvFileType) -> Self {
let mut env_file = EnvFile { EnvFile {
file_type, file_type,
db_type,
db_url,
buffer: serde_json::Value::Object(serde_json::Map::new()), 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) { pub fn write_line(&mut self, key: &str, value: &str) {
@@ -131,12 +120,10 @@ mod tests {
#[test] #[test]
fn test_env_file_write_yaml() { fn test_env_file_write_yaml() {
let mut env_file_nested = EnvFile::new( let mut env_file_nested = EnvFile::new(EnvFileType::Yaml);
EnvFileType::Yaml,
DBType::SQLite,
"mysql://user:pass@localhost/db".to_string(),
);
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(); let mut output_stream = Vec::new();
env_file_nested.write(&mut output_stream, false); env_file_nested.write(&mut output_stream, false);
let output_string = String::from_utf8(output_stream).unwrap(); let output_string = String::from_utf8(output_stream).unwrap();
@@ -150,11 +137,9 @@ DATABASE:
#[test] #[test]
fn test_env_file_write_env() { fn test_env_file_write_env() {
let mut env_file_nested = EnvFile::new( let mut env_file_nested = EnvFile::new(EnvFileType::DotEnv);
EnvFileType::DotEnv, env_file_nested.write_line("DATABASE__TYPE", "PostgreSQL");
DBType::PostgreSQL, env_file_nested.write_line("DATABASE__URL", "postgres://user:pass@localhost/db");
"postgres://user:pass@localhost/db".to_string(),
);
let mut output_stream = Vec::new(); let mut output_stream = Vec::new();
env_file_nested.write(&mut output_stream, true); env_file_nested.write(&mut output_stream, true);
let output_string = String::from_utf8(output_stream).unwrap(); let output_string = String::from_utf8(output_stream).unwrap();

View File

@@ -1,13 +1,11 @@
pub mod agent; pub mod containers;
pub mod db;
mod env; mod env;
pub mod types;
mod util; mod util;
use crate::{ use crate::{
agent::AgentConfigInfoType, containers::{
db::DBConfigInfoType, AgentConfigInfoType, ConfigInfoType, DBConfigInfoType, WithContainer, WithoutContainer,
types::{ConfigInfoType, WithContainer, WithoutContainer}, },
util::{ util::{
await_termination_signal, remove_file_if_exists, stop_container, to_absolute_path, await_termination_signal, remove_file_if_exists, stop_container, to_absolute_path,
write_env_files, write_env_files,

View File

@@ -1,12 +1,15 @@
use std::sync::Arc; use std::sync::Arc;
use clap::Parser; use clap::Parser;
use container::agent::{AgentConfig, AgentContainerConfig, AgentContainerInfo, NginxConfig}; use container::{
use container::start_attached; Config,
use container::types::ConfigInfoType; containers::{
use container::{Config, agent}; ConfigInfoType,
agent::{AgentConfig, AgentContainerConfig, AgentContainerInfo, NginxConfig},
use container::db::DBInfo; db::DBInfo,
},
start_attached,
};
/// Command line arguments /// Command line arguments
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -65,7 +68,7 @@ async fn main() {
println!("Starting container with database type: {}", args.db_type); println!("Starting container with database type: {}", args.db_type);
let db_config = match args.db_type.to_lowercase().as_str() { let db_config = match args.db_type.to_lowercase().as_str() {
"postgres" | "pg" | "pgsql" => { "postgres" | "pg" | "pgsql" => {
use container::db::postgresql::PostgreSQLContainer; use container::containers::db::postgresql::PostgreSQLContainer;
println!("Using PostgreSQL database"); println!("Using PostgreSQL database");
PostgreSQLContainer::new(None) PostgreSQLContainer::new(None)
.await .await
@@ -74,7 +77,7 @@ async fn main() {
} }
"sqlite" | "sql" => { "sqlite" | "sql" => {
println!("Using SQLite database"); println!("Using SQLite database");
use container::db::sqlite::SQLiteContainer; use container::containers::db::sqlite::SQLiteContainer;
SQLiteContainer::new(None) SQLiteContainer::new(None)
.await .await
.get_db_container_config_info() .get_db_container_config_info()
@@ -89,7 +92,7 @@ async fn main() {
let agent_container = if let Some(agent_config) = &args.agent_container_config { let agent_container = if let Some(agent_config) = &args.agent_container_config {
println!( 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 agent_config.agent_config.sock_folder, agent_config.agent_config.nginx_config_dir
); );
Some(agent_config.get_unstarted_container().await) Some(agent_config.get_unstarted_container().await)
@@ -168,6 +171,7 @@ async fn parse_args() -> ParsedArgs {
ParsedArgs { ParsedArgs {
db_type: args.db_type, db_type: args.db_type,
agent_container_config: Some(AgentContainerConfig { agent_container_config: Some(AgentContainerConfig {
// TODO: allow customization of these fields via CLI args
image: "yanpm-agent".to_string(), image: "yanpm-agent".to_string(),
tag: "latest".to_string(), tag: "latest".to_string(),
container_name: format!("yanpm-agent-container-{}", time), container_name: format!("yanpm-agent-container-{}", time),

View File

@@ -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),
}

View File

@@ -4,10 +4,11 @@ use tokio::signal::unix::{SignalKind, signal};
use crate::{ use crate::{
API_CONFIG_PATH, DB_CONFIG_PATH, API_CONFIG_PATH, DB_CONFIG_PATH,
agent::{AgentConfigInfoType, AgentContainerInfo, SOCK_NAME}, containers::{
db::DBConfigInfoType, AgentConfigInfoType, ConfigInfoType, DBConfigInfoType, WithContainer, WithoutContainer,
agent::SOCK_NAME,
},
env::{self, EnvFile}, env::{self, EnvFile},
types::{ConfigInfoType, WithContainer, WithoutContainer},
}; };
// relative to the current working directory // 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()), 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(); let mut db_env = api_env.clone();
db_env.file_type = env::EnvFileType::DotEnv; db_env.file_type = env::EnvFileType::DotEnv;