WIP
This commit is contained in:
117
crete/src/patch.rs
Normal file
117
crete/src/patch.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
net::Ipv4Addr,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use minijinja::{Environment, context};
|
||||
use optional_struct::optional_struct;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::{base_dir, cluster::Cluster, node::Node};
|
||||
|
||||
pub struct PatchEnv<'e>(Environment<'e>);
|
||||
|
||||
impl<'e> Default for PatchEnv<'e> {
|
||||
fn default() -> Self {
|
||||
let mut env = Environment::new();
|
||||
env.set_undefined_behavior(minijinja::UndefinedBehavior::Strict);
|
||||
|
||||
let dir = base_dir().join("patches");
|
||||
for entry in WalkDir::new(&dir)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.metadata().unwrap().is_file())
|
||||
{
|
||||
let source = std::fs::read_to_string(entry.path()).unwrap();
|
||||
let name = entry
|
||||
.path()
|
||||
.strip_prefix(&dir)
|
||||
.unwrap()
|
||||
.with_extension("")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
|
||||
env.add_filter("to_prefix", |netmask: String| {
|
||||
let netmask = Ipv4Addr::from_str(&netmask).map_err(|err| {
|
||||
minijinja::Error::new(minijinja::ErrorKind::InvalidOperation, err.to_string())
|
||||
})?;
|
||||
let mask = netmask.to_bits();
|
||||
let prefix = mask.leading_ones();
|
||||
|
||||
if mask.checked_shl(prefix).unwrap_or(0) == 0 {
|
||||
Ok(prefix as u8)
|
||||
} else {
|
||||
Err(minijinja::Error::new(
|
||||
minijinja::ErrorKind::InvalidOperation,
|
||||
"invalid IP prefix length",
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
env.add_template_owned(name, source).unwrap();
|
||||
}
|
||||
|
||||
Self(env)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub(crate) enum Patch {
|
||||
Name(String),
|
||||
#[serde(skip)]
|
||||
Resolved(serde_yaml::Value),
|
||||
}
|
||||
|
||||
#[optional_struct]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Patches {
|
||||
pub(crate) all: Vec<Patch>,
|
||||
pub(crate) control_plane: Vec<Patch>,
|
||||
}
|
||||
|
||||
fn render(patches: Vec<Patch>, env: &PatchEnv, cluster: &Cluster, node: &Node) -> Vec<Patch> {
|
||||
patches
|
||||
.into_iter()
|
||||
.map(|patch| {
|
||||
if let Patch::Name(name) = patch {
|
||||
Patch::Resolved(
|
||||
serde_yaml::from_str(
|
||||
&env.0
|
||||
.get_template(&name)
|
||||
.unwrap()
|
||||
.render(context!(node, cluster))
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
patch
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Patches {
|
||||
pub(crate) fn extend(mut self, other: Self) -> Self {
|
||||
self.all.extend(other.all);
|
||||
self.control_plane.extend(other.control_plane);
|
||||
|
||||
Self {
|
||||
all: self.all,
|
||||
control_plane: self.control_plane,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render(self, env: &PatchEnv, cluster: &Cluster, node: &Node) -> Self {
|
||||
Self {
|
||||
all: render(self.all.clone(), env, cluster, node),
|
||||
control_plane: render(self.control_plane.clone(), env, cluster, node),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user