summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--Cargo.toml1
-rw-r--r--examples/imagebuild.rs14
-rw-r--r--src/builder.rs77
-rw-r--r--src/lib.rs88
-rw-r--r--src/transport.rs29
6 files changed, 169 insertions, 44 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c8154e5..182b65f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.2.1 (unreleased)
+
+* removed `Body` type with a preference for `Into<hyper::client::Body>`
+
# 0.2.0
* many breaking changes required to make interfaces consistent, idomatic, and future friendly
diff --git a/Cargo.toml b/Cargo.toml
index d91f0ad..c23ac25 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@ jed = "0.1"
mime = "0.1"
openssl = "0.7"
rustc-serialize = "0.3"
+tar = "0.3"
url = "0.5"
[dev-dependencies]
diff --git a/examples/imagebuild.rs b/examples/imagebuild.rs
new file mode 100644
index 0000000..ed08949
--- /dev/null
+++ b/examples/imagebuild.rs
@@ -0,0 +1,14 @@
+extern crate shiplift;
+
+use shiplift::{BuildOptions, Docker};
+use std::env;
+
+fn main() {
+ let docker = Docker::new();
+ if let Some(path) = env::args().nth(1) {
+ let image = docker.images()
+ .build(&BuildOptions::builder(path).build())
+ .unwrap();
+ println!("{:?}", image);
+ }
+}
diff --git a/src/builder.rs b/src/builder.rs
index 77265fc..a436a1e 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -4,6 +4,83 @@ use self::super::Result;
use std::collections::{BTreeMap, HashMap};
use rustc_serialize::json::{self, Json, ToJson};
use url::form_urlencoded;
+use self::super::hyper::client::Body;
+
+#[derive(Default)]
+pub struct BuildOptions {
+ pub path: String,
+ params: HashMap<&'static str, String>,
+}
+
+impl BuildOptions {
+ pub fn builder<S>(path: S) -> BuildOptionsBuilder where S: Into<String> {
+ BuildOptionsBuilder::new(path)
+ }
+ pub fn serialize(&self) -> Option<String> {
+ if self.params.is_empty() {
+ None
+ } else {
+ Some(form_urlencoded::serialize(&self.params))
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct BuildOptionsBuilder {
+ path: String,
+ params: HashMap<&'static str, String>,
+}
+
+impl BuildOptionsBuilder {
+ pub fn new<S>(path: S) -> BuildOptionsBuilder where S: Into<String>{
+ BuildOptionsBuilder {
+ path: path.into(),
+ ..Default::default()
+ }
+ }
+
+ pub fn dockerfile<P>(&mut self, path: P) -> &mut BuildOptionsBuilder where P: Into<String> {
+ self.params.insert("dockerfile", path.into());
+ self
+ }
+
+ pub fn tag<T>(&mut self, t: T) -> &mut BuildOptionsBuilder where T: Into<String> {
+ self.params.insert("t", t.into());
+ self
+ }
+
+ pub fn remote<R>(&mut self, r: R) -> &mut BuildOptionsBuilder where R: Into<String> {
+ self.params.insert("remote", r.into());
+ self
+ }
+
+ pub fn nocache<R>(&mut self, nc: bool) -> &mut BuildOptionsBuilder {
+ self.params.insert("nocache", nc.to_string());
+ self
+ }
+
+ pub fn rm(&mut self, r: bool) -> &mut BuildOptionsBuilder {
+ self.params.insert("rm", r.to_string());
+ self
+ }
+
+ pub fn forcerm(&mut self, fr: bool) -> &mut BuildOptionsBuilder {
+ self.params.insert("forcerm", fr.to_string());
+ self
+ }
+
+ // todo: memory
+ // todo: memswap
+ // todo: cpushares
+ // todo: cpusetcpus
+ // todo: cpuperiod
+ // todo: cpuquota
+ // todo: buildargs
+
+ pub fn build(&self) -> BuildOptions {
+ BuildOptions { path: self.path.clone(), params: self.params.clone() }
+ }
+}
/// Options for filtering container list results
#[derive(Default)]
diff --git a/src/lib.rs b/src/lib.rs
index 6e4039a..1cf96a8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,14 +21,18 @@ extern crate jed;
extern crate openssl;
extern crate rustc_serialize;
extern crate url;
+extern crate tar;
pub mod builder;
pub mod rep;
pub mod transport;
pub mod errors;
+use std::fs::{self, DirEntry, File};
+use tar::Archive;
+
pub use errors::Error;
-pub use builder::{ContainerOptions, ContainerListOptions, ContainerFilter, EventsOptions, ImageFilter,
+pub use builder::{BuildOptions, ContainerOptions, ContainerListOptions, ContainerFilter, EventsOptions, ImageFilter,
ImageListOptions, LogsOptions};
use hyper::{Client, Url};
use hyper::net::{HttpsConnector, Openssl};
@@ -41,12 +45,13 @@ use rep::{Change, ContainerCreateInfo, ContainerDetails, Container as ContainerR
Info, SearchResult, Stats, Status, Top, Version};
use rustc_serialize::json::{self, Json};
use std::env::{self, VarError};
-use std::io::Read;
+use std::io::{self, Read};
use std::iter::IntoIterator;
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
-use transport::{Body, Transport};
+use transport::{Transport};
+use hyper::client::Body;
use url::{form_urlencoded, Host, RelativeSchemeData, SchemeData};
/// Represents the result of all docker operations
@@ -128,6 +133,41 @@ impl<'a> Images<'a> {
Images { docker: docker }
}
+ pub fn build(&self, opts: &BuildOptions) -> Result<String> {
+ let mut path = vec!["/build".to_owned()];
+ if let Some(query) = opts.serialize() {
+ path.push(query)
+ }
+
+
+ let file = File::create("build.tar").unwrap();
+ let mut a = Archive::new(file);
+
+ fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
+ if try!(fs::metadata(dir)).is_dir() {
+ for entry in try!(fs::read_dir(dir)) {
+ let entry = try!(entry);
+ if try!(fs::metadata(entry.path())).is_dir() {
+ try!(visit_dirs(&entry.path(), cb));
+ } else {
+ cb(&entry);
+ }
+ }
+ }
+ Ok(())
+ }
+
+ {
+ let append = |e: &DirEntry| {
+ a.append_path(e.path());
+ };
+ visit_dirs(Path::new(&opts.path[..]), &append);
+ a.finish();
+ }
+
+ self.docker.post(&path.join("?"), Some(Body::ChunkedBody(&mut a.into_inner())))
+ }
+
/// Lists the docker images on the current docker host
pub fn list(&self, opts: &ImageListOptions) -> Result<Vec<ImageRep>> {
let mut path = vec!["/images/json".to_owned()];
@@ -236,8 +276,8 @@ impl<'a, 'b> Container<'a, 'b> {
}
/// Start the container instance
- pub fn start(&self) -> Result<()> {
- self.docker.post(&format!("/containers/{}/start", self.id)[..], None).map(|_| ())
+ pub fn start(&'a self) -> Result<()> {
+ self.docker.post(&format!("/containers/{}/start", self.id)[..], None as Option<&'a str>).map(|_| ())
}
/// Stop the container instance
@@ -247,7 +287,7 @@ impl<'a, 'b> Container<'a, 'b> {
let encoded = form_urlencoded::serialize(vec![("t", w.as_secs().to_string())]);
path.push(encoded)
}
- self.docker.post(&path.join("?"), None).map(|_| ())
+ self.docker.post(&path.join("?"), None as Option<&'a str>).map(|_| ())
}
/// Restart the container instance
@@ -257,7 +297,7 @@ impl<'a, 'b> Container<'a, 'b> {
let encoded = form_urlencoded::serialize(vec![("t", w.as_secs().to_string())]);
path.push(encoded)
}
- self.docker.post(&path.join("?"), None).map(|_| ())
+ self.docker.post(&path.join("?"), None as Option<&'a str>).map(|_| ())
}
/// Kill the container instance
@@ -267,7 +307,7 @@ impl<'a, 'b> Container<'a, 'b> {
let encoded = form_urlencoded::serialize(vec![("signal", sig.to_owned())]);
path.push(encoded)
}
- self.docker.post(&path.join("?"), None).map(|_| ())
+ self.docker.post(&path.join("?"), None as Option<&'a str>).map(|_| ())
}
/// Rename the container instance
@@ -275,23 +315,26 @@ impl<'a, 'b> Container<'a, 'b> {
let query = form_urlencoded::serialize(vec![("name", name)]);
self.docker
.post(&format!("/containers/{}/rename?{}", self.id, query)[..],
- None)
+ None as Option<&'a str>)
.map(|_| ())
}
/// Pause the container instance
pub fn pause(&self) -> Result<()> {
- self.docker.post(&format!("/containers/{}/pause", self.id)[..], None).map(|_| ())
+ let empty: Option<&'a str> = None;
+ self.docker.post(&format!("/containers/{}/pause", self.id)[..], empty).map(|_| ())
}
/// Unpause the container instance
pub fn unpause(&self) -> Result<()> {
- self.docker.post(&format!("/containers/{}/unpause", self.id)[..], None).map(|_| ())
+ let empty: Option<&'a str> = None;
+ self.docker.post(&format!("/containers/{}/unpause", self.id)[..], empty).map(|_| ())
}
/// Wait until the container stops
pub fn wait(&self) -> Result<Exit> {
- let raw = try!(self.docker.post(&format!("/containers/{}/wait", self.id)[..], None));
+ let empty: Option<&'a str> = None;
+ let raw = try!(self.docker.post(&format!("/containers/{}/wait", self.id)[..], empty));
Ok(try!(json::decode::<Exit>(&raw)))
}
@@ -334,8 +377,7 @@ impl<'a> Containers<'a> {
let data = try!(opts.serialize());
let mut bytes = data.as_bytes();
let raw = try!(self.docker.post("/containers/create",
- Some(Body::new(&mut Box::new(&mut bytes),
- bytes.len() as u64))));
+ Some(&mut bytes)));
Ok(try!(json::decode::<ContainerCreateInfo>(&raw)))
}
}
@@ -452,23 +494,23 @@ impl Docker {
Ok(Box::new(it))
}
- fn get(&self, endpoint: &str) -> Result<String> {
- self.transport.request(Method::Get, endpoint, None)
+ fn get<'a>(&self, endpoint: &str) -> Result<String> {
+ self.transport.request(Method::Get, endpoint, None as Option<&'a str>)
}
- fn post(&self, endpoint: &str, body: Option<Body>) -> Result<String> {
+ fn post<'a, B>(&'a self, endpoint: &str, body: Option<B>) -> Result<String> where B: Into<Body<'a>> {
self.transport.request(Method::Post, endpoint, body)
}
- fn delete(&self, endpoint: &str) -> Result<String> {
- self.transport.request(Method::Delete, endpoint, None)
+ fn delete<'a>(&self, endpoint: &str) -> Result<String> {
+ self.transport.request(Method::Delete, endpoint, None as Option<&'a str>)
}
- fn stream_post(&self, endpoint: &str) -> Result<Box<Read>> {
- self.transport.stream(Method::Post, endpoint, None)
+ fn stream_post<'a>(&self, endpoint: &str) -> Result<Box<Read>> {
+ self.transport.stream(Method::Post, endpoint, None as Option<&'a str>)
}
- fn stream_get(&self, endpoint: &str) -> Result<Box<Read>> {
- self.transport.stream(Method::Get, endpoint, None)
+ fn stream_get<'a>(&self, endpoint: &str) -> Result<Box<Read>> {
+ self.transport.stream(Method::Get, endpoint, None as Option<&'a str>)
}
}
diff --git a/src/transport.rs b/src/transport.rs
index dcc70d2..c035fa5 100644
--- a/src/transport.rs
+++ b/src/transport.rs
@@ -5,6 +5,7 @@ extern crate mime;
use hyper::Client;
use hyper::client;
+use hyper::client::Body;
use self::super::{Error, Result};
use self::hyper::buffer::BufReader;
use self::hyper::header::ContentType;
@@ -40,7 +41,7 @@ impl fmt::Debug for Transport {
}
impl Transport {
- pub fn request(&self, method: Method, endpoint: &str, body: Option<Body>) -> Result<String> {
+ pub fn request<'a, B>(&'a self, method: Method, endpoint: &str, body: Option<B>) -> Result<String> where B: Into<Body<'a>> {
let mut res = match self.stream(method, endpoint, body) {
Ok(r) => r,
Err(e) => panic!("failed request {:?}", e),
@@ -51,7 +52,9 @@ impl Transport {
Ok(body)
}
- pub fn stream(&self, method: Method, endpoint: &str, body: Option<Body>) -> Result<Box<Read>> {
+ pub fn stream<'c, B>(
+ &'c self, method: Method, endpoint: &str, body: Option<B>
+ ) -> Result<Box<Read>> where B: Into<Body<'c>> {
let req = match *self {
Transport::Tcp { ref client, ref host } => {
client.request(method, &format!("{}{}", host, endpoint)[..])
@@ -62,9 +65,9 @@ impl Transport {
};
let embodied = match body {
- Some(Body { read: r, size: l }) => {
- let reader: &mut Read = *r.deref_mut();
- req.header(ContentType::json()).body(client::Body::SizedBody(reader, l))
+ Some(b) => {//Body { read: r, size: l }) => {
+ //let reader: &mut Read = *r.deref_mut();
+ req.header(ContentType::json()).body(b)//client::Body::SizedBody(reader, l))
}
_ => req,
};
@@ -109,19 +112,3 @@ impl Transport {
}
}
}
-
-#[doc(hidden)]
-pub struct Body<'a> {
- read: &'a mut Box<&'a mut Read>,
- size: u64,
-}
-
-impl<'a> Body<'a> {
- /// Create a new body instance
- pub fn new(read: &'a mut Box<&'a mut Read>, size: u64) -> Body<'a> {
- Body {
- read: read,
- size: size,
- }
- }
-}