save all uploaded code bundles in database
This commit is contained in:
parent
6ef6a872fe
commit
22a8f3d619
9 changed files with 60 additions and 26 deletions
|
@ -8,7 +8,7 @@ CREATE UNIQUE INDEX bots_index ON bots(owner_id, name);
|
||||||
|
|
||||||
CREATE TABLE code_bundles (
|
CREATE TABLE code_bundles (
|
||||||
id serial PRIMARY KEY,
|
id serial PRIMARY KEY,
|
||||||
bot_id integer REFERENCES bots(id) NOT NULL,
|
bot_id integer REFERENCES bots(id),
|
||||||
path text NOT NULL,
|
path text NOT NULL,
|
||||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
|
@ -42,14 +42,14 @@ pub fn find_all_bots(conn: &PgConnection) -> QueryResult<Vec<Bot>> {
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "code_bundles"]
|
#[table_name = "code_bundles"]
|
||||||
pub struct NewCodeBundle<'a> {
|
pub struct NewCodeBundle<'a> {
|
||||||
pub bot_id: i32,
|
pub bot_id: Option<i32>,
|
||||||
pub path: &'a str,
|
pub path: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Serialize, Deserialize, Debug)]
|
#[derive(Queryable, Serialize, Deserialize, Debug)]
|
||||||
pub struct CodeBundle {
|
pub struct CodeBundle {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub bot_id: i32,
|
pub bot_id: Option<i32>,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub created_at: chrono::NaiveDateTime,
|
pub created_at: chrono::NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,17 @@ extern crate diesel;
|
||||||
|
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod db_types;
|
pub mod db_types;
|
||||||
|
pub mod modules;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use axum;
|
use axum;
|
||||||
use bb8::PooledConnection;
|
use bb8::{Pool, PooledConnection};
|
||||||
use bb8_diesel::{self, DieselConnectionManager};
|
use bb8_diesel::{self, DieselConnectionManager};
|
||||||
use diesel::PgConnection;
|
use diesel::{Connection, PgConnection};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
|
@ -29,9 +31,14 @@ const MAPS_DIR: &str = "./data/maps";
|
||||||
|
|
||||||
type ConnectionPool = bb8::Pool<DieselConnectionManager<PgConnection>>;
|
type ConnectionPool = bb8::Pool<DieselConnectionManager<PgConnection>>;
|
||||||
|
|
||||||
pub async fn api(configuration: Configuration) -> Router {
|
pub async fn prepare_db(database_url: &str) -> Pool<DieselConnectionManager<PgConnection>> {
|
||||||
let manager = DieselConnectionManager::<PgConnection>::new(configuration.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();
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn api(configuration: Configuration) -> Router {
|
||||||
|
let db_pool = prepare_db(&configuration.database_url).await;
|
||||||
|
|
||||||
let api = Router::new()
|
let api = Router::new()
|
||||||
.route("/register", post(routes::users::register))
|
.route("/register", post(routes::users::register))
|
||||||
|
@ -57,7 +64,7 @@ pub async fn api(configuration: Configuration) -> Router {
|
||||||
get(routes::matches::get_match_log),
|
get(routes::matches::get_match_log),
|
||||||
)
|
)
|
||||||
.route("/submit_bot", post(routes::demo::submit_bot))
|
.route("/submit_bot", post(routes::demo::submit_bot))
|
||||||
.layer(AddExtensionLayer::new(pool));
|
.layer(AddExtensionLayer::new(db_pool));
|
||||||
api
|
api
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
planetwars-server/src/modules/bots.rs
Normal file
23
planetwars-server/src/modules/bots.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use diesel::{PgConnection, QueryResult};
|
||||||
|
|
||||||
|
use crate::{db, util::gen_alphanumeric, BOTS_DIR};
|
||||||
|
|
||||||
|
pub fn save_code_bundle(
|
||||||
|
bot_code: &str,
|
||||||
|
bot_id: Option<i32>,
|
||||||
|
conn: &PgConnection,
|
||||||
|
) -> QueryResult<db::bots::CodeBundle> {
|
||||||
|
let bundle_name = gen_alphanumeric(16);
|
||||||
|
|
||||||
|
let code_bundle_dir = PathBuf::from(BOTS_DIR).join(&bundle_name);
|
||||||
|
std::fs::create_dir(&code_bundle_dir).unwrap();
|
||||||
|
std::fs::write(code_bundle_dir.join("bot.py"), bot_code).unwrap();
|
||||||
|
|
||||||
|
let new_code_bundle = db::bots::NewCodeBundle {
|
||||||
|
bot_id,
|
||||||
|
path: &bundle_name,
|
||||||
|
};
|
||||||
|
db::bots::create_code_bundle(&new_code_bundle, conn)
|
||||||
|
}
|
3
planetwars-server/src/modules/mod.rs
Normal file
3
planetwars-server/src/modules/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// This module implements general domain logic, not directly
|
||||||
|
// tied to the database or API layers.
|
||||||
|
pub mod bots;
|
|
@ -97,7 +97,7 @@ pub async fn upload_code_multipart(
|
||||||
.map_err(|_| StatusCode::BAD_REQUEST)?;
|
.map_err(|_| StatusCode::BAD_REQUEST)?;
|
||||||
|
|
||||||
let bundle = bots::NewCodeBundle {
|
let bundle = bots::NewCodeBundle {
|
||||||
bot_id: bot.id,
|
bot_id: Some(bot.id),
|
||||||
path: &folder_name,
|
path: &folder_name,
|
||||||
};
|
};
|
||||||
let code_bundle =
|
let code_bundle =
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::db::matches::{self, MatchState};
|
use crate::db::matches::{self, MatchState};
|
||||||
|
use crate::modules::bots::save_code_bundle;
|
||||||
|
use crate::util::gen_alphanumeric;
|
||||||
use crate::{ConnectionPool, BOTS_DIR, MAPS_DIR, MATCHES_DIR};
|
use crate::{ConnectionPool, BOTS_DIR, MAPS_DIR, MATCHES_DIR};
|
||||||
use axum::extract::Extension;
|
use axum::extract::Extension;
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use planetwars_matchrunner::{docker_runner::DockerBotSpec, run_match, MatchConfig, MatchPlayer};
|
use planetwars_matchrunner::{docker_runner::DockerBotSpec, run_match, MatchConfig, MatchPlayer};
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ const SIMPLEBOT_PATH: &'static str = "../simplebot";
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct SubmitBotParams {
|
pub struct SubmitBotParams {
|
||||||
|
pub bot_name: Option<String>,
|
||||||
pub code: String,
|
pub code: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,14 +34,12 @@ pub async fn submit_bot(
|
||||||
) -> Result<Json<SubmitBotResponse>, StatusCode> {
|
) -> Result<Json<SubmitBotResponse>, StatusCode> {
|
||||||
let conn = pool.get().await.expect("could not get database connection");
|
let conn = pool.get().await.expect("could not get database connection");
|
||||||
|
|
||||||
let uploaded_bot_uuid: String = gen_alphanumeric(16);
|
let code_bundle = save_code_bundle(¶ms.code, None, &conn)
|
||||||
|
// TODO: can we recover from this?
|
||||||
|
.expect("could not save bot code");
|
||||||
|
|
||||||
let log_file_name = format!("{}.log", gen_alphanumeric(16));
|
let log_file_name = format!("{}.log", gen_alphanumeric(16));
|
||||||
|
let uploaded_bot_dir = PathBuf::from(BOTS_DIR).join(&code_bundle.path);
|
||||||
// store uploaded bot
|
|
||||||
let uploaded_bot_dir = PathBuf::from(BOTS_DIR).join(&uploaded_bot_uuid);
|
|
||||||
std::fs::create_dir(&uploaded_bot_dir).unwrap();
|
|
||||||
std::fs::write(uploaded_bot_dir.join("bot.py"), params.code.as_bytes()).unwrap();
|
|
||||||
|
|
||||||
// play the match
|
// play the match
|
||||||
let match_config = MatchConfig {
|
let match_config = MatchConfig {
|
||||||
map_path: PathBuf::from(MAPS_DIR).join("hex.json"),
|
map_path: PathBuf::from(MAPS_DIR).join("hex.json"),
|
||||||
|
@ -95,11 +95,3 @@ async fn run_match_task(match_id: i32, match_config: MatchConfig, connection_poo
|
||||||
matches::set_match_state(match_id, MatchState::Finished, &conn)
|
matches::set_match_state(match_id, MatchState::Finished, &conn)
|
||||||
.expect("failed to update match state");
|
.expect("failed to update match state");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_alphanumeric(length: usize) -> String {
|
|
||||||
rand::thread_rng()
|
|
||||||
.sample_iter(&Alphanumeric)
|
|
||||||
.take(length)
|
|
||||||
.map(char::from)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ table! {
|
||||||
|
|
||||||
code_bundles (id) {
|
code_bundles (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
bot_id -> Int4,
|
bot_id -> Nullable<Int4>,
|
||||||
path -> Text,
|
path -> Text,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
}
|
}
|
||||||
|
|
9
planetwars-server/src/util.rs
Normal file
9
planetwars-server/src/util.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
|
|
||||||
|
pub fn gen_alphanumeric(length: usize) -> String {
|
||||||
|
rand::thread_rng()
|
||||||
|
.sample_iter(&Alphanumeric)
|
||||||
|
.take(length)
|
||||||
|
.map(char::from)
|
||||||
|
.collect()
|
||||||
|
}
|
Loading…
Reference in a new issue