docker bot runner PoC
This commit is contained in:
parent
3e18c840d1
commit
5f1c7385c7
2 changed files with 52 additions and 18 deletions
|
@ -19,3 +19,4 @@ serde_json = "1.0"
|
||||||
planetwars-rules = { path = "../planetwars-rules" }
|
planetwars-rules = { path = "../planetwars-rules" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
bollard = "0.11"
|
bollard = "0.11"
|
||||||
|
bytes = "1.1"
|
|
@ -10,15 +10,17 @@ use std::sync::{Arc, Mutex};
|
||||||
use bollard::container::{self, LogOutput};
|
use bollard::container::{self, LogOutput};
|
||||||
use bollard::exec::StartExecResults;
|
use bollard::exec::StartExecResults;
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
|
use bytes::Bytes;
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use planetwars_matchrunner::{
|
use planetwars_matchrunner::{
|
||||||
match_context::{EventBus, MatchCtx, PlayerHandle},
|
match_context::{EventBus, MatchCtx, PlayerHandle, RequestMessage},
|
||||||
pw_match, MatchConfig, MatchMeta, PlayerInfo,
|
pw_match, MatchConfig, MatchMeta, PlayerInfo,
|
||||||
};
|
};
|
||||||
use planetwars_rules::protocol as proto;
|
use planetwars_rules::protocol as proto;
|
||||||
use planetwars_rules::PwConfig;
|
use planetwars_rules::PwConfig;
|
||||||
use std::env;
|
use std::env;
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
const IMAGE: &'static str = "simplebot:latest";
|
const IMAGE: &'static str = "simplebot:latest";
|
||||||
|
|
||||||
|
@ -64,9 +66,12 @@ async fn create_player_process(docker: &Docker) -> Result<(), bollard::errors::E
|
||||||
.id;
|
.id;
|
||||||
|
|
||||||
let start_exec_results = docker.start_exec(&exec_id, None).await?;
|
let start_exec_results = docker.start_exec(&exec_id, None).await?;
|
||||||
let (mut input, mut output) = match start_exec_results {
|
let mut process = match start_exec_results {
|
||||||
StartExecResults::Detached => panic!("failed to get io channels"),
|
StartExecResults::Detached => panic!("failed to get io channels"),
|
||||||
StartExecResults::Attached { input, output } => (input, output),
|
StartExecResults::Attached { input, output } => ContainerProcess {
|
||||||
|
stdin: input,
|
||||||
|
output,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = proto::State {
|
let state = proto::State {
|
||||||
|
@ -90,22 +95,50 @@ async fn create_player_process(docker: &Docker) -> Result<(), bollard::errors::E
|
||||||
};
|
};
|
||||||
|
|
||||||
let serialized = serde_json::to_vec(&state).unwrap();
|
let serialized = serde_json::to_vec(&state).unwrap();
|
||||||
input.write_all(&serialized).await?;
|
let out = process.communicate(&serialized).await?;
|
||||||
input.write(b"\n").await?;
|
|
||||||
input.flush().await?;
|
|
||||||
|
|
||||||
while let Some(item) = output.next().await {
|
print!("{}", String::from_utf8(out.to_vec()).unwrap());
|
||||||
let log_output = item.expect("failed to get log output");
|
|
||||||
match log_output {
|
|
||||||
LogOutput::StdOut { message } => {
|
|
||||||
println!("stdout: {}", String::from_utf8_lossy(&message));
|
|
||||||
}
|
|
||||||
LogOutput::StdErr { message } => {
|
|
||||||
println!("stderr: {}", String::from_utf8_lossy(&message));
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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!("stderr: {}", String::from_utf8_lossy(&message));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"no response received",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue