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
|
// 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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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(¶ms.bot_name, &conn)
|
let res = bots::find_bot_by_name(¶ms.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(¶ms.code, Some(bot.id), &conn, &config)
|
||||||
save_code_string(¶ms.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)?;
|
||||||
|
|
||||||
|
|
|
@ -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(¶ms.code, None, &conn)
|
let player_bot_version = save_code_string(¶ms.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");
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue