aboutsummaryrefslogtreecommitdiff
path: root/planetwars-matchrunner/src/bin
diff options
context:
space:
mode:
authorIlion Beyst <ilion.beyst@gmail.com>2022-01-22 14:32:43 +0100
committerIlion Beyst <ilion.beyst@gmail.com>2022-01-22 14:32:43 +0100
commitf62196d983c04a94b892086a4ea6926bd7b6e4fb (patch)
tree7e0399883be05dcc78a931df3d7939bb81840e85 /planetwars-matchrunner/src/bin
parent3dd940321cd7f6e4356afa4e505bac8016c83707 (diff)
downloadplanetwars.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.rs162
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;
}