Skip to content

Commit 23adf75

Browse files
authored
Merge pull request #49 from steveeJ/return-future-without-result-and-work-examples
API: substitute `Result<Future..>` with `Future..` for more idiomatic future chaining
2 parents 86e85b4 + edfc08b commit 23adf75

23 files changed

+538
-329
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ strum = "0.11"
3030
strum_macros = "0.11"
3131
tar = "0.4"
3232
tokio-core = "0.1"
33+
dirs = "1.0"
3334

3435
[dev-dependencies]
3536
env_logger = "0.5"

examples/checkregistry.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ extern crate tokio_core;
44
use std::{boxed, error};
55
use tokio_core::reactor::Core;
66

7-
type Result<T> = std::result::Result<T, boxed::Box<error::Error>>;
8-
97
fn main() {
108
let registry = match std::env::args().nth(1) {
119
Some(x) => x,
@@ -20,20 +18,20 @@ fn main() {
2018
};
2119
}
2220

23-
fn run(host: &str) -> Result<bool> {
21+
fn run(host: &str) -> Result<bool, boxed::Box<error::Error>> {
2422
let mut tcore = try!(Core::new());
2523
let dclient = try!(
2624
dkregistry::v2::Client::configure(&tcore.handle())
2725
.registry(host)
2826
.insecure_registry(false)
2927
.build()
3028
);
31-
let futcheck = try!(dclient.is_v2_supported());
29+
let futcheck = dclient.is_v2_supported();
3230

3331
let supported = try!(tcore.run(futcheck));
3432
match supported {
3533
false => println!("{} does NOT support v2", host),
3634
true => println!("{} supports v2", host),
3735
}
38-
return Ok(supported);
36+
Ok(supported)
3937
}

examples/common/mod.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
extern crate dkregistry;
2+
extern crate futures;
3+
4+
use futures::prelude::*;
5+
6+
pub fn authenticate_client<'a>(
7+
client: &'a mut dkregistry::v2::Client,
8+
login_scope: &'a str,
9+
) -> impl futures::future::Future<Item = &'a dkregistry::v2::Client, Error = dkregistry::errors::Error>
10+
{
11+
futures::future::ok::<_, dkregistry::errors::Error>(client)
12+
.and_then(|dclient| {
13+
dclient.is_v2_supported().and_then(|v2_supported| {
14+
if !v2_supported {
15+
Err("API v2 not supported".into())
16+
} else {
17+
Ok(dclient)
18+
}
19+
})
20+
}).and_then(|dclient| {
21+
dclient.is_auth(None).and_then(|is_auth| {
22+
if is_auth {
23+
Err("no login performed, but already authenticated".into())
24+
} else {
25+
Ok(dclient)
26+
}
27+
})
28+
}).and_then(move |dclient| {
29+
dclient.login(&[&login_scope]).and_then(move |token| {
30+
dclient
31+
.is_auth(Some(token.token()))
32+
.and_then(move |is_auth| {
33+
if !is_auth {
34+
Err("login failed".into())
35+
} else {
36+
println!("logged in!");
37+
Ok(dclient.set_token(Some(token.token())))
38+
}
39+
})
40+
})
41+
})
42+
}

examples/image.rs

+83-78
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
extern crate dirs;
12
extern crate dkregistry;
23
extern crate futures;
34
extern crate serde_json;
45
extern crate tokio_core;
56

67
use dkregistry::{reference, render};
8+
use futures::prelude::*;
9+
use std::result::Result;
10+
use std::str::FromStr;
711
use std::{boxed, env, error, fs, io};
812
use tokio_core::reactor::Core;
913

10-
use std::str::FromStr;
11-
12-
type Result<T> = std::result::Result<T, boxed::Box<error::Error>>;
14+
mod common;
1315

1416
fn main() {
1517
let dkr_ref = match std::env::args().nth(1) {
@@ -22,7 +24,7 @@ fn main() {
2224

2325
let mut user = None;
2426
let mut password = None;
25-
let home = env::home_dir().unwrap_or("/root".into());
27+
let home = dirs::home_dir().unwrap();
2628
let cfg = fs::File::open(home.join(".docker/config.json"));
2729
if let Ok(fp) = cfg {
2830
let creds = dkregistry::get_credentials(io::BufReader::new(fp), &registry);
@@ -51,87 +53,90 @@ fn main() {
5153
};
5254
}
5355

54-
fn run(dkr_ref: &reference::Reference, user: Option<String>, passwd: Option<String>) -> Result<()> {
55-
let image = dkr_ref.repository();
56-
let version = dkr_ref.version();
57-
56+
fn run(
57+
dkr_ref: &reference::Reference,
58+
user: Option<String>,
59+
passwd: Option<String>,
60+
) -> Result<(), boxed::Box<error::Error>> {
5861
let mut tcore = try!(Core::new());
59-
let mut dclient = try!(
60-
dkregistry::v2::Client::configure(&tcore.handle())
61-
.registry(&dkr_ref.registry())
62-
.insecure_registry(false)
63-
.username(user)
64-
.password(passwd)
65-
.build()
66-
);
67-
68-
let futcheck = try!(dclient.is_v2_supported());
69-
let supported = try!(tcore.run(futcheck));
70-
if !supported {
71-
return Err("API v2 not supported".into());
72-
}
7362

74-
let fut_token = try!(dclient.login(&[&format!("repository:{}:pull", image)]));
75-
let token_auth = try!(tcore.run(fut_token));
76-
77-
let futauth = try!(dclient.is_auth(Some(token_auth.token())));
78-
if !try!(tcore.run(futauth)) {
79-
return Err("login failed".into());
80-
}
63+
let mut client = dkregistry::v2::Client::configure(&tcore.handle())
64+
.registry(&dkr_ref.registry())
65+
.insecure_registry(false)
66+
.username(user)
67+
.password(passwd)
68+
.build()?;
8169

82-
dclient.set_token(Some(token_auth.token()));
83-
84-
let fut_hasmanif = dclient.has_manifest(&image, &version, None)?;
85-
let manifest_kind = try!(tcore.run(fut_hasmanif)?.ok_or("no manifest found"));
86-
87-
let fut_manif = dclient.get_manifest(&image, &version)?;
88-
let body = tcore.run(fut_manif)?;
70+
let image = dkr_ref.repository();
71+
let login_scope = format!("repository:{}:pull", image);
72+
let version = dkr_ref.version();
8973

90-
let layers = match manifest_kind {
91-
dkregistry::mediatypes::MediaTypes::ManifestV2S1Signed => {
92-
let m: dkregistry::v2::manifest::ManifestSchema1Signed =
93-
try!(serde_json::from_slice(body.as_slice()));
94-
m.get_layers()
95-
}
96-
dkregistry::mediatypes::MediaTypes::ManifestV2S2 => {
97-
let m: dkregistry::v2::manifest::ManifestSchema2 =
98-
try!(serde_json::from_slice(body.as_slice()));
99-
m.get_layers()
100-
}
101-
_ => return Err("unknown format".into()),
74+
let futures = common::authenticate_client(&mut client, &login_scope)
75+
.and_then(|dclient| {
76+
dclient
77+
.has_manifest(&image, &version, None)
78+
.and_then(move |manifest_option| Ok((dclient, manifest_option)))
79+
.and_then(|(dclient, manifest_option)| match manifest_option {
80+
None => Err(format!("{}:{} doesn't have a manifest", &image, &version).into()),
81+
82+
Some(manifest_kind) => Ok((dclient, manifest_kind)),
83+
})
84+
}).and_then(|(dclient, manifest_kind)| {
85+
let image = image.clone();
86+
dclient.get_manifest(&image, &version).and_then(
87+
move |manifest_body| match manifest_kind {
88+
dkregistry::mediatypes::MediaTypes::ManifestV2S1Signed => {
89+
let m: dkregistry::v2::manifest::ManifestSchema1Signed = match
90+
serde_json::from_slice(manifest_body.as_slice()) {
91+
Ok(json) => json,
92+
Err(e) => return Err(e.into()),
93+
94+
};
95+
Ok((dclient, m.get_layers()))
96+
}
97+
dkregistry::mediatypes::MediaTypes::ManifestV2S2 => {
98+
let m: dkregistry::v2::manifest::ManifestSchema2 =
99+
match serde_json::from_slice(manifest_body.as_slice()) {
100+
Ok(json) => json,
101+
Err(e) => return Err(e.into()),
102+
};
103+
Ok((dclient, m.get_layers()))
104+
}
105+
_ => Err("unknown format".into()),
106+
},
107+
)
108+
}).and_then(|(dclient, layers)| {
109+
let image = image.clone();
110+
111+
println!("{} -> got {} layer(s)", &image, layers.len(),);
112+
113+
futures::stream::iter_ok::<_, dkregistry::errors::Error>(layers)
114+
.and_then(move |layer| {
115+
let get_blob_future = dclient.get_blob(&image, &layer);
116+
get_blob_future.inspect(move |blob| {
117+
println!("Layer {}, got {} bytes.\n", layer, blob.len());
118+
})
119+
}).collect()
120+
});
121+
122+
let blobs = match tcore.run(futures) {
123+
Ok(blobs) => blobs,
124+
Err(e) => return Err(Box::new(e)),
102125
};
103126

104-
println!(
105-
"{} -> got {} layer(s), saving to directory {:?}",
106-
image,
107-
layers.len(),
108-
version
109-
);
110-
std::fs::create_dir(&version)?;
111-
let mut blobs: Vec<Vec<u8>> = vec![];
112-
113-
for (i, digest) in layers.iter().enumerate() {
114-
let fut_presence = dclient.has_blob(&image, &digest)?;
115-
let has_blob = tcore.run(fut_presence)?;
116-
if !has_blob {
117-
return Err(format!("missing layer {}", digest).into());
118-
}
127+
println!("Downloaded {} layers", blobs.len());
119128

120-
println!("Downloading layer {}...", digest);
121-
let fut_out = dclient.get_blob(&image, &digest)?;
122-
let out = tcore.run(fut_out)?;
123-
println!(
124-
"Layer {}/{}, got {} bytes.\n",
125-
i + 1,
126-
layers.len(),
127-
out.len()
128-
);
129-
blobs.push(out);
129+
let path = &format!("{}:{}", &image, &version).replace("/", "_");
130+
let path = std::path::Path::new(&path);
131+
if path.exists() {
132+
return Err(format!("path {:?} already exists, exiting", &path).into());
130133
}
134+
// TODO: use async io
135+
std::fs::create_dir(&path).unwrap();
136+
let can_path = path.canonicalize().unwrap();
137+
138+
println!("Unpacking layers to {:?}", &can_path);
139+
let r = render::unpack(&blobs, &can_path).unwrap();
131140

132-
let can_path = std::fs::canonicalize(&version)?;
133-
let r = render::unpack(&blobs, &can_path);
134-
println!("{:?}", r);
135-
r?;
136-
Ok(())
141+
Ok(r)
137142
}

examples/login.rs

+28-40
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
extern crate dkregistry;
2+
extern crate futures;
23
extern crate tokio_core;
34

5+
mod common;
6+
7+
use futures::prelude::*;
8+
use std::result::Result;
49
use std::{boxed, error};
510
use tokio_core::reactor::Core;
611

7-
type Result<T> = std::result::Result<T, boxed::Box<error::Error>>;
8-
912
fn main() {
1013
let registry = match std::env::args().nth(1) {
1114
Some(x) => x,
@@ -29,43 +32,28 @@ fn main() {
2932
};
3033
}
3134

32-
fn run(host: &str, user: Option<String>, passwd: Option<String>) -> Result<()> {
33-
let mut tcore = try!(Core::new());
34-
let dclient = try!(
35-
dkregistry::v2::Client::configure(&tcore.handle())
36-
.registry(host)
37-
.insecure_registry(false)
38-
.username(user)
39-
.password(passwd)
40-
.build()
41-
);
42-
43-
let futcheck = try!(dclient.is_v2_supported());
44-
let supported = try!(tcore.run(futcheck));
45-
if !supported {
46-
return Err("API v2 not supported".into());
35+
fn run(
36+
host: &str,
37+
user: Option<String>,
38+
passwd: Option<String>,
39+
) -> Result<(), boxed::Box<error::Error>> {
40+
let mut tcore = Core::new()?;
41+
42+
let mut client = dkregistry::v2::Client::configure(&tcore.handle())
43+
.registry(host)
44+
.insecure_registry(false)
45+
.username(user)
46+
.password(passwd)
47+
.build()?;
48+
49+
let login_scope = "";
50+
51+
let futures = common::authenticate_client(&mut client, &login_scope)
52+
.and_then(|dclient| dclient.is_v2_supported());
53+
54+
match tcore.run(futures) {
55+
Ok(login_successful) if login_successful => Ok(()),
56+
Err(e) => Err(Box::new(e)),
57+
_ => Err("Login unsucessful".into()),
4758
}
48-
49-
let futauth = try!(dclient.is_auth(None));
50-
let logged_in = try!(tcore.run(futauth));
51-
if logged_in {
52-
return Err("no login performed, but already authenticated".into());
53-
}
54-
55-
let fut_token = try!(dclient.login(&[]));
56-
let token = try!(tcore.run(fut_token));
57-
58-
let futauth = try!(dclient.is_auth(Some(token.token())));
59-
let done = try!(tcore.run(futauth));
60-
61-
match done {
62-
false => return Err("login failed".into()),
63-
true => println!("logged in!",),
64-
}
65-
let futcheck = try!(dclient.is_v2_supported());
66-
if !try!(tcore.run(futcheck)) {
67-
return Err("API check failed after login".into());
68-
};
69-
70-
return Ok(());
7159
}

0 commit comments

Comments
 (0)