summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlbin Suresh <albin.suresh@softwareag.com>2021-11-22 19:13:52 +0530
committerGitHub <noreply@github.com>2021-11-22 19:13:52 +0530
commitbc1eca3fb55be078c4bc90b48b74c8e7ddea8c75 (patch)
tree553dc5d5df0218ae93be18937957eaf1e6a2da67
parenta534f4920b6297eece3255846b513718a21bfd08 (diff)
[CIT-675] Apama plugin (#586)
* [CIT-675] Apama plugin
-rw-r--r--Cargo.lock72
-rw-r--r--Cargo.toml1
-rw-r--r--sm/plugins/tedge_apama_plugin/Cargo.toml15
-rw-r--r--sm/plugins/tedge_apama_plugin/src/error.rs26
-rw-r--r--sm/plugins/tedge_apama_plugin/src/main.rs175
5 files changed, 289 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b365009c..0dcb5393 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -305,6 +305,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
+name = "bzip2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
+dependencies = [
+ "bzip2-sys",
+ "libc",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
name = "c8y_smartrest"
version = "0.4.2"
dependencies = [
@@ -464,6 +485,15 @@ dependencies = [
]
[[package]]
+name = "crc32fast"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
name = "criterion"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -743,6 +773,18 @@ dependencies = [
]
[[package]]
+name = "flate2"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
+dependencies = [
+ "cfg-if 1.0.0",
+ "crc32fast",
+ "libc",
+ "miniz_oxide",
+]
+
+[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1746,6 +1788,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
+name = "pkg-config"
+version = "0.3.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
+
+[[package]]
name = "plotters"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2639,6 +2687,16 @@ dependencies = [
]
[[package]]
+name = "tedge_apama_plugin"
+version = "0.4.2"
+dependencies = [
+ "serde",
+ "structopt",
+ "thiserror",
+ "zip",
+]
+
+[[package]]
name = "tedge_apt_plugin"
version = "0.4.2"
dependencies = [
@@ -3444,3 +3502,17 @@ name = "zeroize"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970"
+
+[[package]]
+name = "zip"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815"
+dependencies = [
+ "byteorder",
+ "bzip2",
+ "crc32fast",
+ "flate2",
+ "thiserror",
+ "time",
+]
diff --git a/Cargo.toml b/Cargo.toml
index fca1517c..15120397 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ members = [
"sm/tedge_agent",
"sm/plugins/tedge_apt_plugin",
"sm/plugins/tedge_dummy_plugin",
+ "sm/plugins/tedge_apama_plugin",
"tedge_config",
"tedge",
]
diff --git a/sm/plugins/tedge_apama_plugin/Cargo.toml b/sm/plugins/tedge_apama_plugin/Cargo.toml
new file mode 100644
index 00000000..958e2e4b
--- /dev/null
+++ b/sm/plugins/tedge_apama_plugin/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "tedge_apama_plugin"
+version = "0.4.2"
+authors = ["thin-edge.io team <info@thin-edge.io>"]
+edition = "2018"
+license = "Apache-2.0"
+description = "thin.edge.io plugin for installing apama projects"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+structopt = "0.3"
+thiserror = "1.0"
+serde = { version = "1", features = ["derive"] }
+zip = "0.5"
diff --git a/sm/plugins/tedge_apama_plugin/src/error.rs b/sm/plugins/tedge_apama_plugin/src/error.rs
new file mode 100644
index 00000000..0e32850c
--- /dev/null
+++ b/sm/plugins/tedge_apama_plugin/src/error.rs
@@ -0,0 +1,26 @@
+#[derive(thiserror::Error, Debug)]
+pub enum InternalError {
+ #[error("Fail to run `{cmd}`: {from}")]
+ ExecError { cmd: String, from: std::io::Error },
+
+ #[error(transparent)]
+ FromIo(#[from] std::io::Error),
+
+ #[error(transparent)]
+ FromUtf8(#[from] std::string::FromUtf8Error),
+
+ #[error(transparent)]
+ FromZipError(#[from] zip::result::ZipError),
+
+ #[error("Apama not installed at /opt/softwareag/Apama")]
+ ApamaNotInstalled,
+}
+
+impl InternalError {
+ pub fn exec_error(cmd: impl Into<String>, from: std::io::Error) -> InternalError {
+ InternalError::ExecError {
+ cmd: cmd.into(),
+ from,
+ }
+ }
+}
diff --git a/sm/plugins/tedge_apama_plugin/src/main.rs b/sm/plugins/tedge_apama_plugin/src/main.rs
new file mode 100644
index 00000000..87015d76
--- /dev/null
+++ b/sm/plugins/tedge_apama_plugin/src/main.rs
@@ -0,0 +1,175 @@
+mod error;
+
+use crate::error::InternalError;
+use std::fs::{self, File};
+use std::os::unix::prelude::ExitStatusExt;
+use std::path::Path;
+use std::process::{Command, ExitStatus, Stdio};
+use structopt::StructOpt;
+
+/// This plugin supports the installation, updation and removal of a single unversioned apama project named "project".
+/// Installation of multiple parallel projects is not supported.
+/// Installing a project will replace the existing project with the new one.
+/// Delta update of a project(for eg: updating just the `mon` file definitions in the project) is not supported either.
+#[derive(StructOpt)]
+struct ApamaCli {
+ #[structopt(subcommand)]
+ operation: PluginOp,
+}
+
+#[derive(StructOpt)]
+pub enum PluginOp {
+ /// List the one and only apama project if one is installed
+ List,
+
+ /// Install an apama project
+ Install {
+ module: String,
+ #[structopt(short = "v", long = "--module-version")]
+ version: Option<String>,
+ #[structopt(long = "--file")]
+ file_path: String,
+ },
+
+ /// Remove an apama project
+ Remove {
+ module: String,
+ #[structopt(short = "v", long = "--module-version")]
+ version: Option<String>,
+ },
+
+ /// Prepare a sequences of install/remove commands
+ Prepare,
+
+ /// Finalize a sequences of install/remove commands
+ Finalize,
+}
+
+const APAMA_ENV_EXE: &str = "/opt/softwareag/Apama/bin/apama_env";
+const TEDGE_APAMA_PROJECT_DIR: &str = "/etc/tedge/apama/project";
+const TMP_APAMA_PROJECT_DIR: &str = "/tmp/tedge_apama_project";
+const APAMA_PROJECT_NAME: &str = "project";
+
+fn run(operation: PluginOp) -> Result<ExitStatus, InternalError> {
+ let success = ExitStatus::from_raw(0);
+
+ let tedge_env_exe_path = Path::new(APAMA_ENV_EXE);
+
+ if !tedge_env_exe_path.exists() {
+ return Err(InternalError::ApamaNotInstalled);
+ }
+
+ let tedge_apama_project_path = Path::new(TEDGE_APAMA_PROJECT_DIR);
+ let tmp_apama_project_path = Path::new(TMP_APAMA_PROJECT_DIR);
+
+ let status = match operation {
+ // Since there can only be a single project named `project`, print its name if installed
+ PluginOp::List => {
+ if tedge_apama_project_path.exists() {
+ println!("{}\t", APAMA_PROJECT_NAME)
+ }
+ success
+ }
+
+ PluginOp::Prepare => success,
+
+ PluginOp::Finalize => {
+ // Cleanup any temporary artefacts created by this plugin
+ if tmp_apama_project_path.exists() {
+ fs::remove_dir_all(tmp_apama_project_path)?;
+ }
+ success
+ }
+
+ PluginOp::Install {
+ module: _,
+ version: _,
+ file_path,
+ } => {
+ let archive_path = Path::new(&file_path);
+ let archive_file = File::open(&archive_path)?;
+
+ let mut archive = zip::ZipArchive::new(archive_file)?;
+
+ // TODO: Validate the zip to be valid apama project before extraction?
+ println!("Extracting the archive at {}", file_path);
+ archive.extract(tmp_apama_project_path)?;
+ println!("Extraction successful");
+
+ // Deleting existing project as the rename API expects the target dir to be empty
+ if tedge_apama_project_path.exists() {
+ println!("Removing existing project at {}", TEDGE_APAMA_PROJECT_DIR);
+ fs::remove_dir_all(tedge_apama_project_path)?;
+ println!("Removal of existing project successful");
+ }
+
+ println!(
+ "Installing newly extracted project to {}",
+ TEDGE_APAMA_PROJECT_DIR
+ );
+ fs::create_dir_all(tedge_apama_project_path)?;
+ fs::rename(tmp_apama_project_path, tedge_apama_project_path)?;
+ println!("Installation of new project successful");
+
+ println!("Restarting apama to load the new project");
+ run_cmd("service", "apama restart")?;
+ println!("Restart of apama service successful");
+
+ success
+ }
+
+ PluginOp::Remove {
+ module: _,
+ version: _,
+ } => {
+ if tedge_apama_project_path.exists() {
+ println!("Stopping apama service");
+ run_cmd("service", "apama stop")?;
+ println!("Stopping apama service successful");
+
+ println!("Removing existing project at {}", TEDGE_APAMA_PROJECT_DIR);
+ fs::remove_dir_all(tedge_apama_project_path)?;
+ println!("Removal of existing project successful");
+ }
+
+ success
+ }
+ };
+
+ Ok(status)
+}
+
+fn run_cmd(cmd: &str, args: &str) -> Result<ExitStatus, InternalError> {
+ let args: Vec<&str> = args.split_whitespace().collect();
+ let status = Command::new(cmd)
+ .args(args)
+ .stdin(Stdio::null())
+ .status()
+ .map_err(|err| InternalError::exec_error(cmd, err))?;
+ Ok(status)
+}
+
+fn main() {
+ // On usage error, the process exits with a status code of 1
+ let apama = ApamaCli::from_args();
+
+ match run(apama.operation) {
+ Ok(status) if status.success() => {
+ std::process::exit(0);
+ }
+
+ Ok(status) => {
+ if status.code().is_some() {
+ std::process::exit(2);
+ } else {
+ eprintln!("Interrupted by a signal!");
+ std::process::exit(4);
+ }
+ }
+
+ Err(err) => {
+ eprintln!("ERROR: {}", err);
+ std::process::exit(5);
+ }
+ }
+}