implemented zinit-client for integration with Rhai-scripts
This commit is contained in:
		@@ -27,6 +27,10 @@ log = "0.4"        # Logging facade
 | 
			
		||||
rhai = { version = "1.12.0", features = ["sync"] } # Embedded scripting language
 | 
			
		||||
rand = "0.8.5"     # Random number generation
 | 
			
		||||
clap = "2.33"      # Command-line argument parsing
 | 
			
		||||
zinit-client = { git = "https://github.com/threefoldtech/zinit", branch = "json_rpc",  package = "zinit-client" }
 | 
			
		||||
anyhow = "1.0.98"
 | 
			
		||||
jsonrpsee = "0.25.1"
 | 
			
		||||
tokio = "1.45.0"
 | 
			
		||||
 | 
			
		||||
# Optional features for specific OS functionality
 | 
			
		||||
[target.'cfg(unix)'.dependencies]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										70
									
								
								examples/zinit/zinit_basic.rhai
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								examples/zinit/zinit_basic.rhai
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
// Basic example of using the Zinit client in Rhai
 | 
			
		||||
 | 
			
		||||
// Socket path for Zinit
 | 
			
		||||
let socket_path = "/var/run/zinit.sock";
 | 
			
		||||
 | 
			
		||||
// List all services
 | 
			
		||||
print("Listing all services:");
 | 
			
		||||
let services = zinit_list(socket_path);
 | 
			
		||||
 | 
			
		||||
for (name, state) in services {
 | 
			
		||||
    print(`${name}: ${state}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get status of a specific service
 | 
			
		||||
let service_name = "example-service";
 | 
			
		||||
print(`\nGetting status for ${service_name}:`);
 | 
			
		||||
 | 
			
		||||
try {
 | 
			
		||||
    let status = zinit_status(socket_path, service_name);
 | 
			
		||||
    print(`Service: ${status.name}`);
 | 
			
		||||
    print(`PID: ${status.pid}`);
 | 
			
		||||
    print(`State: ${status.state}`);
 | 
			
		||||
    print(`Target: ${status.target}`);
 | 
			
		||||
    print("Dependencies:");
 | 
			
		||||
    
 | 
			
		||||
    for (dep, state) in status.after {
 | 
			
		||||
        print(`  ${dep}: ${state}`);
 | 
			
		||||
    }
 | 
			
		||||
} catch(err) {
 | 
			
		||||
    print(`Error getting status: ${err}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a new service
 | 
			
		||||
print("\nCreating a new service:");
 | 
			
		||||
let new_service = "rhai-test-service";
 | 
			
		||||
let exec_command = "echo 'Hello from Rhai'";
 | 
			
		||||
let oneshot = true;
 | 
			
		||||
 | 
			
		||||
try {
 | 
			
		||||
    let result = zinit_create_service(socket_path, new_service, exec_command, oneshot);
 | 
			
		||||
    print(`Service created: ${result}`);
 | 
			
		||||
    
 | 
			
		||||
    // Monitor the service
 | 
			
		||||
    print("\nMonitoring the service:");
 | 
			
		||||
    let monitor_result = zinit_monitor(socket_path, new_service);
 | 
			
		||||
    print(`Service monitored: ${monitor_result}`);
 | 
			
		||||
    
 | 
			
		||||
    // Start the service
 | 
			
		||||
    print("\nStarting the service:");
 | 
			
		||||
    let start_result = zinit_start(socket_path, new_service);
 | 
			
		||||
    print(`Service started: ${start_result}`);
 | 
			
		||||
    
 | 
			
		||||
    // Get logs
 | 
			
		||||
    print("\nGetting logs:");
 | 
			
		||||
    let logs = zinit_logs(socket_path, new_service);
 | 
			
		||||
    
 | 
			
		||||
    for log in logs {
 | 
			
		||||
        print(log);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Clean up
 | 
			
		||||
    print("\nCleaning up:");
 | 
			
		||||
    let forget_result = zinit_forget(socket_path, new_service);
 | 
			
		||||
    print(`Service forgotten: ${forget_result}`);
 | 
			
		||||
    
 | 
			
		||||
    let delete_result = zinit_delete_service(socket_path, new_service);
 | 
			
		||||
    print(`Service deleted: ${delete_result}`);
 | 
			
		||||
} catch(err) {
 | 
			
		||||
    print(`Error: ${err}`);
 | 
			
		||||
}
 | 
			
		||||
@@ -44,6 +44,7 @@ pub mod text;
 | 
			
		||||
pub mod virt;
 | 
			
		||||
pub mod rhai;
 | 
			
		||||
pub mod cmd;
 | 
			
		||||
pub mod zinit_client;
 | 
			
		||||
 | 
			
		||||
// Version information
 | 
			
		||||
/// Returns the version of the SAL library
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ mod nerdctl;
 | 
			
		||||
mod git;
 | 
			
		||||
mod text;
 | 
			
		||||
mod rfs;
 | 
			
		||||
mod zinit;
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests;
 | 
			
		||||
@@ -60,6 +61,9 @@ pub use rfs::register as register_rfs_module;
 | 
			
		||||
pub use git::register_git_module;
 | 
			
		||||
pub use crate::git::{GitTree, GitRepo};
 | 
			
		||||
 | 
			
		||||
// Re-export zinit module
 | 
			
		||||
pub use zinit::register_zinit_module;
 | 
			
		||||
 | 
			
		||||
// Re-export text module
 | 
			
		||||
pub use text::register_text_module;
 | 
			
		||||
// Re-export text functions directly from text module
 | 
			
		||||
@@ -110,6 +114,9 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
 | 
			
		||||
    // Register Git module functions
 | 
			
		||||
    git::register_git_module(engine)?;
 | 
			
		||||
    
 | 
			
		||||
    // Register Zinit module functions
 | 
			
		||||
    zinit::register_zinit_module(engine)?;
 | 
			
		||||
    
 | 
			
		||||
    // Register Text module functions
 | 
			
		||||
    text::register_text_module(engine)?;
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										326
									
								
								src/rhai/zinit.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								src/rhai/zinit.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,326 @@
 | 
			
		||||
//! Rhai wrappers for Zinit client module functions
 | 
			
		||||
//!
 | 
			
		||||
//! This module provides Rhai wrappers for the functions in the Zinit client module.
 | 
			
		||||
 | 
			
		||||
use rhai::{Engine, EvalAltResult, Array, Dynamic, Map};
 | 
			
		||||
use crate::zinit_client as client;
 | 
			
		||||
use tokio::runtime::Runtime;
 | 
			
		||||
use serde_json::{json, Value};
 | 
			
		||||
use crate::rhai::error::ToRhaiError;
 | 
			
		||||
 | 
			
		||||
/// Register Zinit 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_zinit_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
 | 
			
		||||
    // Register Zinit client functions
 | 
			
		||||
    engine.register_fn("zinit_list", zinit_list);
 | 
			
		||||
    engine.register_fn("zinit_status", zinit_status);
 | 
			
		||||
    engine.register_fn("zinit_start", zinit_start);
 | 
			
		||||
    engine.register_fn("zinit_stop", zinit_stop);
 | 
			
		||||
    engine.register_fn("zinit_restart", zinit_restart);
 | 
			
		||||
    engine.register_fn("zinit_monitor", zinit_monitor);
 | 
			
		||||
    engine.register_fn("zinit_forget", zinit_forget);
 | 
			
		||||
    engine.register_fn("zinit_kill", zinit_kill);
 | 
			
		||||
    engine.register_fn("zinit_create_service", zinit_create_service);
 | 
			
		||||
    engine.register_fn("zinit_delete_service", zinit_delete_service);
 | 
			
		||||
    engine.register_fn("zinit_get_service", zinit_get_service);
 | 
			
		||||
    engine.register_fn("zinit_logs", zinit_logs);
 | 
			
		||||
    
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> ToRhaiError<T> for Result<T, zinit_client::ClientError> {
 | 
			
		||||
    fn to_rhai_error(self) -> Result<T, Box<EvalAltResult>> {
 | 
			
		||||
        self.map_err(|e| {
 | 
			
		||||
            Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                format!("Zinit error: {}", e).into(),
 | 
			
		||||
                rhai::Position::NONE
 | 
			
		||||
            ))
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper function to get a runtime
 | 
			
		||||
fn get_runtime() -> Result<Runtime, Box<EvalAltResult>> {
 | 
			
		||||
    tokio::runtime::Runtime::new().map_err(|e| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
            format!("Failed to create Tokio runtime: {}", e).into(),
 | 
			
		||||
            rhai::Position::NONE
 | 
			
		||||
        ))
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Zinit Client Function Wrappers
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::list
 | 
			
		||||
///
 | 
			
		||||
/// Lists all services managed by Zinit.
 | 
			
		||||
pub fn zinit_list(socket_path: &str) -> Result<Map, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    println!("got runtime: {:?}", rt);
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        client::list(socket_path).await
 | 
			
		||||
    });
 | 
			
		||||
    println!("got result: {:?}", result);
 | 
			
		||||
    
 | 
			
		||||
    let services = result.to_rhai_error()?;
 | 
			
		||||
    println!("got services: {:?}", services);
 | 
			
		||||
    
 | 
			
		||||
    // Convert HashMap<String, String> to Rhai Map
 | 
			
		||||
    let mut map = Map::new();
 | 
			
		||||
    for (name, state) in services {
 | 
			
		||||
        map.insert(name.into(), Dynamic::from(state));
 | 
			
		||||
    }
 | 
			
		||||
    println!("got map: {:?}", map);
 | 
			
		||||
    
 | 
			
		||||
    Ok(map)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::status
 | 
			
		||||
///
 | 
			
		||||
/// Gets the status of a specific service.
 | 
			
		||||
pub fn zinit_status(socket_path: &str, name: &str) -> Result<Map, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        client::status(socket_path, name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    let status = result.to_rhai_error()?;
 | 
			
		||||
    
 | 
			
		||||
    // Convert Status to Rhai Map
 | 
			
		||||
    let mut map = Map::new();
 | 
			
		||||
    map.insert("name".into(), Dynamic::from(status.name));
 | 
			
		||||
    map.insert("pid".into(), Dynamic::from(status.pid));
 | 
			
		||||
    map.insert("state".into(), Dynamic::from(status.state));
 | 
			
		||||
    map.insert("target".into(), Dynamic::from(status.target));
 | 
			
		||||
    
 | 
			
		||||
    // Convert dependencies
 | 
			
		||||
    let mut deps_map = Map::new();
 | 
			
		||||
    for (dep, state) in status.after {
 | 
			
		||||
        deps_map.insert(dep.into(), Dynamic::from(state));
 | 
			
		||||
    }
 | 
			
		||||
    map.insert("after".into(), Dynamic::from_map(deps_map));
 | 
			
		||||
    
 | 
			
		||||
    Ok(map)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::start
 | 
			
		||||
///
 | 
			
		||||
/// Starts a service.
 | 
			
		||||
pub fn zinit_start(socket_path: &str, name: &str) -> Result<bool, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        client::start(socket_path, name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()?;
 | 
			
		||||
    Ok(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::stop
 | 
			
		||||
///
 | 
			
		||||
/// Stops a service.
 | 
			
		||||
pub fn zinit_stop(socket_path: &str, name: &str) -> Result<bool, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        client::stop(socket_path, name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()?;
 | 
			
		||||
    Ok(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::restart
 | 
			
		||||
///
 | 
			
		||||
/// Restarts a service.
 | 
			
		||||
pub fn zinit_restart(socket_path: &str, name: &str) -> Result<bool, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        client::restart(socket_path, name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()?;
 | 
			
		||||
    Ok(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::monitor
 | 
			
		||||
///
 | 
			
		||||
/// Starts monitoring a service.
 | 
			
		||||
pub fn zinit_monitor(socket_path: &str, name: &str) -> Result<bool, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        let client = client::get_zinit_client(socket_path).await?;
 | 
			
		||||
        client.monitor(name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()?;
 | 
			
		||||
    Ok(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::forget
 | 
			
		||||
///
 | 
			
		||||
/// Stops monitoring a service.
 | 
			
		||||
pub fn zinit_forget(socket_path: &str, name: &str) -> Result<bool, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        let client = client::get_zinit_client(socket_path).await?;
 | 
			
		||||
        client.forget(name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()?;
 | 
			
		||||
    Ok(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::kill
 | 
			
		||||
///
 | 
			
		||||
/// Sends a signal to a service.
 | 
			
		||||
pub fn zinit_kill(socket_path: &str, name: &str, signal: &str) -> Result<bool, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        let client = client::get_zinit_client(socket_path).await?;
 | 
			
		||||
        client.kill(name, signal).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()?;
 | 
			
		||||
    Ok(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::create_service
 | 
			
		||||
///
 | 
			
		||||
/// Creates a new service.
 | 
			
		||||
pub fn zinit_create_service(socket_path: &str, name: &str, exec: &str, oneshot: bool) -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    // Create service configuration
 | 
			
		||||
    let content = serde_json::from_value(json!({
 | 
			
		||||
        "exec": exec,
 | 
			
		||||
        "oneshot": oneshot
 | 
			
		||||
    })).map_err(|e| {
 | 
			
		||||
        Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
            format!("Failed to create service configuration: {}", e).into(),
 | 
			
		||||
            rhai::Position::NONE
 | 
			
		||||
        ))
 | 
			
		||||
    })?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        let client = client::get_zinit_client(socket_path).await?;
 | 
			
		||||
        client.create_service(name, content).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::delete_service
 | 
			
		||||
///
 | 
			
		||||
/// Deletes a service.
 | 
			
		||||
pub fn zinit_delete_service(socket_path: &str, name: &str) -> Result<String, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        let client = client::get_zinit_client(socket_path).await?;
 | 
			
		||||
        client.delete_service(name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    result.to_rhai_error()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::get_service
 | 
			
		||||
///
 | 
			
		||||
/// Gets a service configuration.
 | 
			
		||||
pub fn zinit_get_service(socket_path: &str, name: &str) -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        let client = client::get_zinit_client(socket_path).await?;
 | 
			
		||||
        client.get_service(name).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    let value = result.to_rhai_error()?;
 | 
			
		||||
    
 | 
			
		||||
    // Convert Value to Dynamic
 | 
			
		||||
    match value {
 | 
			
		||||
        Value::Object(map) => {
 | 
			
		||||
            let mut rhai_map = Map::new();
 | 
			
		||||
            for (k, v) in map {
 | 
			
		||||
                rhai_map.insert(k.into(), value_to_dynamic(v));
 | 
			
		||||
            }
 | 
			
		||||
            Ok(Dynamic::from_map(rhai_map))
 | 
			
		||||
        },
 | 
			
		||||
        _ => Err(Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
            "Expected object from get_service".into(),
 | 
			
		||||
            rhai::Position::NONE
 | 
			
		||||
        )))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper for zinit_client::logs
 | 
			
		||||
///
 | 
			
		||||
/// Gets logs for a service.
 | 
			
		||||
pub fn zinit_logs(socket_path: &str, filter: Option<&str>) -> Result<Array, Box<EvalAltResult>> {
 | 
			
		||||
    let rt = get_runtime()?;
 | 
			
		||||
    
 | 
			
		||||
    let filter_string = filter.map(|s| s.to_string());
 | 
			
		||||
    
 | 
			
		||||
    let result = rt.block_on(async {
 | 
			
		||||
        let client = client::get_zinit_client(socket_path).await?;
 | 
			
		||||
        client.logs(filter_string).await
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    let logs = result.to_rhai_error()?;
 | 
			
		||||
    
 | 
			
		||||
    // Convert Vec<String> to Rhai Array
 | 
			
		||||
    let mut array = Array::new();
 | 
			
		||||
    for log in logs {
 | 
			
		||||
        array.push(Dynamic::from(log));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Ok(array)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper function to convert serde_json::Value to rhai::Dynamic
 | 
			
		||||
fn value_to_dynamic(value: Value) -> Dynamic {
 | 
			
		||||
    match value {
 | 
			
		||||
        Value::Null => Dynamic::UNIT,
 | 
			
		||||
        Value::Bool(b) => Dynamic::from(b),
 | 
			
		||||
        Value::Number(n) => {
 | 
			
		||||
            if let Some(i) = n.as_i64() {
 | 
			
		||||
                Dynamic::from(i)
 | 
			
		||||
            } else if let Some(f) = n.as_f64() {
 | 
			
		||||
                Dynamic::from(f)
 | 
			
		||||
            } else {
 | 
			
		||||
                Dynamic::from(n.to_string())
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Value::String(s) => Dynamic::from(s),
 | 
			
		||||
        Value::Array(arr) => {
 | 
			
		||||
            let mut rhai_arr = Array::new();
 | 
			
		||||
            for item in arr {
 | 
			
		||||
                rhai_arr.push(value_to_dynamic(item));
 | 
			
		||||
            }
 | 
			
		||||
            Dynamic::from(rhai_arr)
 | 
			
		||||
        },
 | 
			
		||||
        Value::Object(map) => {
 | 
			
		||||
            let mut rhai_map = Map::new();
 | 
			
		||||
            for (k, v) in map {
 | 
			
		||||
                rhai_map.insert(k.into(), value_to_dynamic(v));
 | 
			
		||||
            }
 | 
			
		||||
            Dynamic::from_map(rhai_map)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								src/zinit_client/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								src/zinit_client/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
use std::sync::{Arc, Mutex, Once};
 | 
			
		||||
use std::sync::atomic::{AtomicBool, Ordering};
 | 
			
		||||
use lazy_static::lazy_static;
 | 
			
		||||
use zinit_client::{Client as ZinitClient, ClientError, Status};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use serde_json::{Map, Value};
 | 
			
		||||
 | 
			
		||||
// Global Zinit client instance using lazy_static
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    static ref ZINIT_CLIENT: Mutex<Option<Arc<ZinitClientWrapper>>> = Mutex::new(None);
 | 
			
		||||
    static ref INIT: Once = Once::new();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Wrapper for Zinit client to handle connection
 | 
			
		||||
pub struct ZinitClientWrapper {
 | 
			
		||||
    client: ZinitClient,
 | 
			
		||||
    initialized: AtomicBool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ZinitClientWrapper {
 | 
			
		||||
    // Create a new Zinit client wrapper
 | 
			
		||||
    fn new(client: ZinitClient) -> Self {
 | 
			
		||||
        ZinitClientWrapper {
 | 
			
		||||
            client,
 | 
			
		||||
            initialized: AtomicBool::new(false),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Initialize the client
 | 
			
		||||
    async fn initialize(&self) -> Result<(), ClientError> {
 | 
			
		||||
        if self.initialized.load(Ordering::Relaxed) {
 | 
			
		||||
            return Ok(());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Try to list services to check if the connection works
 | 
			
		||||
        let _ = self.client.list().await.map_err(|e| {
 | 
			
		||||
            eprintln!("Failed to initialize Zinit client: {}", e);
 | 
			
		||||
            e
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        self.initialized.store(true, Ordering::Relaxed);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // List all services
 | 
			
		||||
    pub async fn list(&self) -> Result<HashMap<String, String>, ClientError> {
 | 
			
		||||
        self.client.list().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get status of a service
 | 
			
		||||
    pub async fn status(&self, name: &str) -> Result<Status, ClientError> {
 | 
			
		||||
        self.client.status(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Start a service
 | 
			
		||||
    pub async fn start(&self, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.start(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Stop a service
 | 
			
		||||
    pub async fn stop(&self, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.stop(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Restart a service
 | 
			
		||||
    pub async fn restart(&self, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.restart(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Monitor a service
 | 
			
		||||
    pub async fn monitor(&self, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.monitor(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Forget a service
 | 
			
		||||
    pub async fn forget(&self, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.forget(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Send a signal to a service
 | 
			
		||||
    pub async fn kill(&self, name: &str, signal: &str) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.kill(name, signal).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create a new service
 | 
			
		||||
    pub async fn create_service(&self, name: &str, content: Map<String, Value>) -> Result<String, ClientError> {
 | 
			
		||||
        self.client.create_service(name, content).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Delete a service
 | 
			
		||||
    pub async fn delete_service(&self, name: &str) -> Result<String, ClientError> {
 | 
			
		||||
        self.client.delete_service(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get a service configuration
 | 
			
		||||
    pub async fn get_service(&self, name: &str) -> Result<Value, ClientError> {
 | 
			
		||||
        self.client.get_service(name).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Shutdown the system
 | 
			
		||||
    pub async fn shutdown(&self) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.shutdown().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Reboot the system
 | 
			
		||||
    pub async fn reboot(&self) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.reboot().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Start HTTP server
 | 
			
		||||
    pub async fn start_http_server(&self, address: &str) -> Result<String, ClientError> {
 | 
			
		||||
        self.client.start_http_server(address).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Stop HTTP server
 | 
			
		||||
    pub async fn stop_http_server(&self) -> Result<(), ClientError> {
 | 
			
		||||
        self.client.stop_http_server().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get logs
 | 
			
		||||
    pub async fn logs(&self, filter: Option<String>) -> Result<Vec<String>, ClientError> {
 | 
			
		||||
        self.client.logs(filter).await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the Zinit client instance
 | 
			
		||||
pub async fn get_zinit_client(socket_path: &str) -> Result<Arc<ZinitClientWrapper>, ClientError> {
 | 
			
		||||
    // Check if we already have a client
 | 
			
		||||
    {
 | 
			
		||||
        let guard = ZINIT_CLIENT.lock().unwrap();
 | 
			
		||||
        if let Some(ref client) = &*guard {
 | 
			
		||||
            return Ok(Arc::clone(client));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Create a new client
 | 
			
		||||
    let client = create_zinit_client(socket_path).await?;
 | 
			
		||||
    
 | 
			
		||||
    // Store the client globally
 | 
			
		||||
    {
 | 
			
		||||
        let mut guard = ZINIT_CLIENT.lock().unwrap();
 | 
			
		||||
        *guard = Some(Arc::clone(&client));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Ok(client)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a new Zinit client
 | 
			
		||||
async fn create_zinit_client(socket_path: &str) -> Result<Arc<ZinitClientWrapper>, ClientError> {
 | 
			
		||||
    // Connect via Unix socket
 | 
			
		||||
    let client = ZinitClient::unix_socket(socket_path).await?;
 | 
			
		||||
    let wrapper = Arc::new(ZinitClientWrapper::new(client));
 | 
			
		||||
    
 | 
			
		||||
    // Initialize the client
 | 
			
		||||
    wrapper.initialize().await?;
 | 
			
		||||
    
 | 
			
		||||
    Ok(wrapper)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset the Zinit client
 | 
			
		||||
pub async fn reset(socket_path: &str) -> Result<(), ClientError> {
 | 
			
		||||
    // Clear the existing client
 | 
			
		||||
    {
 | 
			
		||||
        let mut client_guard = ZINIT_CLIENT.lock().unwrap();
 | 
			
		||||
        *client_guard = None;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Create a new client, only return error if it fails
 | 
			
		||||
    get_zinit_client(socket_path).await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convenience functions for common operations
 | 
			
		||||
 | 
			
		||||
// List all services
 | 
			
		||||
pub async fn list(socket_path: &str) -> Result<HashMap<String, String>, ClientError> {
 | 
			
		||||
    let client = get_zinit_client(socket_path).await?;
 | 
			
		||||
    client.list().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get status of a service
 | 
			
		||||
pub async fn status(socket_path: &str, name: &str) -> Result<Status, ClientError> {
 | 
			
		||||
    let client = get_zinit_client(socket_path).await?;
 | 
			
		||||
    client.status(name).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start a service
 | 
			
		||||
pub async fn start(socket_path: &str, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
    let client = get_zinit_client(socket_path).await?;
 | 
			
		||||
    client.start(name).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop a service
 | 
			
		||||
pub async fn stop(socket_path: &str, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
    let client = get_zinit_client(socket_path).await?;
 | 
			
		||||
    client.stop(name).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restart a service
 | 
			
		||||
pub async fn restart(socket_path: &str, name: &str) -> Result<(), ClientError> {
 | 
			
		||||
    let client = get_zinit_client(socket_path).await?;
 | 
			
		||||
    client.restart(name).await
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user