Introduce REPL for testing (#14)

This commit is contained in:
vms 2020-08-07 23:24:28 +03:00 committed by GitHub
parent c06dddd5ae
commit 7254fe7e8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 470 additions and 95 deletions

158
Cargo.lock generated
View File

@ -82,6 +82,12 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "bincode"
version = "1.3.1"
@ -99,10 +105,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "blake3"
version = "0.3.5"
name = "blake2b_simd"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59f88a20f7dc23e3896bcbd85add056543c87215de721468b90e0c85d5a9f365"
checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "blake3"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce4f9586c9a3151c4b49b19e82ba163dd073614dd057e53c969e1a4db5b52720"
dependencies = [
"arrayref",
"arrayvec",
@ -133,9 +150,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap"
version = "2.33.1"
version = "2.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
checksum = "10040cdf04294b565d9e0319955430099ec3813a64c952b86a41200ad714ae48"
dependencies = [
"ansi_term",
"atty",
@ -313,6 +330,27 @@ dependencies = [
"generic-array 0.14.3",
]
[[package]]
name = "dirs-next"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cbcf9241d9e8d106295bd496bbe2e9cffd5fa098f2a8c9e2bbcbf09773c11a8"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c60f7b8a8953926148223260454befb50c751d3c50e1c178c4fd1ace4083c9a"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "effector"
version = "0.1.0"
@ -431,6 +469,19 @@ dependencies = [
"serde_json",
]
[[package]]
name = "fce-repl"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"fluence-app-service",
"rustop",
"rustyline",
"serde_json",
"uuid",
]
[[package]]
name = "fce-wit-generator"
version = "0.1.1"
@ -466,7 +517,7 @@ dependencies = [
[[package]]
name = "fluence"
version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#7018a64c29278a673e543867e691f74b3d652288"
source = "git+https://github.com/fluencelabs/rust-sdk#8418111074644a1bc197315060facd34778099de"
dependencies = [
"fluence-sdk-macro",
"fluence-sdk-main",
@ -476,16 +527,10 @@ dependencies = [
name = "fluence-app-service"
version = "0.1.0"
dependencies = [
"cmd_lib",
"fluence-faas",
"log",
"serde",
"serde_derive",
"serde_json",
"toml",
"wasmer-runtime-core-fl",
"wasmer-runtime-fl",
"wasmer-wasi-fl",
]
[[package]]
@ -507,7 +552,7 @@ dependencies = [
[[package]]
name = "fluence-sdk-macro"
version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#7018a64c29278a673e543867e691f74b3d652288"
source = "git+https://github.com/fluencelabs/rust-sdk#8418111074644a1bc197315060facd34778099de"
dependencies = [
"fluence-sdk-wit 0.2.0 (git+https://github.com/fluencelabs/rust-sdk)",
]
@ -515,7 +560,7 @@ dependencies = [
[[package]]
name = "fluence-sdk-main"
version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#7018a64c29278a673e543867e691f74b3d652288"
source = "git+https://github.com/fluencelabs/rust-sdk#8418111074644a1bc197315060facd34778099de"
dependencies = [
"log",
]
@ -523,7 +568,7 @@ dependencies = [
[[package]]
name = "fluence-sdk-wit"
version = "0.2.0"
source = "git+https://github.com/fluencelabs/rust-sdk#7018a64c29278a673e543867e691f74b3d652288"
source = "git+https://github.com/fluencelabs/rust-sdk#8418111074644a1bc197315060facd34778099de"
dependencies = [
"proc-macro2",
"quote",
@ -715,7 +760,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"env_logger",
"fluence-faas",
"fluence-app-service",
"log",
]
@ -752,9 +797,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.73"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
[[package]]
name = "lock_api"
@ -836,6 +881,19 @@ dependencies = [
"void",
]
[[package]]
name = "nix"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"libc",
"void",
]
[[package]]
name = "nom"
version = "5.1.2"
@ -1051,6 +1109,17 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_users"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
dependencies = [
"getrandom",
"redox_syscall",
"rust-argon2",
]
[[package]]
name = "regex"
version = "1.3.9"
@ -1069,6 +1138,18 @@ version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]]
name = "rust-argon2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
dependencies = [
"base64",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
@ -1084,6 +1165,31 @@ dependencies = [
"semver",
]
[[package]]
name = "rustop"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccb57833f46c9d679d68f383e81b9a74b82aa8d6f8a9c9aaae7fc29476bc3274"
[[package]]
name = "rustyline"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3358c21cbbc1a751892528db4e1de4b7a2b6a73f001e215aaba97d712cfa9777"
dependencies = [
"cfg-if",
"dirs-next",
"libc",
"log",
"memchr",
"nix 0.17.0",
"scopeguard",
"unicode-segmentation",
"unicode-width",
"utf8parse",
"winapi",
]
[[package]]
name = "ryu"
version = "1.0.5"
@ -1193,9 +1299,9 @@ checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1"
[[package]]
name = "syn"
version = "1.0.36"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250"
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
dependencies = [
"proc-macro2",
"quote",
@ -1341,6 +1447,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "utf8parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
[[package]]
name = "uuid"
version = "0.8.1"
@ -1434,7 +1546,7 @@ dependencies = [
"cranelift-entity",
"cranelift-native",
"libc",
"nix",
"nix 0.15.0",
"rayon",
"serde",
"serde-bench",
@ -1503,7 +1615,7 @@ dependencies = [
"indexmap",
"lazy_static",
"libc",
"nix",
"nix 0.15.0",
"page_size",
"parking_lot",
"rustc_version",
@ -1532,7 +1644,7 @@ dependencies = [
"indexmap",
"lazy_static",
"libc",
"nix",
"nix 0.15.0",
"page_size",
"parking_lot",
"rustc_version",

View File

@ -16,6 +16,7 @@ members = [
"fluence-app-service",
"fluence-faas",
"tools/cli",
"tools/repl",
]
[profile.release]

View File

@ -67,16 +67,16 @@ impl FCE {
/// Load a new module inside FCE.
pub fn load_module<S: Into<String>>(
&mut self,
module_name: S,
name: S,
wasm_bytes: &[u8],
config: FCEModuleConfig,
) -> Result<()> {
self.load_module_(module_name.into(), wasm_bytes, config)
self.load_module_(name.into(), wasm_bytes, config)
}
pub fn load_module_(
&mut self,
module_name: String,
name: String,
wasm_bytes: &[u8],
config: FCEModuleConfig,
) -> Result<()> {
@ -84,7 +84,7 @@ impl FCE {
let module = FCEModule::new(&wasm_bytes, config, &self.modules)?;
match self.modules.entry(module_name) {
match self.modules.entry(name) {
Entry::Vacant(entry) => {
entry.insert(module);
Ok(())
@ -94,8 +94,8 @@ impl FCE {
}
/// Unload previously loaded module.
pub fn unload_module<S: AsRef<str>>(&mut self, module_name: S) -> Result<()> {
self.unload_module_(module_name.as_ref())
pub fn unload_module<S: AsRef<str>>(&mut self, name: S) -> Result<()> {
self.unload_module_(name.as_ref())
}
pub fn unload_module_(&mut self, module_name: &str) -> Result<()> {

View File

@ -28,7 +28,7 @@ fn main() -> Result<(), anyhow::Error> {
greeting_node.get_interface()
);
let result = greeting_node.call_module(
let result = greeting_node.call(
"greeting.wasm",
"greeting",
&[IValue::String("Fluence".to_string()), IValue::I32(1)],

View File

@ -5,7 +5,7 @@ authors = ["Fluence Labs"]
edition = "2018"
[dependencies]
fluence-faas = { path = "../../fluence-faas" }
fluence-app-service = { path = "../../fluence-app-service" }
anyhow = "1.0.31"
log = "0.4.11"
env_logger = "0.7.1"

View File

@ -1,24 +1,18 @@
core_modules_dir = "wasm/artifacts/wasm_modules"
modules_dir = "wasm/artifacts/wasm_modules"
service_base_dir = "/Users/tmp"
[[core_module]]
[[module]]
name = "ipfs_node.wasm"
mem_pages_count = 100
logger_enabled = true
[core_module.imports]
mysql = "/usr/bin/mysql"
[module.imports]
ipfs = "/usr/local/bin/ipfs"
[core_module.wasi]
[module.wasi]
envs = ["IPFS_ADDR=/dns4/relay02.fluence.dev/tcp/15001", "timeout=1s"]
preopened_files = ["./wasm/artifacts"]
mapped_dirs = { "tmp" = "./wasm/artifacts" }
[rpc_module]
[[module]]
name = "zipfs_rpc.wasm"
mem_pages_count = 100
logger_enabled = true
[rpc_module.wasi]
envs = []
preopened_files = ["./wasm/artifacts"]
mapped_dirs = { "tmp" = "./wasm/artifacts" }

View File

@ -14,27 +14,23 @@
* limitations under the License.
*/
use fluence_faas::FluenceFaaS;
use fluence_faas::IValue;
use fluence_app_service::AppService;
use fluence_app_service::IValue;
use std::path::PathBuf;
use anyhow::Context;
const IPFS_MODULES_CONFIG_PATH: &str = "Config.toml";
const IPFS_RPC: &str = "wasm/artifacts/ipfs_rpc.wasm";
fn main() -> Result<(), anyhow::Error> {
env_logger::init();
let ipfs_rpc = std::fs::read(IPFS_RPC).with_context(|| format!("{} wasn't found", IPFS_RPC))?;
let mut ipfs_node = FluenceFaaS::new(PathBuf::from(IPFS_MODULES_CONFIG_PATH))?;
let mut ipfs_node = AppService::with_raw_config();
println!("ipfs node interface is\n{}", ipfs_node.get_interface());
let node_address = ipfs_node.call_module("ipfs_node.wasm", "get_address", &[])?;
let node_address = ipfs_node.call("ipfs_node.wasm", "get_address", &[])?;
println!("ipfs node address is:\n{:?}", node_address);
let result = ipfs_node.call_code(
&ipfs_rpc,
let result = ipfs_node.call(
"ipfs_rpc.wasm",
"get",
&[IValue::String(
"QmXdC36pX1B1sdHdbri859vMYctQjAhvTmkWyG9xzhShxb".to_string(),

View File

@ -26,7 +26,7 @@ fn main() -> Result<(), anyhow::Error> {
let mut records_test = FluenceFaaS::new(PathBuf::from(RECORDS_MODULES_CONFIG_PATH))?;
println!("ipfs node interface is\n{}", records_test.get_interface());
let result = records_test.call_module("pure.wasm", "invoke", &[])?;
let result = records_test.call("pure.wasm", "invoke", &[])?;
println!("execution result {:?}", result);
Ok(())

View File

@ -7,14 +7,9 @@ edition = "2018"
[dependencies]
fluence-faas = { path = "../fluence-faas" }
wasmer-runtime = { package = "wasmer-runtime-fl", version = "0.17.0" }
# dynamicfunc-fat-closures allows using state inside DynamicFunc
wasmer-core = { package = "wasmer-runtime-core-fl", version = "0.17.0", features = ["dynamicfunc-fat-closures"] }
wasmer-wasi = { package = "wasmer-wasi-fl", version = "0.17.0" }
toml = "0.5.6"
serde = { version = "1.0.111", features = ["derive"] }
serde_json = "1.0.53"
serde_derive = "1.0.111"
cmd_lib = "0.7.8"
log = "0.4.8"
[features]
raw-module-api = ["fluence-faas/raw-module-api"]

View File

@ -22,7 +22,7 @@ use std::error::Error;
#[derive(Debug)]
pub enum AppServiceError {
/// An error related to config parsing.
InvalidArguments(String),
InvalidConfig(String),
/// Various errors related to file i/o.
IOError(String),
@ -36,7 +36,7 @@ impl Error for AppServiceError {}
impl std::fmt::Display for AppServiceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
AppServiceError::InvalidArguments(err_msg) => write!(f, "{}", err_msg),
AppServiceError::InvalidConfig(err_msg) => write!(f, "{}", err_msg),
AppServiceError::IOError(err_msg) => write!(f, "{}", err_msg),
AppServiceError::FaaSError(err) => write!(f, "{}", err),
}
@ -57,12 +57,12 @@ impl From<FaaSError> for AppServiceError {
impl From<toml::de::Error> for AppServiceError {
fn from(err: toml::de::Error) -> Self {
AppServiceError::InvalidArguments(format!("{}", err))
AppServiceError::InvalidConfig(format!("{}", err))
}
}
impl From<std::convert::Infallible> for AppServiceError {
fn from(inf: std::convert::Infallible) -> Self {
match inf {}
fn from(_: std::convert::Infallible) -> Self {
unreachable!()
}
}

View File

@ -22,6 +22,7 @@ use fluence_faas::FluenceFaaS;
use fluence_faas::ModulesConfig;
use std::convert::TryInto;
use std::path::PathBuf;
const SERVICE_ID_ENV_NAME: &str = "service_id";
const SERVICE_LOCAL_DIR_NAME: &str = "local";
@ -35,7 +36,7 @@ pub struct AppService {
}
impl AppService {
/// Creates Service with given modules and service id.
/// Create Service with given modules and service id.
pub fn new<I, C, S>(modules: I, config: C, service_id: S) -> Result<Self>
where
I: IntoIterator<Item = String>,
@ -45,7 +46,7 @@ impl AppService {
{
let config: ModulesConfig = config.try_into()?;
let service_id = service_id.as_ref();
let config = Self::set_env_and_dirs(config, service_id)?;
let config = Self::set_env_and_dirs(config, service_id, None)?;
let modules = modules.into_iter().collect();
let faas = FluenceFaaS::with_module_names(&modules, config)?;
@ -53,9 +54,33 @@ impl AppService {
Ok(Self { faas })
}
/// Create Service with given raw config, service id and service base dir.
pub fn with_raw_config<P, SI>(
config: P,
service_id: SI,
service_base_dir: Option<&str>,
) -> Result<Self>
where
P: Into<PathBuf>,
SI: AsRef<str>,
{
let service_id = service_id.as_ref();
let service_base_dir = service_base_dir;
let config_content = std::fs::read(config.into())?;
let config: crate::RawModulesConfig = toml::from_slice(&config_content)?;
let config = config.try_into()?;
let config = Self::set_env_and_dirs(config, service_id, service_base_dir)?;
let faas = FluenceFaaS::with_raw_config(config)?;
Ok(Self { faas })
}
/// Call a specified function of loaded module by its name.
// TODO: replace serde_json::Value with Vec<u8>?
pub fn call_module<MN: AsRef<str>, FN: AsRef<str>>(
pub fn call<MN: AsRef<str>, FN: AsRef<str>>(
&mut self,
module_name: MN,
func_name: FN,
@ -64,7 +89,7 @@ impl AppService {
let arguments = Self::json_to_ivalue(arguments)?;
self.faas
.call_module(module_name, func_name, &arguments)
.call(module_name, func_name, &arguments)
.map_err(Into::into)
}
@ -78,11 +103,15 @@ impl AppService {
/// - service_base_dir/service_id/SERVICE_LOCAL_DIR_NAME
/// - service_base_dir/service_id/SERVICE_TMP_DIR_NAME
/// 2. adding service_id to environment variables
fn set_env_and_dirs(mut config: ModulesConfig, service_id: &str) -> Result<ModulesConfig> {
let base_dir = match config.service_base_dir {
Some(ref base_dir) => base_dir,
// TODO: refactor it later
None => {
fn set_env_and_dirs(
mut config: ModulesConfig,
service_id: &str,
service_base_dir: Option<&str>,
) -> Result<ModulesConfig> {
let base_dir = match (&config.service_base_dir, service_base_dir) {
(_, Some(base_dir)) => base_dir,
(Some(ref base_dir), None) => base_dir,
_ => {
return Err(AppServiceError::IOError(String::from(
"service_base_dir should be specified",
)))
@ -130,7 +159,7 @@ impl AppService {
let is_empty_obj = arguments.as_object().map_or(false, |m| m.is_empty());
let arguments = if !is_null && !is_empty_arr && !is_empty_obj {
Some(fluence_faas::to_interface_value(&arguments).map_err(|e| {
AppServiceError::InvalidArguments(format!(
AppServiceError::InvalidConfig(format!(
"can't parse arguments as array of interface types: {}",
e
))
@ -143,10 +172,29 @@ impl AppService {
Some(IValue::Record(arguments)) => Ok(arguments.into_vec()),
// Convert null, [] and {} into vec![]
None => Ok(vec![]),
other => Err(AppServiceError::InvalidArguments(format!(
other => Err(AppServiceError::InvalidConfig(format!(
"expected array of interface values: got {:?}",
other
))),
}
}
}
// This API is intended for testing purposes (mostly in FCE REPL)
#[cfg(feature = "raw-module-api")]
impl AppService {
pub fn load_module<S, C>(&mut self, name: S, wasm_bytes: &[u8], config: Option<C>) -> Result<()>
where
S: Into<String>,
C: TryInto<crate::ModuleConfig>,
fluence_faas::FaaSError: From<C::Error>,
{
self.faas
.load_module(name, &wasm_bytes, config)
.map_err(Into::into)
}
pub fn unload_module<S: AsRef<str>>(&mut self, module_name: S) -> Result<()> {
self.faas.unload_module(module_name).map_err(Into::into)
}
}

View File

@ -18,3 +18,6 @@ serde_json = "1.0.53"
serde_derive = "1.0.111"
cmd_lib = "0.7.8"
log = "0.4.8"
[features]
raw-module-api = []

View File

@ -62,7 +62,7 @@ impl From<toml::de::Error> for FaaSError {
}
impl From<std::convert::Infallible> for FaaSError {
fn from(inf: std::convert::Infallible) -> Self {
match inf {}
fn from(_: std::convert::Infallible) -> Self {
unreachable!()
}
}

View File

@ -15,19 +15,18 @@
*/
use crate::misc::ModulesConfig;
use crate::faas_interface::FaaSFunctionSignature;
use crate::faas_interface::FaaSInterface;
use crate::FaaSError;
use crate::Result;
use super::faas_interface::FaaSInterface;
use super::FaaSError;
use super::IValue;
use crate::IValue;
use fce::FCE;
use std::convert::TryInto;
use std::collections::HashSet;
use std::fs;
use std::path::PathBuf;
use crate::faas_interface::FaaSFunctionSignature;
use std::collections::HashSet;
// TODO: remove and use mutex instead
unsafe impl Send for FluenceFaaS {}
@ -86,13 +85,15 @@ impl FluenceFaaS {
/// Creates FaaS from config deserialized from TOML.
pub fn with_raw_config<C>(config: C) -> Result<Self>
where
C: TryInto<ModulesConfig, Error = FaaSError>,
C: TryInto<ModulesConfig>,
FaaSError: From<C::Error>,
{
let config = config.try_into()?;
let modules = config.modules_dir.as_ref().map_or(Ok(vec![]), |dir| {
Self::load_modules(dir, ModulesLoadStrategy::All)
})?;
Self::with_modules(modules, config)
Self::with_modules::<_, ModulesConfig>(modules, config)
}
/// Creates FaaS with given modules.
@ -110,7 +111,6 @@ impl FluenceFaaS {
module_config @ Some(_) => module_config,
None => config.default_modules_config.clone(),
};
let fce_module_config = crate::misc::make_fce_config(module_config)?;
fce.load_module(name.clone(), &bytes, fce_module_config)?;
}
@ -178,7 +178,7 @@ impl FluenceFaaS {
}
/// Call a specified function of loaded on a startup module by its name.
pub fn call_module<MN: AsRef<str>, FN: AsRef<str>>(
pub fn call<MN: AsRef<str>, FN: AsRef<str>>(
&mut self,
module_name: MN,
func_name: FN,
@ -214,3 +214,25 @@ impl FluenceFaaS {
FaaSInterface { modules }
}
}
// This API is intended for testing purposes (mostly in FCE REPL)
#[cfg(feature = "raw-module-api")]
impl FluenceFaaS {
pub fn load_module<S, C>(&mut self, name: S, wasm_bytes: &[u8], config: Option<C>) -> Result<()>
where
S: Into<String>,
C: TryInto<crate::ModuleConfig>,
FaaSError: From<C::Error>,
{
let config = config.map(|c| c.try_into()).transpose()?;
let fce_module_config = crate::misc::make_fce_config(config)?;
self.fce
.load_module(name, &wasm_bytes, fce_module_config)
.map_err(Into::into)
}
pub fn unload_module<S: AsRef<str>>(&mut self, module_name: S) -> Result<()> {
self.fce.unload_module(module_name).map_err(Into::into)
}
}

View File

@ -18,7 +18,6 @@ use crate::FaaSError;
use crate::Result;
use serde_derive::{Serialize, Deserialize};
use toml::from_slice;
use std::collections::HashMap;
use std::convert::TryInto;
@ -70,7 +69,7 @@ impl TryInto<ModulesConfig> for RawModulesConfig {
type Error = FaaSError;
fn try_into(self) -> Result<ModulesConfig> {
from_raw_config(self)
from_raw_modules_config(self)
}
}
@ -83,6 +82,14 @@ pub struct RawModuleConfig {
pub wasi: Option<RawWASIConfig>,
}
impl TryInto<ModuleConfig> for RawModuleConfig {
type Error = FaaSError;
fn try_into(self) -> Result<ModuleConfig> {
from_raw_module_config(self).map(|(_, module_config)| module_config)
}
}
#[derive(Deserialize, Serialize, Debug, Clone, Default)]
pub struct RawDefaultModuleConfig {
pub mem_pages_count: Option<u32>,
@ -209,7 +216,7 @@ pub struct WASIConfig {
}
/// Prepare config after parsing it from TOML.
fn from_raw_config(config: RawModulesConfig) -> Result<ModulesConfig> {
fn from_raw_modules_config(config: RawModulesConfig) -> Result<ModulesConfig> {
let service_base_dir = config.service_base_dir;
let modules_config = config
.module
@ -233,7 +240,7 @@ fn from_raw_config(config: RawModulesConfig) -> Result<ModulesConfig> {
/// Parse config from TOML.
pub(crate) fn load_config(config_file_path: std::path::PathBuf) -> Result<RawModulesConfig> {
let file_content = std::fs::read(config_file_path)?;
Ok(from_slice(&file_content)?)
Ok(toml::from_slice(&file_content)?)
}
fn from_raw_module_config(config: RawModuleConfig) -> Result<(String, ModuleConfig)> {

23
tools/repl/Cargo.toml Normal file
View File

@ -0,0 +1,23 @@
[package]
name = "fce-repl"
description = "Fluence FCE REPL intended for testing purposes"
version = "0.1.0"
authors = ["Fluence Labs"]
repository = "https://github.com/fluencelabs/fce/tools/repl"
license = "Apache-2.0"
edition = "2018"
[[bin]]
name = "fce-repl"
path = "src/main.rs"
[dependencies]
fluence-app-service = { path = "../../fluence-app-service", features = ["raw-module-api"] }
anyhow = "1.0.31"
clap = "2.33.1"
serde_json = "1.0.57"
rustyline = "6.1.2"
rustop = "1.1.0"
uuid = { version = "0.8.1", features = ["v4"] }

174
tools/repl/src/main.rs Normal file
View File

@ -0,0 +1,174 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#![deny(
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
#![warn(rust_2018_idioms)]
/// Command-line tool intended to test Fluence FaaS.
use std::fs;
macro_rules! next_argument {
($arg_name:ident, $args:ident, $error_msg:expr) => {
let $arg_name = if let Some($arg_name) = $args.next() {
$arg_name
} else {
println!($error_msg);
continue;
};
};
}
fn main() -> Result<(), anyhow::Error> {
let (args, _) = rustop::opts! {
synopsis "Fluence Application service REPL";
param config_file_path: Option<String>, desc: "Path to a service config";
}
.parse_or_exit();
println!("Welcome to the Fluence FaaS REPL:");
let mut app_service = create_service_from_config(args.config_file_path)?;
let mut rl = rustyline::Editor::<()>::new();
loop {
let readline = rl.readline(">> ");
let readline = match readline {
Ok(readline) => readline,
Err(e) => {
println!("a error occurred: {}", e);
break;
}
};
let mut args = readline.split_whitespace();
match args.next() {
Some("new") => {
app_service = match create_service_from_config(args.next()) {
Ok(service) => service,
Err(e) => {
println!("failed to create a new application service: {}", e);
app_service
}
};
}
Some("load") => {
next_argument!(module_name, args, "Module name should be specified");
next_argument!(module_path, args, "Module path should be specified");
let wasm_bytes = fs::read(module_path);
if let Err(e) = wasm_bytes {
println!("failed to read wasm module: {}", e);
continue;
}
let result_msg = match app_service
.load_module::<String, fluence_app_service::ModuleConfig>(
module_name.into(),
&wasm_bytes.unwrap(),
None,
) {
Ok(_) => "module successfully loaded into App service".to_string(),
Err(e) => format!("module loaded failed with: {:?}", e),
};
println!("{}", result_msg);
}
Some("unload") => {
next_argument!(module_name, args, "Module name should be specified");
let result_msg = match app_service.unload_module(module_name) {
Ok(_) => "module successfully unloaded from App service".to_string(),
Err(e) => format!("module unloaded failed with: {:?}", e),
};
println!("{}", result_msg);
}
Some("call") => {
next_argument!(module_name, args, "Module name should be specified");
next_argument!(func_name, args, "Function name should be specified");
let module_arg: String = args.collect();
let module_arg: serde_json::Value = match serde_json::from_str(&module_arg) {
Ok(module_arg) => module_arg,
Err(e) => {
println!("incorrect arguments {}", e);
continue;
}
};
let result = match app_service.call(module_name, func_name, module_arg) {
Ok(result) => format!("result: {:?}", result),
Err(e) => format!("execution failed with {:?}", e),
};
println!("{}", result);
}
Some("interface") => {
let interface = app_service.get_interface();
println!("application service interface: {}", interface);
}
Some("h") | Some("help") | None => {
println!(
"Enter:\n\
new [config_path] - to create a new AppService (old will be removed)
load <module_name> <module_path> - to load a new Wasm module into App service\n\
unload <module_name> - to unload Wasm module from AppService\n\
call <module_name> <func_name> [args] - to call function with given name on module with given module_name\n\
interface - to print public interface of current AppService\n\
h/help - to print this message\n\
e/exit/q/quit - to exit"
);
}
Some("e") | Some("exit") | Some("q") | Some("quit") => break,
_ => {
println!("unsupported command");
}
}
}
Ok(())
}
fn create_service_from_config<S: Into<String>>(
config_file_path: Option<S>,
) -> Result<fluence_app_service::AppService, anyhow::Error> {
let tmp_path: String = std::env::temp_dir().to_string_lossy().into();
let service_id = uuid::Uuid::new_v4().to_string();
let app_service = match config_file_path {
Some(config_file_path) => {
let config_file_path = config_file_path.into();
fluence_app_service::AppService::with_raw_config(
config_file_path,
&service_id,
Some(&tmp_path),
)
}
None => {
let mut config: fluence_app_service::RawModulesConfig = <_>::default();
config.service_base_dir = Some(tmp_path);
fluence_app_service::AppService::new(std::iter::empty(), config, &service_id)
}
}?;
println!("app service's created with service id = {}", service_id);
Ok(app_service)
}