feat: Generate talos configs

This commit is contained in:
2026-04-01 06:21:09 +02:00
parent 8e7d0d3a5e
commit 92131ad486
3 changed files with 144 additions and 7 deletions
+67 -5
View File
@@ -1,5 +1,6 @@
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr; use std::str::FromStr;
use minijinja::Environment; use minijinja::Environment;
@@ -9,9 +10,9 @@ use serde::{Deserialize, Serialize};
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::environment::PathEnvironment; use crate::environment::PathEnvironment;
use crate::get_talos_path;
use crate::node::{Node, OptionalNodeDeserialize}; use crate::node::{Node, OptionalNodeDeserialize};
use crate::patch::Patches; use crate::patch::Patches;
use crate::{get_configs_path, get_talos_path};
#[optional_struct] #[optional_struct]
#[derive(Debug, Deserialize, JsonSchema, Clone)] #[derive(Debug, Deserialize, JsonSchema, Clone)]
@@ -31,6 +32,16 @@ pub struct Version {
talos: semver::Version, talos: semver::Version,
} }
impl Version {
pub(crate) fn kubernetes(&self) -> String {
format!("v{}", self.kubernetes)
}
pub(crate) fn talos(&self) -> String {
format!("v{}", self.talos)
}
}
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)] #[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum ClusterEnv { pub enum ClusterEnv {
@@ -60,12 +71,12 @@ struct ClusterDeserialize {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Cluster { pub struct Cluster {
name: String, pub(crate) name: String,
version: Version, pub(crate) version: Version,
nodes: Vec<Node>, nodes: Vec<Node>,
cluster_env: ClusterEnv, cluster_env: ClusterEnv,
control_plane_ip: Ipv4Addr, pub(crate) control_plane_ip: Ipv4Addr,
secrets_file: PathBuf, pub(crate) secrets_file: PathBuf,
} }
impl Cluster { impl Cluster {
@@ -142,6 +153,57 @@ impl Cluster {
pub fn nodes(&self) -> &[Node] { pub fn nodes(&self) -> &[Node] {
&self.nodes &self.nodes
} }
pub fn talosctl_gen_config_command(&self) -> Command {
let path = get_configs_path().join(&self.name).join("talosconfig");
let mut command = Command::new("talosctl");
command.args([
"gen",
"config",
&self.name,
&format!("https://{}:6443", self.control_plane_ip),
"--with-secrets",
self.secrets_file.to_str().expect("Path should be valid"),
"--output-types",
"talosconfig",
"-o",
path.to_str().expect("Path should be valid utf-8"),
]);
command
}
pub fn talosctl_add_endpoint_command(&self) -> Command {
let path = get_configs_path().join(&self.name).join("talosconfig");
let mut command = Command::new("talosctl");
command.args([
"config",
"--talosconfig",
path.to_str().expect("Path should be valid utf-8"),
"endpoint",
&self.control_plane_ip.to_string(),
]);
command
}
pub fn talosctl_merge_command(&self) -> Command {
let cluster_path = get_configs_path().join(&self.name).join("talosconfig");
let path = get_configs_path().join("talosconfig");
let mut command = Command::new("talosctl");
command.args([
"config",
"--talosconfig",
path.to_str().expect("Path should be valid utf-8"),
"merge",
cluster_path.to_str().expect("Path should be valid utf-8"),
]);
command
}
} }
impl JsonSchema for Cluster { impl JsonSchema for Cluster {
+31 -1
View File
@@ -1,10 +1,12 @@
mod cli; mod cli;
use std::process::Command;
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use clap_complete::{Shell, generate as generate_complete}; use clap_complete::{Shell, generate as generate_complete};
use crete::cluster::Cluster; use crete::cluster::Cluster;
use crete::environment::PathEnvironment; use crete::environment::PathEnvironment;
use crete::{get_repo_path, set_repo_path}; use crete::{get_configs_path, get_repo_path, set_repo_path};
use minijinja::context; use minijinja::context;
use thiserror::Error; use thiserror::Error;
@@ -16,6 +18,16 @@ enum Error {
NoClustersFound, NoClustersFound,
} }
fn run_command(mut command: Command) {
let output = command.output().unwrap();
if !output.stdout.is_empty() {
println!("{}", String::from_utf8_lossy(&output.stdout).trim_end());
}
if !output.stderr.is_empty() {
println!("{}", String::from_utf8_lossy(&output.stderr).trim_end());
}
}
fn generate(opts: &GlobalOpts) -> Result<(), Error> { fn generate(opts: &GlobalOpts) -> Result<(), Error> {
set_repo_path(&opts.repo); set_repo_path(&opts.repo);
@@ -46,6 +58,24 @@ fn generate(opts: &GlobalOpts) -> Result<(), Error> {
std::fs::write(path.join(template_name), content).unwrap(); std::fs::write(path.join(template_name), content).unwrap();
} }
// Remove existing config files and create output directory
let path = get_configs_path();
if path.exists() {
std::fs::remove_dir_all(&path).unwrap();
}
std::fs::create_dir(&path).unwrap();
// Generate config files
for cluster in clusters {
for node in cluster.nodes() {
run_command(node.talosctl_gen_config_command(&cluster));
}
run_command(cluster.talosctl_gen_config_command());
run_command(cluster.talosctl_add_endpoint_command());
run_command(cluster.talosctl_merge_command());
}
Ok(()) Ok(())
} }
+46 -1
View File
@@ -1,4 +1,5 @@
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::process::Command;
use minijinja::Environment; use minijinja::Environment;
use optional_struct::{Applicable, optional_struct}; use optional_struct::{Applicable, optional_struct};
@@ -6,10 +7,10 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::cluster::{Base, Cluster}; use crate::cluster::{Base, Cluster};
use crate::get_talos_path;
use crate::patch::{OptionalPatches, OptionalPatchesString, Patches}; use crate::patch::{OptionalPatches, OptionalPatchesString, Patches};
use crate::schematic::Schematic; use crate::schematic::Schematic;
use crate::secret::Secret; use crate::secret::Secret;
use crate::{get_configs_path, get_talos_path};
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@@ -167,6 +168,50 @@ impl Node {
Node { patches, ..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 { impl JsonSchema for Node {