diff options
author | Ilion Beyst <ilion.beyst@gmail.com> | 2022-01-22 14:32:43 +0100 |
---|---|---|
committer | Ilion Beyst <ilion.beyst@gmail.com> | 2022-01-22 14:32:43 +0100 |
commit | f62196d983c04a94b892086a4ea6926bd7b6e4fb (patch) | |
tree | 7e0399883be05dcc78a931df3d7939bb81840e85 /planetwars-matchrunner/src/bin | |
parent | 3dd940321cd7f6e4356afa4e505bac8016c83707 (diff) | |
download | planetwars.dev-f62196d983c04a94b892086a4ea6926bd7b6e4fb.tar.xz planetwars.dev-f62196d983c04a94b892086a4ea6926bd7b6e4fb.zip |
implement docker runner
Diffstat (limited to 'planetwars-matchrunner/src/bin')
-rw-r--r-- | planetwars-matchrunner/src/bin/testmatch.rs | 162 |
1 files changed, 18 insertions, 144 deletions
diff --git a/planetwars-matchrunner/src/bin/testmatch.rs b/planetwars-matchrunner/src/bin/testmatch.rs index a072b9f..db160cf 100644 --- a/planetwars-matchrunner/src/bin/testmatch.rs +++ b/planetwars-matchrunner/src/bin/testmatch.rs @@ -1,28 +1,6 @@ -extern crate planetwars_matchrunner; -extern crate tokio; +use std::{env, path::PathBuf}; -use std::collections::HashMap; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; -use std::pin::Pin; -use std::sync::{Arc, Mutex}; - -use bollard::container::{self, AttachContainerOptions, AttachContainerResults, LogOutput}; -use bollard::exec::StartExecResults; -use bollard::Docker; -use bytes::Bytes; -use futures::{Stream, StreamExt}; -use planetwars_matchrunner::{ - match_context::{EventBus, MatchCtx, PlayerHandle, RequestMessage}, - pw_match, MatchConfig, MatchMeta, PlayerInfo, -}; -use planetwars_rules::protocol as proto; -use std::env; -use tokio::io::{AsyncWrite, AsyncWriteExt}; -use tokio::sync::mpsc; - -const IMAGE: &'static str = "python:3.10.1-slim-buster"; -// const IMAGE: &'static str = "simplebot:latest"; +use planetwars_matchrunner::{docker_runner::DockerBotSpec, run_match, MatchConfig, MatchPlayer}; #[tokio::main] async fn main() { @@ -32,134 +10,30 @@ async fn main() { _run_match(map_path).await; } +const IMAGE: &'static str = "python:3.10-slim-buster"; + async fn _run_match(map_path: String) { - let docker = Docker::connect_with_socket_defaults().unwrap(); let code_dir_path = PathBuf::from("../simplebot"); - let params = BotParams { - image: IMAGE, - code_path: &code_dir_path, - argv: vec!["python", "simplebot.py"], + let bot_spec = DockerBotSpec { + image: IMAGE.to_string(), + code_path: code_dir_path, + argv: vec!["python".to_string(), "simplebot.py".to_string()], }; - let mut process = spawn_docker_process(&docker, params).await.unwrap(); - let state = proto::State { - planets: vec![ - proto::Planet { + run_match(MatchConfig { + map_path: PathBuf::from(map_path), + map_name: "hex".to_string(), + log_path: PathBuf::from("match.log"), + players: vec![ + MatchPlayer { name: "a".to_string(), - owner: Some(1), - ship_count: 100, - x: -1.0, - y: 0.0, + bot_spec: Box::new(bot_spec.clone()), }, - proto::Planet { + MatchPlayer { name: "b".to_string(), - owner: Some(2), - ship_count: 100, - x: 1.0, - y: 0.0, + bot_spec: Box::new(bot_spec.clone()), }, ], - expeditions: vec![], - }; - - let serialized = serde_json::to_vec(&state).unwrap(); - let out = process.communicate(&serialized).await.unwrap(); - - print!("got output: {}", String::from_utf8(out.to_vec()).unwrap()); -} - -pub struct BotParams<'a> { - pub image: &'a str, - pub code_path: &'a Path, - pub argv: Vec<&'a str>, -} - -async fn spawn_docker_process( - docker: &Docker, - params: BotParams<'_>, -) -> Result<ContainerProcess, bollard::errors::Error> { - let bot_code_dir = std::fs::canonicalize(params.code_path).unwrap(); - let code_dir_str = bot_code_dir.as_os_str().to_str().unwrap(); - - let config = container::Config { - image: Some(params.image), - host_config: Some(bollard::models::HostConfig { - binds: Some(vec![format!("{}:{}", code_dir_str, "/workdir")]), - ..Default::default() - }), - working_dir: Some("/workdir"), - cmd: Some(params.argv), - attach_stdin: Some(true), - attach_stdout: Some(true), - attach_stderr: Some(true), - open_stdin: Some(true), - ..Default::default() - }; - - let response = docker.create_container::<&str, &str>(None, config).await?; - let container_id = response.id; - - docker - .start_container::<String>(&container_id, None) - .await?; - - let AttachContainerResults { output, input } = docker - .attach_container( - &container_id, - Some(AttachContainerOptions::<String> { - stdout: Some(true), - stderr: Some(true), - stdin: Some(true), - stream: Some(true), - logs: Some(true), - ..Default::default() - }), - ) - .await?; - - Ok(ContainerProcess { - stdin: input, - output, }) -} - -pub struct ContainerProcess { - stdin: Pin<Box<dyn AsyncWrite + Send>>, - output: Pin<Box<dyn Stream<Item = Result<LogOutput, bollard::errors::Error>>>>, -} - -impl ContainerProcess { - pub async fn communicate(&mut self, input: &[u8]) -> io::Result<Bytes> { - self.write_line(input).await?; - self.read_line().await - } - - async fn write_line(&mut self, bytes: &[u8]) -> io::Result<()> { - self.stdin.write_all(bytes).await?; - self.stdin.write_u8(b'\n').await?; - self.stdin.flush().await?; - Ok(()) - } - - async fn read_line(&mut self) -> io::Result<Bytes> { - while let Some(item) = self.output.next().await { - let log_output = item.expect("failed to get log output"); - match log_output { - LogOutput::StdOut { message } => { - // TODO: this is not correct (buffering and such) - return Ok(message); - } - LogOutput::StdErr { message } => { - // TODO - println!("{}", String::from_utf8_lossy(&message)); - } - _ => (), - } - } - - Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "no response received", - )) - } + .await; } |