Render templates to file and set permissions

master
Dustin 2024-01-11 13:36:39 -06:00
parent bc1d8b187c
commit 47d138a9c6
3 changed files with 287 additions and 14 deletions

179
Cargo.lock generated
View File

@ -168,6 +168,33 @@ dependencies = [
"typenum",
]
[[package]]
name = "derive-adhoc"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5283ac2881753c76c0892406705553f0d9ab30649f81e18964d3408f4501edb8"
dependencies = [
"derive-adhoc-macros",
"heck",
]
[[package]]
name = "derive-adhoc-macros"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c21b673a9b8c78c34908e6fcb42b922e11c4df2de5237f1c3f58d3285904a84b"
dependencies = [
"heck",
"itertools",
"proc-macro-crate",
"proc-macro2",
"quote",
"sha3",
"strum",
"syn 1.0.109",
"void",
]
[[package]]
name = "deunicode"
version = "1.4.2"
@ -184,6 +211,12 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
@ -251,6 +284,12 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "humansize"
version = "2.1.3"
@ -309,6 +348,15 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
@ -324,6 +372,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "keccak"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
dependencies = [
"cpufeatures",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -403,6 +460,12 @@ dependencies = [
"regex",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -440,7 +503,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn",
"syn 2.0.48",
]
[[package]]
@ -504,6 +567,16 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.76"
@ -513,6 +586,18 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "pwd-grp"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6955c41fd7e4283bdf6ff3e7218b7e3f8ef24c4236b31d22be050f4cfd5e2a2c"
dependencies = [
"derive-adhoc",
"libc",
"paste",
"thiserror",
]
[[package]]
name = "quote"
version = "1.0.35"
@ -596,6 +681,12 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustversion"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "ryu"
version = "1.0.16"
@ -628,7 +719,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.48",
]
[[package]]
@ -666,6 +757,16 @@ dependencies = [
"digest",
]
[[package]]
name = "sha3"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
dependencies = [
"digest",
"keccak",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@ -697,6 +798,39 @@ version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.48",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.48"
@ -747,7 +881,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.48",
]
[[package]]
@ -766,6 +900,7 @@ version = "0.1.0"
dependencies = [
"argparse",
"file-mode",
"pwd-grp",
"serde",
"serde_yaml",
"tera",
@ -774,6 +909,23 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "tracing"
version = "0.1.40"
@ -794,7 +946,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.48",
]
[[package]]
@ -922,6 +1074,12 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "walkdir"
version = "2.4.0"
@ -959,7 +1117,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 2.0.48",
"wasm-bindgen-shared",
]
@ -981,7 +1139,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.48",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1145,3 +1303,12 @@ name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
dependencies = [
"memchr",
]

View File

@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
argparse = "0.2.2"
file-mode = { version = "0.1.2", features = ["serde"] }
pwd-grp = "0.1.1"
serde = { version = "1.0.195", features = ["derive"] }
serde_yaml = "0.9.30"
tera = "1.19.1"

View File

@ -1,13 +1,13 @@
mod model;
use std::path::PathBuf;
use std::io::Read;
use std::io::{Read, Seek, Write};
use std::path::{Path, PathBuf};
use argparse::{ArgumentParser, Store, StoreOption};
use serde::de::DeserializeOwned;
use serde_yaml::Value;
use tera::{Context, Tera};
use tracing::error;
use tracing::{debug, error, info};
use model::Instructions;
@ -19,6 +19,18 @@ enum LoadError {
Yaml(#[from] serde_yaml::Error),
}
#[derive(Debug, thiserror::Error)]
enum SetPermissionsError {
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("User not found: {0}")]
UserNotFound(String),
#[error("Group not found: {0}")]
GroupNotFound(String),
#[error("Bad mode string")]
BadMode,
}
#[derive(Debug, thiserror::Error)]
enum ProcessInstructionsError {
#[error("Error loading templates: {0}")]
@ -82,7 +94,10 @@ fn main() {
}
};
let templates = args.templates.as_deref().unwrap_or("templates");
if let Err(e) = process_instructions(templates, instructions, values) {
let destdir = args.destdir.as_deref().unwrap_or(Path::new("/"));
if let Err(e) =
process_instructions(templates, destdir, instructions, values)
{
error!("Failed to process instructions: {}", e);
std::process::exit(1);
}
@ -101,20 +116,110 @@ fn load_yaml<T: DeserializeOwned>(path: &str) -> Result<T, LoadError> {
fn process_instructions(
templates: &str,
destdir: impl AsRef<Path>,
instructions: Instructions,
values: Value,
) -> Result<(), ProcessInstructionsError> {
let tera = Tera::new(&format!("{}/**", templates))?;
let ctx = Context::from_serialize(&values)?;
for i in instructions.render {
match tera.render(&i.template, &ctx) {
Ok(o) => {
print!("{}", o);
}
let out = match tera.render(&i.template, &ctx) {
Ok(o) => o,
Err(e) => {
error!("Failed to render template {}: {}", i.template, e);
continue;
}
};
let mut dest = PathBuf::from(destdir.as_ref());
dest.push(i.dest.strip_prefix("/").unwrap_or(i.dest.as_path()));
if let Err(e) = write_file(&dest, out.as_bytes()) {
error!("Failed to write output file {}: {}", dest.display(), e);
continue;
}
if i.owner.is_some() || i.group.is_some() {
if let Err(e) =
chown(&dest, i.owner.as_deref(), i.group.as_deref())
{
error!("Failed to set ownership of {}: {}", dest.display(), e);
}
}
if let Some(mode) = i.mode {
if let Err(e) = chmod(&dest, &mode) {
error!("Failed to set mode of {}: {}", dest.display(), e);
}
}
}
Ok(())
}
fn write_file(
dest: impl AsRef<Path>,
data: &[u8],
) -> Result<(), std::io::Error> {
if let Some(p) = dest.as_ref().parent() {
if !p.exists() {
info!("Creating directory {}", p.display());
std::fs::create_dir_all(p)?;
}
}
debug!("Writing output: {}", dest.as_ref().display());
let mut f = std::fs::File::create(&dest)?;
f.write_all(data)?;
let size = f.stream_position()?;
info!("Wrote output: {} ({} bytes)", dest.as_ref().display(), size);
Ok(())
}
fn chown(
path: impl AsRef<Path>,
owner: Option<&str>,
group: Option<&str>,
) -> Result<(), SetPermissionsError> {
let uid = if let Some(owner) = owner {
debug!("Looking up UID for user {}", owner);
if let Some(pw) = pwd_grp::getpwnam(owner)? {
debug!("Found UID {} for user {}", pw.uid, owner);
Some(pw.uid)
} else {
return Err(SetPermissionsError::UserNotFound(owner.into()));
}
} else {
None
};
let gid = if let Some(group) = group {
debug!("Looking up GID for group {}", group);
if let Some(gr) = pwd_grp::getgrnam(group)? {
debug!("Found GID {} for group {}", gr.gid, group);
Some(gr.gid)
} else {
return Err(SetPermissionsError::GroupNotFound(group.into()));
}
} else {
None
};
debug!(
"Setting ownership of {} to {:?} / {:?}",
path.as_ref().display(),
uid,
gid
);
Ok(std::os::unix::fs::chown(path, uid, gid)?)
}
fn chmod(
path: impl AsRef<Path>,
mode: &str,
) -> Result<(), SetPermissionsError> {
let mut filemode = file_mode::Mode::empty();
filemode
.set_str(mode)
.map_err(|_| SetPermissionsError::BadMode)?;
debug!(
"Changing mode of {} to {:o}",
path.as_ref().display(),
filemode.mode()
);
let newmode = filemode.set_mode_path(&path)?;
info!("Set mode of {} to {:o}", path.as_ref().display(), newmode);
Ok(())
}