420 lines
14 KiB
Rust
420 lines
14 KiB
Rust
//! 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<Mutex<Runtime>> =
|
|
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<std::collections::HashMap<String, String>>` - Converted HashMap or None if empty
|
|
fn convert_rhai_map_to_env_vars(
|
|
rhai_map: Map,
|
|
) -> Option<std::collections::HashMap<String, String>> {
|
|
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<F, T>(future: F) -> Result<T, Box<EvalAltResult>>
|
|
where
|
|
F: std::future::Future<Output = Result<T, KubernetesError>>,
|
|
{
|
|
// 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<EvalAltResult> {
|
|
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<EvalAltResult>>` - Ok if registration was successful, Err otherwise
|
|
pub fn register_kubernetes_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
|
// Register KubernetesManager type
|
|
engine.register_type::<KubernetesManager>();
|
|
|
|
// 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<KubernetesManager, Box<EvalAltResult>> {
|
|
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<Array, Box<EvalAltResult>> {
|
|
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<Array, Box<EvalAltResult>> {
|
|
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<Array, Box<EvalAltResult>> {
|
|
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<Array, Box<EvalAltResult>> {
|
|
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<Array, Box<EvalAltResult>> {
|
|
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<String, Box<EvalAltResult>> {
|
|
let labels_map: Option<std::collections::HashMap<String, String>> = 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<String, Box<EvalAltResult>> {
|
|
let labels_map: Option<std::collections::HashMap<String, String>> = 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<String, Box<EvalAltResult>> {
|
|
let selector_map: std::collections::HashMap<String, String> = 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<String, Box<EvalAltResult>> {
|
|
let labels_map: Option<std::collections::HashMap<String, String>> = 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<String, Box<EvalAltResult>> {
|
|
let data_map: std::collections::HashMap<String, String> = 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<String, Box<EvalAltResult>> {
|
|
let data_map: std::collections::HashMap<String, String> = 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<String, Box<EvalAltResult>> {
|
|
let pod = execute_async(km.pod_get(&name))?;
|
|
Ok(pod.metadata.name.unwrap_or(name))
|
|
}
|
|
|
|
fn service_get(km: &mut KubernetesManager, name: String) -> Result<String, Box<EvalAltResult>> {
|
|
let service = execute_async(km.service_get(&name))?;
|
|
Ok(service.metadata.name.unwrap_or(name))
|
|
}
|
|
|
|
fn deployment_get(km: &mut KubernetesManager, name: String) -> Result<String, Box<EvalAltResult>> {
|
|
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<i64, Box<EvalAltResult>> {
|
|
let deleted_count = execute_async(km.delete(&pattern))?;
|
|
Ok(deleted_count as i64)
|
|
}
|
|
|
|
fn pod_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box<EvalAltResult>> {
|
|
execute_async(km.pod_delete(&name))
|
|
}
|
|
|
|
fn service_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box<EvalAltResult>> {
|
|
execute_async(km.service_delete(&name))
|
|
}
|
|
|
|
fn deployment_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box<EvalAltResult>> {
|
|
execute_async(km.deployment_delete(&name))
|
|
}
|
|
|
|
fn configmap_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box<EvalAltResult>> {
|
|
execute_async(km.configmap_delete(&name))
|
|
}
|
|
|
|
fn secret_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box<EvalAltResult>> {
|
|
execute_async(km.secret_delete(&name))
|
|
}
|
|
|
|
// Namespace management functions
|
|
fn namespace_create(km: &mut KubernetesManager, name: String) -> Result<(), Box<EvalAltResult>> {
|
|
execute_async(km.namespace_create(&name))
|
|
}
|
|
|
|
fn namespace_delete(km: &mut KubernetesManager, name: String) -> Result<(), Box<EvalAltResult>> {
|
|
execute_async(km.namespace_delete(&name))
|
|
}
|
|
|
|
fn namespace_exists(km: &mut KubernetesManager, name: String) -> Result<bool, Box<EvalAltResult>> {
|
|
execute_async(km.namespace_exists(&name))
|
|
}
|
|
|
|
fn namespaces_list(km: &mut KubernetesManager) -> Result<Array, Box<EvalAltResult>> {
|
|
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<Map, Box<EvalAltResult>> {
|
|
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<String, Box<EvalAltResult>> {
|
|
let labels_map: Option<std::collections::HashMap<String, String>> = 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}'"))
|
|
}
|