From 208b7f45986a82f02f83ebc8c0c1805e477ca323 Mon Sep 17 00:00:00 2001 From: Guillermo Roche Date: Thu, 25 Dec 2025 09:53:46 +0000 Subject: [PATCH] improve proxy functionality and configuration --- config/config_template.toml | 9 ++- src/api/routes.rs | 15 ++++- src/config/docker.rs | 67 +++++++++++++++++++ src/config/mod.rs | 106 ++---------------------------- src/config/mrproxy.rs | 44 +++++++++++++ src/config/server.rs | 24 +++++++ src/config/utils.rs | 8 +++ src/controller/mod.rs | 107 +++++++++++++++++++++---------- src/database/exposer.rs | 2 +- src/deploy/container.rs | 5 +- src/deploy/container_options.rs | 12 ++-- src/deploy/starter.rs | 49 +++++++++++--- src/mcproxy_client/controller.rs | 6 +- 13 files changed, 293 insertions(+), 161 deletions(-) create mode 100644 src/config/docker.rs create mode 100644 src/config/mrproxy.rs create mode 100644 src/config/server.rs create mode 100644 src/config/utils.rs diff --git a/config/config_template.toml b/config/config_template.toml index 471b5c9..9fbf7a1 100644 --- a/config/config_template.toml +++ b/config/config_template.toml @@ -1,7 +1,12 @@ +[server] +addr = "127.0.0.1" +port = 8080 [docker] -connection = "http" +connection = "unix_default" string = "http://my-custom-docker-server:2735" - +timeout = 60 +network_name = "customnetwork" +network_addr = "172.20.0.0/24" [mrproxy] connection = "tcp" string = "127.0.0.1:25564" diff --git a/src/api/routes.rs b/src/api/routes.rs index fa4bbad..5eff52f 100644 --- a/src/api/routes.rs +++ b/src/api/routes.rs @@ -1,7 +1,11 @@ -use crate::api::error::ReturnedError; +use crate::config::{ + docker::DockerConnectionConfig, mrproxy::MrproxyConnectionConfig, server::ServerConfig, + CONFIG_PATH, +}; use crate::controller::Controller; use crate::deploy::container_options::Options; use crate::deploy::starter; +use crate::{api::error::ReturnedError, config}; use actix_web::{delete, get, patch, put, web, App, HttpServer, Responder}; use serde::Deserialize; @@ -73,7 +77,12 @@ async fn get_ip( } pub async fn start() -> std::io::Result<()> { - let controller = starter::start_docker().await; + let controller = starter::start_docker( + DockerConnectionConfig::get_config(CONFIG_PATH).unwrap(), + MrproxyConnectionConfig::get_config(config::CONFIG_PATH).unwrap(), + ) + .await; + let server_config = ServerConfig::get_config(CONFIG_PATH).unwrap(); let data = web::Data::new(controller); HttpServer::new(move || { App::new() @@ -84,7 +93,7 @@ pub async fn start() -> std::io::Result<()> { .service(delete_container) .service(get_ip) }) - .bind(("127.0.0.1", 8080))? + .bind((server_config.addr, server_config.port))? .run() .await } diff --git a/src/config/docker.rs b/src/config/docker.rs new file mode 100644 index 0000000..4eb1bf7 --- /dev/null +++ b/src/config/docker.rs @@ -0,0 +1,67 @@ +use crate::config::utils::generate_toml_parser_error_in_field; +use std::{error::Error, fs::read_to_string}; +use toml::Table; + +pub enum DockerConnectionKind { + Http, + HttpDefault, + Unix, + UnixDefault, +} + +pub struct DockerConnectionConfig { + pub connection_kind: DockerConnectionKind, + pub connection_string: Option, + pub network_name: String, + pub network_addr: String, +} + +impl DockerConnectionConfig { + pub fn get_config(file_name: &str) -> Result> { + let stored_file = read_to_string(file_name)?.parse::()?; + let connection_kind_string = stored_file["docker"]["connection"].as_str().ok_or( + generate_toml_parser_error_in_field("docker connection kind"), + )?; + + let connection_kind = match connection_kind_string { + "http" => DockerConnectionKind::Http, + "http_default" => DockerConnectionKind::HttpDefault, + "unix" => DockerConnectionKind::Unix, + "unix_default" => DockerConnectionKind::UnixDefault, + _ => { + return Err(Box::new(generate_toml_parser_error_in_field( + "docker connection kind", + ))); + } + }; + + let connection_string = match connection_kind { + DockerConnectionKind::Http | DockerConnectionKind::Unix => Some( + stored_file["docker"]["string"] + .as_str() + .ok_or(generate_toml_parser_error_in_field( + "docker connection string", + ))? + .to_string(), + ), + _ => None, + }; + + let network_name = stored_file["docker"]["network_name"] + .as_str() + .ok_or(generate_toml_parser_error_in_field("docker network name"))? + .to_string(); + + let network_addr = stored_file["docker"]["network_addr"] + .as_str() + .ok_or(generate_toml_parser_error_in_field("docker network addr"))? + .to_string(); + + Ok(Self { + connection_kind, + connection_string, + network_name, + network_addr, + }) + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 666fb83..99fbf15 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,102 +1,6 @@ -use std::{error::Error, fs::read_to_string, io}; -use toml::Table; +pub mod docker; +pub mod mrproxy; +pub mod server; +pub(in crate::config) mod utils; -pub const CONFIG_PATH: &str = "config/config.tom"; - -enum DockerConnectionKind { - HTTP, - HTTP_DEFAULT, - UNIX, - UNIX_DEFAULT, -} - -pub enum MrproxyConnectionKind { - TCP, - UNIX, -} - -pub struct DockerConnectionConfig { - connection_kind: DockerConnectionKind, - connection_string: Option, -} - -pub struct MrproxyConnectionData { - pub connection_kind: MrproxyConnectionKind, - pub connection_string: String, -} - -impl DockerConnectionConfig { - pub fn get_config(file_name: &str) -> Result> { - let stored_file = read_to_string(file_name)?.parse::
()?; - let connection_kind_string = stored_file["docker"]["connection"].as_str().ok_or( - generate_toml_parser_error_in_field("docker connection kind"), - )?; - - let connection_kind = match connection_kind_string { - "http" => DockerConnectionKind::HTTP, - "http_default" => DockerConnectionKind::HTTP_DEFAULT, - "unix" => DockerConnectionKind::UNIX, - "unix_default" => DockerConnectionKind::UNIX_DEFAULT, - _ => { - return Err(Box::new(generate_toml_parser_error_in_field( - "docker connection kind", - ))); - } - }; - - let connection_string = match connection_kind { - DockerConnectionKind::HTTP | DockerConnectionKind::UNIX => Some( - stored_file["docker"]["string"] - .as_str() - .ok_or(generate_toml_parser_error_in_field( - "docker connection string", - ))? - .to_string(), - ), - _ => None, - }; - - Ok(Self { - connection_kind, - connection_string, - }) - } -} - -impl MrproxyConnectionData { - pub fn get_config(file_name: &str) -> Result> { - let stored_file = read_to_string(file_name)?.parse::
()?; - let connection_kind_string = stored_file["mrproxy"]["connection"].as_str().ok_or( - generate_toml_parser_error_in_field("mrproxy connection kind"), - )?; - - let connection_kind = match connection_kind_string { - "tcp" => MrproxyConnectionKind::TCP, - "unix" => MrproxyConnectionKind::UNIX, - _ => { - return Err(Box::new(generate_toml_parser_error_in_field( - "mrproxy connection kind", - ))); - } - }; - - let connection_string = stored_file["mrproxy"]["string"] - .as_str() - .ok_or(generate_toml_parser_error_in_field( - "mrproxy connection string", - ))? - .to_string(); - - Ok(Self { - connection_kind, - connection_string, - }) - } -} - -fn generate_toml_parser_error_in_field(field: &str) -> io::Error { - io::Error::new( - io::ErrorKind::InvalidData, - format!("Invalid format for config.toml in {} field", field), - ) -} +pub const CONFIG_PATH: &str = "config/config.toml"; diff --git a/src/config/mrproxy.rs b/src/config/mrproxy.rs new file mode 100644 index 0000000..a8dd8d6 --- /dev/null +++ b/src/config/mrproxy.rs @@ -0,0 +1,44 @@ +use crate::config::utils::generate_toml_parser_error_in_field; +use std::{error::Error, fs::read_to_string}; +use toml::Table; + +pub enum MrproxyConnectionKind { + TCP, + UNIX, +} + +pub struct MrproxyConnectionConfig { + pub connection_kind: MrproxyConnectionKind, + pub connection_string: String, +} + +impl MrproxyConnectionConfig { + pub fn get_config(file_name: &str) -> Result> { + let stored_file = read_to_string(file_name)?.parse::
()?; + let connection_kind_string = stored_file["mrproxy"]["connection"].as_str().ok_or( + generate_toml_parser_error_in_field("mrproxy connection kind"), + )?; + + let connection_kind = match connection_kind_string { + "tcp" => MrproxyConnectionKind::TCP, + "unix" => MrproxyConnectionKind::UNIX, + _ => { + return Err(Box::new(generate_toml_parser_error_in_field( + "mrproxy connection kind", + ))); + } + }; + + let connection_string = stored_file["mrproxy"]["string"] + .as_str() + .ok_or(generate_toml_parser_error_in_field( + "mrproxy connection string", + ))? + .to_string(); + + Ok(Self { + connection_kind, + connection_string, + }) + } +} diff --git a/src/config/server.rs b/src/config/server.rs new file mode 100644 index 0000000..5a66538 --- /dev/null +++ b/src/config/server.rs @@ -0,0 +1,24 @@ +use crate::config::utils::generate_toml_parser_error_in_field; +use std::{error::Error, fs::read_to_string}; +use toml::Table; + +pub struct ServerConfig { + pub addr: String, + pub port: u16, +} + +impl ServerConfig { + pub fn get_config(file_name: &str) -> Result> { + let stored_file = read_to_string(file_name)?.parse::
()?; + let addr = stored_file["server"]["addr"] + .as_str() + .ok_or(generate_toml_parser_error_in_field("server addr"))? + .to_string(); + + let port = stored_file["server"]["port"] + .as_integer() + .ok_or(generate_toml_parser_error_in_field("server port"))? as u16; + + Ok(Self { addr, port }) + } +} diff --git a/src/config/utils.rs b/src/config/utils.rs new file mode 100644 index 0000000..b9ad228 --- /dev/null +++ b/src/config/utils.rs @@ -0,0 +1,8 @@ +use std::io; + +pub fn generate_toml_parser_error_in_field(field: &str) -> io::Error { + io::Error::new( + io::ErrorKind::InvalidData, + format!("Invalid format for config.toml in {} field", field), + ) +} diff --git a/src/controller/mod.rs b/src/controller/mod.rs index 9316e9a..6f196b3 100644 --- a/src/controller/mod.rs +++ b/src/controller/mod.rs @@ -1,14 +1,13 @@ -use crate::config::{DockerConnectionConfig, MrproxyConnectionData}; +use crate::config::{docker::DockerConnectionConfig, mrproxy::MrproxyConnectionConfig}; use crate::database::exposer::MemStorage; use crate::database::instance::Instance; use crate::deploy::container::Container; use crate::deploy::container_options::Options; -use crate::mcproxy_client::client; use crate::{deploy, mcproxy_client}; use bollard::errors::Error; use bollard::Docker; use log::error; -use std::os::unix::net::UnixStream; +use std::error::Error as GenericError; use std::sync::Mutex; pub struct Controller { @@ -17,7 +16,7 @@ pub struct Controller { storage: Mutex, started: bool, docker_config: DockerConnectionConfig, - mrproxy_config: MrproxyConnectionData, + mrproxy_config: MrproxyConnectionConfig, } impl Controller { @@ -26,7 +25,7 @@ impl Controller { network: String, range: String, docker_config: DockerConnectionConfig, - mrproxy_config: MrproxyConnectionData, + mrproxy_config: MrproxyConnectionConfig, ) -> Result { deploy::network::Network::new(driver.clone(), network.clone(), range).await?; let cont = Self { @@ -49,31 +48,16 @@ impl Controller { ) -> String { let is_stored = self.storage.lock().unwrap().search_instance(domain.clone()); match is_stored { - Some(c) => { - let mut mrcp_controller = - mcproxy_client::controller::Controller::new(&self.mrproxy_config).unwrap(); - _ = mrcp_controller.insert_new_domain( - &domain, - &ip.unwrap_or(c.get_ip(&self.driver).await.unwrap()), - ); - match c.docker_id { - Some(id) => id, - None => "Container without docker_id".to_string(), - } - } + Some(c) => match c.docker_id { + Some(id) => id, + None => "Container without docker_id".to_string(), + }, None => { match self - .load_container(None, domain.clone(), ip.clone(), image.clone(), ops) + .load_container_and_bind(None, domain.clone(), ip.clone(), image.clone(), ops) .await { Ok(c) => { - let mut mrcp_controller = - mcproxy_client::controller::Controller::new(&self.mrproxy_config) - .unwrap(); - _ = mrcp_controller.insert_new_domain( - &domain, - &ip.unwrap_or(c.get_ip(&self.driver).await.unwrap()), - ); self.storage .try_lock() .unwrap() @@ -87,6 +71,57 @@ impl Controller { } } + async fn load_container_and_bind( + &self, + docker_id: Option, + domain: String, + ip: Option, + image: String, + ops: Options, + ) -> Result> { + match self + .load_container(docker_id, domain.clone(), ip.clone(), image, ops) + .await + { + Ok(c) => { + match self + .bind_container_in_proxy(domain.clone(), ip, c.clone()) + .await + { + Ok(_) => Ok(c), + Err(e) => { + error!("failed in the bind process with the proxy, deleting the container"); + self.delete_container(domain).await?; + return Err(e); + } + } + } + Err(e) => return Err(Box::new(e)), + } + } + + async fn bind_container_in_proxy( + &self, + domain: String, + ip: Option, + container: Container, + ) -> Result<(), Box> { + let mut mrcp_controller = + mcproxy_client::controller::Controller::new(&self.mrproxy_config)?; + mrcp_controller.insert_new_domain( + &domain, + &ip.unwrap_or(container.get_ip(&self.driver).await?), + )?; + Ok(()) + } + + async fn unbind_container_in_proxy(&self, domain: &str) -> Result<(), Box> { + let mut mrcp_controller = + mcproxy_client::controller::Controller::new(&self.mrproxy_config)?; + mrcp_controller.remove_domain(&domain)?; + Ok(()) + } + pub async fn start_container_from_instance(&self, instance: Instance) { let image = match self .storage @@ -101,7 +136,7 @@ impl Controller { } }; match self - .load_container( + .load_container_and_bind( Some(instance.docker_id.clone()), instance.domain.clone(), instance.ip.clone(), @@ -163,10 +198,13 @@ impl Controller { } } - async fn prune_given_container(&self, container: Container) -> Result { + async fn prune_given_container( + &self, + container: Container, + ) -> Result> { match container.remove(&self.driver).await { Ok(_i) => Ok(container.get_id()), - Err(e) => Err(e), + Err(e) => Err(Box::new(e)), } } @@ -180,16 +218,19 @@ impl Controller { } } - pub async fn delete_container(&self, domain: String) -> Result { - match self.storage.lock().unwrap().remove_instance(domain) { + pub async fn delete_container(&self, domain: String) -> Result> { + match self.storage.lock().unwrap().remove_instance(&domain) { Some(data) => match data.1 { - Some(c) => self.prune_given_container(c).await, + Some(c) => { + self.unbind_container_in_proxy(&domain).await?; + self.prune_given_container(c).await + } None => Ok(data.0.docker_id), }, - None => Err(Error::DockerResponseServerError { + None => Err(Box::new(Error::DockerResponseServerError { status_code: 404, message: "container not found".to_string(), - }), + })), } } diff --git a/src/database/exposer.rs b/src/database/exposer.rs index 317c394..ca797d3 100644 --- a/src/database/exposer.rs +++ b/src/database/exposer.rs @@ -71,7 +71,7 @@ impl MemStorage { None } - pub fn remove_instance(&mut self, name: String) -> Option<(Instance, Option)> { + pub fn remove_instance(&mut self, name: &str) -> Option<(Instance, Option)> { for i in 0..self.containers.len() { match self.containers.get(i) { Some(c) => { diff --git a/src/deploy/container.rs b/src/deploy/container.rs index 5bf85cb..e22da06 100644 --- a/src/deploy/container.rs +++ b/src/deploy/container.rs @@ -1,11 +1,10 @@ use crate::deploy::bind; use crate::deploy::container_options::Options; -//use bollard::container::InspectContainerOptions; use bollard::errors::{Error, Error::IOError}; use bollard::models::{EndpointIpamConfig, EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::{ - CreateContainerOptions, InspectContainerOptions, InspectContainerOptionsBuilder, - ListContainersOptions, RemoveContainerOptions, StartContainerOptions, StopContainerOptions, + CreateContainerOptions, InspectContainerOptionsBuilder, ListContainersOptions, + RemoveContainerOptions, StartContainerOptions, StopContainerOptions, }; use bollard::secret::ContainerCreateBody; use std::collections::hash_map::HashMap; diff --git a/src/deploy/container_options.rs b/src/deploy/container_options.rs index 813a7c6..eda51ad 100644 --- a/src/deploy/container_options.rs +++ b/src/deploy/container_options.rs @@ -1,11 +1,10 @@ use serde::{Deserialize, Serialize}; -use serde_json::Result; #[derive(Clone, Serialize, Deserialize)] pub struct Options { memory: Option, rolling_logs: bool, - msg: Option + msg: Option, } impl Options { @@ -13,7 +12,7 @@ impl Options { Self { memory: memory, rolling_logs: true, - msg: msg + msg: msg, } } @@ -21,7 +20,7 @@ impl Options { match serde_json::from_str(&json) { Ok(r) => r, Err(e) => { - log::warn!("Error deserialicing:{}",e); + log::warn!("Error deserialicing:{}", e); log::warn!("insert default values"); Self { memory: None, @@ -47,7 +46,7 @@ impl Options { Some(mut val) => { val.insert_str(0, variable); data.push(val); - }, + } None => (), } } @@ -56,10 +55,9 @@ impl Options { match serde_json::to_string(self) { Ok(j) => j, Err(e) => { - log::warn!("Error serialicing:{}",e); + log::warn!("Error serialicing:{}", e); String::from("") } } } } - diff --git a/src/deploy/starter.rs b/src/deploy/starter.rs index 49016f9..3545946 100644 --- a/src/deploy/starter.rs +++ b/src/deploy/starter.rs @@ -1,18 +1,28 @@ -use crate::{config, controller::Controller}; -use bollard::Docker; +use crate::{ + config::{ + docker::DockerConnectionConfig, + docker::DockerConnectionKind::{Http, HttpDefault, Unix, UnixDefault}, + mrproxy::MrproxyConnectionConfig, + }, + controller::Controller, +}; +use bollard::{errors::Error, ClientVersion, Docker}; -pub async fn start_docker() -> Controller { - let docker = match Docker::connect_with_local_defaults() { +pub async fn start_docker( + docker_config: DockerConnectionConfig, + mrproxy_config: MrproxyConnectionConfig, +) -> Controller { + let docker = match generate_docker_connection(&docker_config) { Ok(d) => d, Err(e) => panic!("error:{}", e.to_string()), }; env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug")); let controller = match Controller::new( docker, - "customnetwork".to_string(), - "172.20.0.0/24".to_string(), - config::DockerConnectionConfig::get_config(config::CONFIG_PATH).unwrap(), - config::MrproxyConnectionData::get_config(config::CONFIG_PATH).unwrap(), + docker_config.network_name.clone(), + docker_config.network_addr.clone(), + docker_config, + mrproxy_config, ) .await { @@ -22,3 +32,26 @@ pub async fn start_docker() -> Controller { controller.load_all_instances().await; return controller; } + +fn generate_docker_connection(docker_config: &DockerConnectionConfig) -> Result { + match docker_config.connection_kind { + Http => Docker::connect_with_http( + docker_config.connection_string.as_ref().unwrap(), + 60, + &ClientVersion { + minor_version: usize::MIN, + major_version: usize::MAX, + }, + ), + HttpDefault => Docker::connect_with_http_defaults(), + Unix => Docker::connect_with_unix( + docker_config.connection_string.as_ref().unwrap(), + 60, + &ClientVersion { + minor_version: usize::MIN, + major_version: usize::MAX, + }, + ), + UnixDefault => Docker::connect_with_defaults(), + } +} diff --git a/src/mcproxy_client/controller.rs b/src/mcproxy_client/controller.rs index 6c2220f..494c8a6 100644 --- a/src/mcproxy_client/controller.rs +++ b/src/mcproxy_client/controller.rs @@ -1,8 +1,8 @@ use std::{error::Error, net::TcpStream, os::unix::net::UnixStream}; use crate::{ - config::{ - MrproxyConnectionData, + config::mrproxy::{ + MrproxyConnectionConfig, MrproxyConnectionKind::{TCP, UNIX}, }, mcproxy_client::client, @@ -20,7 +20,7 @@ pub struct Controller { } impl Controller { - pub fn new(conexion_data: &MrproxyConnectionData) -> Result> { + pub fn new(conexion_data: &MrproxyConnectionConfig) -> Result> { let streams: (Option, Option, KindStream) = match conexion_data.connection_kind { TCP => (