rename dockermgr to srvmgr

This commit is contained in:
2026-06-12 18:32:52 +02:00
parent 97f3e4c139
commit b2ab524b7e
11 changed files with 15 additions and 15 deletions

7
src/srvmgr/mod.rs Normal file
View File

@ -0,0 +1,7 @@
mod start;
pub use start::start_server;
pub mod utils;
mod stop;
pub use stop::stop_server;

88
src/srvmgr/start.rs Normal file
View File

@ -0,0 +1,88 @@
use bollard::{Docker, plugin::{ContainerCreateBody, HostConfig, Mount, MountType}, query_parameters::CreateContainerOptionsBuilder};
use diesel::{prelude::*, result::Error::NotFound};
use crate::{srvmgr::utils::get_image_tag, models, schema, status::ServerStatus};
use crate::{DbPool, srvmgr::utils::{self, get_container_ip}};
async fn start_docker_container(docker: &Docker, name: &String) -> Result<String, String> {
let container_name = format!("minecraft-{}", name);
if let Some(_) = utils::get_container(&docker, &container_name).await {
return Err("Container already exists".to_string())
}
let config_path = crate::Config::load().config_path;
let env_file = std::fs::read_to_string(config_path.clone() + "/" + name + "/env").map_err(|e| format!("Failed to read env file {}", e))?;
let env = env_file.split("\n").filter_map(|x| if x.len() == 0 { None } else { Some(x.to_string()) } ).collect();
let image_tag = match get_image_tag(&env).await {
Some(tag) => {tag},
None => { return Err("env missing IMAGE_TAG".to_string()); }
};
utils::check_image(&docker, &image_tag).await?;
let mut mount = Mount::default();
mount.typ = Some(MountType::BIND);
mount.target = Some("/data".to_string());
mount.source = Some(config_path + "/" + name + "/server");
mount.read_only = Some(false);
let mut host_config = HostConfig::default();
host_config.mounts = Some(vec![mount]);
host_config.auto_remove = Some(true);
let config = crate::Config::load();
let mut create_config = ContainerCreateBody::default();
create_config.tty = Some(true);
create_config.open_stdin = Some(true);
create_config.user = Some("1000:100".to_string());
create_config.stop_timeout = Some(60);
create_config.host_config = Some(host_config);
create_config.image = Some(config.docker_image + ":" + &image_tag);
create_config.env = Some(env);
let options = CreateContainerOptionsBuilder::default()
.name(&container_name)
.build();
docker.create_container(Some(options), create_config).await.map_err(|e| format!("failed to create the container : {}", e))?;
println!("created container");
println!("starting container");
docker.start_container(&container_name, None).await.map_err(|e| format!("Failed to start the container : {}", e))?;
Ok(get_container_ip(docker, &container_name).await?)
}
pub async fn start_server(docker: &Docker, pool: &DbPool, server_name: &String) -> Result<(), String> {
use schema::servers::dsl::*;
let conn = &mut pool.get().unwrap();
let server = match servers.select(models::Servers::as_select()).filter(name.eq(server_name)).first(conn) {
Err(NotFound) => { return Err("This server does not exist".to_string()); },
Err(e) => { return Err(format!("Failed to get server from db : {}", e)) },
Ok(server) => { server }
};
if server.status == ServerStatus::Starting {
return Err("Server is starting, please wait".to_string());
}
if server.status != ServerStatus::Stopped {
return Err(format!("Invalid server state ({}), refusing to start", server.status));
}
diesel::update(servers).filter(name.eq(server_name)).set(status.eq(ServerStatus::Starting)).execute(conn).map_err(|e| format!("Failed to set server to starting : {}", e))?;
let ip = start_docker_container(docker, server_name).await?;
diesel::update(servers).filter(name.eq(server_name)).set((status.eq(ServerStatus::Running), redirect_ip.eq(ip.to_string() + ":25565"))).execute(conn).map_err(|e| format!("Failed to set server to starting : {}", e))?;
Ok(())
}

41
src/srvmgr/stop.rs Normal file
View File

@ -0,0 +1,41 @@
use bollard::{Docker, query_parameters::StopContainerOptionsBuilder};
use diesel::result::Error::NotFound;
use diesel::prelude::*;
use crate::{ DbPool, models, schema, status::ServerStatus };
async fn stop_container(docker: &Docker, name: &String) -> Result<(), String> {
let container_name = format!("minecraft-{}", name);
let options = StopContainerOptionsBuilder::new()
.t(60)
.build();
docker.stop_container(&container_name, Some(options)).await.map_err(|e| format!("Failed to stop the container : {}", e))?;
Ok(())
}
pub async fn stop_server(docker: &Docker, pool: &DbPool, server_name: &String) -> Result<(), String> {
use schema::servers::dsl::*;
let conn = &mut pool.get().unwrap();
let server = match servers.select(models::Servers::as_select()).filter(name.eq(server_name)).first(conn) {
Err(NotFound) => { return Err("This server does not exist".to_string()); },
Err(e) => { return Err(format!("Failed to get server from db : {}", e)) },
Ok(server) => { server }
};
if server.status != ServerStatus::Running {
return Err(format!("Invalid server state ({}), refusing to stop the server", server.status));
}
diesel::update(servers).filter(name.eq(server_name)).set(status.eq(ServerStatus::Stopping)).execute(conn).map_err(|e| format!("Failed to set the server state to stopping : {}", e))?;
stop_container(docker, server_name).await?;
diesel::update(servers).filter(name.eq(server_name)).set((redirect_ip.eq(None::<String>), status.eq(ServerStatus::Stopped))).execute(conn).map_err(|e| format!("Failed to set the server state to stopped : {}", e))?;
Ok(())
}

101
src/srvmgr/utils.rs Normal file
View File

@ -0,0 +1,101 @@
use std::collections::HashMap;
use bollard::{
Docker,
plugin::{ContainerInspectResponse},
query_parameters::{CreateImageOptionsBuilder, ListImagesOptionsBuilder}
};
use futures_util::stream::StreamExt;
pub async fn check_image(docker: &Docker, tag: &String) -> Result<(), String> {
let mut filters = HashMap::new();
let config = crate::Config::load();
let container_name = config.docker_image + ":" + tag;
filters.insert("reference", vec![&container_name]);
let options = ListImagesOptionsBuilder::default()
.all(true)
.filters(&filters)
.build();
let images = docker.list_images(Some(options)).await.map_err(|e| format!("failed to check if image exists, error : {:?}", e))?;
if images.len() >= 1 {
return Ok(());
}
let options = CreateImageOptionsBuilder::default()
.from_image(&container_name)
.tag(tag)
.build();
println!("pulling {} from docker hub ... (this might take a while and I'm too lazy to do an Interface to show the progress, if you want progress info, pull it yourself on with `docker pull {}`)", container_name, container_name);
let mut pull_result = docker.create_image(Some(options), None, None);
while let Some(update) = pull_result.next().await {
if let Err(e) = update {
return Err(format!("Failed to pull the image from docker hub, error : {:?}", e));
}
}
println!("pulled successfully");
Ok(())
}
pub async fn get_container(docker: &Docker, name: &String) -> Option<ContainerInspectResponse> {
println!("get {}", name);
let result = docker.inspect_container(name, None).await.map_err(|e| format!("failed to retreive container : {:?}", e));
match result {
Err(_) => { None },
Ok(container) => { Some(container) }
}
}
pub async fn get_container_ip(docker: &Docker, name: &String) -> Result<String, String>{
let mut retries = 20;
while retries > 0 {
let container = get_container(docker, name).await;
if let Some(container) = container && let Some(network) = container.network_settings.unwrap().networks.unwrap().get("bridge") {
if let Some(ip) = &network.ip_address {
return Ok(ip.to_string());
}
}
async_std::task::sleep(std::time::Duration::from_millis(500)).await;
retries -= 1;
}
Ok("can't get container ip".to_string())
}
pub async fn get_image_tag(env: &Vec<String>) -> Option<String> {
for var in env {
match var.split_once("=") {
Some(split) => { if split.0 == "IMAGE_TAG" { return Some(split.1.to_string()); }},
None => { },
}
}
None
}