archive server
This commit is contained in:
37
Cargo.lock
generated
37
Cargo.lock
generated
@ -174,6 +174,42 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-process"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel 2.5.0",
|
||||||
|
"async-io",
|
||||||
|
"async-lock",
|
||||||
|
"async-signal",
|
||||||
|
"async-task",
|
||||||
|
"blocking",
|
||||||
|
"cfg-if",
|
||||||
|
"event-listener 5.4.1",
|
||||||
|
"futures-lite",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-signal"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
|
||||||
|
dependencies = [
|
||||||
|
"async-io",
|
||||||
|
"async-lock",
|
||||||
|
"atomic-waker",
|
||||||
|
"cfg-if",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"rustix",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"slab",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
@ -609,6 +645,7 @@ dependencies = [
|
|||||||
name = "dockermcmgr"
|
name = "dockermcmgr"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-process",
|
||||||
"async-std",
|
"async-std",
|
||||||
"bollard",
|
"bollard",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@ -8,6 +8,7 @@ name = "dmm"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-process = "2.5.0"
|
||||||
async-std = "1.13.2"
|
async-std = "1.13.2"
|
||||||
bollard = "0.21.0"
|
bollard = "0.21.0"
|
||||||
chrono = "0.4.44"
|
chrono = "0.4.44"
|
||||||
|
|||||||
@ -46,10 +46,13 @@
|
|||||||
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
|
||||||
buildInputs =
|
# env.COMPRESSION_COMMAND="${pkgs.lrzip}/bin/lrzip";
|
||||||
with pkgs; [ sqlite ];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
sqlite
|
||||||
|
lrzip
|
||||||
|
];
|
||||||
|
}
|
||||||
// specialArgs
|
// specialArgs
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
|
|||||||
@ -10,7 +10,7 @@ pkgs.mkShell {
|
|||||||
|
|
||||||
];
|
];
|
||||||
PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig";
|
PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig";
|
||||||
|
COMPRESSION_COMMAND = "${pkgs.lrzip}/bin/lrzip";
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
[ ! -f .env ] || export $(grep -v '^#' .env | xargs)
|
[ ! -f .env ] || export $(grep -v '^#' .env | xargs)
|
||||||
|
|||||||
69
src/cli/archive.rs
Normal file
69
src/cli/archive.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use bollard::Docker;
|
||||||
|
use clap::Arg;
|
||||||
|
use clap::{Command, ArgMatches};
|
||||||
|
use diesel::{SelectableHelper};
|
||||||
|
use diesel::result::Error::NotFound;
|
||||||
|
|
||||||
|
use crate::models::Servers;
|
||||||
|
use crate::status::ServerStatus;
|
||||||
|
use crate::{DbPool, schema, models};
|
||||||
|
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
use crate::dockermgr;
|
||||||
|
|
||||||
|
use async_process;
|
||||||
|
|
||||||
|
pub fn create_subcommand() -> Command {
|
||||||
|
Command::new("archive")
|
||||||
|
.about("archive a server")
|
||||||
|
.arg(Arg::new("NAME").required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn subcommand(arguments: &ArgMatches, pool: &DbPool, docker: &Docker) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
let server = match servers.select(models::Servers::as_select()).filter(name.eq(server_name)).first(conn) {
|
||||||
|
Ok(server) => {server},
|
||||||
|
Err(NotFound) => { return Err("server not found".to_string()); },
|
||||||
|
Err(e) => { return Err(format!("Failed to fetch server from db : {}", e)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
if server.status != ServerStatus::Running && server.status != ServerStatus::Stopped {
|
||||||
|
return Err(format!("invalid current server state : {}", server.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if server.status == ServerStatus::Running {
|
||||||
|
println!("Stopping container");
|
||||||
|
dockermgr::stop_server(&docker, pool, server_name).await?;
|
||||||
|
println!("container stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
archive_server(&server, pool).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn archive_server(server: &Servers, pool: &DbPool) -> Result<(), String> {
|
||||||
|
use schema::servers::dsl::*;
|
||||||
|
let conn = &mut pool.get().unwrap();
|
||||||
|
|
||||||
|
let config = crate::Config::load();
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(&server.name)).set(status.eq(ServerStatus::Archiving)).execute(conn).map_err(|e| format!("Failed to set the server as archiving :{}", e))?;
|
||||||
|
|
||||||
|
println!("Starting compression (this might take a while)");
|
||||||
|
|
||||||
|
let cmd = format!("cd {} && tar cf /dev/stdout {} | lrzip - -Q -o {}.tar.lrz", config.config_path, server.name, server.name);
|
||||||
|
|
||||||
|
let _ = async_process::Command::new("sh").arg("-c").arg(cmd).output().await.map_err(|e| format!("compression failed : {}", e))?;
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(config.config_path.to_owned() + "/" + &server.name).map_err(|e| format!("Failed to delete server directory: {}", e))?;
|
||||||
|
|
||||||
|
diesel::update(servers).filter(name.eq(&server.name)).set(status.eq(ServerStatus::Archived)).execute(conn).map_err(|e| format!("Failed to set the server as achived :{}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ pub mod ls;
|
|||||||
pub mod start;
|
pub mod start;
|
||||||
pub mod stop;
|
pub mod stop;
|
||||||
pub mod set_state;
|
pub mod set_state;
|
||||||
|
pub mod archive;
|
||||||
|
|
||||||
pub mod env_settings;
|
pub mod env_settings;
|
||||||
|
|
||||||
|
|||||||
@ -76,7 +76,7 @@ pub async fn start_server(docker: &Docker, pool: &DbPool, server_name: &String)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if server.status != ServerStatus::Stopped {
|
if server.status != ServerStatus::Stopped {
|
||||||
return Err(format!("Invalid server state({}), refusing to start", server.status));
|
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))?;
|
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))?;
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
||||||
/* +#+#+#+#+#+ +#+ */
|
/* +#+#+#+#+#+ +#+ */
|
||||||
/* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */
|
/* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */
|
||||||
/* Updated: 2026/06/12 12:02:28 by tomoron ### ########.fr */
|
/* Updated: 2026/06/12 17:03:13 by tomoron ### ########.fr */
|
||||||
/* */
|
/* */
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ fn cli() -> Command {
|
|||||||
.subcommand(set_state::create_subcommand())
|
.subcommand(set_state::create_subcommand())
|
||||||
.subcommand(start::create_subcommand())
|
.subcommand(start::create_subcommand())
|
||||||
.subcommand(stop::create_subcommand())
|
.subcommand(stop::create_subcommand())
|
||||||
|
.subcommand(archive::create_subcommand())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
@ -80,10 +81,11 @@ async fn main() -> Result<(), String> {
|
|||||||
Some(("remove", sub_matches)) => { remove::subcommand(sub_matches, &pool, &docker).await?; },
|
Some(("remove", sub_matches)) => { remove::subcommand(sub_matches, &pool, &docker).await?; },
|
||||||
Some(("ls", sub_matches)) => { ls::subcommand(sub_matches, pool).await?; },
|
Some(("ls", sub_matches)) => { ls::subcommand(sub_matches, pool).await?; },
|
||||||
Some(("edit", sub_matches)) => { edit::subcommand(sub_matches, pool).await?; },
|
Some(("edit", sub_matches)) => { edit::subcommand(sub_matches, pool).await?; },
|
||||||
Some(("setState", sub_matches)) => { set_state::subcommand(sub_matches, pool).await? }
|
Some(("setState", sub_matches)) => { set_state::subcommand(sub_matches, pool).await? },
|
||||||
|
|
||||||
Some(("start", sub_matches)) => { start::subcommand(sub_matches, &pool, &docker).await?; },
|
Some(("start", sub_matches)) => { start::subcommand(sub_matches, &pool, &docker).await?; },
|
||||||
Some(("stop", sub_matches)) => { stop::subcommand(sub_matches, &pool, &docker).await?; },
|
Some(("stop", sub_matches)) => { stop::subcommand(sub_matches, &pool, &docker).await?; },
|
||||||
|
Some(("archive", sub_matches)) => { archive::subcommand(sub_matches, &pool, &docker).await? },
|
||||||
_ => {panic!("subcommand not implemented")}
|
_ => {panic!("subcommand not implemented")}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
use crate::minecraft::client::client::Client;
|
use crate::{dockermgr::start_server, minecraft::client::client::Client};
|
||||||
|
|
||||||
use diesel::result::Error::NotFound;
|
use diesel::result::Error::NotFound;
|
||||||
use json::object;
|
use json::object;
|
||||||
use crate::minecraft::varint::varint_write;
|
use crate::minecraft::varint::varint_write;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use crate::{ dockermgr };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque<u8>, _packet_id: i32) -> Result<(),String> {
|
pub async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque<u8>, _packet_id: i32) -> Result<(),String> {
|
||||||
@ -30,7 +27,8 @@ impl Client {
|
|||||||
Err(e) => { return Err(format!("db fetch error : {}", e)); }
|
Err(e) => { return Err(format!("db fetch error : {}", e)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
dockermgr::start_server(&self.docker, &self.db_pool, &server.name).await?;
|
start_server(&self.docker, &self.db_pool, &server.name).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use chrono::{TimeDelta, Utc};
|
use chrono::{TimeDelta, Utc};
|
||||||
|
|
||||||
use crate::{ DbPool, dockermgr, models::{self, Servers}, schema, status::ServerStatus };
|
use crate::{ DbPool, cli::archive::archive_server, dockermgr, models::{self, Servers}, schema, status::ServerStatus };
|
||||||
|
|
||||||
use async_std::task;
|
use async_std::task;
|
||||||
|
|
||||||
@ -45,6 +45,7 @@ pub async fn server_manager_loop(pool: DbPool, docker: Docker)
|
|||||||
for srv in a_servers {
|
for srv in a_servers {
|
||||||
let time_diff = Utc::now().naive_utc() - srv.last_login.unwrap();
|
let time_diff = Utc::now().naive_utc() - srv.last_login.unwrap();
|
||||||
should_stop(&docker, &pool, &time_diff, &srv).await;
|
should_stop(&docker, &pool, &time_diff, &srv).await;
|
||||||
|
should_archive(&docker, &pool, &time_diff, &srv).await;
|
||||||
check_still_online(&docker, &pool, &srv).await;
|
check_still_online(&docker, &pool, &srv).await;
|
||||||
}
|
}
|
||||||
task::sleep(Duration::from_secs(30)).await;
|
task::sleep(Duration::from_secs(30)).await;
|
||||||
@ -61,6 +62,16 @@ async fn should_stop(docker: &Docker, pool: &DbPool, diff: &TimeDelta, srv: &Ser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn should_archive(_docker: &Docker, pool: &DbPool, diff: &TimeDelta, srv: &Servers) {
|
||||||
|
if srv.status != ServerStatus::Stopped { return ; }
|
||||||
|
|
||||||
|
if diff.num_days() >= 30 {
|
||||||
|
println!("last login time exceedd, archiving {}", srv.name);
|
||||||
|
let _ = archive_server(srv, pool);
|
||||||
|
println!("{} archived", srv.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn check_still_online(docker: &Docker, pool: &DbPool, srv: &Servers) {
|
async fn check_still_online(docker: &Docker, pool: &DbPool, srv: &Servers) {
|
||||||
use schema::servers::dsl::*;
|
use schema::servers::dsl::*;
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,8 @@ pub enum ServerStatus {
|
|||||||
Stopping = 2,
|
Stopping = 2,
|
||||||
Starting = 3,
|
Starting = 3,
|
||||||
Running = 4,
|
Running = 4,
|
||||||
Unknown = 5,
|
Archiving = 5,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromSql<SmallInt, Sqlite> for ServerStatus{
|
impl FromSql<SmallInt, Sqlite> for ServerStatus{
|
||||||
@ -42,6 +43,7 @@ impl fmt::Display for ServerStatus {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let str_status = match self {
|
let str_status = match self {
|
||||||
ServerStatus::Archived => "archived",
|
ServerStatus::Archived => "archived",
|
||||||
|
ServerStatus::Archiving => "archiving",
|
||||||
ServerStatus::Stopped => "stopped",
|
ServerStatus::Stopped => "stopped",
|
||||||
ServerStatus::Stopping => "stopping",
|
ServerStatus::Stopping => "stopping",
|
||||||
ServerStatus::Starting => "starting",
|
ServerStatus::Starting => "starting",
|
||||||
|
|||||||
Reference in New Issue
Block a user