use crate::Result;
use crate::config::AppServiceConfig;
use crate::service_interface::ServiceInterface;
use super::AppServiceError;
use fluence_faas::FluenceFaaS;
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 {
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,
})
}
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)
}
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())
.unwrap();
into_service_interface(faas_facade_interface)
}
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(())
}
}
#[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)
}
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)
}
}