use std::{io::Write, path::PathBuf}; use bindings::{ component::churn_tasks::process::Process, exports::component::churn_tasks::task::Guest, }; use minijinja::{context, Environment}; #[allow(warnings)] mod bindings; const ALLOY_CONFIG_PATH: &str = "/etc/alloy/config.alloy"; const ALLOY_CONFIG_FILE: &str = include_str!("../files/config.alloy"); struct Component; impl Guest for Component { fn id() -> String { "grafana/alloy".into() } fn should_run() -> bool { let output = Process::new().run_process( &["systemctl", "is-enabled", "alloy.service"] .into_iter() .map(|i| i.into()) .collect::>(), ); if !output.contains("enabled") { println!("alloy is not enabled"); return true; } let output = Process::new().run_process( &["systemctl", "is-active", "alloy.service"] .into_iter() .map(|i| i.into()) .collect::>(), ); if output.contains("inactive") { println!("alloy is inactive"); return true; } match std::fs::read_to_string(ALLOY_CONFIG_PATH) { Ok(content) => { let process = Process::new(); let node_name = process.get_variable("node_name"); let config_file = generate_alloy_file(node_name); let is_different = content != config_file; if is_different { println!("config file was different"); return true; } } Err(e) => { if e.kind() == std::io::ErrorKind::NotFound { return true; } } } false } fn execute() { println!("running alloy installation"); let process = Process::new(); let node_name = process.get_variable("node_name"); let output = Process::new().run_process( &["systemctl", "is-enabled", "alloy.service"] .into_iter() .map(|i| i.into()) .collect::>(), ); if !output.contains("enabled") { install_alloy().expect("to be able to install alloy"); } let config_file = generate_alloy_file(node_name); let restart = match std::fs::read_to_string(ALLOY_CONFIG_PATH) { Ok(content) => { if content != config_file { let mut file = std::fs::File::create(ALLOY_CONFIG_PATH) .expect("to be able to create file"); file.write_all(config_file.as_bytes()) .expect("to be able to write file"); file.flush().expect("to be able to flush file"); true } else { false } } Err(e) => { if e.kind() == std::io::ErrorKind::NotFound { if let Some(parent) = PathBuf::from(ALLOY_CONFIG_PATH).parent() { std::fs::create_dir_all(parent) .expect("to be able to create dir for alloy"); } let mut file = std::fs::File::create(ALLOY_CONFIG_PATH) .expect("to be able to create file"); file.write_all(config_file.as_bytes()) .expect("to be able to write file"); file.flush().expect("to be able to flush file"); false } else { false } } }; if restart { restart_alloy().expect("to be able to restart alloy"); return; } run_and_activate_alloy().expect("to be able to active alloy"); } } fn generate_alloy_file(node_name: String) -> String { let mut env = Environment::new(); env.add_template("alloy.config", ALLOY_CONFIG_FILE).unwrap(); let tmpl = env.get_template("alloy.config").unwrap(); tmpl.render(context! {node_name => node_name}).unwrap() } fn install_alloy() -> Result<(), String> { println!("=== installing alloy ==="); run_command(["apt", "install", "-y", "gpg"])?; run_command(["mkdir", "-p", "/etc/apt/keyrings/"])?; run_command(["bash", "-c", "wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null"])?; run_command(["bash", "-c", "echo \"deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main\" | sudo tee /etc/apt/sources.list.d/grafana.list"])?; run_command(["apt-get", "update"])?; run_command([ "apt-get", "install", "-o Dpkg::Options::=\"--force-confdef\"", "-o Dpkg::Options::=\"--force-confold\"", "alloy", ])?; println!("=== finished installing alloy ==="); Ok(()) } fn restart_alloy() -> Result<(), String> { println!("=== restarting alloy ==="); run_command(["systemctl", "restart", "alloy"])?; println!("=== finished restarting alloy ==="); Ok(()) } fn run_and_activate_alloy() -> Result<(), String> { println!("=== starting alloy ==="); run_command(["systemctl", "start", "alloy"])?; println!("=== finished starting alloy ==="); println!("=== enabling alloy ==="); run_command(["systemctl", "enable", "alloy"])?; println!("=== finished enabling alloy ==="); println!("=== adding alloy to docker group ==="); run_command(["usermod", "-aG", "docker", "alloy"])?; println!("=== finished adding alloy to docker group ==="); Ok(()) } fn run_command(input: impl IntoIterator>) -> Result<(), String> { let args = input.into_iter().map(|i| i.into()).collect::>(); println!("running {}", args.join(" ")); let output = Process::new().run_process(&args); println!("output: {}", output); Ok(()) } bindings::export!(Component with_types_in bindings);