diff --git a/planetwars-server/src/lib.rs b/planetwars-server/src/lib.rs index d9f5e8e..7e14add 100644 --- a/planetwars-server/src/lib.rs +++ b/planetwars-server/src/lib.rs @@ -29,9 +29,6 @@ use axum::{ }; // TODO: make these configurable -const BOTS_DIR: &str = "./data/bots"; -const MATCHES_DIR: &str = "./data/matches"; -const MAPS_DIR: &str = "./data/maps"; const SIMPLEBOT_PATH: &str = "../simplebot/simplebot.py"; type ConnectionPool = bb8::Pool>; @@ -39,9 +36,13 @@ type ConnectionPool = bb8::Pool>; pub struct GlobalConfig { pub python_runner_image: String, pub container_registry_url: String, + + pub bots_directory: String, + pub match_logs_directory: String, + pub maps_directory: String, } -pub async fn seed_simplebot(pool: &ConnectionPool) { +pub async fn seed_simplebot(config: &GlobalConfig, pool: &ConnectionPool) { let conn = pool.get().await.expect("could not get database connection"); // This transaction is expected to fail when simplebot already exists. let _res = conn.transaction::<(), diesel::result::Error, _>(|| { @@ -57,7 +58,7 @@ pub async fn seed_simplebot(pool: &ConnectionPool) { let simplebot_code = std::fs::read_to_string(SIMPLEBOT_PATH).expect("could not read simplebot code"); - modules::bots::save_code_string(&simplebot_code, Some(simplebot.id), &conn)?; + modules::bots::save_code_string(&simplebot_code, Some(simplebot.id), &conn, config)?; println!("initialized simplebot"); @@ -67,10 +68,10 @@ pub async fn seed_simplebot(pool: &ConnectionPool) { pub type DbPool = Pool>; -pub async fn prepare_db(database_url: &str) -> DbPool { +pub async fn prepare_db(database_url: &str, config: &GlobalConfig) -> DbPool { let manager = DieselConnectionManager::::new(database_url); let pool = bb8::Pool::builder().build(manager).await.unwrap(); - seed_simplebot(&pool).await; + seed_simplebot(&config, &pool).await; pool } @@ -124,20 +125,25 @@ async fn run_registry(db_pool: DbPool) { pub async fn run_app() { let configuration = get_config().unwrap(); - let db_pool = prepare_db(&configuration.database_url).await; - let runner_config = Arc::new(GlobalConfig { + let global_config = Arc::new(GlobalConfig { python_runner_image: "python:3.10-slim-buster".to_string(), container_registry_url: "localhost:9001".to_string(), + + bots_directory: "./data/bots".to_string(), + match_logs_directory: "./data/matches".to_string(), + maps_directory: "./data/maps".to_string(), }); - tokio::spawn(run_ranker(runner_config.clone(), db_pool.clone())); + let db_pool = prepare_db(&configuration.database_url, &global_config).await; + + tokio::spawn(run_ranker(global_config.clone(), db_pool.clone())); tokio::spawn(run_registry(db_pool.clone())); let api_service = Router::new() .nest("/api", api()) .layer(Extension(db_pool)) - .layer(Extension(runner_config)) + .layer(Extension(global_config)) .into_make_service(); // TODO: put in config diff --git a/planetwars-server/src/modules/bots.rs b/planetwars-server/src/modules/bots.rs index b82ad41..5513539 100644 --- a/planetwars-server/src/modules/bots.rs +++ b/planetwars-server/src/modules/bots.rs @@ -2,17 +2,18 @@ use std::path::PathBuf; use diesel::{PgConnection, QueryResult}; -use crate::{db, util::gen_alphanumeric, BOTS_DIR}; +use crate::{db, util::gen_alphanumeric, GlobalConfig}; /// Save a string containing bot code as a code bundle. pub fn save_code_string( bot_code: &str, bot_id: Option, conn: &PgConnection, + config: &GlobalConfig, ) -> QueryResult { let bundle_name = gen_alphanumeric(16); - let code_bundle_dir = PathBuf::from(BOTS_DIR).join(&bundle_name); + let code_bundle_dir = PathBuf::from(&config.bots_directory).join(&bundle_name); std::fs::create_dir(&code_bundle_dir).unwrap(); std::fs::write(code_bundle_dir.join("bot.py"), bot_code).unwrap(); diff --git a/planetwars-server/src/modules/matches.rs b/planetwars-server/src/modules/matches.rs index dd5e523..a1fe63d 100644 --- a/planetwars-server/src/modules/matches.rs +++ b/planetwars-server/src/modules/matches.rs @@ -11,13 +11,13 @@ use crate::{ matches::{MatchData, MatchResult}, }, util::gen_alphanumeric, - ConnectionPool, GlobalConfig, BOTS_DIR, MAPS_DIR, MATCHES_DIR, + ConnectionPool, GlobalConfig, }; pub struct RunMatch { log_file_name: String, players: Vec, - runner_config: Arc, + config: Arc, } pub enum MatchPlayer { @@ -31,10 +31,10 @@ pub enum MatchPlayer { } impl RunMatch { - pub fn from_players(runner_config: Arc, players: Vec) -> Self { + pub fn from_players(config: Arc, players: Vec) -> Self { let log_file_name = format!("{}.log", gen_alphanumeric(16)); RunMatch { - runner_config, + config, log_file_name, players, } @@ -42,16 +42,16 @@ impl RunMatch { fn into_runner_config(self) -> runner::MatchConfig { runner::MatchConfig { - map_path: PathBuf::from(MAPS_DIR).join("hex.json"), + map_path: PathBuf::from(&self.config.maps_directory).join("hex.json"), map_name: "hex".to_string(), - log_path: PathBuf::from(MATCHES_DIR).join(&self.log_file_name), + log_path: PathBuf::from(&self.config.match_logs_directory).join(&self.log_file_name), players: self .players .into_iter() .map(|player| runner::MatchPlayer { bot_spec: match player { MatchPlayer::BotVersion { bot, version } => { - bot_version_to_botspec(&self.runner_config, bot.as_ref(), &version) + bot_version_to_botspec(&self.config, bot.as_ref(), &version) } MatchPlayer::BotSpec { spec } => spec, }, @@ -98,7 +98,7 @@ impl RunMatch { } pub fn bot_version_to_botspec( - runner_config: &Arc, + runner_config: &GlobalConfig, bot: Option<&db::bots::Bot>, bot_version: &db::bots::BotVersion, ) -> Box { @@ -120,17 +120,14 @@ pub fn bot_version_to_botspec( } } -fn python_docker_bot_spec( - runner_config: &Arc, - code_bundle_path: &str, -) -> Box { - let code_bundle_rel_path = PathBuf::from(BOTS_DIR).join(code_bundle_path); +fn python_docker_bot_spec(config: &GlobalConfig, code_bundle_path: &str) -> Box { + let code_bundle_rel_path = PathBuf::from(&config.bots_directory).join(code_bundle_path); let code_bundle_abs_path = std::fs::canonicalize(&code_bundle_rel_path).unwrap(); let code_bundle_path_str = code_bundle_abs_path.as_os_str().to_str().unwrap(); // TODO: it would be good to simplify this configuration Box::new(DockerBotSpec { - image: runner_config.python_runner_image.clone(), + image: config.python_runner_image.clone(), binds: Some(vec![format!("{}:{}", code_bundle_path_str, "/workdir")]), argv: Some(vec!["python".to_string(), "bot.py".to_string()]), working_dir: Some("/workdir".to_string()), diff --git a/planetwars-server/src/routes/bots.rs b/planetwars-server/src/routes/bots.rs index edb68ae..9ddb109 100644 --- a/planetwars-server/src/routes/bots.rs +++ b/planetwars-server/src/routes/bots.rs @@ -1,7 +1,7 @@ use axum::extract::{Multipart, Path}; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; -use axum::{body, Json}; +use axum::{body, Extension, Json}; use diesel::OptionalExtension; use rand::distributions::Alphanumeric; use rand::Rng; @@ -9,13 +9,14 @@ use serde::{Deserialize, Serialize}; use serde_json::{self, json, value::Value as JsonValue}; use std::io::Cursor; use std::path::PathBuf; +use std::sync::Arc; use thiserror; use crate::db::bots::{self, BotVersion}; use crate::db::ratings::{self, RankedBot}; use crate::db::users::User; use crate::modules::bots::save_code_string; -use crate::{DatabaseConnection, BOTS_DIR}; +use crate::{DatabaseConnection, GlobalConfig}; use bots::Bot; #[derive(Serialize, Deserialize, Debug)] @@ -96,6 +97,7 @@ pub async fn save_bot( Json(params): Json, user: User, conn: DatabaseConnection, + Extension(config): Extension>, ) -> Result, SaveBotError> { let res = bots::find_bot_by_name(¶ms.bot_name, &conn) .optional() @@ -119,8 +121,8 @@ pub async fn save_bot( bots::create_bot(&new_bot, &conn).expect("could not create bot") } }; - let _code_bundle = - save_code_string(¶ms.code, Some(bot.id), &conn).expect("failed to save code bundle"); + let _code_bundle = save_code_string(¶ms.code, Some(bot.id), &conn, &config) + .expect("failed to save code bundle"); Ok(Json(bot)) } @@ -183,8 +185,9 @@ pub async fn upload_code_multipart( user: User, Path(bot_id): Path, mut multipart: Multipart, + Extension(config): Extension>, ) -> Result, StatusCode> { - let bots_dir = PathBuf::from(BOTS_DIR); + let bots_dir = PathBuf::from(&config.bots_directory); let bot = bots::find_bot(bot_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?; diff --git a/planetwars-server/src/routes/demo.rs b/planetwars-server/src/routes/demo.rs index 77f9e8d..69838f3 100644 --- a/planetwars-server/src/routes/demo.rs +++ b/planetwars-server/src/routes/demo.rs @@ -46,7 +46,7 @@ pub async fn submit_bot( let opponent_bot_version = db::bots::active_bot_version(opponent_bot.id, &conn) .map_err(|_| StatusCode::BAD_REQUEST)?; - let player_bot_version = save_code_string(¶ms.code, None, &conn) + let player_bot_version = save_code_string(¶ms.code, None, &conn, &config) // TODO: can we recover from this? .expect("could not save bot code"); diff --git a/planetwars-server/src/routes/matches.rs b/planetwars-server/src/routes/matches.rs index f33a5f1..a980daa 100644 --- a/planetwars-server/src/routes/matches.rs +++ b/planetwars-server/src/routes/matches.rs @@ -1,11 +1,11 @@ -use axum::{extract::Path, Json}; +use axum::{extract::Path, Extension, Json}; use hyper::StatusCode; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::{path::PathBuf, sync::Arc}; use crate::{ db::matches::{self, MatchState}, - DatabaseConnection, MATCHES_DIR, + DatabaseConnection, GlobalConfig, }; #[derive(Serialize, Deserialize)] @@ -59,10 +59,11 @@ pub async fn get_match_data( pub async fn get_match_log( Path(match_id): Path, conn: DatabaseConnection, + Extension(config): Extension>, ) -> Result, StatusCode> { let match_base = matches::find_match_base(match_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?; - let log_path = PathBuf::from(MATCHES_DIR).join(&match_base.log_path); + let log_path = PathBuf::from(&config.match_logs_directory).join(&match_base.log_path); let log_contents = std::fs::read(log_path).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(log_contents) }