353 lines
11 KiB
Rust
Raw Normal View History

#![allow(
dead_code,
unused_imports,
unused_variables,
unused_assignments,
unused,
clippy::excessive_nesting
)]
2025-10-07 10:59:52 +01:00
//! Core provisioning engine for executing infrastructure commands
use std::process::Stdio;
2025-10-07 10:59:52 +01:00
use anyhow::{Context, Result};
use serde_json::Value;
use tracing::{debug, info};
2025-10-07 10:59:52 +01:00
use crate::config::Config;
use crate::errors::ProvisioningError;
#[derive(Debug, Clone)]
pub struct ServerConfig {
pub hostname: String,
pub instance_type: String,
pub count: u32,
pub provider: String,
pub region: Option<String>,
pub purpose: Option<String>,
}
pub struct ProvisioningEngine {
config: Config,
}
impl ProvisioningEngine {
pub fn new(config: &Config) -> Result<Self> {
Ok(Self {
config: config.clone(),
})
}
/// Create servers based on parsed configuration
pub fn create_server(&self, server_config: &ServerConfig, check_mode: bool) -> Result<String> {
info!("Creating server: {:?}", server_config);
let mut args = vec![
"server".to_string(),
"create".to_string(),
"--infra".to_string(),
format!("ai-{}", server_config.hostname),
];
if check_mode {
args.push("--check".to_string());
}
if let Some(region) = &server_config.region {
args.extend(vec!["--region".to_string(), region.clone()]);
}
args.push("--out".to_string());
args.push("json".to_string());
let result = self.execute_provisioning_command(&args)?;
2025-10-07 10:59:52 +01:00
// Parse and format the result
if let Ok(json_result) = serde_json::from_str::<Value>(&result) {
Ok(self.format_server_result(&json_result, check_mode))
} else {
Ok(result)
}
}
/// Process natural language infrastructure queries
pub fn process_query(
&self,
query: &str,
infra_name: Option<&str>,
output_format: &str,
) -> Result<String> {
2025-10-07 10:59:52 +01:00
info!("Processing query: {}", query);
let mut args = vec!["show".to_string()];
2025-10-07 10:59:52 +01:00
// Determine what to show based on the query
if query.to_lowercase().contains("server") || query.to_lowercase().contains("instance") {
args.push("servers".to_string());
} else if query.to_lowercase().contains("cost") || query.to_lowercase().contains("price") {
args.push("costs".to_string());
} else if query.to_lowercase().contains("status") {
args.push("data".to_string());
} else {
args.push("settings".to_string());
}
if let Some(infra) = infra_name {
args.extend(vec!["--infra".to_string(), infra.to_string()]);
}
args.extend(vec!["--out".to_string(), output_format.to_string()]);
let result = self.execute_provisioning_command(&args)?;
2025-10-07 10:59:52 +01:00
// Add natural language interpretation
Ok(format!(
"Based on your query: \"{}\"\n\n{}",
query,
self.interpret_query_result(query, &result)
))
}
/// Deploy a task service
pub fn deploy_taskserv(
&self,
service_name: &str,
infra_name: &str,
_configuration: &Value,
check_mode: bool,
) -> Result<String> {
2025-10-07 10:59:52 +01:00
info!("Deploying service {} to {}", service_name, infra_name);
let mut args = vec![
"taskserv".to_string(),
"create".to_string(),
service_name.to_string(),
"--infra".to_string(),
infra_name.to_string(),
];
if check_mode {
args.push("--check".to_string());
}
args.extend(vec!["--out".to_string(), "json".to_string()]);
let result = self.execute_provisioning_command(&args)?;
2025-10-07 10:59:52 +01:00
Ok(format!(
"🚀 Service '{}' deployment on '{}' infrastructure:\n\n{}",
service_name, infra_name, result
2025-10-07 10:59:52 +01:00
))
}
/// Create a complete cluster
pub fn create_cluster(
&self,
description: &str,
cluster_type: &str,
services: &[&str],
infra_name: &str,
) -> Result<String> {
2025-10-07 10:59:52 +01:00
info!("Creating {} cluster: {}", cluster_type, description);
// First create the infrastructure
let mut results = Vec::new();
2025-10-07 10:59:52 +01:00
// Generate infrastructure
let gen_args = vec![
"generate".to_string(),
"infra".to_string(),
"--new".to_string(),
infra_name.to_string(),
"--template".to_string(),
format!("{}-cluster", cluster_type),
];
2025-10-07 10:59:52 +01:00
match self.execute_provisioning_command(&gen_args) {
Ok(result) => results.push(format!("📋 Infrastructure generated:\n{}", result)),
Err(e) => results.push(format!("⚠️ Infrastructure generation: {}", e)),
}
// Create servers
let server_args = vec![
"server".to_string(),
"create".to_string(),
"--infra".to_string(),
infra_name.to_string(),
];
2025-10-07 10:59:52 +01:00
match self.execute_provisioning_command(&server_args) {
Ok(result) => results.push(format!("🖥️ Servers created:\n{}", result)),
Err(e) => results.push(format!("⚠️ Server creation: {}", e)),
}
// Deploy each service
for service in services {
let service_args = vec![
"taskserv".to_string(),
"create".to_string(),
service.to_string(),
"--infra".to_string(),
infra_name.to_string(),
];
2025-10-07 10:59:52 +01:00
match self.execute_provisioning_command(&service_args) {
Ok(result) => {
results.push(format!("⚙️ Service '{}' deployed:\n{}", service, result))
}
2025-10-07 10:59:52 +01:00
Err(e) => results.push(format!("⚠️ Service '{}': {}", service, e)),
}
}
Ok(results.join("\n\n"))
}
/// Get status of infrastructure
pub fn get_status(&self, infra_name: Option<&str>, detailed: bool) -> Result<String> {
let mut args = vec!["show".to_string()];
2025-10-07 10:59:52 +01:00
if detailed {
args.push("alldata".to_string());
} else {
args.push("data".to_string());
}
if let Some(infra) = infra_name {
args.extend(vec!["--infra".to_string(), infra.to_string()]);
}
args.extend(vec!["--out".to_string(), "json".to_string()]);
let result = self.execute_provisioning_command(&args)?;
2025-10-07 10:59:52 +01:00
// Parse and format the status
if let Ok(json_result) = serde_json::from_str::<Value>(&result) {
Ok(self.format_status_result(&json_result, detailed))
} else {
Ok(result)
}
}
/// Execute a provisioning command
fn execute_provisioning_command(&self, args: &[String]) -> Result<String> {
debug!("Executing command: {:?}", args);
let cmd_path = self.config.provisioning_command();
2025-10-07 10:59:52 +01:00
let output = std::process::Command::new(&cmd_path)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.with_context(|| format!("Failed to execute command: {}", cmd_path.display()))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(ProvisioningError::command_failed(format!(
"Command failed with status {}: {}",
output.status.code().unwrap_or(-1),
stderr
))
.into());
2025-10-07 10:59:52 +01:00
}
let stdout = String::from_utf8_lossy(&output.stdout);
Ok(stdout.to_string())
}
/// Format server creation result
fn format_server_result(&self, result: &Value, check_mode: bool) -> String {
let prefix = if check_mode {
"🔍 Check mode result"
} else {
"✅ Server creation result"
};
if let Some(servers) = result.get("servers").and_then(|s| s.as_array()) {
let mut output = format!("{}\n\n", prefix);
2025-10-07 10:59:52 +01:00
for server in servers {
if let (Some(hostname), Some(status)) = (
server.get("hostname").and_then(|h| h.as_str()),
server.get("status").and_then(|s| s.as_str()),
) {
2025-10-07 10:59:52 +01:00
output.push_str(&format!("{}: {}\n", hostname, status));
2025-10-07 10:59:52 +01:00
if let Some(ip) = server.get("public_ip").and_then(|ip| ip.as_str()) {
output.push_str(&format!(" IP: {}\n", ip));
}
2025-10-07 10:59:52 +01:00
if let Some(cost) = server.get("cost_hour").and_then(|c| c.as_str()) {
output.push_str(&format!(" Cost: {}/hour\n", cost));
}
}
}
2025-10-07 10:59:52 +01:00
output
} else {
format!(
"{}\n\n{}",
prefix,
serde_json::to_string_pretty(result).unwrap_or_default()
)
2025-10-07 10:59:52 +01:00
}
}
/// Format status result
fn format_status_result(&self, result: &Value, detailed: bool) -> String {
let mut output = String::new();
2025-10-07 10:59:52 +01:00
if let Some(main_name) = result.get("main_name").and_then(|n| n.as_str()) {
output.push_str(&format!("📊 Infrastructure: {}\n\n", main_name));
}
2025-10-07 10:59:52 +01:00
if let Some(servers) = result.get("servers").and_then(|s| s.as_array()) {
output.push_str("🖥️ Servers:\n");
for server in servers {
if let Some(hostname) = server.get("hostname").and_then(|h| h.as_str()) {
let status = server
.get("status")
.and_then(|s| s.as_str())
.unwrap_or("unknown");
2025-10-07 10:59:52 +01:00
let emoji = match status {
"running" => "🟢",
"stopped" => "🔴",
2025-10-07 10:59:52 +01:00
"pending" => "🟡",
_ => "",
};
output.push_str(&format!(" {} {}: {}\n", emoji, hostname, status));
}
}
output.push('\n');
}
2025-10-07 10:59:52 +01:00
if detailed {
if let Some(costs) = result.get("costs") {
output.push_str(&format!(
"💰 Costs: {}\n",
serde_json::to_string_pretty(costs).unwrap_or_default()
));
2025-10-07 10:59:52 +01:00
}
}
2025-10-07 10:59:52 +01:00
output
}
/// Interpret query result with natural language
fn interpret_query_result(&self, query: &str, result: &str) -> String {
// Simple interpretation based on query keywords
if query.to_lowercase().contains("cost") || query.to_lowercase().contains("price") {
format!("💰 Cost analysis:\n{}", result)
} else if query.to_lowercase().contains("status") || query.to_lowercase().contains("health")
{
2025-10-07 10:59:52 +01:00
format!("📊 System status:\n{}", result)
} else if query.to_lowercase().contains("problem") || query.to_lowercase().contains("error")
{
2025-10-07 10:59:52 +01:00
format!("🔍 Issue analysis:\n{}", result)
} else {
result.to_string()
}
}
}