Skip to content

Commit e32e6c2

Browse files
authoredJan 25, 2021
Merge pull request #11 from ereslibre/oci-artifacts
Implement fetching from an artifact-enabled OCI registry
2 parents e216ef3 + 7c0ba63 commit e32e6c2

File tree

7 files changed

+777
-173
lines changed

7 files changed

+777
-173
lines changed
 

‎Cargo.lock

+695-161
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ clap = "2.33.3"
1414
hyper-tls = "0.5.0"
1515
hyper = { version = "0.14", features = ["full"] }
1616
native-tls = "^0.2.1"
17+
oci-distribution = "0.4"
1718
serde_json = "1.0"
1819
serde = { version = "1.0", features = ["derive"] }
1920
tokio = { version = "^1", features = ["full"] }
21+
tokio-compat-02 = "0.2.0"
2022
url = "2.2.0"
2123
wapc = "0.10.1"
22-
wasmtime-provider = "0.0.2"
24+
wasmtime-provider = "0.0.2"

‎src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async fn main() {
5151
.env("CHIMERA_WASM_URI")
5252
.required(true)
5353
.help(
54-
"Wasm URI (file:///some/local/program.wasm,
54+
"Wasm URI (file:///some/local/program.wasm,
5555
https://some-host.com/some/remote/program.wasm,
5656
registry://localhost:5000/project/artifact:some-version)",
5757
),

‎src/wasm_fetcher/https.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ use anyhow::{anyhow, Result};
22
use async_std::fs::File;
33
use async_std::prelude::*;
44
use async_trait::async_trait;
5-
use hyper::{client::HttpConnector, Client, StatusCode, Uri};
5+
use hyper::{client::HttpConnector, Client, StatusCode};
66
use hyper_tls::HttpsConnector;
77
use native_tls::TlsConnector;
88
use std::boxed::Box;
9+
use url::Url;
910

1011
use crate::wasm_fetcher::fetcher::Fetcher;
1112

@@ -14,20 +15,20 @@ pub(crate) struct Https {
1415
// full path to the WASM module
1516
destination: String,
1617
// url of the remote WASM module
17-
wasm_url: Uri,
18+
wasm_url: Url,
1819
// do not verify the remote TLS certificate
1920
insecure: bool,
2021
}
2122

2223
impl Https {
2324
// Allocates a LocalWASM instance starting from the user
2425
// provided URL
25-
pub(crate) fn new(url: Uri, remote_insecure: bool) -> Result<Https> {
26+
pub(crate) fn new(url: Url, remote_insecure: bool) -> Result<Https> {
2627
let dest = match url.path().rsplit('/').next() {
2728
Some(d) => d,
2829
None => {
2930
return Err(anyhow!(
30-
"Cannot find infer name of the remote file by looking at {}",
31+
"Cannot infer name of the remote file by looking at {}",
3132
url.path()
3233
))
3334
}
@@ -56,7 +57,7 @@ impl Fetcher for Https {
5657
let client = Client::builder().build::<_, hyper::Body>(https);
5758

5859
// not well: the hyper-tls connector handles both http and https scheme
59-
let res = client.get(self.wasm_url.clone()).await?;
60+
let res = client.get(self.wasm_url.clone().into_string().parse()?).await?;
6061
if res.status() != StatusCode::OK {
6162
return Err(anyhow!(
6263
"Error while downloading remote WASM module from {}, got HTTP status {}",

‎src/wasm_fetcher/local.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use anyhow::Result;
22
use async_trait::async_trait;
3-
use hyper::Uri;
43

54
use crate::wasm_fetcher::fetcher::Fetcher;
65

‎src/wasm_fetcher/mod.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use anyhow::{anyhow, Result};
2-
use hyper::Uri;
2+
use url::Url;
33
use std::boxed::Box;
44

55
pub mod fetcher;
66
mod https;
77
mod local;
8+
mod registry;
89

910
use crate::wasm_fetcher::fetcher::Fetcher;
1011
use crate::wasm_fetcher::https::Https;
1112
use crate::wasm_fetcher::local::Local;
13+
use crate::wasm_fetcher::registry::Registry;
1214

1315
// Helper function, takes the URL of the WASM module and allocates
1416
// the right struct to interact with it
@@ -19,7 +21,7 @@ pub(crate) fn parse_wasm_url(
1921
) -> Result<Box<dyn Fetcher>> {
2022
// we have to use url::Url instead of hyper::Uri because the latter one can't
2123
// parse urls like file://
22-
let parsed_url = match url::Url::parse(url) {
24+
let parsed_url: Url = match url::Url::parse(url) {
2325
Ok(u) => u,
2426
Err(e) => {
2527
return Err(anyhow!("Invalid WASI url: {}", e));
@@ -28,8 +30,8 @@ pub(crate) fn parse_wasm_url(
2830

2931
match parsed_url.scheme() {
3032
"file" => Ok(Box::new(Local::new(parsed_url.path())?)),
31-
"http" => Ok(Box::new(Https::new(url.parse::<Uri>()?, remote_insecure)?)),
32-
"https" => Ok(Box::new(Https::new(url.parse::<Uri>()?, remote_insecure)?)),
33+
"http" | "https" => Ok(Box::new(Https::new(url.parse::<Url>()?, remote_insecure)?)),
34+
"registry" => Ok(Box::new(Registry::new(parsed_url, remote_non_tls)?)),
3335
_ => Err(anyhow!("unknown scheme: {}", parsed_url.scheme())),
3436
}
3537
}

‎src/wasm_fetcher/registry.rs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use anyhow::{anyhow, Result};
2+
use async_std::fs::File;
3+
use async_std::prelude::*;
4+
use async_trait::async_trait;
5+
use oci_distribution::Reference;
6+
use oci_distribution::client::{Client, ClientConfig, ClientProtocol};
7+
use oci_distribution::secrets::RegistryAuth;
8+
use std::str::FromStr;
9+
use tokio_compat_02::FutureExt;
10+
use url::Url;
11+
12+
use crate::wasm_fetcher::fetcher::Fetcher;
13+
14+
// Struct used to reference a WASM module that is hosted on an OCI registry
15+
pub(crate) struct Registry {
16+
// full path to the WASM module
17+
destination: String,
18+
// url of the remote WASM module
19+
wasm_url: String,
20+
// whether TLS should be skipped
21+
skip_tls: bool,
22+
}
23+
24+
impl Registry {
25+
pub(crate) fn new(url: Url, skip_tls: bool) -> Result<Registry> {
26+
match url.path().rsplit('/').next() {
27+
Some(image_ref) => {
28+
let wasm_url = url.to_string();
29+
Ok(Registry{
30+
destination: image_ref.into(),
31+
wasm_url: wasm_url
32+
.strip_prefix("registry://")
33+
.map_or(Default::default(), |url| url.into()),
34+
skip_tls: skip_tls,
35+
})
36+
},
37+
None => Err(anyhow!(
38+
"Cannot infer name of the remote file by looking at {}",
39+
url.path()
40+
)),
41+
}
42+
}
43+
}
44+
45+
#[async_trait]
46+
impl Fetcher for Registry {
47+
async fn fetch(&self) -> Result<String> {
48+
let mut client = Client::new(ClientConfig{
49+
protocol: if self.skip_tls { ClientProtocol::Http } else { ClientProtocol::Https },
50+
});
51+
let reference = Reference::from_str(self.wasm_url.as_str())?;
52+
let image_content = client
53+
.pull_image(&reference, &RegistryAuth::Anonymous)
54+
// We need to call to `compat()` provided by the `tokio-compat-02` crate
55+
// so that the Future returned by the `oci-distribution` crate can be
56+
// executed by a newer Tokio runtime.
57+
.compat()
58+
.await?
59+
.content;
60+
61+
let mut file = File::create(self.destination.clone()).await?;
62+
file.write_all(&image_content[..]).await?;
63+
64+
Ok(self.destination.clone())
65+
}
66+
}

0 commit comments

Comments
 (0)
Please sign in to comment.