rename dockermgr to srvmgr
This commit is contained in:
7
src/srvmgr/mod.rs
Normal file
7
src/srvmgr/mod.rs
Normal 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
88
src/srvmgr/start.rs
Normal 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
41
src/srvmgr/stop.rs
Normal 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
101
src/srvmgr/utils.rs
Normal 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user