//! Rhai wrappers for Kubernetes module functions //! //! This module provides Rhai wrappers for the functions in the Kubernetes module, //! enabling scripting access to Kubernetes operations. use crate::{KubernetesError, KubernetesManager}; use once_cell::sync::Lazy; use rhai::{Array, Dynamic, Engine, EvalAltResult, Map}; use std::sync::Mutex; use tokio::runtime::Runtime; // Global Tokio runtime for blocking async operations static RUNTIME: Lazy> = Lazy::new(|| Mutex::new(Runtime::new().expect("Failed to create Tokio runtime"))); /// Helper function to convert Rhai Map to HashMap for environment variables /// /// # Arguments /// /// * `rhai_map` - Rhai Map containing key-value pairs /// /// # Returns /// /// * `Option>` - Converted HashMap or None if empty fn convert_rhai_map_to_env_vars( rhai_map: Map, ) -> Option> { if rhai_map.is_empty() { None } else { Some( rhai_map .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(), ) } } /// Helper function to execute async operations with proper runtime handling /// /// This uses a global runtime to ensure consistent async execution fn execute_async(future: F) -> Result> where F: std::future::Future>, { // Get the global runtime let rt = match RUNTIME.lock() { Ok(rt) => rt, Err(e) => { return Err(Box::new(EvalAltResult::ErrorRuntime( format!("Failed to acquire runtime lock: {e}").into(), rhai::Position::NONE, ))); } }; // Execute the future in a blocking manner rt.block_on(future).map_err(kubernetes_error_to_rhai_error) } /// Helper function for error conversion fn kubernetes_error_to_rhai_error(error: KubernetesError) -> Box { Box::new(EvalAltResult::ErrorRuntime( format!("Kubernetes error: {error}").into(), rhai::Position::NONE, )) } /// Register Kubernetes module functions with the Rhai engine /// /// # Arguments /// /// * `engine` - The Rhai engine to register the functions with /// /// # Returns /// /// * `Result<(), Box>` - Ok if registration was successful, Err otherwise pub fn register_kubernetes_module(engine: &mut Engine) -> Result<(), Box> { // Register KubernetesManager type engine.register_type::(); // Register KubernetesManager constructor and methods engine.register_fn("kubernetes_manager_new", kubernetes_manager_new); engine.register_fn("namespace", kubernetes_manager_namespace); // Register resource listing functions engine.register_fn("pods_list", pods_list); engine.register_fn("services_list", services_list); engine.register_fn("deployments_list", deployments_list); engine.register_fn("configmaps_list", configmaps_list); engine.register_fn("secrets_list", secrets_list); engine.register_fn("namespaces_list", namespaces_list); // Register resource creation methods (object-oriented style) engine.register_fn("create_pod", pod_create); engine.register_fn("create_pod_with_env", pod_create_with_env); engine.register_fn("create_service", service_create); engine.register_fn("create_deployment", deployment_create); engine.register_fn("create_configmap", configmap_create); engine.register_fn("create_secret", secret_create); // Register resource get methods engine.register_fn("get_pod", pod_get); engine.register_fn("get_service", service_get); engine.register_fn("get_deployment", deployment_get); // Register resource management methods engine.register_fn("delete", delete); engine.register_fn("delete_pod", pod_delete); engine.register_fn("delete_service", service_delete); engine.register_fn("delete_deployment", deployment_delete); engine.register_fn("delete_configmap", configmap_delete); engine.register_fn("delete_secret", secret_delete); // Register namespace methods (object-oriented style) engine.register_fn("create_namespace", namespace_create); engine.register_fn("delete_namespace", namespace_delete); engine.register_fn("namespace_exists", namespace_exists); // Register utility functions engine.register_fn("resource_counts", resource_counts); // Register convenience functions engine.register_fn("deploy_application", deploy_application); Ok(()) } // KubernetesManager constructor and methods fn kubernetes_manager_new(namespace: String) -> Result> { execute_async(KubernetesManager::new(namespace)) } fn kubernetes_manager_namespace(km: &mut KubernetesManager) -> String { km.namespace().to_string() } // Resource listing functions fn pods_list(km: &mut KubernetesManager) -> Result> { let pods = execute_async(km.pods_list())?; let pod_names: Array = pods .iter() .filter_map(|pod| pod.metadata.name.as_ref()) .map(|name| Dynamic::from(name.clone())) .collect(); Ok(pod_names) } fn services_list(km: &mut KubernetesManager) -> Result> { let services = execute_async(km.services_list())?; let service_names: Array = services .iter() .filter_map(|service| service.metadata.name.as_ref()) .map(|name| Dynamic::from(name.clone())) .collect(); Ok(service_names) } fn deployments_list(km: &mut KubernetesManager) -> Result> { let deployments = execute_async(km.deployments_list())?; let deployment_names: Array = deployments .iter() .filter_map(|deployment| deployment.metadata.name.as_ref()) .map(|name| Dynamic::from(name.clone())) .collect(); Ok(deployment_names) } fn configmaps_list(km: &mut KubernetesManager) -> Result> { let configmaps = execute_async(km.configmaps_list())?; let configmap_names: Array = configmaps .iter() .filter_map(|configmap| configmap.metadata.name.as_ref()) .map(|name| Dynamic::from(name.clone())) .collect(); Ok(configmap_names) } fn secrets_list(km: &mut KubernetesManager) -> Result> { let secrets = execute_async(km.secrets_list())?; let secret_names: Array = secrets .iter() .filter_map(|secret| secret.metadata.name.as_ref()) .map(|name| Dynamic::from(name.clone())) .collect(); Ok(secret_names) } // Resource creation functions fn pod_create( km: &mut KubernetesManager, name: String, image: String, labels: Map, ) -> Result> { let labels_map: Option> = if labels.is_empty() { None } else { Some( labels .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(), ) }; let pod = execute_async(km.pod_create(&name, &image, labels_map, None))?; Ok(pod.metadata.name.unwrap_or(name)) } fn pod_create_with_env( km: &mut KubernetesManager, name: String, image: String, labels: Map, env_vars: Map, ) -> Result> { let labels_map: Option> = if labels.is_empty() { None } else { Some( labels .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(), ) }; let env_vars_map = convert_rhai_map_to_env_vars(env_vars); let pod = execute_async(km.pod_create(&name, &image, labels_map, env_vars_map))?; Ok(pod.metadata.name.unwrap_or(name)) } fn service_create( km: &mut KubernetesManager, name: String, selector: Map, port: i64, target_port: i64, ) -> Result> { let selector_map: std::collections::HashMap = selector .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); let target_port_opt = if target_port == 0 { None } else { Some(target_port as i32) }; let service = execute_async(km.service_create(&name, selector_map, port as i32, target_port_opt))?; Ok(service.metadata.name.unwrap_or(name)) } fn deployment_create( km: &mut KubernetesManager, name: String, image: String, replicas: i64, labels: Map, env_vars: Map, ) -> Result> { let labels_map: Option> = if labels.is_empty() { None } else { Some( labels .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(), ) }; let env_vars_map = convert_rhai_map_to_env_vars(env_vars); let deployment = execute_async(km.deployment_create( &name, &image, replicas as i32, labels_map, env_vars_map, ))?; Ok(deployment.metadata.name.unwrap_or(name)) } fn configmap_create( km: &mut KubernetesManager, name: String, data: Map, ) -> Result> { let data_map: std::collections::HashMap = data .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); let configmap = execute_async(km.configmap_create(&name, data_map))?; Ok(configmap.metadata.name.unwrap_or(name)) } fn secret_create( km: &mut KubernetesManager, name: String, data: Map, secret_type: String, ) -> Result> { let data_map: std::collections::HashMap = data .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); let secret_type_opt = if secret_type.is_empty() { None } else { Some(secret_type.as_str()) }; let secret = execute_async(km.secret_create(&name, data_map, secret_type_opt))?; Ok(secret.metadata.name.unwrap_or(name)) } // Resource get functions fn pod_get(km: &mut KubernetesManager, name: String) -> Result> { let pod = execute_async(km.pod_get(&name))?; Ok(pod.metadata.name.unwrap_or(name)) } fn service_get(km: &mut KubernetesManager, name: String) -> Result> { let service = execute_async(km.service_get(&name))?; Ok(service.metadata.name.unwrap_or(name)) } fn deployment_get(km: &mut KubernetesManager, name: String) -> Result> { let deployment = execute_async(km.deployment_get(&name))?; Ok(deployment.metadata.name.unwrap_or(name)) } // Resource deletion functions fn delete(km: &mut KubernetesManager, pattern: String) -> Result> { let deleted_count = execute_async(km.delete(&pattern))?; Ok(deleted_count as i64) } fn pod_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box> { execute_async(km.pod_delete(&name)) } fn service_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box> { execute_async(km.service_delete(&name)) } fn deployment_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box> { execute_async(km.deployment_delete(&name)) } fn configmap_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box> { execute_async(km.configmap_delete(&name)) } fn secret_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box> { execute_async(km.secret_delete(&name)) } // Namespace management functions fn namespace_create(km: &mut KubernetesManager, name: String) -> Result<(), Box> { execute_async(km.namespace_create(&name)) } fn namespace_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box> { execute_async(km.namespace_delete(&name)) } fn namespace_exists(km: &mut KubernetesManager, name: String) -> Result> { execute_async(km.namespace_exists(&name)) } fn namespaces_list(km: &mut KubernetesManager) -> Result> { let namespaces = execute_async(km.namespaces_list())?; let namespace_names: Array = namespaces .iter() .filter_map(|ns| ns.metadata.name.as_ref()) .map(|name| Dynamic::from(name.clone())) .collect(); Ok(namespace_names) } // Utility and convenience functions fn resource_counts(km: &mut KubernetesManager) -> Result> { let counts = execute_async(km.resource_counts())?; let mut rhai_map = Map::new(); for (key, value) in counts { rhai_map.insert(key.into(), Dynamic::from(value as i64)); } Ok(rhai_map) } fn deploy_application( km: &mut KubernetesManager, name: String, image: String, replicas: i64, port: i64, labels: Map, env_vars: Map, ) -> Result> { let labels_map: Option> = if labels.is_empty() { None } else { Some( labels .into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(), ) }; let env_vars_map = convert_rhai_map_to_env_vars(env_vars); execute_async(km.deploy_application( &name, &image, replicas as i32, port as i32, labels_map, env_vars_map, ))?; Ok(format!("Successfully deployed application '{name}'")) }