145 lines
4.3 KiB
Rust
145 lines
4.3 KiB
Rust
mod cli;
|
|
|
|
use std::pin::pin;
|
|
|
|
use clap::{CommandFactory, Parser};
|
|
use clap_complete::{Shell, generate as generate_complete};
|
|
use crete::cluster::Cluster;
|
|
use crete::environment::PathEnvironment;
|
|
use crete::talosconfig::{get_current_context, set_context};
|
|
use crete::tftp::ServerError;
|
|
use crete::{get_configs_path, get_repo_path, set_repo_path, tftp};
|
|
use futures::StreamExt;
|
|
use futures::future::join_all;
|
|
use ipxe::IPXE_DIR;
|
|
use minijinja::context;
|
|
use regex::Regex;
|
|
use thiserror::Error;
|
|
|
|
use crate::cli::{Cli, Commands, GlobalOpts};
|
|
|
|
#[derive(Debug, Error)]
|
|
enum Error {
|
|
#[error("No clusters where found")]
|
|
NoClustersFound,
|
|
#[error("Server error: {0}")]
|
|
ServerError(#[from] ServerError),
|
|
}
|
|
|
|
async fn generate(opts: &GlobalOpts) -> Result<(), Error> {
|
|
let clusters = Cluster::get_all().await;
|
|
|
|
if clusters.is_empty() {
|
|
return Err(Error::NoClustersFound);
|
|
}
|
|
|
|
let path = opts.repo.join("rendered");
|
|
// We don't really care if this fails, as that likely mean that the directory did not exist
|
|
tokio::fs::remove_dir_all(&path).await.ok();
|
|
tokio::fs::create_dir_all(&path).await.unwrap();
|
|
|
|
// Render templates
|
|
let template_env = PathEnvironment::new(&get_repo_path().join("templates"));
|
|
|
|
let mut stream = pin!(template_env.files());
|
|
while let Some(template_name) = stream.next().await {
|
|
let template = template_env.get_template(&template_name).unwrap();
|
|
|
|
let content = template
|
|
.render(context! {
|
|
clusters => clusters.values().collect::<Vec<_>>(),
|
|
root => opts.repo
|
|
})
|
|
.unwrap();
|
|
|
|
tokio::fs::write(path.join(template_name), content)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
let context = get_current_context().await;
|
|
|
|
// Remove existing config files and create output directory
|
|
let path = get_configs_path();
|
|
tokio::fs::remove_dir_all(&path).await.ok();
|
|
tokio::fs::create_dir_all(&path).await.unwrap();
|
|
|
|
// Generate config files
|
|
join_all(clusters.values().map(|cluster| async move {
|
|
join_all(cluster.nodes().map(|node| {
|
|
let cluster = cluster.clone();
|
|
async move {
|
|
node.config_write(&cluster).await;
|
|
}
|
|
}))
|
|
.await;
|
|
|
|
// Run these commands in parallel with the the node commands
|
|
// NOTE: These do need to happen in the correct order
|
|
cluster.talosconfig_write().await;
|
|
}))
|
|
.await;
|
|
|
|
// Merge all the cluster talosconfig files into a single file
|
|
// Since this all modifies the same output file this has to happen sequentially
|
|
for (_, cluster) in clusters {
|
|
cluster.talosconfig_merge().await;
|
|
}
|
|
|
|
if let Some(context) = context {
|
|
set_context(context).await;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Error> {
|
|
let cli = Cli::parse();
|
|
|
|
set_repo_path(&cli.global_opts.repo);
|
|
|
|
match cli.command {
|
|
Commands::Generate => generate(&cli.global_opts).await?,
|
|
Commands::ShellCompletions => generate_complete(
|
|
Shell::from_env().unwrap_or(Shell::Bash),
|
|
&mut Cli::command(),
|
|
"crete",
|
|
&mut std::io::stdout(),
|
|
),
|
|
Commands::Serve { ip } => {
|
|
let re = Regex::new(r"talos/(?<cluster>.+)/(?<node>.+)\.ya?ml")
|
|
.expect("regex should be valid");
|
|
|
|
tftp::serve(ip, move |filename, _| {
|
|
let filename = filename.to_owned();
|
|
let re = re.clone();
|
|
|
|
async move {
|
|
const IPXE_PATTERN: &str = "ipxe/";
|
|
if let Some(filename) = filename.strip_prefix(IPXE_PATTERN) {
|
|
Some(IPXE_DIR.get_file(filename)?.contents().to_owned())
|
|
} else if let Some(caps) = re.captures(&filename) {
|
|
let cluster = &caps["cluster"];
|
|
let node = &caps["node"];
|
|
|
|
let clusters = Cluster::get_all().await;
|
|
let cluster = clusters.get(cluster)?;
|
|
|
|
let node = cluster.node(node)?;
|
|
|
|
let config = node.config(cluster).await;
|
|
|
|
Some(config.into())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
})
|
|
.await?
|
|
}
|
|
};
|
|
|
|
Ok(())
|
|
}
|