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
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

View file

@ -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();

View file

@ -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()),

View file

@ -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(&params.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(&params.code, Some(bot.id), &conn).expect("failed to save code bundle");
let _code_bundle = save_code_string(&params.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)?;

View file

@ -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(&params.code, None, &conn)
let player_bot_version = save_code_string(&params.code, None, &conn, &config)
// TODO: can we recover from this?
.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 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)
}