143 lines
4.6 KiB
Rust
143 lines
4.6 KiB
Rust
use crate::log::LogLevel;
|
|
use crate::spec::Specification;
|
|
use crate::tmpl;
|
|
use crate::{crit, dbug, info, verb};
|
|
use std::error::Error;
|
|
use std::io::Write;
|
|
|
|
pub struct DeviceConfigBundle {
|
|
pub hostname: String,
|
|
pub configs: Vec<ConfigVariant>,
|
|
pub spec: Specification,
|
|
pub platform: String,
|
|
}
|
|
|
|
pub struct ConfigVariant {
|
|
pub name: String,
|
|
pub configs: Vec<Configuration>,
|
|
}
|
|
|
|
pub struct Configuration {
|
|
pub name: String,
|
|
pub data: String,
|
|
}
|
|
|
|
impl DeviceConfigBundle {
|
|
pub fn from_spec(spec: Specification) -> Result<Self, Box<dyn std::error::Error>> {
|
|
let mut context = Self::merge_context(&spec);
|
|
|
|
let hostname = Self::get_hostname(&context, &spec);
|
|
info!("Processing templates for '{hostname}'");
|
|
|
|
let structure_path = spec.tmplpath.join("structure.yaml");
|
|
let structure = tmpl::Structure::from_file(&structure_path)
|
|
.map_err(|e| format!("Failed to parse {:?}: {e}", structure_path))?;
|
|
|
|
let mut renderer = tera::Tera::default();
|
|
renderer.add_raw_templates(structure.load_template_data(&spec.tmplpath))?;
|
|
dbug!("Rendering templates for {hostname}");
|
|
|
|
let mut failures = false;
|
|
let mut configs = Vec::new();
|
|
for variant in &structure.variations {
|
|
context.insert(variant, &true);
|
|
let mut cfgs = Vec::new();
|
|
for template_name in &structure.files {
|
|
dbug!(" | {template_name}.{variant}");
|
|
match renderer.render(template_name, &context) {
|
|
Ok(data) => cfgs.push(Configuration::new(template_name, data)),
|
|
Err(e) => {
|
|
crit!("[!] {}", Error::source(&e).unwrap_or_else(|| &e));
|
|
renderer.add_raw_template(template_name, "")?;
|
|
failures = true;
|
|
}
|
|
}
|
|
}
|
|
configs.push(ConfigVariant::new(variant, cfgs));
|
|
context.remove(variant);
|
|
}
|
|
|
|
match failures {
|
|
true => Err(Box::<dyn Error>::from("Rendering failed.")),
|
|
false => Ok(Self {
|
|
hostname,
|
|
configs,
|
|
spec,
|
|
platform: structure.platform,
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn get_hostname(context: &tera::Context, spec: &Specification) -> String {
|
|
context
|
|
.get("hostname")
|
|
.and_then(|v| v.as_str())
|
|
.map(|s| s.to_string())
|
|
.unwrap_or_else(|| spec.components.get_hostname())
|
|
}
|
|
|
|
fn merge_context(spec: &Specification) -> tera::Context {
|
|
let mut context = tera::Context::new();
|
|
if let Some(data_map) = spec.compiled.get("data").and_then(|v| v.as_object()) {
|
|
for (key, val) in data_map {
|
|
context.insert(key, val);
|
|
}
|
|
}
|
|
context
|
|
}
|
|
|
|
pub fn output_artifacts(self: Self) {
|
|
info!("Writing Output:");
|
|
|
|
let device_outpath = std::path::Path::new(&self.spec.outpath).join(self.hostname);
|
|
std::fs::create_dir_all(&device_outpath).ok();
|
|
|
|
for variant_configs in self.configs {
|
|
let out_path = device_outpath.join(&variant_configs.name);
|
|
std::fs::create_dir_all(&out_path).ok();
|
|
|
|
let merged_config_path =
|
|
device_outpath.join(format!("all.{}.{}", variant_configs.name, self.platform));
|
|
let mut all_file: std::fs::File = std::fs::OpenOptions::new()
|
|
.create(true)
|
|
.append(true)
|
|
.open(&merged_config_path)
|
|
.expect("unable to open");
|
|
|
|
for config in &variant_configs.configs {
|
|
let config_outpath = out_path.join(format!("{}.{}", config.name, self.platform));
|
|
verb!(" | {}", &config_outpath.display());
|
|
std::fs::write(&config_outpath, &config.data).ok();
|
|
all_file.write_all(&config.data.as_bytes()).ok();
|
|
}
|
|
info!(" | {} ", &merged_config_path.display());
|
|
}
|
|
|
|
let compiled_spec_path = device_outpath.join("context.yaml");
|
|
verb!(" | {}", &compiled_spec_path.display());
|
|
std::fs::write(
|
|
&compiled_spec_path,
|
|
yaml_serde::to_string(&self.spec.compiled).unwrap(),
|
|
)
|
|
.ok();
|
|
}
|
|
}
|
|
|
|
impl ConfigVariant {
|
|
pub fn new(name: &str, configs: Vec<Configuration>) -> Self {
|
|
Self {
|
|
name: String::from(name),
|
|
configs,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Configuration {
|
|
pub fn new(name: &str, data: String) -> Self {
|
|
Self {
|
|
name: String::from(name),
|
|
data,
|
|
}
|
|
}
|
|
}
|