improve proxy functionality and configuration

This commit is contained in:
2025-12-25 09:53:46 +00:00
parent 5045c05f9f
commit 208b7f4598
13 changed files with 293 additions and 161 deletions

View File

@@ -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"

View File

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

67
src/config/docker.rs Normal file
View File

@@ -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<String>,
pub network_name: String,
pub network_addr: String,
}
impl DockerConnectionConfig {
pub fn get_config(file_name: &str) -> Result<Self, Box<dyn Error>> {
let stored_file = read_to_string(file_name)?.parse::<Table>()?;
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,
})
}
}

View File

@@ -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<String>,
}
pub struct MrproxyConnectionData {
pub connection_kind: MrproxyConnectionKind,
pub connection_string: String,
}
impl DockerConnectionConfig {
pub fn get_config(file_name: &str) -> Result<Self, Box<dyn Error>> {
let stored_file = read_to_string(file_name)?.parse::<Table>()?;
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<Self, Box<dyn Error>> {
let stored_file = read_to_string(file_name)?.parse::<Table>()?;
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";

44
src/config/mrproxy.rs Normal file
View File

@@ -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<Self, Box<dyn Error>> {
let stored_file = read_to_string(file_name)?.parse::<Table>()?;
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,
})
}
}

24
src/config/server.rs Normal file
View File

@@ -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<Self, Box<dyn Error>> {
let stored_file = read_to_string(file_name)?.parse::<Table>()?;
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 })
}
}

8
src/config/utils.rs Normal file
View File

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

View File

@@ -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<MemStorage>,
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<Self, bollard::errors::Error> {
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<String>,
domain: String,
ip: Option<String>,
image: String,
ops: Options,
) -> Result<deploy::container::Container, Box<dyn GenericError>> {
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<String>,
container: Container,
) -> Result<(), Box<dyn GenericError>> {
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<dyn GenericError>> {
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<String, Error> {
async fn prune_given_container(
&self,
container: Container,
) -> Result<String, Box<dyn GenericError>> {
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<String, Error> {
match self.storage.lock().unwrap().remove_instance(domain) {
pub async fn delete_container(&self, domain: String) -> Result<String, Box<dyn GenericError>> {
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(),
}),
})),
}
}

View File

@@ -71,7 +71,7 @@ impl MemStorage {
None
}
pub fn remove_instance(&mut self, name: String) -> Option<(Instance, Option<Container>)> {
pub fn remove_instance(&mut self, name: &str) -> Option<(Instance, Option<Container>)> {
for i in 0..self.containers.len() {
match self.containers.get(i) {
Some(c) => {

View File

@@ -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;

View File

@@ -1,11 +1,10 @@
use serde::{Deserialize, Serialize};
use serde_json::Result;
#[derive(Clone, Serialize, Deserialize)]
pub struct Options {
memory: Option<String>,
rolling_logs: bool,
msg: Option<String>
msg: Option<String>,
}
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("")
}
}
}
}

View File

@@ -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<Docker, Error> {
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(),
}
}

View File

@@ -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<Self, Box<dyn Error>> {
pub fn new(conexion_data: &MrproxyConnectionConfig) -> Result<Self, Box<dyn Error>> {
let streams: (Option<TcpStream>, Option<UnixStream>, KindStream) =
match conexion_data.connection_kind {
TCP => (