diff --git a/Cargo.toml b/Cargo.toml index c0ccdafe..da13cedd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ strum = "0.11" strum_macros = "0.11" tar = "0.4" tokio-core = "0.1" +dirs = "1.0" [dev-dependencies] env_logger = "0.5" diff --git a/examples/checkregistry.rs b/examples/checkregistry.rs index ae391b11..9440a14b 100644 --- a/examples/checkregistry.rs +++ b/examples/checkregistry.rs @@ -4,8 +4,6 @@ extern crate tokio_core; use std::{boxed, error}; use tokio_core::reactor::Core; -type Result = std::result::Result>; - fn main() { let registry = match std::env::args().nth(1) { Some(x) => x, @@ -20,7 +18,7 @@ fn main() { }; } -fn run(host: &str) -> Result { +fn run(host: &str) -> Result> { let mut tcore = try!(Core::new()); let dclient = try!( dkregistry::v2::Client::configure(&tcore.handle()) @@ -28,12 +26,12 @@ fn run(host: &str) -> Result { .insecure_registry(false) .build() ); - let futcheck = try!(dclient.is_v2_supported()); + let futcheck = dclient.is_v2_supported(); let supported = try!(tcore.run(futcheck)); match supported { false => println!("{} does NOT support v2", host), true => println!("{} supports v2", host), } - return Ok(supported); + Ok(supported) } diff --git a/examples/common/mod.rs b/examples/common/mod.rs new file mode 100644 index 00000000..a1287c08 --- /dev/null +++ b/examples/common/mod.rs @@ -0,0 +1,42 @@ +extern crate dkregistry; +extern crate futures; + +use futures::prelude::*; + +pub fn authenticate_client<'a>( + client: &'a mut dkregistry::v2::Client, + login_scope: &'a str, +) -> impl futures::future::Future +{ + futures::future::ok::<_, dkregistry::errors::Error>(client) + .and_then(|dclient| { + dclient.is_v2_supported().and_then(|v2_supported| { + if !v2_supported { + Err("API v2 not supported".into()) + } else { + Ok(dclient) + } + }) + }).and_then(|dclient| { + dclient.is_auth(None).and_then(|is_auth| { + if is_auth { + Err("no login performed, but already authenticated".into()) + } else { + Ok(dclient) + } + }) + }).and_then(move |dclient| { + dclient.login(&[&login_scope]).and_then(move |token| { + dclient + .is_auth(Some(token.token())) + .and_then(move |is_auth| { + if !is_auth { + Err("login failed".into()) + } else { + println!("logged in!"); + Ok(dclient.set_token(Some(token.token()))) + } + }) + }) + }) +} diff --git a/examples/image.rs b/examples/image.rs index 068f9514..a116a106 100644 --- a/examples/image.rs +++ b/examples/image.rs @@ -1,15 +1,17 @@ +extern crate dirs; extern crate dkregistry; extern crate futures; extern crate serde_json; extern crate tokio_core; use dkregistry::{reference, render}; +use futures::prelude::*; +use std::result::Result; +use std::str::FromStr; use std::{boxed, env, error, fs, io}; use tokio_core::reactor::Core; -use std::str::FromStr; - -type Result = std::result::Result>; +mod common; fn main() { let dkr_ref = match std::env::args().nth(1) { @@ -22,7 +24,7 @@ fn main() { let mut user = None; let mut password = None; - let home = env::home_dir().unwrap_or("/root".into()); + let home = dirs::home_dir().unwrap(); let cfg = fs::File::open(home.join(".docker/config.json")); if let Ok(fp) = cfg { let creds = dkregistry::get_credentials(io::BufReader::new(fp), ®istry); @@ -51,87 +53,90 @@ fn main() { }; } -fn run(dkr_ref: &reference::Reference, user: Option, passwd: Option) -> Result<()> { - let image = dkr_ref.repository(); - let version = dkr_ref.version(); - +fn run( + dkr_ref: &reference::Reference, + user: Option, + passwd: Option, +) -> Result<(), boxed::Box> { let mut tcore = try!(Core::new()); - let mut dclient = try!( - dkregistry::v2::Client::configure(&tcore.handle()) - .registry(&dkr_ref.registry()) - .insecure_registry(false) - .username(user) - .password(passwd) - .build() - ); - - let futcheck = try!(dclient.is_v2_supported()); - let supported = try!(tcore.run(futcheck)); - if !supported { - return Err("API v2 not supported".into()); - } - let fut_token = try!(dclient.login(&[&format!("repository:{}:pull", image)])); - let token_auth = try!(tcore.run(fut_token)); - - let futauth = try!(dclient.is_auth(Some(token_auth.token()))); - if !try!(tcore.run(futauth)) { - return Err("login failed".into()); - } + let mut client = dkregistry::v2::Client::configure(&tcore.handle()) + .registry(&dkr_ref.registry()) + .insecure_registry(false) + .username(user) + .password(passwd) + .build()?; - dclient.set_token(Some(token_auth.token())); - - let fut_hasmanif = dclient.has_manifest(&image, &version, None)?; - let manifest_kind = try!(tcore.run(fut_hasmanif)?.ok_or("no manifest found")); - - let fut_manif = dclient.get_manifest(&image, &version)?; - let body = tcore.run(fut_manif)?; + let image = dkr_ref.repository(); + let login_scope = format!("repository:{}:pull", image); + let version = dkr_ref.version(); - let layers = match manifest_kind { - dkregistry::mediatypes::MediaTypes::ManifestV2S1Signed => { - let m: dkregistry::v2::manifest::ManifestSchema1Signed = - try!(serde_json::from_slice(body.as_slice())); - m.get_layers() - } - dkregistry::mediatypes::MediaTypes::ManifestV2S2 => { - let m: dkregistry::v2::manifest::ManifestSchema2 = - try!(serde_json::from_slice(body.as_slice())); - m.get_layers() - } - _ => return Err("unknown format".into()), + let futures = common::authenticate_client(&mut client, &login_scope) + .and_then(|dclient| { + dclient + .has_manifest(&image, &version, None) + .and_then(move |manifest_option| Ok((dclient, manifest_option))) + .and_then(|(dclient, manifest_option)| match manifest_option { + None => Err(format!("{}:{} doesn't have a manifest", &image, &version).into()), + + Some(manifest_kind) => Ok((dclient, manifest_kind)), + }) + }).and_then(|(dclient, manifest_kind)| { + let image = image.clone(); + dclient.get_manifest(&image, &version).and_then( + move |manifest_body| match manifest_kind { + dkregistry::mediatypes::MediaTypes::ManifestV2S1Signed => { + let m: dkregistry::v2::manifest::ManifestSchema1Signed = match + serde_json::from_slice(manifest_body.as_slice()) { + Ok(json) => json, + Err(e) => return Err(e.into()), + + }; + Ok((dclient, m.get_layers())) + } + dkregistry::mediatypes::MediaTypes::ManifestV2S2 => { + let m: dkregistry::v2::manifest::ManifestSchema2 = + match serde_json::from_slice(manifest_body.as_slice()) { + Ok(json) => json, + Err(e) => return Err(e.into()), + }; + Ok((dclient, m.get_layers())) + } + _ => Err("unknown format".into()), + }, + ) + }).and_then(|(dclient, layers)| { + let image = image.clone(); + + println!("{} -> got {} layer(s)", &image, layers.len(),); + + futures::stream::iter_ok::<_, dkregistry::errors::Error>(layers) + .and_then(move |layer| { + let get_blob_future = dclient.get_blob(&image, &layer); + get_blob_future.inspect(move |blob| { + println!("Layer {}, got {} bytes.\n", layer, blob.len()); + }) + }).collect() + }); + + let blobs = match tcore.run(futures) { + Ok(blobs) => blobs, + Err(e) => return Err(Box::new(e)), }; - println!( - "{} -> got {} layer(s), saving to directory {:?}", - image, - layers.len(), - version - ); - std::fs::create_dir(&version)?; - let mut blobs: Vec> = vec![]; - - for (i, digest) in layers.iter().enumerate() { - let fut_presence = dclient.has_blob(&image, &digest)?; - let has_blob = tcore.run(fut_presence)?; - if !has_blob { - return Err(format!("missing layer {}", digest).into()); - } + println!("Downloaded {} layers", blobs.len()); - println!("Downloading layer {}...", digest); - let fut_out = dclient.get_blob(&image, &digest)?; - let out = tcore.run(fut_out)?; - println!( - "Layer {}/{}, got {} bytes.\n", - i + 1, - layers.len(), - out.len() - ); - blobs.push(out); + // FIXME: use async io + let path = &format!("{}:{}", &image, &version).replace("/", "_"); + let path = std::path::Path::new(&path); + if path.exists() { + return Err(format!("path {:?} already exists, exiting", &path).into()); } + std::fs::create_dir(&path).unwrap(); + let can_path = path.canonicalize().unwrap(); + + println!("Unpacking layers to {:?}", &can_path); + let r = render::unpack(&blobs, &can_path).unwrap(); - let can_path = std::fs::canonicalize(&version)?; - let r = render::unpack(&blobs, &can_path); - println!("{:?}", r); - r?; - Ok(()) + Ok(r) } diff --git a/examples/login.rs b/examples/login.rs index e5a976c6..53755247 100644 --- a/examples/login.rs +++ b/examples/login.rs @@ -1,11 +1,14 @@ extern crate dkregistry; +extern crate futures; extern crate tokio_core; +mod common; + +use futures::prelude::*; +use std::result::Result; use std::{boxed, error}; use tokio_core::reactor::Core; -type Result = std::result::Result>; - fn main() { let registry = match std::env::args().nth(1) { Some(x) => x, @@ -29,43 +32,28 @@ fn main() { }; } -fn run(host: &str, user: Option, passwd: Option) -> Result<()> { - let mut tcore = try!(Core::new()); - let dclient = try!( - dkregistry::v2::Client::configure(&tcore.handle()) - .registry(host) - .insecure_registry(false) - .username(user) - .password(passwd) - .build() - ); - - let futcheck = try!(dclient.is_v2_supported()); - let supported = try!(tcore.run(futcheck)); - if !supported { - return Err("API v2 not supported".into()); +fn run( + host: &str, + user: Option, + passwd: Option, +) -> Result<(), boxed::Box> { + let mut tcore = Core::new()?; + + let mut client = dkregistry::v2::Client::configure(&tcore.handle()) + .registry(host) + .insecure_registry(false) + .username(user) + .password(passwd) + .build()?; + + let login_scope = ""; + + let futures = common::authenticate_client(&mut client, &login_scope) + .and_then(|dclient| dclient.is_v2_supported()); + + match tcore.run(futures) { + Ok(login_successful) if login_successful => Ok(()), + Err(e) => Err(Box::new(e)), + _ => Err("Login unsucessful".into()), } - - let futauth = try!(dclient.is_auth(None)); - let logged_in = try!(tcore.run(futauth)); - if logged_in { - return Err("no login performed, but already authenticated".into()); - } - - let fut_token = try!(dclient.login(&[])); - let token = try!(tcore.run(fut_token)); - - let futauth = try!(dclient.is_auth(Some(token.token()))); - let done = try!(tcore.run(futauth)); - - match done { - false => return Err("login failed".into()), - true => println!("logged in!",), - } - let futcheck = try!(dclient.is_v2_supported()); - if !try!(tcore.run(futcheck)) { - return Err("API check failed after login".into()); - }; - - return Ok(()); } diff --git a/examples/tags.rs b/examples/tags.rs index f3b9f57e..526c3e7b 100644 --- a/examples/tags.rs +++ b/examples/tags.rs @@ -2,12 +2,13 @@ extern crate dkregistry; extern crate futures; extern crate tokio_core; -use futures::stream::Stream; +mod common; + +use futures::prelude::*; +use std::result::Result; use std::{boxed, error}; use tokio_core::reactor::Core; -type Result = std::result::Result>; - fn main() { let registry = match std::env::args().nth(1) { Some(x) => x, @@ -37,37 +38,31 @@ fn main() { }; } -fn run(host: &str, user: Option, passwd: Option, image: &str) -> Result<()> { - let mut tcore = try!(Core::new()); - let mut dclient = try!( - dkregistry::v2::Client::configure(&tcore.handle()) - .registry(host) - .insecure_registry(false) - .username(user) - .password(passwd) - .build() - ); +fn run( + host: &str, + user: Option, + passwd: Option, + image: &str, +) -> Result<(), boxed::Box> { + let mut tcore = Core::new()?; + let mut client = dkregistry::v2::Client::configure(&tcore.handle()) + .registry(host) + .insecure_registry(false) + .username(user) + .password(passwd) + .build()?; - let futcheck = try!(dclient.is_v2_supported()); - let supported = try!(tcore.run(futcheck)); - if !supported { - return Err("API v2 not supported".into()); - } + let login_scope = format!("repository:{}:pull", image); - let fut_token = try!(dclient.login(&[&format!("repository:{}:pull", image)])); - let token_auth = try!(tcore.run(fut_token)); + let futures = common::authenticate_client(&mut client, &login_scope) + .and_then(|dclient| Ok(dclient.get_tags(&image, Some(7)).collect())) + .and_then(|tags_collect| tags_collect); - let futauth = try!(dclient.is_auth(Some(token_auth.token()))); - if !try!(tcore.run(futauth)) { - return Err("login failed".into()); + match tcore.run(futures) { + Ok(tags) => { + println!("{:?}", tags); + Ok(()) + } + Err(e) => Err(Box::new(e)), } - - dclient.set_token(Some(token_auth.token())); - - let fut_tags = dclient.get_tags(image, Some(7))?; - let tags = tcore.run(fut_tags.collect()); - - println!("{:?}", tags); - - return Ok(()); } diff --git a/examples/trace.rs b/examples/trace.rs index 9452ea94..2ae17e25 100644 --- a/examples/trace.rs +++ b/examples/trace.rs @@ -1,3 +1,4 @@ +extern crate dirs; extern crate dkregistry; extern crate env_logger; extern crate futures; @@ -5,14 +6,14 @@ extern crate log; extern crate serde_json; extern crate tokio_core; +mod common; + use dkregistry::reference; +use futures::prelude::*; +use std::str::FromStr; use std::{boxed, env, error, fs, io}; use tokio_core::reactor::Core; -use std::str::FromStr; - -type Result = std::result::Result>; - fn main() { let dkr_ref = match std::env::args().nth(1) { Some(ref x) => reference::Reference::from_str(x), @@ -24,7 +25,7 @@ fn main() { let mut user = None; let mut password = None; - let home = env::home_dir().unwrap_or("/root".into()); + let home = dirs::home_dir().unwrap(); let cfg = fs::File::open(home.join(".docker/config.json")); if let Ok(fp) = cfg { let creds = dkregistry::get_credentials(io::BufReader::new(fp), ®istry); @@ -53,70 +54,83 @@ fn main() { }; } -fn run(dkr_ref: &reference::Reference, user: Option, passwd: Option) -> Result<()> { +fn run( + dkr_ref: &reference::Reference, + user: Option, + passwd: Option, +) -> Result<(), boxed::Box> { env_logger::Builder::new() .filter(Some("dkregistry"), log::LevelFilter::Trace) .filter(Some("trace"), log::LevelFilter::Trace) .try_init()?; + let image = dkr_ref.repository(); let version = dkr_ref.version(); - - let mut tcore = try!(Core::new()); - let mut dclient = try!( - dkregistry::v2::Client::configure(&tcore.handle()) - .registry(&dkr_ref.registry()) - .insecure_registry(false) - .username(user) - .password(passwd) - .build() - ); - - let futcheck = try!(dclient.is_v2_supported()); - let supported = try!(tcore.run(futcheck)); - if !supported { - return Err("API v2 not supported".into()); - } - - let fut_token = try!(dclient.login(&[&format!("repository:{}:pull", image)])); - let token_auth = try!(tcore.run(fut_token)); - - let futauth = try!(dclient.is_auth(Some(token_auth.token()))); - if !try!(tcore.run(futauth)) { - return Err("login failed".into()); - } - - dclient.set_token(Some(token_auth.token())); - - let fut_hasmanif = dclient.has_manifest(&image, &version, None)?; - let manifest_kind = try!(tcore.run(fut_hasmanif)?.ok_or("no manifest found")); - - let fut_manif = dclient.get_manifest(&image, &version)?; - let body = tcore.run(fut_manif)?; - - let layers = match manifest_kind { - dkregistry::mediatypes::MediaTypes::ManifestV2S1Signed => { - let m: dkregistry::v2::manifest::ManifestSchema1Signed = - try!(serde_json::from_slice(body.as_slice())); - m.get_layers() - } - dkregistry::mediatypes::MediaTypes::ManifestV2S2 => { - let m: dkregistry::v2::manifest::ManifestSchema2 = - try!(serde_json::from_slice(body.as_slice())); - m.get_layers() - } - _ => return Err("unknown format".into()), + let mut tcore = Core::new()?; + + let mut client = dkregistry::v2::Client::configure(&tcore.handle()) + .registry(&dkr_ref.registry()) + .insecure_registry(false) + .username(user) + .password(passwd) + .build()?; + + let login_scope = ""; + + let futures = common::authenticate_client(&mut client, &login_scope) + .and_then(|dclient| { + dclient + .has_manifest(&image, &version, None) + .and_then(move |manifest_option| Ok((dclient, manifest_option))) + .and_then(|(dclient, manifest_option)| match manifest_option { + None => { + return Err( + format!("{}:{} doesn't have a manifest", &image, &version).into() + ) + } + + Some(manifest_kind) => Ok((dclient, manifest_kind)), + }) + }).and_then(|(dclient, manifest_kind)| { + let image = image.clone(); + dclient + .get_manifest(&image, &version) + .and_then(move |manifest_body| { + let layers = match manifest_kind { + dkregistry::mediatypes::MediaTypes::ManifestV2S1Signed => { + let m: dkregistry::v2::manifest::ManifestSchema1Signed = + serde_json::from_slice(manifest_body.as_slice()).unwrap(); + m.get_layers() + } + dkregistry::mediatypes::MediaTypes::ManifestV2S2 => { + let m: dkregistry::v2::manifest::ManifestSchema2 = + serde_json::from_slice(manifest_body.as_slice()).unwrap(); + m.get_layers() + } + _ => return Err("unknown format".into()), + }; + Ok((dclient, layers)) + }) + }).and_then(|(dclient, layers)| { + let image = image.clone(); + + println!("{} -> got {} layer(s)", &image, layers.len(),); + + futures::stream::iter_ok::<_, dkregistry::errors::Error>(layers) + .and_then(move |layer| { + let get_blob_future = dclient.get_blob(&image, &layer); + get_blob_future.inspect(move |blob| { + println!("Layer {}, got {} bytes.\n", layer, blob.len()); + }) + }).collect() + }); + + let blobs = match tcore.run(futures) { + Ok(blobs) => blobs, + Err(e) => return Err(Box::new(e)), }; - for digest in layers { - let fut_presence = dclient.has_blob(&image, &digest)?; - let has_blob = tcore.run(fut_presence)?; - if !has_blob { - return Err(format!("missing layer {}", digest).into()); - } - - let fut_out = dclient.get_blob(&image, &digest)?; - let _out = tcore.run(fut_out)?; - } + println!("Downloaded {} layers", blobs.len()); - return Ok(()); + Ok(()) }