Compare commits
11 Commits
fe458a4679
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23fc91f54e | ||
|
|
1953f8cf1c | ||
|
|
4c7d1a857e | ||
|
|
6e236f64df | ||
|
|
30c813bcf2 | ||
|
|
37c86fc994 | ||
|
|
19d1bfe9a2 | ||
|
|
9146046b91 | ||
|
|
9e6b2be0ac | ||
|
|
909fdfcae1 | ||
|
|
66b5dff359 |
82
Cargo.lock
generated
82
Cargo.lock
generated
@@ -54,9 +54,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.41"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
|
||||
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -64,9 +64,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.41"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
|
||||
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -76,9 +76,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.41"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -98,6 +98,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -118,9 +124,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.10.0"
|
||||
version = "2.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -132,6 +138,15 @@ version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
@@ -140,9 +155,10 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "podman-openrc"
|
||||
version = "0.1.1"
|
||||
version = "0.1.4"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"itertools",
|
||||
"serde",
|
||||
"toml",
|
||||
]
|
||||
@@ -167,18 +183,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
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 = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -187,11 +213,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.0"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||
checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -213,12 +239,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.2"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac"
|
||||
checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
@@ -228,27 +254,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.0"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30"
|
||||
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
|
||||
checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@@ -337,6 +363,6 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.12"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "podman-openrc"
|
||||
version = "0.1.1"
|
||||
version = "0.1.4"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
keywords = ["podman", "openrc", "service", "generator"]
|
||||
@@ -12,6 +12,7 @@ repository = "https://git.plabble.org/Maurice/podman-openrc"
|
||||
readme = "README.md"
|
||||
|
||||
[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"] }
|
||||
toml = "0.9.2"
|
||||
toml = "0.9.7"
|
||||
|
||||
28
README.md
28
README.md
@@ -9,7 +9,10 @@ cargo install podman-openrc
|
||||
# Usage
|
||||
```sh
|
||||
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
|
||||
@@ -18,15 +21,16 @@ The format is like this:
|
||||
|
||||
```toml
|
||||
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
|
||||
[service]
|
||||
name = "<CONTAINER NAME>" # Container name, required
|
||||
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
|
||||
restart = "unless-stopped" # Restart, optional. Defaults to "unless-stopped"
|
||||
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.
|
||||
command = "<COMMAND>" # Container command to run, optional.
|
||||
|
||||
@@ -34,6 +38,19 @@ command = "<COMMAND>" # Container command to run, optional.
|
||||
[environment]
|
||||
ASPNETCORE_ENVIRONMENT = "Test"
|
||||
# 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
|
||||
[[ports]]
|
||||
@@ -43,8 +60,9 @@ protocol = "tcp" # Protocol, optional
|
||||
|
||||
# Optionally you can also assign volumes
|
||||
[[volumes]]
|
||||
volume = "<VOLUME NAME>" # Volume name or path on host
|
||||
path = "<PATH>" # Volume location/target inside container
|
||||
source = "<VOLUME NAME>" # Volume name or path on host
|
||||
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
|
||||
[[mounts]]
|
||||
@@ -56,7 +74,7 @@ read_only = true # Whether to use ro mode, optional
|
||||
# Optionally you can use Podman secrets in an array
|
||||
[[secrets]]
|
||||
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
|
||||
[[environment_secrets]]
|
||||
|
||||
229
src/main.rs
229
src/main.rs
@@ -1,16 +1,28 @@
|
||||
use std::fs;
|
||||
use std::{collections::HashMap, fs, path::Path};
|
||||
|
||||
use clap::Parser;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::service::ServiceConfig;
|
||||
use crate::service::{NetworkMapping, ServiceConfig};
|
||||
|
||||
mod service;
|
||||
|
||||
fn escape(str: &str) -> String {
|
||||
str.replace("$", "\\$")
|
||||
}
|
||||
|
||||
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 wrap = |cmd: &str| {
|
||||
if let Some(user) = config.user.as_ref() {
|
||||
format!("\tsu -c \"{}\" {} ", cmd, user)
|
||||
format!("\tsu -c \"{}\" {} ", escape(cmd), user)
|
||||
} else {
|
||||
format!("\t{}", cmd)
|
||||
}
|
||||
@@ -27,11 +39,14 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
|
||||
// start_pre()
|
||||
script.push_str("start_pre() {\n");
|
||||
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 rm {} --ignore;", config.service.name));
|
||||
script.push_str(&wrap(&start_pre_commands.join("\n")));
|
||||
for volume in config.volumes.iter().filter(|v| v.create.is_some_and(|c|c)) {
|
||||
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");
|
||||
// }
|
||||
|
||||
@@ -39,8 +54,15 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
|
||||
script.push_str("start() {\n");
|
||||
|
||||
let mut arguments = vec![
|
||||
format!("--restart {}", config.service.restart.as_deref().unwrap_or("unless-stopped")),
|
||||
format!("--name {}", config.service.name)
|
||||
format!(
|
||||
"--restart {}",
|
||||
config
|
||||
.service
|
||||
.restart
|
||||
.as_deref()
|
||||
.unwrap_or("unless-stopped")
|
||||
),
|
||||
format!("--name {}", config.service.name),
|
||||
];
|
||||
|
||||
if let Some(hostname) = &config.service.hostname {
|
||||
@@ -51,7 +73,11 @@ pub fn generate_openrc(config: &ServiceConfig) -> 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));
|
||||
}
|
||||
|
||||
@@ -59,6 +85,16 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
|
||||
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 {
|
||||
if let Some(target) = &secret.target {
|
||||
arguments.push(format!("--secret {},target={}", &secret.key, target));
|
||||
@@ -72,21 +108,27 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
|
||||
}
|
||||
|
||||
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 {
|
||||
arguments.push(format!("--volume {}:{}", &volume.volume, &volume.path));
|
||||
arguments.push(format!("--volume {}:{}", &volume.source, &volume.target));
|
||||
}
|
||||
|
||||
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) {
|
||||
mount_str.push_str(",readonly");
|
||||
}
|
||||
arguments.push(mount_str);
|
||||
}
|
||||
|
||||
|
||||
if let Some(healthcheck) = &config.service.healthcheck {
|
||||
arguments.push(format!("--health-cmd '{}'", healthcheck.cmd));
|
||||
if let Some(interval) = &healthcheck.interval {
|
||||
@@ -109,42 +151,173 @@ pub fn generate_openrc(config: &ServiceConfig) -> String {
|
||||
arguments.push(command.clone());
|
||||
}
|
||||
|
||||
script.push_str(&wrap(&format!("podman run {}", arguments.iter()
|
||||
.enumerate()
|
||||
.map(|(i, arg)| if i > 0 { format!("\t{}", arg) } else { arg.to_string() })
|
||||
.collect::<Vec<_>>()
|
||||
.join(" \\\n"))));
|
||||
script.push_str(&wrap(&format!(
|
||||
"podman run {}",
|
||||
arguments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, arg)| if i > 0 {
|
||||
format!("\t{}", arg)
|
||||
} else {
|
||||
arg.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.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(&wrap(&format!(
|
||||
"podman stop {} --ignore",
|
||||
config.service.name
|
||||
)));
|
||||
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
|
||||
}
|
||||
|
||||
/// Program to generate OpenRC scripts from Podman service definitions in TOML format.
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
/// Definition file in TOML format
|
||||
/// Definition file in TOML format, or directory with definition files
|
||||
definition: String,
|
||||
|
||||
/// Output file for the OpenRC script
|
||||
/// Output file for the OpenRC script, or output directory for the service files
|
||||
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() {
|
||||
let args = Args::parse();
|
||||
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 input_path = Path::new(&args.definition);
|
||||
let output_path = Path::new(&args.out);
|
||||
|
||||
let output = generate_openrc(&config);
|
||||
fs::write(&args.out, output)
|
||||
.expect("Failed to write OpenRC script to output 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);
|
||||
fs::write(output_path.join(format!("{}.service", config.service.name)), output)
|
||||
.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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ pub struct ServiceConfig {
|
||||
#[serde(default)]
|
||||
pub mounts: Vec<MountMapping>,
|
||||
|
||||
#[serde(default)]
|
||||
pub networks: Vec<NetworkMapping>,
|
||||
|
||||
pub user: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
@@ -35,9 +38,10 @@ pub struct Service {
|
||||
pub name: String,
|
||||
pub hostname: Option<String>,
|
||||
pub image: String,
|
||||
pub network: Option<String>,
|
||||
|
||||
pub restart: Option<String>,
|
||||
pub detach: Option<bool>,
|
||||
pub interactive: Option<bool>,
|
||||
pub healthcheck: Option<HealthCheck>,
|
||||
pub command: Option<String>,
|
||||
|
||||
@@ -66,8 +70,9 @@ pub struct PortMapping {
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct VolumeMapping {
|
||||
pub volume: String,
|
||||
pub path: String
|
||||
pub source: String,
|
||||
pub target: String,
|
||||
pub create: Option<bool>
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -86,4 +91,10 @@ pub struct HealthCheck {
|
||||
pub start_period: Option<String>,
|
||||
pub retries: Option<u32>,
|
||||
pub on_failure: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct NetworkMapping {
|
||||
pub name: Option<String>,
|
||||
pub group: Option<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user