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::{get_configs_path, get_repo_path, set_repo_path}; use futures::StreamExt; use futures::future::join_all; use minijinja::context; use thiserror::Error; use crate::cli::{Cli, Commands, GlobalOpts}; #[derive(Debug, Error)] enum Error { #[error("No clusters where found")] NoClustersFound, } async fn generate(opts: &GlobalOpts) -> Result<(), Error> { set_repo_path(&opts.repo); 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, 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.iter().map(|cluster| async move { join_all(cluster.nodes().iter().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(); 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(), ), }; Ok(()) }