use std::net::Ipv4Addr; use optional_struct::{Applicable, optional_struct}; use schemars::JsonSchema; use serde::Deserialize; use crate::{ base_dir, cluster::Cluster, patch::{OptionalPatches, Patches}, }; #[derive(Debug, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)] #[serde(rename_all = "camelCase")] enum NodeType { Worker, ControlPlane, } #[derive(Debug, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)] #[serde(rename_all = "camelCase")] enum NodeArch { Amd64, } #[optional_struct] #[derive(Debug, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Network { interface: String, ip: Ipv4Addr, netmask: Ipv4Addr, gateway: Ipv4Addr, dns: [Ipv4Addr; 2], advertise_routes: bool, } #[optional_struct] #[derive(Debug, Deserialize, JsonSchema, Default)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct Install { auto: bool, disk: String, serial: Option, } #[optional_struct] #[derive(Debug, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct Node { #[serde(skip_deserializing)] hostname: String, arch: NodeArch, r#type: NodeType, #[optional_rename(OptionalNetwork)] network: Network, // TODO: Type that verifies url? ntp: String, #[optional_rename(OptionalInstall)] #[serde(default)] install: Install, kernel_args: Vec, #[optional_rename(OptionalPatches)] #[serde(default)] patches: Patches, // TODO: Per machine patches, append to global list of patches // Any patches are specified under default they will get overridden } impl Node { pub fn get(mut cluster: Cluster, node_name: &str) -> Self { let mut path = base_dir().join("nodes").join(node_name); let named = OptionalNode { hostname: Some( path.file_name() .expect("Path should be valid") .to_string_lossy() .to_string(), ), ..OptionalNode::default() }; path.add_extension("yaml"); let content = std::fs::read_to_string(path).unwrap(); let node: OptionalNode = serde_yaml::from_str(&content).unwrap(); // We want all vectors to be empty vectors by default // Sadly we have to this manually // TODO: Find a better way of doing this let default = OptionalNode { patches: OptionalPatches { all: vec![].into(), control_plane: vec![].into(), }, kernel_args: vec![].into(), ..Default::default() }; // Combine all the optional node parts into complete struct let mut node: Node = default // Apply cluster default settings .apply(cluster.default) // Apply hostname based on filename .apply(named) // Override node specific settings .apply(node) .try_into() .unwrap(); // Prepend the cluster base values cluster.base.kernel_args.extend(node.kernel_args); node.kernel_args = cluster.base.kernel_args; cluster.base.patches.extend(node.patches); node.patches = cluster.base.patches; node } }