diff options
author | Joxit <Joxit972@gmail.com> | 2017-05-06 23:34:39 +0200 |
---|---|---|
committer | Joxit <Joxit972@gmail.com> | 2017-05-06 23:34:39 +0200 |
commit | c2bf1d2d8056eae8297fb1b4d452f0656816f7fa (patch) | |
tree | bed0f382d606a1e5142c16a7ac013b170f1e26c6 | |
parent | e57f0932741003ded8913de2385d5ce6672e645e (diff) |
[ExecContainer] exec can now return stdout and stderr #50
-rw-r--r-- | examples/containerexec.rs | 13 | ||||
-rw-r--r-- | src/builder.rs | 23 | ||||
-rw-r--r-- | src/lib.rs | 27 | ||||
-rw-r--r-- | src/tty.rs | 34 |
4 files changed, 78 insertions, 19 deletions
diff --git a/examples/containerexec.rs b/examples/containerexec.rs index fe542f2..b41d1ac 100644 --- a/examples/containerexec.rs +++ b/examples/containerexec.rs @@ -6,14 +6,17 @@ use std::env; fn main() { let docker = Docker::new(); let options = ExecContainerOptions::builder() - .cmd(vec!["ls"]) + .cmd(vec!["bash", "-c", "echo -n \"echo VAR=$VAR on stdout\"; echo -n \"echo VAR=$VAR on stderr\" >&2"]) .env(vec!["VAR=value"]) + .attach_stdout(true) + .attach_stderr(true) .build(); if let Some(id) = env::args().nth(1) { - match docker.containers() - .get(&id) - .exec(&options) { - Ok(res) => println!("Success: {:?}", res), + match docker.containers().get(&id).exec(&options) { + Ok(res) => { + println!("Stdout: {}", res.stdout); + println!("Stderr: {}", res.stderr); + } Err(err) => println!("An error occured: {:?}", err), } } diff --git a/src/builder.rs b/src/builder.rs index aa5b7df..fac07d1 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -454,6 +454,7 @@ impl ContainerOptionsBuilder { pub struct ExecContainerOptions { params: HashMap<&'static str, Vec<String>>, + params_bool: HashMap<&'static str, bool>, } impl ExecContainerOptions { @@ -469,6 +470,9 @@ impl ExecContainerOptions { for (k, v) in &self.params { body.insert(k.to_string(), v.to_json()); } + for (k, v) in &self.params_bool { + body.insert(k.to_string(), v.to_json()); + } let json_obj: Json = body.to_json(); Ok(try!(json::encode(&json_obj))) @@ -478,11 +482,13 @@ impl ExecContainerOptions { #[derive(Default)] pub struct ExecContainerOptionsBuilder { params: HashMap<&'static str, Vec<String>>, + params_bool: HashMap<&'static str, bool>, } impl ExecContainerOptionsBuilder { pub fn new() -> ExecContainerOptionsBuilder { - ExecContainerOptionsBuilder { params: HashMap::new() } + ExecContainerOptionsBuilder { params: HashMap::new(), + params_bool: HashMap::new() } } /// Command to run, as an array of strings @@ -501,8 +507,21 @@ impl ExecContainerOptionsBuilder { self } +/// Attach to stdout of the exec command + pub fn attach_stdout(&mut self, stdout: bool) -> &mut ExecContainerOptionsBuilder { + self.params_bool.insert("AttachStdout", stdout); + self + } + +/// Attach to stderr of the exec command + pub fn attach_stderr(&mut self, stderr: bool) -> &mut ExecContainerOptionsBuilder { + self.params_bool.insert("AttachStderr", stderr); + self + } + pub fn build(&self) -> ExecContainerOptions { - ExecContainerOptions { params: self.params.clone() } + ExecContainerOptions { params: self.params.clone(), + params_bool: self.params_bool.clone() } } } @@ -29,6 +29,7 @@ pub mod builder; pub mod rep; pub mod transport; pub mod errors; +pub mod tty; mod tarball; @@ -50,6 +51,7 @@ use rep::{NetworkDetails as NetworkInfo, NetworkCreateInfo}; use rep::{Output, PullInfo, Change, ContainerCreateInfo, ContainerDetails, Container as ContainerRep, Event, Exit, History, ImageDetails, Info, SearchResult, Stats, Status, Top, Version}; +use tty::Tty; use rustc_serialize::json::{self, Json}; use std::borrow::Cow; use std::env::{self, VarError}; @@ -419,26 +421,27 @@ impl<'a, 'b> Container<'a, 'b> { } /// Exec the specified command in the container - pub fn exec(&self, opts: &ExecContainerOptions) -> Result<()> { + pub fn exec(&self, opts: &ExecContainerOptions) -> Result<Tty> { let data = try!(opts.serialize()); let mut bytes = data.as_bytes(); match self.docker - .post(&format!("/containers/{}/exec", self.id)[..], - Some((&mut bytes, ContentType::json()))) { + .post(&format!("/containers/{}/exec", self.id)[..], + Some((&mut bytes, ContentType::json()))) { Err(e) => Err(e), Ok(res) => { let data = "{}"; let mut bytes = data.as_bytes(); self.docker - .post(&format!("/exec/{}/start", - Json::from_str(res.as_str()) - .unwrap() - .search("Id") - .unwrap() - .as_string() - .unwrap())[..], - Some((&mut bytes, ContentType::json()))) - .map(|_| ()) + .stream_post(&format!("/exec/{}/start", + Json::from_str(res.as_str()) + .unwrap() + .search("Id") + .unwrap() + .as_string() + .unwrap()) + [..], + Some((&mut bytes, ContentType::json()))) + .map(|stream| Tty::new(stream)) } } } diff --git a/src/tty.rs b/src/tty.rs new file mode 100644 index 0000000..446ab26 --- /dev/null +++ b/src/tty.rs @@ -0,0 +1,34 @@ +use std::io::Read; +pub struct Tty { + pub stdout: String, + pub stderr: String, +} + +impl Tty { + pub fn new(mut stream: Box<Read>) -> Tty { + let mut stdout: Vec<u8> = vec![]; + let mut stderr: Vec<u8> = vec![]; + loop { + let mut header = [0; 8]; + match stream.read(&mut header) { + Ok(0) => break, + Ok(_) => { + let mut body: Vec<u8> = vec![0; header[7] as usize]; + if let Ok(_) = stream.read(&mut body) { + if header[0] == 1 { + stdout.append(&mut body); + } + if header[0] == 2 { + stderr.append(&mut body); + } + }; + } + Err(_) => break, + } + } + Tty { + stdout: String::from_utf8_lossy(&stdout).to_string(), + stderr: String::from_utf8_lossy(&stderr).to_string(), + } + } +} |