From da47934db9c82577ade26bb9bb0e9320c7e4350a Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 21 Jul 2025 21:44:02 +0200 Subject: [PATCH] OpenRC generator --- .gitignore | 1 + Cargo.lock | 142 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 +++ example.toml | 31 +++++++++++ src/main.rs | 81 ++++++++++++++++++++++++++++ src/service.rs | 50 +++++++++++++++++ 6 files changed, 313 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 example.toml create mode 100644 src/main.rs create mode 100644 src/service.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d12d20a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,142 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "podman-openrc" +version = "0.1.0" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_parser" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d6d1b90 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "podman-openrc" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +toml = "0.9.2" diff --git a/example.toml b/example.toml new file mode 100644 index 0000000..9761ef2 --- /dev/null +++ b/example.toml @@ -0,0 +1,31 @@ +user = "maurict" + +[service] +name = "nc-test-api" +image = "numberchords-api" +network = "nc-network" +depend = ["nc-test-database"] + +[environment] +ASPNETCORE_ENVIRONMENT = "Test" +"NC_Security__OtherIssuerKeys__identity.numberchords.com__AuthPublicKey" = "" + +[[secrets]] +key = "connection_string" +target = "Database__ConnectionString" + +[[secrets]] +key = "api_secret_key" + +[[ports]] +host = 80 +container = 8080 +protocol = "tcp" + +[[ports]] +host = 22 +container = 2222 + +[[volumes]] +volume = "test" +path = "/data/test" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..41b819b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,81 @@ +use std::fs; + +use crate::service::ServiceConfig; + +mod service; + +pub fn generate_openrc(config: &ServiceConfig) { + let mut script = String::from("#!/sbin/openrc-run\n# !!! AUTO GENERATED - DO NOT EDIT !!!\n\n"); + let wrap = |cmd: &str| { + if let Some(user) = config.user.as_ref() { + format!("\tsu -c \"{}\" {} ", cmd, user) + } else { + format!("\t{}", cmd) + } + }; + + // depend() { + script.push_str("depend() {\n\tneed sysfs cgroups; "); + if config.service.depend.len() > 0 { + script.push_str(&format!("\n\tuse {}; ", config.service.depend.join(" "))); + } + script.push_str("\n}\n\n"); + // } + + // start_pre() + script.push_str("start_pre() {\n"); + script.push_str(&wrap(&format!("podman rm {} --ignore", config.service.name))); + script.push_str("\n}\n\n"); + // } + + // start() + script.push_str("start() {\n"); + + let mut arguments = vec![String::from("--restart unless-stopped"), format!("--name {}", config.service.name)]; + if let Some(network) = &config.service.network { + arguments.push(format!("--network {}", network)); + } + + for secret in &config.secrets { + if let Some(target) = &secret.target { + arguments.push(format!("--secret {},target={}", &secret.key, target)); + } else { + arguments.push(format!("--secret {}", &secret.key)); + } + } + + for (key, value) in &config.environment { + arguments.push(format!("--env {}={}", key, value)); + } + + for volume in &config.volumes { + arguments.push(format!("--volume {}:{}", &volume.volume, &volume.path)); + } + + arguments.push(config.service.image.clone()); + + script.push_str(&wrap(&format!("podman run -d {}", arguments.iter() + .enumerate() + .map(|(i, arg)| if i > 0 { format!("\t{}", arg) } else { arg.to_string() }) + .collect::>() + .join(" \\\n")))); + + script.push_str("\n}\n\n"); + // } + + // stop() + script.push_str("stop() {\n"); + script.push_str(&wrap(&format!("podman stop {} --ignore", config.service.name))); + script.push_str("\n}\n\n"); + // } + + println!("\n\n{}", script); +} + +fn main() { + let file = fs::read_to_string("example.toml").unwrap(); + let service: ServiceConfig = toml::from_str(&file).unwrap(); + println!("{:?}", service); + + generate_openrc(&service); +} diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 0000000..f7bbbcb --- /dev/null +++ b/src/service.rs @@ -0,0 +1,50 @@ +use serde::Deserialize; +use std::collections::HashMap; + +#[derive(Debug, Deserialize)] +pub struct ServiceConfig { + pub service: Service, + + // Optionally support simple or structured secrets list + #[serde(default)] + pub secrets: Vec, + + #[serde(default)] + pub environment: HashMap, + + #[serde(default)] + pub ports: Vec, + + #[serde(default)] + pub volumes: Vec, + + pub user: Option +} + +#[derive(Debug, Deserialize)] +pub struct Service { + pub name: String, + pub image: String, + pub network: Option, + #[serde(default)] + pub depend: Vec +} + +#[derive(Debug, Deserialize)] +pub struct Secret { + pub key: String, + pub target: Option +} + +#[derive(Debug, Deserialize)] +pub struct PortMapping { + pub host: u16, + pub container: u16, + pub protocol: Option +} + +#[derive(Debug, Deserialize)] +pub struct VolumeMapping { + pub volume: String, + pub path: String +} \ No newline at end of file