diff --git a/Cargo.lock b/Cargo.lock index a72021fb..19b0aa4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -922,6 +922,14 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "mariadb" +version = "0.1.0" +dependencies = [ + "fluence", + "log", +] + [[package]] name = "maybe-uninit" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index 6cac8ed0..a37f45c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "examples/greeting", "examples/ipfs-node/effector", "examples/ipfs-node/pure", + "examples/mariadb", "examples/records/effector", "examples/records/pure", "examples/records/test-record", diff --git a/examples/mariadb/Cargo.toml b/examples/mariadb/Cargo.toml new file mode 100644 index 00000000..0a780a66 --- /dev/null +++ b/examples/mariadb/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "mariadb" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" + +[[bin]] +name = "mariadb" +path = "src/main.rs" + +[dependencies] +fluence = { git = "https://github.com/fluencelabs/rust-sdk", features = ["logger"] } +log = "0.4.11" diff --git a/examples/mariadb/Config.toml b/examples/mariadb/Config.toml new file mode 100644 index 00000000..1cc60480 --- /dev/null +++ b/examples/mariadb/Config.toml @@ -0,0 +1,12 @@ +modules_dir = "artifacts/" + +[[module]] + name = "greeting.wasm" + mem_pages_count = 1 + logger_enabled = false + + [module.imports] + mariadb = "/usr/local/bin/mariadb" + + [module.wasi] + envs = ["USER=root", "PASSWORD=toor", "HOST=192.168.88.25", "PORT=3306", "DB_NAME=test"] diff --git a/examples/mariadb/src/main.rs b/examples/mariadb/src/main.rs new file mode 100644 index 00000000..827d80d7 --- /dev/null +++ b/examples/mariadb/src/main.rs @@ -0,0 +1,97 @@ +/* + * 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. + */ + +mod path; + +use fluence::fce; +use fluence::WasmLogger; +use crate::path::to_full_path; + +use std::path::PathBuf; + +const PASSWORD_ENV_NAME: &str = "PASSWORD"; +const DB_NAME_ENV_NAME: &str = "DB_NAME"; +const PORT_ENV_NAME: &str = "PORT"; +const USER_ENV_NAME: &str = "USER"; +const HOST_ENV_NAME: &str = "HOST_ENV_NAME"; + +const TMP_FILE_NAME: &str = "mariadb.sql"; + +pub fn main() { + WasmLogger::init_with_level(log::Level::Info).unwrap(); +} + +#[fce] +pub struct ExecutionResult { + // Contain execution result if error_code is 0, and empty string or error message otherwise + pub result: String, + + // 0 is success, otherwise error code + pub error_code: i32, +} + +impl ExecutionResult { + pub fn success(result: String) -> Self { + Self { + result, + error_code: 0, + } + } + pub fn error(error_code: i32) -> Self { + Self { + result: String::new(), + error_code, + } + } +} + +#[fce] +pub fn sql(sql: String) -> ExecutionResult { + log::info!("sql called with command {}", sql); + + let sql_cmd_filepath = TMP_FILE_NAME.to_string(); + + if let Err(e) = std::fs::write(PathBuf::from(sql_cmd_filepath), sql) { + return ExecutionResult { + result: String::new(), + // it is safe because write return only OS errors + error_code: e.raw_os_error().unwrap(), + }; + } + + let sql_cmd_filepath = to_full_path(TMP_FILE_NAME); + + let password = std::env::var(PASSWORD_ENV_NAME).unwrap_or_else(|_| "toor".to_string()); + let db_name = std::env::var(DB_NAME_ENV_NAME).unwrap_or_else(|_| "test".to_string()); + let port = std::env::var(PORT_ENV_NAME).unwrap_or_else(|_| "3306".to_string()); + let user = std::env::var(USER_ENV_NAME).unwrap_or_else(|_| "root".to_string()); + let host = std::env::var(HOST_ENV_NAME).unwrap_or_else(|_| "127.0.0.1".to_string()); + + let cmd = format!( + "-u{} -p{} -h{} -P{} -D{} < {}", + user, password, host, port, db_name, sql_cmd_filepath + ); + + let result = mariadb(cmd); + ExecutionResult::success(result) +} + +#[fce] +#[link(wasm_import_module = "host")] +extern "C" { + /// Execute provided sql as a parameters of mariadb cli, return result. + pub fn mariadb(sql: String) -> String; +} diff --git a/examples/mariadb/src/path.rs b/examples/mariadb/src/path.rs new file mode 100644 index 00000000..4f7d0d5e --- /dev/null +++ b/examples/mariadb/src/path.rs @@ -0,0 +1,52 @@ +/* + * 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. + */ + +pub(super) fn to_full_path(cmd: S) -> String +where + S: Into, +{ + use std::path::Path; + use std::path::Component; + + let cmd = cmd.into(); + let path = Path::new(&cmd); + + let mut components = path.components(); + let is_absolute = components.next() == Some(Component::RootDir); + + if !is_absolute { + return cmd; + } + + let parent = match components.next() { + Some(Component::Normal(path)) => path.to_str().unwrap(), + _ => return cmd, + }; + + match std::env::var(parent) { + Ok(to_dir) => { + let mut full_path = std::path::PathBuf::from(to_dir); + + // TODO: optimize this + #[allow(clippy::while_let_on_iterator)] + while let Some(component) = components.next() { + full_path.push(component); + } + full_path.to_string_lossy().into_owned() + } + Err(_) => cmd, + } +}