diff --git a/coffee_lib/docker/pip.Dockerfile b/coffee_lib/docker/pip.Dockerfile new file mode 100644 index 00000000..caba8c55 --- /dev/null +++ b/coffee_lib/docker/pip.Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.10-slim + +COPY requirements.txt /plugin/requirements.txt + +WORKDIR /plugin + +RUN pip install -r requirements.txt --no-cache-dir + +COPY . /plugin/ + +CMD [ "" ] \ No newline at end of file diff --git a/coffee_lib/docker/poetry.Dockerfile b/coffee_lib/docker/poetry.Dockerfile new file mode 100644 index 00000000..3edac705 --- /dev/null +++ b/coffee_lib/docker/poetry.Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.10-slim + +RUN pip3 install poetry + +COPY pyproject.toml /plugin/pyproject.toml + +WORKDIR /plugin + +RUN poetry config virtualenvs.create false && poetry install --without dev + +COPY . /plugin/ + +CMD [ "python" ] \ No newline at end of file diff --git a/coffee_lib/src/plugin.rs b/coffee_lib/src/plugin.rs index 897ed711..d0173ae6 100644 --- a/coffee_lib/src/plugin.rs +++ b/coffee_lib/src/plugin.rs @@ -1,6 +1,9 @@ //! Plugin module that abstract the concept of a cln plugin //! from a plugin manager point of view. use std::fmt::{self, Display}; +use std::fs::{File, Permissions}; +use std::io::Write; +use std::os::unix::prelude::PermissionsExt; use log; use serde::{Deserialize, Serialize}; @@ -42,52 +45,87 @@ impl Display for PluginLang { } impl PluginLang { + fn docker_file(&self) -> Option<&str> { + match self { + PluginLang::PyPip => Some(include_str!("../docker/pip.Dockerfile")), + PluginLang::PyPoetry => Some(include_str!("../docker/poetry.Dockerfile")), + _ => None, + } + } + pub async fn default_install( &self, path: &str, name: &str, verbose: bool, ) -> Result { - match self { - PluginLang::PyPip => { - /* 1. RUN PIP install or poetry install - * 2. return the path of the main file */ - let script = "pip3 install -r requirements.txt --break-system-packages"; - sh!(path, script, verbose); - let main_file = format!("{path}/{name}.py"); - Ok(main_file) - } - PluginLang::PyPoetry => { - let mut script = "pip3 install poetry\n".to_string(); - script += "poetry export -f requirements.txt --output requirements.txt\n"; - script += "pip3 install -r requirements.txt"; + match self.docker_file() { + Some(docker_file) => { + let container_name = format!("plugin.{name}"); + log::debug!("Using dockerfile: \n{docker_file}"); + let script = + format!("echo \"{docker_file}\" | docker build . -f - -t {container_name}"); + sh!(path, script, verbose); - Ok(format!("{path}/{name}.py")) - } - PluginLang::Go => Err(error!( - "golang is not supported as default language, please us the coffee.yml manifest" - )), - PluginLang::Rust => Err(error!( - "rust is not supported as default language, please use the coffee.yml manifest" - )), - PluginLang::Dart => Err(error!( - "dart is not supported as default language, please use the cofee.yml manifest" - )), - PluginLang::JavaScript => Err(error!( - "js is not supported as default language, please use the coffee.yml manifest" - )), - PluginLang::TypeScript => Err(error!( - "ts is not supported as default language, please use the coffee.yml manifest" - )), - PluginLang::JVM => Err(error!( - "JVM is not supported as default language, please use the coffee.yml manifest" - )), - PluginLang::Unknown => { - /* 1. emit an error message */ - Err(error!( - "unknown default install procedure, the language in undefined" - )) + let exec = format!("{path}/{name}.sh"); + + log::debug!("{}", exec); + + let args = match self { + PluginLang::PyPip | PluginLang::PyPoetry => format!("python {name}.py"), + _ => String::new(), + }; + + let mut f = File::create(&exec)?; + f.write_all(format!("docker run {container_name} {args}").as_bytes()) + .map_err(|_| error!("could not write executable file"))?; + + f.set_permissions(Permissions::from_mode(0o755)) + .map_err(|_| error!("could not set permissions for executable file"))?; + + Ok(exec) } + None => match self { + PluginLang::PyPip => { + /* 1. RUN PIP install or poetry install + * 2. return the path of the main file */ + let script = "pip3 install -r requirements.txt --break-system-packages"; + sh!(path, script, verbose); + let main_file = format!("{path}/{name}.py"); + Ok(main_file) + } + PluginLang::PyPoetry => { + let mut script = "pip3 install poetry\n".to_string(); + script += "poetry export -f requirements.txt --output requirements.txt\n"; + script += "pip3 install -r requirements.txt"; + sh!(path, script, verbose); + Ok(format!("{path}/{name}.py")) + } + PluginLang::Go => Err(error!( + "golang is not supported as default language, please us the coffee.yml manifest" + )), + PluginLang::Rust => Err(error!( + "rust is not supported as default language, please use the coffee.yml manifest" + )), + PluginLang::Dart => Err(error!( + "dart is not supported as default language, please use the cofee.yml manifest" + )), + PluginLang::JavaScript => Err(error!( + "js is not supported as default language, please use the coffee.yml manifest" + )), + PluginLang::TypeScript => Err(error!( + "ts is not supported as default language, please use the coffee.yml manifest" + )), + PluginLang::JVM => Err(error!( + "JVM is not supported as default language, please use the coffee.yml manifest" + )), + PluginLang::Unknown => { + /* 1. emit an error message */ + Err(error!( + "unknown default install procedure, the language in undefined" + )) + } + }, } } } @@ -131,21 +169,19 @@ impl Plugin { /// In case of success return the path of the executable. pub async fn configure(&mut self, verbose: bool) -> Result { log::debug!("install plugin inside from root dir {}", self.root_path); - let exec_path = if let Some(conf) = &self.conf { - if let Some(script) = &conf.plugin.install { - sh!(self.root_path.clone(), script, verbose); - self.exec_path.clone() - } else { - self.lang + + let conf = self.conf.as_ref(); + match conf.and_then(|conf| conf.plugin.install.clone()) { + Some(script) => sh!(self.root_path.clone(), script, verbose), + None => { + self.exec_path = self + .lang .default_install(&self.root_path, &self.name, verbose) - .await? + .await?; } - } else { - self.lang - .default_install(&self.root_path, &self.name, verbose) - .await? }; - Ok(exec_path) + + Ok(self.exec_path.clone()) } /// upgrade the plugin to a new version.