226 lines
6.3 KiB
Rust
226 lines
6.3 KiB
Rust
use std::net::Ipv4Addr;
|
|
use std::process::Command;
|
|
|
|
use minijinja::Environment;
|
|
use optional_struct::{Applicable, optional_struct};
|
|
use schemars::JsonSchema;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::cluster::{Base, Cluster};
|
|
use crate::patch::{OptionalPatches, OptionalPatchesString, Patches};
|
|
use crate::schematic::Schematic;
|
|
use crate::secret::Secret;
|
|
use crate::{get_configs_path, get_talos_path};
|
|
|
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
enum NodeType {
|
|
Worker,
|
|
#[serde(rename(serialize = "controlplane"))]
|
|
ControlPlane,
|
|
}
|
|
|
|
impl From<NodeType> for &str {
|
|
fn from(value: NodeType) -> Self {
|
|
match value {
|
|
NodeType::Worker => "worker",
|
|
NodeType::ControlPlane => "controlplane",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
enum NodeArch {
|
|
Amd64,
|
|
}
|
|
|
|
#[optional_struct]
|
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
|
struct Tailscale {
|
|
auth_key: Secret,
|
|
advertise_routes: bool,
|
|
#[serde(default)]
|
|
server: Option<String>,
|
|
}
|
|
|
|
#[optional_struct]
|
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
|
struct Network {
|
|
interface: String,
|
|
ip: Ipv4Addr,
|
|
netmask: Ipv4Addr,
|
|
gateway: Ipv4Addr,
|
|
dns: [Ipv4Addr; 2],
|
|
#[optional_rename(OptionalTailscale)]
|
|
#[optional_wrap]
|
|
tailscale: Tailscale,
|
|
}
|
|
|
|
#[optional_struct]
|
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, Default, Clone, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
|
struct Install {
|
|
auto: bool,
|
|
disk: String,
|
|
serial: Option<String>,
|
|
}
|
|
|
|
#[optional_struct]
|
|
#[derive(Debug, Deserialize, JsonSchema, Clone, PartialEq, Eq)]
|
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
|
pub(crate) struct NodeDeserialize {
|
|
arch: NodeArch,
|
|
schematic: Schematic,
|
|
r#type: NodeType,
|
|
#[optional_rename(OptionalNetwork)]
|
|
#[optional_wrap]
|
|
network: Network,
|
|
ntp: String,
|
|
#[optional_rename(OptionalInstall)]
|
|
#[optional_wrap]
|
|
install: Install,
|
|
kernel_args: Vec<String>,
|
|
#[optional_rename(OptionalPatchesString)]
|
|
#[optional_wrap]
|
|
patches: Patches<String>,
|
|
sops: Secret,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct Node {
|
|
hostname: String,
|
|
arch: NodeArch,
|
|
schematic: Schematic,
|
|
r#type: NodeType,
|
|
network: Network,
|
|
ntp: String,
|
|
install: Install,
|
|
kernel_args: Vec<String>,
|
|
patches: Patches<serde_yaml::Value>,
|
|
sops: Secret,
|
|
}
|
|
|
|
impl Node {
|
|
pub(crate) fn get(
|
|
node_name: &str,
|
|
env: &Environment,
|
|
cluster: &Cluster,
|
|
default: &OptionalNodeDeserialize,
|
|
base: &Base,
|
|
) -> Self {
|
|
let mut path = get_talos_path().join("nodes").join(node_name);
|
|
let hostname = path
|
|
.file_name()
|
|
.expect("Path should be valid")
|
|
.to_string_lossy()
|
|
.to_string();
|
|
|
|
path.add_extension("yaml");
|
|
let content = std::fs::read_to_string(path).unwrap();
|
|
|
|
let node: OptionalNodeDeserialize = 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 = OptionalNodeDeserialize {
|
|
patches: Some(OptionalPatches {
|
|
all: Some(vec![]),
|
|
control_plane: Some(vec![]),
|
|
}),
|
|
kernel_args: vec![].into(),
|
|
..Default::default()
|
|
}
|
|
.apply(default.clone());
|
|
|
|
// Combine all the optional node parts into complete struct
|
|
let node: NodeDeserialize = default
|
|
// Override node specific settings
|
|
.apply(node)
|
|
.try_into()
|
|
.unwrap();
|
|
|
|
// Prepend the cluster base values
|
|
let mut kernel_args = base.kernel_args.clone();
|
|
kernel_args.extend(node.kernel_args);
|
|
|
|
let patches = base.patches.clone().extend(node.patches);
|
|
|
|
let node = Node {
|
|
hostname,
|
|
arch: node.arch,
|
|
schematic: node.schematic,
|
|
r#type: node.r#type,
|
|
network: node.network,
|
|
ntp: node.ntp,
|
|
install: node.install,
|
|
kernel_args,
|
|
patches: Default::default(),
|
|
sops: node.sops,
|
|
};
|
|
|
|
// Render patches
|
|
let patches = patches.render(env, cluster, &node);
|
|
|
|
Node { patches, ..node }
|
|
}
|
|
|
|
pub fn talosctl_gen_config_command(&self, cluster: &Cluster) -> Command {
|
|
let mut path = get_configs_path().join(&cluster.name).join(&self.hostname);
|
|
path.add_extension("yaml");
|
|
|
|
let mut command = Command::new("talosctl");
|
|
command.args([
|
|
"gen",
|
|
"config",
|
|
&cluster.name,
|
|
&format!("https://{}:6443", cluster.control_plane_ip),
|
|
"--with-secrets",
|
|
cluster.secrets_file.to_str().expect("Path should be valid"),
|
|
"--talos-version",
|
|
&cluster.version.talos(),
|
|
"--kubernetes-version",
|
|
&cluster.version.kubernetes(),
|
|
"--output-types",
|
|
self.r#type.into(),
|
|
"--install-image",
|
|
&format!(
|
|
"factory.talos.dev/metal-installer/{}:{}",
|
|
self.schematic,
|
|
cluster.version.talos()
|
|
),
|
|
"--with-docs=false",
|
|
"--with-examples=false",
|
|
"-o",
|
|
path.to_str().expect("Path should be valid utf-8"),
|
|
]);
|
|
|
|
for patch in &self.patches.all {
|
|
command.args(["--config-patch", &serde_json::to_string(&patch).unwrap()]);
|
|
}
|
|
|
|
for patch in &self.patches.control_plane {
|
|
command.args([
|
|
"--config-patch-control-plane",
|
|
&serde_json::to_string(&patch).unwrap(),
|
|
]);
|
|
}
|
|
|
|
command
|
|
}
|
|
}
|
|
|
|
impl JsonSchema for Node {
|
|
fn schema_name() -> std::borrow::Cow<'static, str> {
|
|
"Node".into()
|
|
}
|
|
|
|
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
|
|
OptionalNodeDeserialize::json_schema(generator)
|
|
}
|
|
}
|