use colored::Colorize;
use kube::{ core::ErrorResponse };
use clap::{ Args, Subcommand, command };
use std::{ process::{ Command }, thread, time::Duration };
use crate::{
essential::{ connect_to_client, create_config_file, create_configs, BASE_COMMAND, CliError },
};
use kube::Error;
enum InstallationType {
Components(Vec<String>),
SimpleExample(String),
}
#[derive(Subcommand, Debug, Clone)]
pub enum InstallCommands {
#[command(name = "cortexflow", about = "Install all the CortexBrain core components")]
All,
#[command(
name = "simple-example",
about = "Deploys a simple example contained in deploy-test-pod.yaml"
)]
TestPods,
}
#[derive(Args, Debug, Clone)]
pub struct InstallArgs {
#[command(subcommand)]
pub install_cmd: InstallCommands,
}
pub async fn install_cortexflow() -> Result<(), CliError> {
println!("{} {}", "=====>".blue().bold(), "Preparing cortexflow installation".white());
println!("{} {}", "=====>".blue().bold(), "Creating the config files".white());
println!("{} {}", "=====>".blue().bold(), "Creating cortexflow namespace".white());
Command::new("kubectl")
.args(["create", "namespace", "cortexflow"])
.output()
.expect("Failed to create cortexflow namespace");
let metadata_configs = create_configs();
create_config_file(metadata_configs).await?;
install_cluster_components().await?;
Ok(())
}
pub async fn install_simple_example() -> Result<(), CliError> {
println!("{} {}", "=====>".blue().bold(), "Installing simple example".white());
install_simple_example_component().await?;
Ok(())
}
async fn install_cluster_components() -> Result<(), CliError> {
match connect_to_client().await {
Ok(_) => {
println!("{} {}", "=====>".blue().bold(), "Copying installation files".white());
download_installation_files(
InstallationType::Components(
vec![
"https://raw.githubusercontent.com/CortexFlow/CortexBrain/refs/heads/main/core/src/testing/configmap-role.yaml".to_string(),
"https://raw.githubusercontent.com/CortexFlow/CortexBrain/refs/heads/main/core/src/testing/rolebinding.yaml".to_string(),
"https://raw.githubusercontent.com/CortexFlow/CortexBrain/refs/heads/main/core/src/testing/cortexflow-rolebinding.yaml".to_string(),
"https://raw.githubusercontent.com/CortexFlow/CortexBrain/refs/heads/feature/ebpf-core/core/src/testing/identity.yaml".to_string(),
"https://raw.githubusercontent.com/CortexFlow/CortexBrain/refs/heads/feature/ebpf-core/core/src/testing/agent.yaml".to_string()
]
)
)?;
thread::sleep(Duration::from_secs(1));
install_components("cortexbrain")?;
println!("\n");
rm_installation_files(
InstallationType::Components(
vec![
"configmap-role.yaml".to_string(),
"rolebinding.yaml".to_string(),
"cortexflow-rolebinding.yaml".to_string(),
"identity.yaml".to_string(),
"agent.yaml".to_string()
]
)
)?;
println!("{} {}", "=====>".blue().bold(), "installation completed".white());
Ok(())
}
Err(e) => {
Err(
CliError::ClientError(
Error::Api(ErrorResponse {
status: "failed".to_string(),
message: "Failed to connect to kubernetes client".to_string(),
reason: "Your cluster is probably disconnected".to_string(),
code: 404,
})
)
)
}
}
}
async fn install_simple_example_component() -> Result<(), CliError> {
match connect_to_client().await {
Ok(_) => {
println!("{} {}", "=====>".blue().bold(), "Copying installation files".white());
download_installation_files(
InstallationType::SimpleExample(
"https://raw.githubusercontent.com/CortexFlow/CortexBrain/refs/heads/feature/ebpf-core/core/src/testing/deploy-test-pod.yaml".to_string()
)
)?;
thread::sleep(Duration::from_secs(1));
install_components("simple-example")?;
println!("\n");
rm_installation_files(
InstallationType::SimpleExample("deploy-test-pod.yaml".to_string())
)?;
println!("{} {}", "=====>".blue().bold(), "installation completed".white());
Ok(())
}
Err(e) => {
Err(
CliError::ClientError(
Error::Api(ErrorResponse {
status: "failed".to_string(),
message: "Failed to connect to kubernetes client".to_string(),
reason: "Your cluster is probably disconnected".to_string(),
code: 404,
})
)
)
}
}
}
fn install_components(components_type: &str) -> Result<(), CliError> {
if components_type == "cortexbrain" {
let files_to_install = vec![
"configmap-role.yaml",
"rolebinding.yaml",
"cortexflow-rolebinding.yaml",
"identity.yaml",
"agent.yaml"
];
let tot_files = files_to_install.len();
println!("{} {}", "=====>".blue().bold(), "Installing cortexflow components".white());
let mut i = 1;
for component in files_to_install {
println!(
"{} {}{}{}{} {} {} {}",
"=====>".blue().bold(),
"(",
i,
"/",
tot_files,
")",
"Applying ",
component
);
apply_component(component);
i = i + 1;
}
} else if components_type == "simple-example" {
let files_to_install = vec!["deploy-test-pod.yaml"];
let tot_files = files_to_install.len();
let mut i = 1;
for component in files_to_install {
println!(
"{} {}{}{}{} {} {} {}",
"=====>".blue().bold(),
"(",
i,
"/",
tot_files,
")",
"Applying ",
component
);
apply_component(component);
i = i + 1;
}
} else {
return Err(CliError::InstallerError {
reason: "No installation type selected".to_string(),
});
}
Ok(())
}
fn apply_component(file: &str) -> Result<(), CliError> {
let output = Command::new(BASE_COMMAND)
.args(["apply", "-f", file])
.output()
.map_err(|_| CliError::InstallerError {
reason: "Can't install component from file".to_string(),
})?;
if !output.status.success() {
eprintln!("Error installing file: {}:\n{}", file, String::from_utf8_lossy(&output.stderr));
} else {
println!("✅ Applied {}", file);
}
thread::sleep(Duration::from_secs(2));
Ok(())
}
fn download_installation_files(installation_files: InstallationType) -> Result<(), CliError> {
match installation_files {
InstallationType::Components(files) => {
for src in files.iter() {
download_file(&src)?;
}
}
InstallationType::SimpleExample(file) => {
download_file(&file)?;
}
}
println!("\n");
Ok(())
}
fn rm_installation_files(file_to_remove: InstallationType) -> Result<(), CliError> {
println!("{} {}", "=====>".blue().bold(), "Removing temporary installation files".white());
match file_to_remove {
InstallationType::Components(files) => {
for src in files.iter() {
rm_file(&src)?;
}
}
InstallationType::SimpleExample(file) => {
rm_file(&file)?;
}
}
Ok(())
}
fn download_file(src: &str) -> Result<(), CliError> {
let output = Command::new("wget")
.args([src])
.output()
.map_err(|_| CliError::InstallerError {
reason: "An error occured: component download failed".to_string(),
})?;
if !output.status.success() {
eprintln!("Error copying file: {}.\n{}", src, String::from_utf8_lossy(&output.stderr));
} else {
println!("✅ Copied file from {} ", src);
}
thread::sleep(Duration::from_secs(2));
Ok(())
}
fn rm_file(file_to_remove: &str) -> Result<(), CliError> {
let output = Command::new("rm")
.args(["-f", file_to_remove])
.output()
.map_err(|_| CliError::InstallerError {
reason: "cannot remove temporary installation file".to_string(),
})?;
if !output.status.success() {
eprintln!(
"Error removing file: {}:\n{}",
file_to_remove,
String::from_utf8_lossy(&output.stderr)
);
} else {
println!("✅ Removed file {}", file_to_remove);
}
thread::sleep(Duration::from_secs(2));
Ok(())
}