Compare commits

..

11 Commits

Author SHA1 Message Date
Maurice
23fc91f54e Update 2025-10-22 16:59:26 +02:00
Maurice
1953f8cf1c Change extension of service file 2025-09-30 13:04:06 +02:00
Maurice
4c7d1a857e Update version 2025-08-16 20:30:36 +02:00
Maurice
6e236f64df Update 2025-08-16 20:30:01 +02:00
Maurice
30c813bcf2 Update 2025-08-16 20:08:50 +02:00
Maurice
37c86fc994 Port mappings 2025-08-13 14:26:49 +02:00
Maurice
19d1bfe9a2 Allow multiple networks and network groups 2025-08-13 13:57:08 +02:00
Maurice
9146046b91 Add multiple networks 2025-08-13 10:45:03 +02:00
Maurice
9e6b2be0ac Update version 2025-07-25 20:12:28 +02:00
Maurice
909fdfcae1 Overlooked Rusts curly brace escape :D 2025-07-25 20:12:20 +02:00
Maurice
66b5dff359 Fix bug 2025-07-25 20:09:22 +02:00
5 changed files with 296 additions and 67 deletions

82
Cargo.lock generated
View File

@@ -54,9 +54,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.41" version = "4.5.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -64,9 +64,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.41" version = "4.5.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -76,9 +76,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.41" version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@@ -98,6 +98,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.2" version = "1.0.2"
@@ -118,9 +124,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.10.0" version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@@ -132,6 +138,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
version = "1.70.1" version = "1.70.1"
@@ -140,9 +155,10 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]] [[package]]
name = "podman-openrc" name = "podman-openrc"
version = "0.1.1" version = "0.1.4"
dependencies = [ dependencies = [
"clap", "clap",
"itertools",
"serde", "serde",
"toml", "toml",
] ]
@@ -167,18 +183,28 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.219" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.219" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -187,11 +213,11 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "1.0.0" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
@@ -213,12 +239,12 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.9.2" version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde_core",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_parser", "toml_parser",
@@ -228,27 +254,27 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.7.0" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
name = "toml_parser" name = "toml_parser"
version = "1.0.1" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
dependencies = [ dependencies = [
"winnow", "winnow",
] ]
[[package]] [[package]]
name = "toml_writer" name = "toml_writer"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@@ -337,6 +363,6 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.7.12" version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "podman-openrc" name = "podman-openrc"
version = "0.1.1" version = "0.1.4"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
keywords = ["podman", "openrc", "service", "generator"] keywords = ["podman", "openrc", "service", "generator"]
@@ -12,6 +12,7 @@ repository = "https://git.plabble.org/Maurice/podman-openrc"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
clap = { version = "4.5.41", features = ["derive"] } clap = { version = "4.5.48", features = ["derive"] }
itertools = "0.14.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
toml = "0.9.2" toml = "0.9.7"

View File

@@ -9,7 +9,10 @@ cargo install podman-openrc
# Usage # Usage
```sh ```sh
podman-openrc <INPUT TOML FILE> <OUTPUT OPENRC FILE> podman-openrc <INPUT TOML FILE> <OUTPUT OPENRC FILE>
# For example: podman-openrc input.toml output.service.sh # For example: podman-openrc input.toml output.service
# or:
podman-openrc ./input-folder /etc/init.d
``` ```
# TOML service description format # TOML service description format
@@ -18,15 +21,16 @@ The format is like this:
```toml ```toml
user = "<USERNAME>" # Optional property, set if you don't want to run the Podman command with the root user user = "<USERNAME>" # Optional property, set if you don't want to run the Podman command with the root user
capabilities = ["NET_BIND_SERVICE"] # Optional property, add Linux capabilities if you need some
# Required section # Required section
[service] [service]
name = "<CONTAINER NAME>" # Container name, required name = "<CONTAINER NAME>" # Container name, required
image = "<IMAGE>" # Podman image name image = "<IMAGE>" # Podman image name
network = "<NETWORK>" # Optional, if you want to run the container within a specific network. Set to "host" if you don't want to use the podman networking.
depend = ["<SERVICE NAME>"] # Name of any service in /etc/init.d to depend on depend = ["<SERVICE NAME>"] # Name of any service in /etc/init.d to depend on
restart = "unless-stopped" # Restart, optional. Defaults to "unless-stopped" restart = "unless-stopped" # Restart, optional. Defaults to "unless-stopped"
detach = true # Run container in detach mode, optional, default true. Recommended. detach = true # Run container in detach mode, optional, default true. Recommended.
interactive = false # Run container in interactive mode, optional, default false
hostname = "<HOSTNAME>" # Host name, optional. hostname = "<HOSTNAME>" # Host name, optional.
command = "<COMMAND>" # Container command to run, optional. command = "<COMMAND>" # Container command to run, optional.
@@ -35,6 +39,19 @@ command = "<COMMAND>" # Container command to run, optional.
ASPNETCORE_ENVIRONMENT = "Test" ASPNETCORE_ENVIRONMENT = "Test"
# If you have a not TOML-compatible key name, use "" around the key name # If you have a not TOML-compatible key name, use "" around the key name
# Optional, if you want to run the container within specific network(s). Set to "host" if you don't want to use the podman networking.
[[networks]]
name = "host"
# You can also create groups
[[networks]]
name = "netw-service-test"
group = "http-networks"
# And assign ALL networks assigned to a group to a service
[[networks]]
group = "http-networks"
# Optionally, you can assign one or more port mappings # Optionally, you can assign one or more port mappings
[[ports]] [[ports]]
host = 80 # Port on your computer host = 80 # Port on your computer
@@ -43,8 +60,9 @@ protocol = "tcp" # Protocol, optional
# Optionally you can also assign volumes # Optionally you can also assign volumes
[[volumes]] [[volumes]]
volume = "<VOLUME NAME>" # Volume name or path on host source = "<VOLUME NAME>" # Volume name or path on host
path = "<PATH>" # Volume location/target inside container target = "<PATH>" # Volume location/target inside container
create = true # Optional, set to true if volume is NOT a path but named volume and you want to create it
# Or you can make more advanced volumes with mounts # Or you can make more advanced volumes with mounts
[[mounts]] [[mounts]]
@@ -56,7 +74,7 @@ read_only = true # Whether to use ro mode, optional
# Optionally you can use Podman secrets in an array # Optionally you can use Podman secrets in an array
[[secrets]] [[secrets]]
key = "<SECRET KEY>" # Secret key used in `podman secret` key = "<SECRET KEY>" # Secret key used in `podman secret`
target = "<TARGET SECRET FILE>" # Target secret filename in /var/run/secrets. Optional, defaults to the key target = "<TARGET SECRET FILE>" # Target secret filename in {/var}/run/secrets. Optional, defaults to the key
# Between environment and secrets: get secret from Podman and set it as environment variable # Between environment and secrets: get secret from Podman and set it as environment variable
[[environment_secrets]] [[environment_secrets]]

View File

@@ -1,16 +1,28 @@
use std::fs; use std::{collections::HashMap, fs, path::Path};
use clap::Parser; use clap::Parser;
use itertools::Itertools;
use crate::service::ServiceConfig; use crate::service::{NetworkMapping, ServiceConfig};
mod service; mod service;
fn escape(str: &str) -> String {
str.replace("$", "\\$")
}
pub fn generate_openrc(config: &ServiceConfig) -> String { pub fn generate_openrc(config: &ServiceConfig) -> String {
let networks: Vec<String> = config.networks.clone()
.into_iter()
.filter(|n|n.name.is_some())
.map(|n|n.name.unwrap())
.dedup()
.collect();
let mut script = String::from("#!/sbin/openrc-run\n# !!! AUTO GENERATED - DO NOT EDIT !!!\n\n"); let mut script = String::from("#!/sbin/openrc-run\n# !!! AUTO GENERATED - DO NOT EDIT !!!\n\n");
let wrap = |cmd: &str| { let wrap = |cmd: &str| {
if let Some(user) = config.user.as_ref() { if let Some(user) = config.user.as_ref() {
format!("\tsu -c \"{}\" {} ", cmd, user) format!("\tsu -c \"{}\" {} ", escape(cmd), user)
} else { } else {
format!("\t{}", cmd) format!("\t{}", cmd)
} }
@@ -27,11 +39,14 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
// start_pre() // start_pre()
script.push_str("start_pre() {\n"); script.push_str("start_pre() {\n");
let mut start_pre_commands = Vec::new(); let mut start_pre_commands = Vec::new();
if let Some(network) = &config.service.network { start_pre_commands.push(format!("podman rm {} --ignore;", config.service.name));
for network in networks.iter().filter(|n| *n != "host" && *n != "bridge") {
start_pre_commands.push(format!("podman network create {} --ignore;", network)); start_pre_commands.push(format!("podman network create {} --ignore;", network));
} }
start_pre_commands.push(format!("podman rm {} --ignore;", config.service.name)); for volume in config.volumes.iter().filter(|v| v.create.is_some_and(|c|c)) {
script.push_str(&wrap(&start_pre_commands.join("\n"))); start_pre_commands.push(format!("podman volume create {} --ignore;", volume.source));
}
script.push_str(&start_pre_commands.iter().map(|c|wrap(c)).collect::<Vec<String>>().join("\n"));
script.push_str("\n}\n\n"); script.push_str("\n}\n\n");
// } // }
@@ -39,8 +54,15 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
script.push_str("start() {\n"); script.push_str("start() {\n");
let mut arguments = vec![ let mut arguments = vec![
format!("--restart {}", config.service.restart.as_deref().unwrap_or("unless-stopped")), format!(
format!("--name {}", config.service.name) "--restart {}",
config
.service
.restart
.as_deref()
.unwrap_or("unless-stopped")
),
format!("--name {}", config.service.name),
]; ];
if let Some(hostname) = &config.service.hostname { if let Some(hostname) = &config.service.hostname {
@@ -51,7 +73,11 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
arguments.push("--detach".to_string()); arguments.push("--detach".to_string());
} }
if let Some(network) = &config.service.network { if config.service.interactive.unwrap_or(false) {
arguments.push("--interactive".to_string());
}
for network in networks.iter() {
arguments.push(format!("--network {}", network)); arguments.push(format!("--network {}", network));
} }
@@ -59,6 +85,16 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
arguments.push(format!("--cap-add {}", capability)); arguments.push(format!("--cap-add {}", capability));
} }
for port in &config.ports {
if let Some(protocol) = &port.protocol {
arguments.push(format!(
"--publish {}:{}/{}", port.host, port.container, protocol
));
} else {
arguments.push(format!("--publish {}:{}", port.host, port.container));
}
}
for secret in &config.secrets { for secret in &config.secrets {
if let Some(target) = &secret.target { if let Some(target) = &secret.target {
arguments.push(format!("--secret {},target={}", &secret.key, target)); arguments.push(format!("--secret {},target={}", &secret.key, target));
@@ -72,15 +108,21 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
} }
for secret in &config.environment_secrets { for secret in &config.environment_secrets {
arguments.push(format!("--env {}='$(podman secret inspect --showsecret --format {{.SecretData}} {})'", secret.name, secret.secret)); arguments.push(format!(
"--env {}=$(podman secret inspect --showsecret --format {{{{.SecretData}}}} {})",
secret.name, secret.secret
));
} }
for volume in &config.volumes { for volume in &config.volumes {
arguments.push(format!("--volume {}:{}", &volume.volume, &volume.path)); arguments.push(format!("--volume {}:{}", &volume.source, &volume.target));
} }
for mount in &config.mounts { for mount in &config.mounts {
let mut mount_str = format!("--mount type={},source={},target={}", mount.typ, mount.source, mount.target); let mut mount_str = format!(
"--mount type={},source={},target={}",
mount.typ, mount.source, mount.target
);
if mount.read_only.unwrap_or(false) { if mount.read_only.unwrap_or(false) {
mount_str.push_str(",readonly"); mount_str.push_str(",readonly");
} }
@@ -109,42 +151,173 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
arguments.push(command.clone()); arguments.push(command.clone());
} }
script.push_str(&wrap(&format!("podman run {}", arguments.iter() script.push_str(&wrap(&format!(
"podman run {}",
arguments
.iter()
.enumerate() .enumerate()
.map(|(i, arg)| if i > 0 { format!("\t{}", arg) } else { arg.to_string() }) .map(|(i, arg)| if i > 0 {
format!("\t{}", arg)
} else {
arg.to_string()
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" \\\n")))); .join(" \\\n")
)));
script.push_str("\n}\n\n"); script.push_str("\n}\n\n");
// } // }
// stop() // stop()
script.push_str("stop() {\n"); script.push_str("stop() {\n");
script.push_str(&wrap(&format!("podman stop {} --ignore", config.service.name))); script.push_str(&wrap(&format!(
"podman stop {} --ignore",
config.service.name
)));
script.push_str("\n}\n\n"); script.push_str("\n}\n\n");
// } // }
// cleanup()
script.push_str("cleanup() {\n");
let mut cleanup_commands = Vec::new();
cleanup_commands.push(format!("podman rm {} --ignore --force;", config.service.name));
for network in networks.iter().filter(|n| *n != "host" && *n != "bridge") {
cleanup_commands.push(format!("podman network rm {} --force;", network));
}
for volume in config.volumes.iter().filter(|v| v.create.is_some_and(|c|c)) {
cleanup_commands.push(format!("podman volume rm {} --force;", volume.source));
}
script.push_str(&cleanup_commands.iter().map(|c|wrap(c)).collect::<Vec<String>>().join("\n"));
script.push_str("\n}\n");
// }
script script
} }
/// Program to generate OpenRC scripts from Podman service definitions in TOML format. /// Program to generate OpenRC scripts from Podman service definitions in TOML format.
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
struct Args { struct Args {
/// Definition file in TOML format /// Definition file in TOML format, or directory with definition files
definition: String, definition: String,
/// Output file for the OpenRC script /// Output file for the OpenRC script, or output directory for the service files
out: String, out: String,
} }
fn read_dir<P: AsRef<Path>>(path: &P, services: &mut Vec<ServiceConfig>) {
for entry in fs::read_dir(path).expect(&format!(
"Failed to read directory: {}",
path.as_ref().display()
)) {
let path = entry.unwrap().path();
if path.is_dir() {
read_dir(&path, services);
} else if path.is_file()
&& path.extension().is_some_and(|ext| ext == "toml")
&& path
.file_name()
.unwrap()
.to_str()
.unwrap()
.ends_with("service.toml")
{
println!("Processing: {}", path.display());
let input = fs::read_to_string(&path).expect(&format!(
"Failed to read definition file: {}",
path.display()
));
let config: ServiceConfig = toml::from_str(&input).expect(&format!(
"Failed to parse definition file: {}",
path.display()
));
services.push(config);
} else {
println!("Skipped: {}", path.display());
}
}
}
fn process_network_groups(services: &mut Vec<ServiceConfig>) {
// Map network groups
let mut network_groups: HashMap<String, Vec<String>> = HashMap::new();
for service in services.iter() {
for network in &service.networks {
if let NetworkMapping {
name: Some(name),
group: Some(group),
} = network
{
if network_groups.contains_key(group) {
network_groups.get_mut(group).unwrap().push(name.clone());
} else {
network_groups.insert(group.clone(), vec![name.clone()]);
}
}
}
}
// Expand network groups in services
for service in services {
let mut networks_to_add = Vec::new();
service.networks.retain(|network| {
if let NetworkMapping {
name: None,
group: Some(group),
} = network
{
if let Some(names) = network_groups.get(group) {
for name in names {
networks_to_add.push(NetworkMapping {
name: Some(name.clone()),
group: Some(group.clone()),
});
}
}
false // Remove this network mapping
} else {
true // Keep this network mapping
}
});
service.networks.extend(networks_to_add);
}
}
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
let input = fs::read_to_string(&args.definition) let input_path = Path::new(&args.definition);
.expect("Failed to read definition file"); let output_path = Path::new(&args.out);
let config: ServiceConfig = toml::from_str(&input)
.expect("Failed to parse definition file");
if input_path.is_dir() {
if !output_path.exists() {
fs::create_dir_all(&output_path).expect("Failed to create output directory");
}
if !output_path.is_dir() {
panic!(
"Input path is a directory, but output path is not a directory: {}",
output_path.display()
);
}
let mut services: Vec<ServiceConfig> = Vec::new();
read_dir(&input_path, &mut services);
process_network_groups(&mut services);
// Write
for config in services {
let output = generate_openrc(&config); let output = generate_openrc(&config);
fs::write(&args.out, output) fs::write(output_path.join(format!("{}.service", config.service.name)), output)
.expect("Failed to write OpenRC script to output file"); .expect("Failed to write OpenRC script to output file");
} }
} else {
let input = fs::read_to_string(&args.definition).expect("Failed to read definition file");
let config: ServiceConfig =
toml::from_str(&input).expect("Failed to parse definition file");
let output = generate_openrc(&config);
fs::write(&args.out, output).expect("Failed to write OpenRC script to output file");
}
}

View File

@@ -24,6 +24,9 @@ pub struct ServiceConfig {
#[serde(default)] #[serde(default)]
pub mounts: Vec<MountMapping>, pub mounts: Vec<MountMapping>,
#[serde(default)]
pub networks: Vec<NetworkMapping>,
pub user: Option<String>, pub user: Option<String>,
#[serde(default)] #[serde(default)]
@@ -35,9 +38,10 @@ pub struct Service {
pub name: String, pub name: String,
pub hostname: Option<String>, pub hostname: Option<String>,
pub image: String, pub image: String,
pub network: Option<String>,
pub restart: Option<String>, pub restart: Option<String>,
pub detach: Option<bool>, pub detach: Option<bool>,
pub interactive: Option<bool>,
pub healthcheck: Option<HealthCheck>, pub healthcheck: Option<HealthCheck>,
pub command: Option<String>, pub command: Option<String>,
@@ -66,8 +70,9 @@ pub struct PortMapping {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct VolumeMapping { pub struct VolumeMapping {
pub volume: String, pub source: String,
pub path: String pub target: String,
pub create: Option<bool>
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@@ -87,3 +92,9 @@ pub struct HealthCheck {
pub retries: Option<u32>, pub retries: Option<u32>,
pub on_failure: Option<String>, pub on_failure: Option<String>,
} }
#[derive(Debug, Deserialize, Clone)]
pub struct NetworkMapping {
pub name: Option<String>,
pub group: Option<String>,
}