move storage paths to GlobalConfig
This commit is contained in:
parent
ec5c91d37b
commit
d13d131130
6 changed files with 45 additions and 37 deletions
|
@ -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<DieselConnectionManager<PgConnection>>;
|
||||
|
@ -39,9 +36,13 @@ type ConnectionPool = bb8::Pool<DieselConnectionManager<PgConnection>>;
|
|||
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<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 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
|
||||
|
|
|
@ -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<i32>,
|
||||
conn: &PgConnection,
|
||||
config: &GlobalConfig,
|
||||
) -> QueryResult<db::bots::BotVersion> {
|
||||
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();
|
||||
|
||||
|
|
|
@ -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<MatchPlayer>,
|
||||
runner_config: Arc<GlobalConfig>,
|
||||
config: Arc<GlobalConfig>,
|
||||
}
|
||||
|
||||
pub enum MatchPlayer {
|
||||
|
@ -31,10 +31,10 @@ pub enum MatchPlayer {
|
|||
}
|
||||
|
||||
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));
|
||||
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<GlobalConfig>,
|
||||
runner_config: &GlobalConfig,
|
||||
bot: Option<&db::bots::Bot>,
|
||||
bot_version: &db::bots::BotVersion,
|
||||
) -> Box<dyn BotSpec> {
|
||||
|
@ -120,17 +120,14 @@ pub fn bot_version_to_botspec(
|
|||
}
|
||||
}
|
||||
|
||||
fn python_docker_bot_spec(
|
||||
runner_config: &Arc<GlobalConfig>,
|
||||
code_bundle_path: &str,
|
||||
) -> Box<dyn BotSpec> {
|
||||
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<dyn BotSpec> {
|
||||
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()),
|
||||
|
|
|
@ -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<SaveBotParams>,
|
||||
user: User,
|
||||
conn: DatabaseConnection,
|
||||
Extension(config): Extension<Arc<GlobalConfig>>,
|
||||
) -> Result<Json<Bot>, 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<i32>,
|
||||
mut multipart: Multipart,
|
||||
Extension(config): Extension<Arc<GlobalConfig>>,
|
||||
) -> 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)?;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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<i32>,
|
||||
conn: DatabaseConnection,
|
||||
Extension(config): Extension<Arc<GlobalConfig>>,
|
||||
) -> Result<Vec<u8>, 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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue