Skip to content

Commit

Permalink
feat: allow deb, config, and container generation (#10)
Browse files Browse the repository at this point in the history
* feat: allow deb, config, and container generation

Signed-off-by: Jeremy HERGAULT <[email protected]>

* fix: Update cargo-prosa/README.md according to code review

Co-authored-by: Anthony Thomas <[email protected]>
Signed-off-by: Jérémy HERGAULT <[email protected]>

* fix: cargo-prosa unit test with missing new inj processor

Signed-off-by: Jeremy HERGAULT <[email protected]>

---------

Signed-off-by: Jeremy HERGAULT <[email protected]>
Signed-off-by: Jérémy HERGAULT <[email protected]>
Signed-off-by: Jeremy HERGAULT <[email protected]>
Co-authored-by: Anthony Thomas <[email protected]>
  • Loading branch information
reneca and Timmy80 authored Oct 2, 2024
1 parent b849cfd commit 4944dab
Show file tree
Hide file tree
Showing 13 changed files with 694 additions and 41 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
*.profraw
.idea
.vscode
.DS_Store
2 changes: 1 addition & 1 deletion cargo-prosa/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cargo-prosa"
version = "0.1.1"
version = "0.1.2"
authors.workspace = true
description = "ProSA utility to package and deliver a builded ProSA"
homepage.workspace = true
Expand Down
34 changes: 32 additions & 2 deletions cargo-prosa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ cargo prosa new my-prosa
cargo prosa init
```

cargo-prosa is meant to evolve in the future.
_cargo-prosa_ is meant to evolve in the future.
So maybe new things will be introduced.
To update your model, you can update the generated file with `cargo prosa update`.

Expand All @@ -47,8 +47,9 @@ Your project uses a _build.rs_/_main.rs_ to create a binary that you can use.
## Configuration

Keep in mind that you also need to have a settings file.
A `target/config.yml` and `target/config.toml` will be generated when building.

You can initiate a default one with:
But you can initiate a default one with:
```bash
cargo run -- -c default_config.yaml --dry_run
```
Expand All @@ -69,3 +70,32 @@ cargo run -- -n "MyBuiltProSA" -c default_config.yaml
# or with binary
target/debug/my-prosa -n "MyBuiltProSA" -c default_config.yaml
```

## Deploy

This builder offer you several possibilities to deploy your ProSA.
The goal is to use the easiest method of a plateform to run your application.

### Container

Containerization will allow you to build and load ProSA in an image:
```bash
# Generate a Containerfile
cargo prosa container
# Generate a Dockerfile
cargo prosa container --docker
```

For your own needs, you can:
- Select from which image the container need to be build `--image debian:stable-slim`
- Along that you may have to specify the package manager use to install mandatory packages `--package_manager apt`
- If you want to compile ProSA through a builder, you can specify it with `--builder rust:latest`. A multi stage container file will be created.

### Deb package

Deb package can be created with the [cargo-deb](https://crates.io/crates/cargo-deb) crate.

To enable this feature, _create_, _init_ or _update_ your ProSA with the option `--deb`.
It'll add every needed properties to generate a deb package.

The deb package will include the released binary, a default configuration file, and a systemd service file.
86 changes: 82 additions & 4 deletions cargo-prosa/assets/build.rs.j2
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::collections::HashMap;
use std::io::Write;
use std::{{ '{' }}env, io{{ '}' }};
use std::{{ '{' }}env, io, path{{ '}' }};
use std::ffi::OsString;
use std::fs;
use std::path::Path;
use std::{{ '{' }}fs, path::Path{{ '}' }};

use cargo_prosa::builder::Desc;
use cargo_prosa::cargo::{{ '{' }}CargoMetadata, Metadata{{ '}' }};
use cargo_prosa::CONFIGURATION_FILENAME;
{%- if deb_pkg %}
use cargo_prosa::package::deb::DebPkg;
{% endif %}

fn write_settings_rs(out_dir: &OsString, desc: &Desc, metadata: &HashMap<String, Metadata>) -> io::Result<()> {{ '{' }}
let mut f = fs::File::create(Path::new(&out_dir).join("settings.rs"))?;
Expand Down Expand Up @@ -114,7 +116,7 @@ fn write_run_rs(out_dir: &OsString, desc: &Desc, metadata: &HashMap<String, Meta
proc_id += 1;
writeln!(f, "debug!(\"Start processor {{ '{}' }}\");", processor.get_name())?;
let proc_metadata = metadata.get(&processor.proc_name).unwrap_or_else(|| panic!("Can't get the processor {{ '{}' }} metadata ({{ '{:?}' }})", processor.proc, processor.name));
if let Some(_) = &proc_metadata.settings {{ '{' }}
if proc_metadata.settings.is_some() {{ '{' }}
writeln!(f, "let proc = {{ '{}' }}::<{{ '{}' }}>::create({{ '{}' }}, bus.clone(), settings.{{ '{}' }}.clone());", processor.proc, desc.prosa.tvf, proc_id, processor.get_name().replace('-', "_"))?;
{{ '}' }} else {{ '{' }}
writeln!(f, "let proc = {{ '{}' }}::<{{ '{}' }}>::create_raw({{ '{}' }}, bus.clone());", processor.proc, desc.prosa.tvf, proc_id)?;
Expand Down Expand Up @@ -176,8 +178,73 @@ fn write_run_rs(out_dir: &OsString, desc: &Desc, metadata: &HashMap<String, Meta
writeln!(f, "{{ '}}' }}")
{{ '}' }}

fn write_target_config(out_dir: &OsString, target_dir: &Path) -> io::Result<()> {{ '{' }}
// Create temporary project to generate config file
let prosa_config_path = Path::new(&out_dir).join("prosa_config");
fs::create_dir_all(prosa_config_path.join("src"))?;
fs::copy(Path::new(&out_dir).join("settings.rs"), prosa_config_path.join("src").join("settings.rs"))?;

// Correct relative path in the Cargo.toml by the `out_dir` one
let cargo_content = fs::read_to_string("Cargo.toml")?.replace("\"../", format!("\"{}/../", env::current_dir()?.display()).as_str());
let mut cargo_dst = fs::File::create(&prosa_config_path.join("Cargo.toml"))?;
cargo_dst.write(cargo_content.as_bytes())?;

let mut f = fs::File::create(prosa_config_path.join("src").join("main.rs"))?;
writeln!(f, "use prosa::core::settings::Settings;\n")?;
writeln!(f, "use serde::{{ '{{' }}Deserialize, Serialize{{ '}}' }};\n")?;
writeln!(f, "include!(\"settings.rs\");\n")?;
writeln!(f, "fn main() -> std::io::Result<()> {{ '{{' }}")?;
writeln!(f, " let args: Vec<String> = std::env::args().collect();")?;
writeln!(f, " RunSettings::default().write_config(args.last().expect(\"Missing config path\"))")?;
writeln!(f, "{{ '}}' }}")?;

let config_build_yml = std::process::Command::new("cargo")
.args(["run", "--", target_dir.join("config.yml").to_str().unwrap()])
.current_dir(&prosa_config_path)
.output()
.expect("Failed to generate config.yml");
if !config_build_yml.status.success() {{ '{' }}
return Err(io::Error::new(
io::ErrorKind::Other,
std::str::from_utf8(config_build_yml.stderr.as_slice()).unwrap_or(
format!(
"Can't build config.yml config file {:?}",
config_build_yml.status.code()
)
.as_str(),
),
));
{{ '}' }}

let config_build_toml = std::process::Command::new("cargo")
.args([
"run",
"--",
target_dir.join("config.toml").to_str().unwrap(),
])
.current_dir(&prosa_config_path)
.output()
.expect("Failed to generate config.toml");
if !config_build_toml.status.success() {{ '{' }}
return Err(io::Error::new(
io::ErrorKind::Other,
std::str::from_utf8(config_build_toml.stderr.as_slice()).unwrap_or(
format!(
"Can't build config.toml config file {{ '{' }}:?{{ '}' }}",
config_build_toml.status.code()
)
.as_str(),
),
));
{{ '}' }}

Ok(())
{{ '}' }}

fn main() {{ '{' }}
// Generate Rust code for ProSA
let out_dir = env::var_os("OUT_DIR").unwrap();
let target_path = path::absolute("target").unwrap();
let cargo_metadata = CargoMetadata::load_metadata().unwrap();
let prosa_proc_metadata = cargo_metadata.prosa_proc_metadata();
let prosa_desc = toml::from_str::<Desc>(fs::read_to_string(CONFIGURATION_FILENAME).unwrap().as_str()).unwrap();
Expand All @@ -186,6 +253,17 @@ fn main() {{ '{' }}
write_config_rs(&out_dir, &prosa_desc, &cargo_metadata).unwrap();
write_run_rs(&out_dir, &prosa_desc, &prosa_proc_metadata).unwrap();

write_target_config(&out_dir, &target_path).unwrap();

println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=ProSA.toml");
{%- if deb_pkg %}
println!("cargo:rerun-if-changed=Cargo.toml");

// Generate files for ProSA packages
{% endif -%}
{%- if deb_pkg %}
let deb_pkg = DebPkg::new(target_path.to_path_buf()).unwrap();
deb_pkg.write_package_data().unwrap();
{% endif -%}
{{ '}' }}
50 changes: 50 additions & 0 deletions cargo-prosa/assets/container.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{% if builder_image is defined -%}
FROM {{ builder_image }} AS builder
WORKDIR /opt
COPY . .

RUN mkdir -p ~/.ssh \
&& touch ~/.ssh/config \
&& echo "Host *\n StrictHostKeyChecking=accept-new" >> ~/.ssh/config \
&& chmod 644 ~/.ssh/config

{% if package_manager == "apt" -%}
RUN export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y update && apt-get -y install \
libssl-dev
{% endif -%}

{% if docker -%}
RUN --mount=type=ssh export CARGO_NET_GIT_FETCH_WITH_CLI=true \
&& cargo build -r
{% else %}
RUN cargo build -r
{% endif %}
{% endif -%}
FROM {{ image }}

LABEL org.opencontainers.image.title="{{ name }}"
LABEL org.opencontainers.image.version="{{ version }}"
{% if license is defined %}LABEL org.opencontainers.image.licenses="{{ license }}"{% endif %}
{% if authors is defined %}LABEL org.opencontainers.image.authors="{{ authors | join(sep=", ") }}"{% endif %}
{% if description is defined %}LABEL org.opencontainers.image.description="{{ description }}"{% endif %}
{% if documentation is defined %}LABEL org.opencontainers.image.documentation="{{ documentation }}"{% endif %}

{% if builder_image is defined -%}
COPY --from=builder --chmod=755 /opt/target/release/{{ name }} /usr/local/bin/{{ name }}
{% else %}
COPY --chmod=755 target/release/{{ name }} /usr/local/bin/{{ name }}
{% endif -%}

{% if package_manager == "apt" %}
RUN export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y update && apt-get -y install \
libssl3 \
&& apt-get -y clean && apt-get -y autoclean \
&& rm -rf /tmp/* \
&& /usr/local/bin/{{ name }} -c /etc/{{ name }}.yml --dry_run
{% else %}
RUN /usr/local/bin/{{ name }} -c /etc/{{ name }}.yml --dry_run
{% endif %}

ENTRYPOINT ["/usr/local/bin/{{ name }}", "-c", "/etc/{{ name }}.yml"]
13 changes: 13 additions & 0 deletions cargo-prosa/assets/systemd.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
{% if description is defined %}Description={{ description }}{% endif %}
{% if documentation is defined %}Documentation={{ documentation }}{% endif %}
After=network-online.target
ConditionFileNotEmpty={{ config }}

[Service]
ExecStart={{ bin }} -c {{ config }}
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias={{ name }}.service
Loading

0 comments on commit 4944dab

Please sign in to comment.