save all uploaded code bundles in database

This commit is contained in:
Ilion Beyst 2022-02-27 20:35:22 +01:00
parent 6ef6a872fe
commit 22a8f3d619
9 changed files with 60 additions and 26 deletions

View file

@ -8,7 +8,7 @@ CREATE UNIQUE INDEX bots_index ON bots(owner_id, name);
CREATE TABLE code_bundles (
id serial PRIMARY KEY,
bot_id integer REFERENCES bots(id) NOT NULL,
bot_id integer REFERENCES bots(id),
path text NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

View file

@ -42,14 +42,14 @@ pub fn find_all_bots(conn: &PgConnection) -> QueryResult<Vec<Bot>> {
#[derive(Insertable)]
#[table_name = "code_bundles"]
pub struct NewCodeBundle<'a> {
pub bot_id: i32,
pub bot_id: Option<i32>,
pub path: &'a str,
}
#[derive(Queryable, Serialize, Deserialize, Debug)]
pub struct CodeBundle {
pub id: i32,
pub bot_id: i32,
pub bot_id: Option<i32>,
pub path: String,
pub created_at: chrono::NaiveDateTime,
}

View file

@ -3,15 +3,17 @@ extern crate diesel;
pub mod db;
pub mod db_types;
pub mod modules;
pub mod routes;
pub mod schema;
pub mod util;
use std::ops::Deref;
use axum;
use bb8::PooledConnection;
use bb8::{Pool, PooledConnection};
use bb8_diesel::{self, DieselConnectionManager};
use diesel::PgConnection;
use diesel::{Connection, PgConnection};
use serde::Deserialize;
use axum::{
@ -29,9 +31,14 @@ const MAPS_DIR: &str = "./data/maps";
type ConnectionPool = bb8::Pool<DieselConnectionManager<PgConnection>>;
pub async fn api(configuration: Configuration) -> Router {
let manager = DieselConnectionManager::<PgConnection>::new(configuration.database_url);
pub async fn prepare_db(database_url: &str) -> Pool<DieselConnectionManager<PgConnection>> {
let manager = DieselConnectionManager::<PgConnection>::new(database_url);
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()
.route("/register", post(routes::users::register))
@ -57,7 +64,7 @@ pub async fn api(configuration: Configuration) -> Router {
get(routes::matches::get_match_log),
)
.route("/submit_bot", post(routes::demo::submit_bot))
.layer(AddExtensionLayer::new(pool));
.layer(AddExtensionLayer::new(db_pool));
api
}

View 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)
}

View file

@ -0,0 +1,3 @@
// This module implements general domain logic, not directly
// tied to the database or API layers.
pub mod bots;

View file

@ -97,7 +97,7 @@ pub async fn upload_code_multipart(
.map_err(|_| StatusCode::BAD_REQUEST)?;
let bundle = bots::NewCodeBundle {
bot_id: bot.id,
bot_id: Some(bot.id),
path: &folder_name,
};
let code_bundle =

View file

@ -1,10 +1,11 @@
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 axum::extract::Extension;
use axum::Json;
use hyper::StatusCode;
use planetwars_matchrunner::{docker_runner::DockerBotSpec, run_match, MatchConfig, MatchPlayer};
use rand::{distributions::Alphanumeric, Rng};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
@ -15,6 +16,7 @@ const SIMPLEBOT_PATH: &'static str = "../simplebot";
#[derive(Serialize, Deserialize, Debug)]
pub struct SubmitBotParams {
pub bot_name: Option<String>,
pub code: String,
}
@ -32,14 +34,12 @@ pub async fn submit_bot(
) -> Result<Json<SubmitBotResponse>, StatusCode> {
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(&params.code, None, &conn)
// TODO: can we recover from this?
.expect("could not save bot code");
let log_file_name = format!("{}.log", gen_alphanumeric(16));
// 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();
let uploaded_bot_dir = PathBuf::from(BOTS_DIR).join(&code_bundle.path);
// play the match
let match_config = MatchConfig {
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)
.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()
}

View file

@ -18,7 +18,7 @@ table! {
code_bundles (id) {
id -> Int4,
bot_id -> Int4,
bot_id -> Nullable<Int4>,
path -> Text,
created_at -> Timestamp,
}

View 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()
}