move storage paths to GlobalConfig

This commit is contained in:
Ilion Beyst 2022-07-16 21:22:03 +02:00
parent ec5c91d37b
commit d13d131130
6 changed files with 45 additions and 37 deletions

View file

@ -29,9 +29,6 @@ use axum::{
}; };
// TODO: make these configurable // 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"; const SIMPLEBOT_PATH: &str = "../simplebot/simplebot.py";
type ConnectionPool = bb8::Pool<DieselConnectionManager<PgConnection>>; type ConnectionPool = bb8::Pool<DieselConnectionManager<PgConnection>>;
@ -39,9 +36,13 @@ type ConnectionPool = bb8::Pool<DieselConnectionManager<PgConnection>>;
pub struct GlobalConfig { pub struct GlobalConfig {
pub python_runner_image: String, pub python_runner_image: String,
pub container_registry_url: 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"); let conn = pool.get().await.expect("could not get database connection");
// This transaction is expected to fail when simplebot already exists. // This transaction is expected to fail when simplebot already exists.
let _res = conn.transaction::<(), diesel::result::Error, _>(|| { let _res = conn.transaction::<(), diesel::result::Error, _>(|| {
@ -57,7 +58,7 @@ pub async fn seed_simplebot(pool: &ConnectionPool) {
let simplebot_code = let simplebot_code =
std::fs::read_to_string(SIMPLEBOT_PATH).expect("could not read 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"); println!("initialized simplebot");
@ -67,10 +68,10 @@ pub async fn seed_simplebot(pool: &ConnectionPool) {
pub type DbPool = Pool<DieselConnectionManager<PgConnection>>; pub type DbPool = Pool<DieselConnectionManager<PgConnection>>;
pub async fn prepare_db(database_url: &str) -> DbPool { pub async fn prepare_db(database_url: &str, config: &GlobalConfig) -> DbPool {
let manager = DieselConnectionManager::<PgConnection>::new(database_url); let manager = DieselConnectionManager::<PgConnection>::new(database_url);
let pool = bb8::Pool::builder().build(manager).await.unwrap(); let pool = bb8::Pool::builder().build(manager).await.unwrap();
seed_simplebot(&pool).await; seed_simplebot(&config, &pool).await;
pool pool
} }
@ -124,20 +125,25 @@ async fn run_registry(db_pool: DbPool) {
pub async fn run_app() { pub async fn run_app() {
let configuration = get_config().unwrap(); 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(), python_runner_image: "python:3.10-slim-buster".to_string(),
container_registry_url: "localhost:9001".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())); tokio::spawn(run_registry(db_pool.clone()));
let api_service = Router::new() let api_service = Router::new()
.nest("/api", api()) .nest("/api", api())
.layer(Extension(db_pool)) .layer(Extension(db_pool))
.layer(Extension(runner_config)) .layer(Extension(global_config))
.into_make_service(); .into_make_service();
// TODO: put in config // TODO: put in config

View file

@ -2,17 +2,18 @@ use std::path::PathBuf;
use diesel::{PgConnection, QueryResult}; 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. /// Save a string containing bot code as a code bundle.
pub fn save_code_string( pub fn save_code_string(
bot_code: &str, bot_code: &str,
bot_id: Option<i32>, bot_id: Option<i32>,
conn: &PgConnection, conn: &PgConnection,
config: &GlobalConfig,
) -> QueryResult<db::bots::BotVersion> { ) -> QueryResult<db::bots::BotVersion> {
let bundle_name = gen_alphanumeric(16); 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::create_dir(&code_bundle_dir).unwrap();
std::fs::write(code_bundle_dir.join("bot.py"), bot_code).unwrap(); std::fs::write(code_bundle_dir.join("bot.py"), bot_code).unwrap();

View file

@ -11,13 +11,13 @@ use crate::{
matches::{MatchData, MatchResult}, matches::{MatchData, MatchResult},
}, },
util::gen_alphanumeric, util::gen_alphanumeric,
ConnectionPool, GlobalConfig, BOTS_DIR, MAPS_DIR, MATCHES_DIR, ConnectionPool, GlobalConfig,
}; };
pub struct RunMatch { pub struct RunMatch {
log_file_name: String, log_file_name: String,
players: Vec<MatchPlayer>, players: Vec<MatchPlayer>,
runner_config: Arc<GlobalConfig>, config: Arc<GlobalConfig>,
} }
pub enum MatchPlayer { pub enum MatchPlayer {
@ -31,10 +31,10 @@ pub enum MatchPlayer {
} }
impl RunMatch { impl RunMatch {
pub fn from_players(runner_config: Arc<GlobalConfig>, players: Vec<MatchPlayer>) -> Self { pub fn from_players(config: Arc<GlobalConfig>, players: Vec<MatchPlayer>) -> Self {
let log_file_name = format!("{}.log", gen_alphanumeric(16)); let log_file_name = format!("{}.log", gen_alphanumeric(16));
RunMatch { RunMatch {
runner_config, config,
log_file_name, log_file_name,
players, players,
} }
@ -42,16 +42,16 @@ impl RunMatch {
fn into_runner_config(self) -> runner::MatchConfig { fn into_runner_config(self) -> runner::MatchConfig {
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(), 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: self
.players .players
.into_iter() .into_iter()
.map(|player| runner::MatchPlayer { .map(|player| runner::MatchPlayer {
bot_spec: match player { bot_spec: match player {
MatchPlayer::BotVersion { bot, version } => { 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, MatchPlayer::BotSpec { spec } => spec,
}, },
@ -98,7 +98,7 @@ impl RunMatch {
} }
pub fn bot_version_to_botspec( pub fn bot_version_to_botspec(
runner_config: &Arc<GlobalConfig>, runner_config: &GlobalConfig,
bot: Option<&db::bots::Bot>, bot: Option<&db::bots::Bot>,
bot_version: &db::bots::BotVersion, bot_version: &db::bots::BotVersion,
) -> Box<dyn BotSpec> { ) -> Box<dyn BotSpec> {
@ -120,17 +120,14 @@ pub fn bot_version_to_botspec(
} }
} }
fn python_docker_bot_spec( fn python_docker_bot_spec(config: &GlobalConfig, code_bundle_path: &str) -> Box<dyn BotSpec> {
runner_config: &Arc<GlobalConfig>, let code_bundle_rel_path = PathBuf::from(&config.bots_directory).join(code_bundle_path);
code_bundle_path: &str,
) -> Box<dyn BotSpec> {
let code_bundle_rel_path = PathBuf::from(BOTS_DIR).join(code_bundle_path);
let code_bundle_abs_path = std::fs::canonicalize(&code_bundle_rel_path).unwrap(); 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(); let code_bundle_path_str = code_bundle_abs_path.as_os_str().to_str().unwrap();
// TODO: it would be good to simplify this configuration // TODO: it would be good to simplify this configuration
Box::new(DockerBotSpec { 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")]), binds: Some(vec![format!("{}:{}", code_bundle_path_str, "/workdir")]),
argv: Some(vec!["python".to_string(), "bot.py".to_string()]), argv: Some(vec!["python".to_string(), "bot.py".to_string()]),
working_dir: Some("/workdir".to_string()), working_dir: Some("/workdir".to_string()),

View file

@ -1,7 +1,7 @@
use axum::extract::{Multipart, Path}; use axum::extract::{Multipart, Path};
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use axum::{body, Json}; use axum::{body, Extension, Json};
use diesel::OptionalExtension; use diesel::OptionalExtension;
use rand::distributions::Alphanumeric; use rand::distributions::Alphanumeric;
use rand::Rng; use rand::Rng;
@ -9,13 +9,14 @@ use serde::{Deserialize, Serialize};
use serde_json::{self, json, value::Value as JsonValue}; use serde_json::{self, json, value::Value as JsonValue};
use std::io::Cursor; use std::io::Cursor;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use thiserror; use thiserror;
use crate::db::bots::{self, BotVersion}; use crate::db::bots::{self, BotVersion};
use crate::db::ratings::{self, RankedBot}; use crate::db::ratings::{self, RankedBot};
use crate::db::users::User; use crate::db::users::User;
use crate::modules::bots::save_code_string; use crate::modules::bots::save_code_string;
use crate::{DatabaseConnection, BOTS_DIR}; use crate::{DatabaseConnection, GlobalConfig};
use bots::Bot; use bots::Bot;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -96,6 +97,7 @@ pub async fn save_bot(
Json(params): Json<SaveBotParams>, Json(params): Json<SaveBotParams>,
user: User, user: User,
conn: DatabaseConnection, conn: DatabaseConnection,
Extension(config): Extension<Arc<GlobalConfig>>,
) -> Result<Json<Bot>, SaveBotError> { ) -> Result<Json<Bot>, SaveBotError> {
let res = bots::find_bot_by_name(&params.bot_name, &conn) let res = bots::find_bot_by_name(&params.bot_name, &conn)
.optional() .optional()
@ -119,8 +121,8 @@ pub async fn save_bot(
bots::create_bot(&new_bot, &conn).expect("could not create bot") bots::create_bot(&new_bot, &conn).expect("could not create bot")
} }
}; };
let _code_bundle = let _code_bundle = save_code_string(&params.code, Some(bot.id), &conn, &config)
save_code_string(&params.code, Some(bot.id), &conn).expect("failed to save code bundle"); .expect("failed to save code bundle");
Ok(Json(bot)) Ok(Json(bot))
} }
@ -183,8 +185,9 @@ pub async fn upload_code_multipart(
user: User, user: User,
Path(bot_id): Path<i32>, Path(bot_id): Path<i32>,
mut multipart: Multipart, mut multipart: Multipart,
Extension(config): Extension<Arc<GlobalConfig>>,
) -> Result<Json<BotVersion>, StatusCode> { ) -> Result<Json<BotVersion>, 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)?; let bot = bots::find_bot(bot_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?;

View file

@ -46,7 +46,7 @@ pub async fn submit_bot(
let opponent_bot_version = db::bots::active_bot_version(opponent_bot.id, &conn) let opponent_bot_version = db::bots::active_bot_version(opponent_bot.id, &conn)
.map_err(|_| StatusCode::BAD_REQUEST)?; .map_err(|_| StatusCode::BAD_REQUEST)?;
let player_bot_version = save_code_string(&params.code, None, &conn) let player_bot_version = save_code_string(&params.code, None, &conn, &config)
// TODO: can we recover from this? // TODO: can we recover from this?
.expect("could not save bot code"); .expect("could not save bot code");

View file

@ -1,11 +1,11 @@
use axum::{extract::Path, Json}; use axum::{extract::Path, Extension, Json};
use hyper::StatusCode; use hyper::StatusCode;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::{path::PathBuf, sync::Arc};
use crate::{ use crate::{
db::matches::{self, MatchState}, db::matches::{self, MatchState},
DatabaseConnection, MATCHES_DIR, DatabaseConnection, GlobalConfig,
}; };
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -59,10 +59,11 @@ pub async fn get_match_data(
pub async fn get_match_log( pub async fn get_match_log(
Path(match_id): Path<i32>, Path(match_id): Path<i32>,
conn: DatabaseConnection, conn: DatabaseConnection,
Extension(config): Extension<Arc<GlobalConfig>>,
) -> Result<Vec<u8>, StatusCode> { ) -> Result<Vec<u8>, StatusCode> {
let match_base = let match_base =
matches::find_match_base(match_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?; 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)?; let log_contents = std::fs::read(log_path).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(log_contents) Ok(log_contents)
} }