diff --git a/Cargo.lock b/Cargo.lock index fd7cbe2c..ca1e7ffa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,6 +472,22 @@ dependencies = [ "fluence-sdk-main", ] +[[package]] +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]] name = "fluence-faas" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0aa23f08..bd13de23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "examples/records/wasm/effector", "examples/records/wasm/pure", "examples/records/wasm/test-record", + "fluence-app-service", "fluence-faas", "tools/cli", ] diff --git a/fluence-app-service/Cargo.toml b/fluence-app-service/Cargo.toml new file mode 100644 index 00000000..c5dd6303 --- /dev/null +++ b/fluence-app-service/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "fluence-app-service" +version = "0.1.0" +authors = ["Fluence Labs"] +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" diff --git a/fluence-app-service/README.md b/fluence-app-service/README.md new file mode 100644 index 00000000..a7133ab1 --- /dev/null +++ b/fluence-app-service/README.md @@ -0,0 +1,3 @@ +# Fluence Application Service + +Fluence Application Service intended to run various Wasm binaries. At now, it is in the heavily developing phase. For more details please see the IPFS [example](https://github.com/fluencelabs/fce/tree/master/examples/ipfs_node). diff --git a/fluence-app-service/src/errors.rs b/fluence-app-service/src/errors.rs new file mode 100644 index 00000000..9bb4c36d --- /dev/null +++ b/fluence-app-service/src/errors.rs @@ -0,0 +1,68 @@ +/* + * 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. + */ + +use fluence_faas::FaaSError; + +use std::io::Error as IOError; +use std::error::Error; + +#[derive(Debug)] +pub enum AppServiceError { + /// An error related to config parsing. + InvalidArguments(String), + + /// Various errors related to file i/o. + IOError(String), + + /// FaaS errors. + FaaSError(FaaSError), +} + +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::IOError(err_msg) => write!(f, "{}", err_msg), + AppServiceError::FaaSError(err) => write!(f, "{}", err), + } + } +} + +impl From for AppServiceError { + fn from(err: IOError) -> Self { + AppServiceError::IOError(format!("{}", err)) + } +} + +impl From for AppServiceError { + fn from(err: FaaSError) -> Self { + AppServiceError::FaaSError(err) + } +} + +impl From for AppServiceError { + fn from(err: toml::de::Error) -> Self { + AppServiceError::InvalidArguments(format!("{}", err)) + } +} + +impl From for AppServiceError { + fn from(inf: std::convert::Infallible) -> Self { + match inf {} + } +} diff --git a/fluence-app-service/src/lib.rs b/fluence-app-service/src/lib.rs new file mode 100644 index 00000000..0209d1d9 --- /dev/null +++ b/fluence-app-service/src/lib.rs @@ -0,0 +1,44 @@ +/* + * 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. + */ +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] + +mod errors; +mod service; + +pub(crate) type Result = std::result::Result; + +pub use errors::AppServiceError; +pub use service::AppService; + +pub use fluence_faas::IValue; +pub use fluence_faas::IType; +pub use fluence_faas::FaaSInterface; +pub use fluence_faas::RawModulesConfig; +pub use fluence_faas::RawModuleConfig; +pub use fluence_faas::ModulesConfig; +pub use fluence_faas::ModuleConfig; +pub use fluence_faas::WASIConfig; +pub use fluence_faas::to_interface_value; +pub use fluence_faas::from_interface_values; diff --git a/fluence-app-service/src/service.rs b/fluence-app-service/src/service.rs new file mode 100644 index 00000000..bbcb1e02 --- /dev/null +++ b/fluence-app-service/src/service.rs @@ -0,0 +1,152 @@ +/* + * 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. + */ + +use crate::Result; +use super::AppServiceError; +use super::IValue; + +use fluence_faas::FluenceFaaS; +use fluence_faas::ModulesConfig; + +use std::convert::TryInto; + +const SERVICE_ID_ENV_NAME: &str = "service_id"; +const SERVICE_LOCAL_DIR_NAME: &str = "local"; +const SERVICE_TMP_DIR_NAME: &str = "tmp"; + +// TODO: remove and use mutex instead +unsafe impl Send for AppService {} + +pub struct AppService { + faas: FluenceFaaS, +} + +impl AppService { + /// Creates Service with given modules and service id. + pub fn new(modules: I, config: C, service_id: S) -> Result + where + I: IntoIterator, + C: TryInto, + S: AsRef, + AppServiceError: From, + { + let config: ModulesConfig = config.try_into()?; + let service_id = service_id.as_ref(); + let config = Self::set_env_and_dirs(config, service_id)?; + + let modules = modules.into_iter().collect(); + let faas = FluenceFaaS::with_module_names(&modules, config)?; + + Ok(Self { faas }) + } + + /// Call a specified function of loaded module by its name. + // TODO: replace serde_json::Value with Vec? + pub fn call_module, FN: AsRef>( + &mut self, + module_name: MN, + func_name: FN, + arguments: serde_json::Value, + ) -> Result> { + let arguments = Self::json_to_ivalue(arguments)?; + + self.faas + .call_module(module_name, func_name, &arguments) + .map_err(Into::into) + } + + /// Return all export functions (name and signatures) of loaded modules. + pub fn get_interface(&self) -> fluence_faas::FaaSInterface<'_> { + self.faas.get_interface() + } + + /// Prepare service before starting by: + /// 1. creating a directory structure in the following form: + /// - 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 { + let base_dir = match config.service_base_dir { + Some(ref base_dir) => base_dir, + // TODO: refactor it later + None => { + return Err(AppServiceError::IOError(String::from( + "service_base_dir should be specified", + ))) + } + }; + + let service_dir_path = std::path::Path::new(base_dir).join(service_id); + std::fs::create_dir(service_dir_path.clone())?; // will return an error if dir is already exists + + let local_dir_path = service_dir_path.join(SERVICE_LOCAL_DIR_NAME); + std::fs::create_dir(local_dir_path.clone())?; // will return an error if dir is already exists + + let tmp_dir_path = service_dir_path.join(SERVICE_TMP_DIR_NAME); + std::fs::create_dir(tmp_dir_path.clone())?; // will return an error if dir is already exists + + let local_dir: String = local_dir_path.to_string_lossy().into(); + let tmp_dir: String = tmp_dir_path.to_string_lossy().into(); + + let service_id_env = vec![format!("{}={}", SERVICE_ID_ENV_NAME, service_id).into_bytes()]; + let preopened_files = vec![local_dir.clone(), tmp_dir.clone()]; + let mapped_dirs = vec![ + (String::from(SERVICE_LOCAL_DIR_NAME), local_dir), + (String::from(SERVICE_TMP_DIR_NAME), tmp_dir), + ]; + + config.modules_config = config + .modules_config + .into_iter() + .map(|(name, module_config)| { + let module_config = module_config + .extend_wasi_envs(service_id_env.clone()) + .extend_wasi_files(preopened_files.clone(), mapped_dirs.clone()); + + (name, module_config) + }) + .collect(); + + Ok(config) + } + + fn json_to_ivalue(arguments: serde_json::Value) -> Result> { + // If arguments are on of: null, [] or {}, avoid calling `to_interface_value` + let is_null = arguments.is_null(); + let is_empty_arr = arguments.as_array().map_or(false, |a| a.is_empty()); + 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!( + "can't parse arguments as array of interface types: {}", + e + )) + })?) + } else { + None + }; + + match arguments { + Some(IValue::Record(arguments)) => Ok(arguments.into_vec()), + // Convert null, [] and {} into vec![] + None => Ok(vec![]), + other => Err(AppServiceError::InvalidArguments(format!( + "expected array of interface values: got {:?}", + other + ))), + } + } +} diff --git a/fluence-faas/src/faas.rs b/fluence-faas/src/faas.rs index f22a38a6..587c1546 100644 --- a/fluence-faas/src/faas.rs +++ b/fluence-faas/src/faas.rs @@ -14,8 +14,7 @@ * limitations under the License. */ -use crate::misc::{CoreModulesConfig, make_fce_config}; -use crate::RawCoreModulesConfig; +use crate::misc::ModulesConfig; use crate::Result; use super::faas_interface::FaaSInterface; @@ -23,7 +22,6 @@ use super::FaaSError; use super::IValue; use fce::FCE; -use fce::FCEModuleConfig; use std::convert::TryInto; use std::fs; @@ -76,9 +74,6 @@ impl<'a> ModulesLoadStrategy<'a> { pub struct FluenceFaaS { fce: FCE, - - // config for code loaded by call_code function - faas_code_config: FCEModuleConfig, } impl FluenceFaaS { @@ -89,9 +84,12 @@ impl FluenceFaaS { } /// Creates FaaS from config deserialized from TOML. - pub fn with_raw_config(config: RawCoreModulesConfig) -> Result { - let config = crate::misc::from_raw_config(config)?; - let modules = config.core_modules_dir.as_ref().map_or(Ok(vec![]), |dir| { + pub fn with_raw_config(config: C) -> Result + where + C: TryInto, + { + 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) @@ -101,48 +99,48 @@ impl FluenceFaaS { pub fn with_modules(modules: I, config: C) -> Result where I: IntoIterator)>, - C: TryInto, + C: TryInto, FaaSError: From, { let mut fce = FCE::new(); let mut config = config.try_into()?; for (name, bytes) in modules { - let module_config = crate::misc::make_fce_config(config.modules_config.remove(&name))?; - fce.load_module(name.clone(), &bytes, module_config)?; + let module_config = match config.modules_config.remove(&name) { + 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)?; } - let faas_code_config = make_fce_config(config.rpc_module_config)?; - - Ok(Self { - fce, - faas_code_config, - }) + Ok(Self { fce }) } - /// Searches for modules in `config.core_modules_dir`, loads only those in the `names` set + /// Searches for modules in `config.modules_dir`, loads only those in the `names` set pub fn with_module_names(names: &HashSet, config: C) -> Result where - C: TryInto, + C: TryInto, FaaSError: From, { let config = config.try_into()?; - let modules = config.core_modules_dir.as_ref().map_or(Ok(vec![]), |dir| { + let modules = config.modules_dir.as_ref().map_or(Ok(vec![]), |dir| { Self::load_modules(dir, ModulesLoadStrategy::Named(names)) })?; - Self::with_modules::<_, CoreModulesConfig>(modules, config) + Self::with_modules::<_, ModulesConfig>(modules, config) } /// Loads modules from a directory at a given path. Non-recursive, ignores subdirectories. fn load_modules( - core_modules_dir: &str, - modules: ModulesLoadStrategy, + modules_dir: &str, + modules: ModulesLoadStrategy<'_>, ) -> Result)>> { use FaaSError::IOError; - let mut dir_entries = fs::read_dir(core_modules_dir) - .map_err(|e| IOError(format!("{}: {}", core_modules_dir, e)))?; + let mut dir_entries = + fs::read_dir(modules_dir).map_err(|e| IOError(format!("{}: {}", modules_dir, e)))?; let loaded = dir_entries.try_fold(vec![], |mut vec, entry| { let entry = entry?; @@ -179,34 +177,6 @@ impl FluenceFaaS { Ok(loaded) } - /// Executes provided Wasm code in the internal environment (with access to module exports). - pub fn call_code>( - &mut self, - wasm: &[u8], - func_name: S, - args: &[IValue], - ) -> Result> { - self.call_code_(wasm, func_name.as_ref(), args) - } - - pub fn call_code_( - &mut self, - wasm: &[u8], - func_name: &str, - args: &[IValue], - ) -> Result> { - // We need this because every wasm code loaded into VM needs a module name - let anonymous_module = "anonymous_module_name"; - - self.fce - .load_module(anonymous_module, wasm, self.faas_code_config.clone())?; - - let call_result = self.fce.call(anonymous_module, func_name, args)?; - self.fce.unload_module(anonymous_module)?; - - Ok(call_result) - } - /// Call a specified function of loaded on a startup module by its name. pub fn call_module, FN: AsRef>( &mut self, @@ -219,8 +189,8 @@ impl FluenceFaaS { .map_err(Into::into) } - /// Return all export functions (name and signatures) of loaded on a startup modules. - pub fn get_interface(&self) -> FaaSInterface { + /// Return all export functions (name and signatures) of loaded modules. + pub fn get_interface(&self) -> FaaSInterface<'_> { let modules = self .fce .interface() diff --git a/fluence-faas/src/faas_interface.rs b/fluence-faas/src/faas_interface.rs index cc953823..70f919f2 100644 --- a/fluence-faas/src/faas_interface.rs +++ b/fluence-faas/src/faas_interface.rs @@ -15,7 +15,9 @@ */ use super::IType; -use serde::{Serialize, Serializer}; + +use serde::Serialize; +use serde::Serializer; use std::fmt; use std::collections::HashMap; diff --git a/fluence-faas/src/lib.rs b/fluence-faas/src/lib.rs index 916df8ea..42c2995a 100644 --- a/fluence-faas/src/lib.rs +++ b/fluence-faas/src/lib.rs @@ -13,6 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#![warn(rust_2018_idioms)] +#![deny( + dead_code, + nonstandard_style, + unused_imports, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns +)] mod errors; mod faas; @@ -21,12 +31,18 @@ mod misc; pub(crate) type Result = std::result::Result; -pub use fce::IValue; -pub use fce::{to_interface_value, from_interface_values}; -pub use fce::IType; - pub use errors::FaaSError; + +pub use fce::IValue; +pub use fce::IType; +pub use fce::to_interface_value; +pub use fce::from_interface_values; + pub use faas::FluenceFaaS; pub use faas_interface::FaaSInterface; -pub use misc::{RawCoreModulesConfig, RawModuleConfig}; +pub use misc::RawModulesConfig; +pub use misc::RawModuleConfig; +pub use misc::ModulesConfig; +pub use misc::ModuleConfig; +pub use misc::WASIConfig; diff --git a/fluence-faas/src/misc/config.rs b/fluence-faas/src/misc/config.rs index 9c07e3c8..0b79777a 100644 --- a/fluence-faas/src/misc/config.rs +++ b/fluence-faas/src/misc/config.rs @@ -26,43 +26,50 @@ use std::convert::TryInto; /* An example of the config: -core_modules_dir = "wasm/artifacts/wasm_modules" +modules_dir = "wasm/artifacts/wasm_modules" +service_base_dir = "/Users/user/tmp" -[[core_module]] +[[module]] name = "ipfs_node.wasm" mem_pages_count = 100 logger_enabled = true - [core_module.imports] + [module.imports] mysql = "/usr/bin/mysql" ipfs = "/usr/local/bin/ipfs" - [core_module.wasi] + [module.wasi] envs = [] - preopened_files = ["/Users/user/tmp/"] - mapped_dirs = { "tmp" = "/Users/user/tmp" } + preopened_files = ["service_id"] + # it has to be full path from the right side + mapped_dirs = ["tmp" = "/Users/user/tmp"] -[rpc_module] +[default] mem_pages_count = 100 logger_enabled = true - [rpc_module.wasi] + [default.imports] + mysql = "/usr/bin/mysql" + ipfs = "/usr/local/bin/ipfs" + + [default.wasi] envs = [] - preopened_files = ["/Users/user/tmp"] - mapped_dirs = { "tmp" = "/Users/user/tmp" } + preopened_files = ["service_id"] + mapped_dirs = ["tmp" = "/Users/user/tmp"] */ #[derive(Deserialize, Serialize, Debug, Clone, Default)] -pub struct RawCoreModulesConfig { - pub core_modules_dir: Option, - pub core_module: Vec, - pub rpc_module: Option, +pub struct RawModulesConfig { + pub modules_dir: Option, + pub service_base_dir: Option, + pub module: Vec, + pub default: Option, } -impl TryInto for RawCoreModulesConfig { +impl TryInto for RawModulesConfig { type Error = FaaSError; - fn try_into(self) -> Result { + fn try_into(self) -> Result { from_raw_config(self) } } @@ -76,6 +83,14 @@ pub struct RawModuleConfig { pub wasi: Option, } +#[derive(Deserialize, Serialize, Debug, Clone, Default)] +pub struct RawDefaultModuleConfig { + pub mem_pages_count: Option, + pub logger_enabled: Option, + pub imports: Option, + pub wasi: Option, +} + impl RawModuleConfig { pub fn new(name: String) -> Self { Self { @@ -95,92 +110,158 @@ pub struct RawWASIConfig { pub mapped_dirs: Option, } -#[derive(Deserialize, Serialize, Debug, Clone, Default)] -pub struct RawRPCModuleConfig { - pub mem_pages_count: Option, - pub logger_enabled: Option, - pub wasi: Option, -} - +/// Describes behaviour of all modules from a node. #[derive(Debug, Clone, Default)] -pub struct CoreModulesConfig { - pub core_modules_dir: Option, +pub struct ModulesConfig { + /// Used for preparing filesystem on the service initialization stage. + pub service_base_dir: Option, + + /// Path to a dir where compiled Wasm modules are located. + pub modules_dir: Option, + + /// Settings for a module with particular name. pub modules_config: HashMap, - pub rpc_module_config: Option, + + /// Settings for a module that name's not been found in modules_config. + pub default_modules_config: Option, } +/// Various settings that could be used to guide FCE how to load a module in a proper way. #[derive(Debug, Clone, Default)] pub struct ModuleConfig { + /// Maximum memory size accessible by a module in Wasm pages (64 Kb). pub mem_pages_count: Option, - pub logger_enabled: Option, + + /// Defines whether FaaS should provide a special host log_utf8_string function for this module. + pub logger_enabled: bool, + + /// A list of CLI host imports that should be provided for this module. pub imports: Option>, + + /// A WASI config. pub wasi: Option, } +impl ModuleConfig { + pub fn extend_wasi_envs(mut self, new_envs: Vec>) -> Self { + match &mut self.wasi { + Some(WASIConfig { + envs: Some(envs), .. + }) => envs.extend(new_envs), + Some(w @ WASIConfig { envs: None, .. }) => w.envs = Some(new_envs), + w @ None => { + *w = Some(WASIConfig { + envs: Some(new_envs), + preopened_files: None, + mapped_dirs: None, + }) + } + } + + self + } + + #[rustfmt::skip] + pub fn extend_wasi_files( + mut self, + new_preopened_files: Vec, + new_mapped_dirs: Vec<(String, String)>, + ) -> Self { + match &mut self.wasi { + Some(WASIConfig { + preopened_files, + mapped_dirs, + .. + }) => { + match preopened_files { + Some(files) => files.extend(new_preopened_files), + f @ None => *f = Some(new_preopened_files), + }; + match mapped_dirs { + Some(dirs) => dirs.extend(new_mapped_dirs), + d @ None => *d = Some(new_mapped_dirs), + }; + }, + w @ None => { + *w = Some(WASIConfig { + envs: None, + preopened_files: Some(new_preopened_files), + mapped_dirs: Some(new_mapped_dirs), + }) + } + } + + self + } +} + #[derive(Debug, Clone, Default)] pub struct WASIConfig { + /// A list of environment variables available for this module. pub envs: Option>>, + + /// A list of files available for this module. + /// A loaded module could have access only to files from this list. pub preopened_files: Option>, + + /// Mapping from a usually short to full file name. pub mapped_dirs: Option>, } -/// Prepare config after parsing it from TOML -pub(crate) fn from_raw_config(config: RawCoreModulesConfig) -> Result { +/// Prepare config after parsing it from TOML. +fn from_raw_config(config: RawModulesConfig) -> Result { + let service_base_dir = config.service_base_dir; let modules_config = config - .core_module + .module .into_iter() - .map(|module| { - let imports = module - .imports - .map(|import| { - Ok(import - .into_iter() - .map(|(import_func_name, host_cmd)| { - let host_cmd = host_cmd.try_into::()?; - Ok((import_func_name, host_cmd)) - }) - .collect::>>()?) as Result<_> - }) - .transpose()?; - - let wasi = module.wasi.map(parse_raw_wasi); - Ok(( - module.name, - ModuleConfig { - mem_pages_count: module.mem_pages_count, - logger_enabled: module.logger_enabled, - imports, - wasi, - }, - )) - }) + .map(from_raw_module_config) .collect::>>()?; - let rpc_module_config = config.rpc_module.map(|rpc_module| { - let wasi = rpc_module.wasi.map(parse_raw_wasi); + let default_modules_config = config + .default + .map(from_raw_default_module_config) + .transpose()?; - ModuleConfig { - mem_pages_count: rpc_module.mem_pages_count, - logger_enabled: rpc_module.logger_enabled, - imports: None, - wasi, - } - }); - - Ok(CoreModulesConfig { - core_modules_dir: config.core_modules_dir, + Ok(ModulesConfig { + service_base_dir, + modules_dir: config.modules_dir, modules_config, - rpc_module_config, + default_modules_config, }) } -/// Parse config from TOML -pub(crate) fn load_config(config_file_path: std::path::PathBuf) -> Result { +/// Parse config from TOML. +pub(crate) fn load_config(config_file_path: std::path::PathBuf) -> Result { let file_content = std::fs::read(config_file_path)?; Ok(from_slice(&file_content)?) } -fn parse_raw_wasi(wasi: RawWASIConfig) -> WASIConfig { +fn from_raw_module_config(config: RawModuleConfig) -> Result<(String, ModuleConfig)> { + let imports = config.imports.map(parse_imports).transpose()?; + let wasi = config.wasi.map(from_raw_wasi_config); + Ok(( + config.name, + ModuleConfig { + mem_pages_count: config.mem_pages_count, + logger_enabled: config.logger_enabled.unwrap_or_default(), + imports, + wasi, + }, + )) +} + +fn from_raw_default_module_config(config: RawDefaultModuleConfig) -> Result { + let imports = config.imports.map(parse_imports).transpose()?; + let wasi = config.wasi.map(from_raw_wasi_config); + Ok(ModuleConfig { + mem_pages_count: config.mem_pages_count, + logger_enabled: config.logger_enabled.unwrap_or_default(), + imports, + wasi, + }) +} + +fn from_raw_wasi_config(wasi: RawWASIConfig) -> WASIConfig { let envs = wasi .envs .map(|env| env.into_iter().map(|e| e.into_bytes()).collect::>()); @@ -198,3 +279,13 @@ fn parse_raw_wasi(wasi: RawWASIConfig) -> WASIConfig { mapped_dirs, } } + +fn parse_imports(imports: toml::value::Table) -> Result> { + imports + .into_iter() + .map(|(import_func_name, host_cmd)| { + let host_cmd = host_cmd.try_into::()?; + Ok((import_func_name, host_cmd)) + }) + .collect::>>() +} diff --git a/fluence-faas/src/misc/mod.rs b/fluence-faas/src/misc/mod.rs index 80b736a2..cb3e0780 100644 --- a/fluence-faas/src/misc/mod.rs +++ b/fluence-faas/src/misc/mod.rs @@ -4,7 +4,9 @@ mod config; pub(crate) use utils::make_fce_config; pub(crate) use config::load_config; -pub(crate) use config::from_raw_config; -pub(crate) use config::CoreModulesConfig; -pub use config::{RawCoreModulesConfig, RawModuleConfig}; +pub use config::RawModulesConfig; +pub use config::RawModuleConfig; +pub use config::ModulesConfig; +pub use config::ModuleConfig; +pub use config::WASIConfig; diff --git a/fluence-faas/src/misc/utils.rs b/fluence-faas/src/misc/utils.rs index dc9680f3..8e39a7fb 100644 --- a/fluence-faas/src/misc/utils.rs +++ b/fluence-faas/src/misc/utils.rs @@ -28,8 +28,6 @@ use wasmer_runtime::Func; use wasmer_runtime::error::ResolveError; use wasmer_runtime::types::LocalOrImport; -use std::path::PathBuf; - // based on Wasmer: https://github.com/wasmerio/wasmer/blob/081f6250e69b98b9f95a8f62ad6d8386534f3279/lib/runtime-core/src/instance.rs#L863 /// Extract export function from Wasmer instance by name. pub(crate) unsafe fn get_export_func_by_name<'a, Args, Rets>( @@ -95,22 +93,25 @@ where } }; - let typed_func: Func = + let typed_func: Func<'_, Args, Rets, wasmer_core::typed_func::Wasm> = Func::from_raw_parts(func_wasm_inner, export_func_ptr, None, ctx as _); Ok(typed_func) } -/// Make FCE config based on parsed raw config. -pub(crate) fn make_fce_config(config: Option) -> crate::Result { +/// Make FCE config based on parsed config. +pub(crate) fn make_fce_config( + module_config: Option, +) -> crate::Result { use super::imports::create_host_import_func; use super::imports::log_utf8_string; use wasmer_core::import::Namespace; + use std::path::PathBuf; let mut wasm_module_config = FCEModuleConfig::default(); - let module_config = match config { - Some(config) => config, + let module_config = match module_config { + Some(module_config) => module_config, None => return Ok(wasm_module_config), }; @@ -120,10 +121,8 @@ pub(crate) fn make_fce_config(config: Option) -> crate::Result) -> crate::Result>(); } diff --git a/tools/cli/Cargo.toml b/tools/cli/Cargo.toml index cad998d9..ca6dca2d 100644 --- a/tools/cli/Cargo.toml +++ b/tools/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fce-cli" -description = "Fluence FCE command line tool for develop services for the Fluence network" +description = "Fluence FCE command line tool" version = "0.1.1" authors = ["Fluence Labs"] repository = "https://github.com/fluencelabs/fce/tools/cli"