Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial docker implementation #196

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions coffee_lib/docker/pip.Dockerfile
Original file line number Diff line number Diff line change
@@ -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 [ "" ]
13 changes: 13 additions & 0 deletions coffee_lib/docker/poetry.Dockerfile
Original file line number Diff line number Diff line change
@@ -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" ]
136 changes: 86 additions & 50 deletions coffee_lib/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<String, CoffeeError> {
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"
))
}
},
}
}
}
Expand Down Expand Up @@ -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<String, CoffeeError> {
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.
Expand Down
Loading