-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
304 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
use crate::Result; | ||
use anyhow::Context; | ||
use std::{collections::HashMap, path::Path}; | ||
use structopt::StructOpt; | ||
use wasmcloud_host::{deserialize, Actor, HostBuilder}; | ||
|
||
use crate::{commands::init_logger, oci::fetch_oci_bytes, util::generate_run_manifest}; | ||
|
||
use super::LoggingOpts; | ||
|
||
#[derive(Debug, Clone, StructOpt)] | ||
#[structopt(rename_all = "kebab-case")] | ||
pub struct RunCli { | ||
#[structopt(flatten)] | ||
pub logging: LoggingOpts, | ||
|
||
/// Turn on info logging | ||
#[structopt(long = "info")] | ||
pub info: bool, | ||
|
||
/// Allows the use of HTTP registry connections to these registries | ||
#[structopt(long = "allowed-insecure")] | ||
pub allowed_insecure: Vec<String>, | ||
|
||
/// Input filename or URL | ||
#[structopt()] | ||
actor_ref: String, | ||
|
||
/// JSON data | ||
#[structopt(default_value = "\"\"")] | ||
data: String, | ||
} | ||
|
||
pub(crate) async fn handle_command(command: RunCli) -> Result<String> { | ||
let mut logging = command.logging.clone(); | ||
if !(command.info || command.logging.trace || command.logging.debug) { | ||
logging.quiet = true; | ||
} | ||
init_logger(&logging)?; | ||
|
||
let mut host_builder = HostBuilder::new(); | ||
host_builder = host_builder.oci_allow_latest(); | ||
|
||
if !command.allowed_insecure.is_empty() { | ||
host_builder = host_builder.oci_allow_insecure(command.allowed_insecure.clone()); | ||
} | ||
|
||
let host = host_builder.build(); | ||
let actor_ref = command.actor_ref.to_string(); | ||
|
||
let actor = fetch_actor(actor_ref.to_string(), true, command.allowed_insecure).await?; | ||
|
||
let json_string = command.data; | ||
let json: HashMap<String, serde_json::value::Value> = | ||
serde_json::from_str(&json_string).context("Could not deserialized JSON input data")?; | ||
|
||
info!("Starting host"); | ||
match host.start().await { | ||
Ok(_) => { | ||
info!("Loading dynamic manifest"); | ||
let hm = generate_run_manifest(actor_ref, actor, &json)?; | ||
host.apply_manifest(hm).await?; | ||
debug!("Manifest applied, executing component"); | ||
let raw_result = host.request("dynamic", json).await?; | ||
debug!("Raw result: {:?}", raw_result); | ||
let msg_result: HashMap<String, Vec<u8>> = deserialize(&raw_result)?; | ||
let result: serde_json::Value = msg_result | ||
.iter() | ||
.map(|(k, v)| { | ||
( | ||
k.to_string(), | ||
deserialize(&v).unwrap_or_else(|e| { | ||
serde_json::Value::String(format!( | ||
"Error deserializing output for port {}: {}", | ||
k, | ||
e.to_string() | ||
)) | ||
}), | ||
) | ||
}) | ||
.collect(); | ||
println!("{}", result); | ||
info!("Done"); | ||
host.stop().await; | ||
} | ||
Err(e) => { | ||
error!("Failed to start host: {}", e); | ||
} | ||
} | ||
Ok("Done".to_string()) | ||
} | ||
|
||
async fn fetch_actor( | ||
actor_ref: String, | ||
allow_latest: bool, | ||
allowed_insecure: Vec<String>, | ||
) -> Result<Actor> { | ||
let p = Path::new(&actor_ref); | ||
if p.exists() { | ||
Ok(wasmcloud_host::Actor::from_file(p)?) | ||
} else { | ||
let actor_bytes = fetch_oci_bytes(&actor_ref, allow_latest, &allowed_insecure).await?; | ||
Ok(wasmcloud_host::Actor::from_slice(&actor_bytes)?) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use crate::{error::VinoError, Result}; | ||
use anyhow::Context; | ||
use std::env::temp_dir; | ||
use std::io::{Read, Write}; | ||
use std::path::PathBuf; | ||
use std::str::FromStr; | ||
|
||
pub(crate) const OCI_VAR_USER: &str = "OCI_REGISTRY_USER"; | ||
pub(crate) const OCI_VAR_PASSWORD: &str = "OCI_REGISTRY_PASSWORD"; | ||
const PROVIDER_ARCHIVE_MEDIA_TYPE: &str = "application/vnd.wasmcloud.provider.archive.layer.v1+par"; | ||
const WASM_MEDIA_TYPE: &str = "application/vnd.module.wasm.content.layer.v1+wasm"; | ||
const OCI_MEDIA_TYPE: &str = "application/vnd.oci.image.layer.v1.tar"; | ||
|
||
pub(crate) async fn fetch_oci_bytes( | ||
img: &str, | ||
allow_latest: bool, | ||
allowed_insecure: &[String], | ||
) -> Result<Vec<u8>> { | ||
if !allow_latest && img.ends_with(":latest") { | ||
return Err(VinoError::LatestDisallowed(img.to_string())); | ||
} | ||
let cf = cached_file(img); | ||
if !cf.exists() { | ||
let img = | ||
oci_distribution::Reference::from_str(img).context("Could not parse OCI reference")?; | ||
let auth = if let Ok(u) = std::env::var(OCI_VAR_USER) { | ||
if let Ok(p) = std::env::var(OCI_VAR_PASSWORD) { | ||
oci_distribution::secrets::RegistryAuth::Basic(u, p) | ||
} else { | ||
oci_distribution::secrets::RegistryAuth::Anonymous | ||
} | ||
} else { | ||
oci_distribution::secrets::RegistryAuth::Anonymous | ||
}; | ||
|
||
let protocol = | ||
oci_distribution::client::ClientProtocol::HttpsExcept(allowed_insecure.to_vec()); | ||
let config = oci_distribution::client::ClientConfig { protocol }; | ||
let mut c = oci_distribution::Client::new(config); | ||
let imgdata = pull(&mut c, &img, &auth).await; | ||
|
||
match imgdata { | ||
Ok(imgdata) => { | ||
let mut f = std::fs::File::create(cf).context("Could not create cache file")?; | ||
let content = imgdata | ||
.layers | ||
.iter() | ||
.map(|l| l.data.clone()) | ||
.flatten() | ||
.collect::<Vec<_>>(); | ||
f.write_all(&content) | ||
.context("Could not write actor bytes contents to cache file")?; | ||
f.flush() | ||
.context("Failed while flushing data to cache file")?; | ||
Ok(content) | ||
} | ||
Err(e) => { | ||
error!("Failed to fetch OCI bytes: {}", e); | ||
Err(VinoError::OciFetchFailure(img.to_string(), e.to_string())) | ||
} | ||
} | ||
} else { | ||
let mut buf = vec![]; | ||
let mut f = std::fs::File::open(cached_file(img)) | ||
.context(format!("Could not open cache file {}", img))?; | ||
f.read_to_end(&mut buf) | ||
.context("Failed reading to the end of cache file")?; | ||
Ok(buf) | ||
} | ||
} | ||
|
||
fn cached_file(img: &str) -> PathBuf { | ||
let path = temp_dir(); | ||
let path = path.join("wasmcloud_ocicache"); | ||
let _ = ::std::fs::create_dir_all(&path); | ||
// should produce a file like wasmcloud_azurecr_io_kvcounter_v1.bin | ||
let img = img.replace(":", "_"); | ||
let img = img.replace("/", "_"); | ||
let img = img.replace(".", "_"); | ||
let mut path = path.join(img); | ||
path.set_extension("bin"); | ||
|
||
path | ||
} | ||
|
||
async fn pull( | ||
client: &mut oci_distribution::Client, | ||
img: &oci_distribution::Reference, | ||
auth: &oci_distribution::secrets::RegistryAuth, | ||
) -> Result<oci_distribution::client::ImageData> { | ||
Ok(client | ||
.pull( | ||
&img, | ||
&auth, | ||
vec![PROVIDER_ARCHIVE_MEDIA_TYPE, WASM_MEDIA_TYPE, OCI_MEDIA_TYPE], | ||
) | ||
.await?) | ||
} |
Oops, something went wrong.