mirror of
https://github.com/fluencelabs/marine.git
synced 2025-04-01 05:31:04 +00:00
246 lines
7.6 KiB
Rust
246 lines
7.6 KiB
Rust
/*
|
|
* 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 crate::config::AppServiceConfig;
|
|
use crate::service_interface::ServiceInterface;
|
|
use super::AppServiceError;
|
|
|
|
use fluence_faas::FluenceFaaS;
|
|
use fluence_faas::IValue;
|
|
use serde_json::Value as JValue;
|
|
|
|
use std::convert::TryInto;
|
|
use std::collections::HashMap;
|
|
use std::collections::HashSet;
|
|
use std::path::PathBuf;
|
|
use std::io::ErrorKind;
|
|
|
|
const SERVICE_ID_ENV_NAME: &str = "service_id";
|
|
const SERVICE_LOCAL_DIR_NAME: &str = "local";
|
|
const SERVICE_TMP_DIR_NAME: &str = "tmp";
|
|
|
|
pub struct AppService {
|
|
faas: FluenceFaaS,
|
|
facade_module_name: String,
|
|
}
|
|
|
|
impl AppService {
|
|
/// Create Service with given modules and service id.
|
|
pub fn new<C, S>(config: C, service_id: S, envs: HashMap<Vec<u8>, Vec<u8>>) -> Result<Self>
|
|
where
|
|
C: TryInto<AppServiceConfig>,
|
|
S: Into<String>,
|
|
AppServiceError: From<C::Error>,
|
|
{
|
|
let mut config: AppServiceConfig = config.try_into()?;
|
|
let facade_module_name = config
|
|
.faas_config
|
|
.modules_config
|
|
.last()
|
|
.ok_or_else(|| {
|
|
AppServiceError::ConfigParseError(String::from(
|
|
"config should contain at least one module",
|
|
))
|
|
})?
|
|
.import_name
|
|
.clone();
|
|
|
|
let service_id = service_id.into();
|
|
Self::set_env_and_dirs(&mut config, service_id, envs)?;
|
|
|
|
let faas = FluenceFaaS::with_raw_config(config.faas_config)?;
|
|
|
|
Ok(Self {
|
|
faas,
|
|
facade_module_name,
|
|
})
|
|
}
|
|
|
|
/// Call a specified function of loaded module by its name with arguments in json format.
|
|
// TODO: replace serde_json::Value with Vec<u8>?
|
|
pub fn call<S: AsRef<str>>(
|
|
&mut self,
|
|
func_name: S,
|
|
arguments: JValue,
|
|
call_parameters: crate::CallParameters,
|
|
) -> Result<JValue> {
|
|
self.faas
|
|
.call_with_json(
|
|
&self.facade_module_name,
|
|
func_name,
|
|
arguments,
|
|
call_parameters,
|
|
)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
/// Call a specified function of loaded module by its name with arguments in IValue format.
|
|
pub fn call_with_ivalues<S: AsRef<str>>(
|
|
&mut self,
|
|
func_name: S,
|
|
arguments: &[IValue],
|
|
call_parameters: crate::CallParameters,
|
|
) -> Result<Vec<IValue>> {
|
|
self.faas
|
|
.call_with_ivalues(
|
|
&self.facade_module_name,
|
|
func_name,
|
|
arguments,
|
|
call_parameters,
|
|
)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
/// Return interface (function signatures and record types) of this service.
|
|
pub fn get_interface(&self) -> ServiceInterface {
|
|
use crate::service_interface::into_service_interface;
|
|
|
|
let faas_facade_interface = self
|
|
.faas
|
|
.get_interface()
|
|
.modules
|
|
.remove(self.facade_module_name.as_str())
|
|
// facade module must be loaded into FaaS, so unwrap is safe here
|
|
.unwrap();
|
|
|
|
into_service_interface(faas_facade_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(
|
|
config: &mut AppServiceConfig,
|
|
service_id: String,
|
|
mut envs: HashMap<Vec<u8>, Vec<u8>>,
|
|
) -> Result<()> {
|
|
use maplit::hashmap;
|
|
|
|
let create = |dir: &PathBuf| match std::fs::create_dir(dir) {
|
|
Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()),
|
|
Err(err) => Err(AppServiceError::CreateDir {
|
|
err,
|
|
path: dir.clone(),
|
|
}),
|
|
_ => Ok(()),
|
|
};
|
|
|
|
let base_dir = &config.service_base_dir;
|
|
let service_dir = base_dir.join(&service_id);
|
|
create(&service_dir)?;
|
|
|
|
let local_dir = service_dir.join(SERVICE_LOCAL_DIR_NAME);
|
|
create(&local_dir)?;
|
|
|
|
let tmp_dir = service_dir.join(SERVICE_TMP_DIR_NAME);
|
|
create(&tmp_dir)?;
|
|
|
|
let local_dir = local_dir.to_string_lossy().to_string();
|
|
let tmp_dir = tmp_dir.to_string_lossy().to_string();
|
|
|
|
let mut preopened_files = HashSet::new();
|
|
preopened_files.insert(PathBuf::from(local_dir.clone()));
|
|
preopened_files.insert(PathBuf::from(tmp_dir.clone()));
|
|
|
|
let mapped_dirs = hashmap! {
|
|
String::from(SERVICE_LOCAL_DIR_NAME) => PathBuf::from(local_dir),
|
|
String::from(SERVICE_TMP_DIR_NAME) => PathBuf::from(tmp_dir),
|
|
};
|
|
|
|
envs.insert(
|
|
SERVICE_ID_ENV_NAME.as_bytes().to_vec(),
|
|
service_id.into_bytes(),
|
|
);
|
|
|
|
for module in &mut config.faas_config.modules_config {
|
|
module.config.extend_wasi_envs(envs.clone());
|
|
module
|
|
.config
|
|
.extend_wasi_files(preopened_files.clone(), mapped_dirs.clone());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// This API is intended for testing purposes (mostly in Marine REPL)
|
|
#[cfg(feature = "raw-module-api")]
|
|
impl AppService {
|
|
pub fn new_with_empty_facade<C, S>(
|
|
config: C,
|
|
service_id: S,
|
|
envs: HashMap<Vec<u8>, Vec<u8>>,
|
|
) -> Result<Self>
|
|
where
|
|
C: TryInto<AppServiceConfig>,
|
|
S: Into<String>,
|
|
AppServiceError: From<C::Error>,
|
|
{
|
|
let mut config: AppServiceConfig = config.try_into()?;
|
|
let service_id = service_id.into();
|
|
Self::set_env_and_dirs(&mut config, service_id, envs)?;
|
|
|
|
let faas = FluenceFaaS::with_raw_config(config.faas_config)?;
|
|
|
|
Ok(Self {
|
|
faas,
|
|
facade_module_name: String::new(),
|
|
})
|
|
}
|
|
|
|
pub fn call_module<MN: AsRef<str>, FN: AsRef<str>>(
|
|
&mut self,
|
|
module_name: MN,
|
|
func_name: FN,
|
|
arguments: JValue,
|
|
call_parameters: crate::CallParameters,
|
|
) -> Result<JValue> {
|
|
self.faas
|
|
.call_with_json(module_name, func_name, arguments, call_parameters)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn load_module<S, C>(&mut self, name: S, wasm_bytes: &[u8], config: Option<C>) -> Result<()>
|
|
where
|
|
S: Into<String>,
|
|
C: TryInto<crate::FaaSModuleConfig>,
|
|
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)
|
|
}
|
|
|
|
/// Return raw interface of the underlying [[FluenceFaaS]] instance
|
|
pub fn get_full_interface(&self) -> fluence_faas::FaaSInterface<'_> {
|
|
self.faas.get_interface()
|
|
}
|
|
|
|
pub fn get_wasi_state<S: AsRef<str>>(
|
|
&mut self,
|
|
module_name: S,
|
|
) -> Result<&wasmer_wasi::state::WasiState> {
|
|
self.faas.module_wasi_state(module_name).map_err(Into::into)
|
|
}
|
|
}
|