feat: Add zinit_client package to workspace
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
- Add `zinit_client` package to the workspace, enabling its use in the SAL monorepo. This allows for better organization and dependency management. - Update `MONOREPO_CONVERSION_PLAN.md` to reflect the addition of `zinit_client` and its status. This ensures the conversion plan stays up-to-date. - Move `src/zinit_client/` directory to `zinit_client/` for better organization. This improves the overall structure of the project. - Update references to `zinit_client` to use the new path. This ensures the codebase correctly links to the `zinit_client` package.
This commit is contained in:
@@ -48,7 +48,7 @@ pub mod rhai;
|
||||
pub use sal_text as text;
|
||||
pub mod vault;
|
||||
pub mod virt;
|
||||
pub mod zinit_client;
|
||||
pub use sal_zinit_client as zinit_client;
|
||||
|
||||
// Version information
|
||||
/// Returns the version of the SAL library
|
||||
|
@@ -15,7 +15,7 @@ mod process;
|
||||
mod rfs;
|
||||
mod screen;
|
||||
mod vault;
|
||||
mod zinit;
|
||||
// zinit module is now in sal-zinit-client package
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -93,8 +93,8 @@ pub use rfs::register as register_rfs_module;
|
||||
pub use sal_git::rhai::register_git_module;
|
||||
pub use sal_git::{GitRepo, GitTree};
|
||||
|
||||
// Re-export zinit module
|
||||
pub use zinit::register_zinit_module;
|
||||
// Re-export zinit module from sal-zinit-client package
|
||||
pub use sal_zinit_client::rhai::register_zinit_module;
|
||||
|
||||
// Re-export mycelium module
|
||||
pub use sal_mycelium::rhai::register_mycelium_module;
|
||||
@@ -150,7 +150,7 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
sal_git::rhai::register_git_module(engine)?;
|
||||
|
||||
// Register Zinit module functions
|
||||
zinit::register_zinit_module(engine)?;
|
||||
sal_zinit_client::rhai::register_zinit_module(engine)?;
|
||||
|
||||
// Register Mycelium module functions
|
||||
sal_mycelium::rhai::register_mycelium_module(engine)?;
|
||||
|
@@ -1,342 +0,0 @@
|
||||
//! Rhai wrappers for Zinit client module functions
|
||||
//!
|
||||
//! This module provides Rhai wrappers for the functions in the Zinit client module.
|
||||
|
||||
use crate::rhai::error::ToRhaiError;
|
||||
use crate::zinit_client as client;
|
||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
|
||||
use std::path::Path;
|
||||
use serde_json::{json, Value};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
/// 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);
|
||||
engine.register_fn("zinit_logs_all", zinit_logs_all);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
// 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>> {
|
||||
if !Path::new(socket_path).exists() {
|
||||
return Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||
format!("Zinit socket not found at '{}'", socket_path).into(),
|
||||
rhai::Position::NONE,
|
||||
)));
|
||||
}
|
||||
let rt = get_runtime()?;
|
||||
|
||||
let result = rt.block_on(async { client::list(socket_path).await });
|
||||
|
||||
let services = result.to_rhai_error()?;
|
||||
|
||||
// Convert HashMap<String, String> to Rhai Map
|
||||
let mut map = Map::new();
|
||||
for (name, state) in services {
|
||||
map.insert(name.into(), Dynamic::from(state));
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
// Convert () result to success message
|
||||
result.to_rhai_error()?;
|
||||
Ok(format!("Service '{}' created successfully", name))
|
||||
}
|
||||
|
||||
/// 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
|
||||
});
|
||||
|
||||
// Convert () result to success message
|
||||
result.to_rhai_error()?;
|
||||
Ok(format!("Service '{}' deleted successfully", name))
|
||||
}
|
||||
|
||||
/// 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 with a filter
|
||||
///
|
||||
/// Gets logs for a specific service.
|
||||
pub fn zinit_logs(socket_path: &str, filter: &str) -> Result<Array, Box<EvalAltResult>> {
|
||||
let rt = get_runtime()?;
|
||||
|
||||
let filter_string = Some(filter.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)
|
||||
}
|
||||
|
||||
/// Wrapper for zinit_client::logs without a filter
|
||||
///
|
||||
/// Gets all logs.
|
||||
pub fn zinit_logs_all(socket_path: &str) -> Result<Array, Box<EvalAltResult>> {
|
||||
let rt = get_runtime()?;
|
||||
|
||||
let result = rt.block_on(async {
|
||||
let client = client::get_zinit_client(socket_path).await?;
|
||||
client.logs(None).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)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,163 +0,0 @@
|
||||
# SAL Zinit Client Module (`sal::zinit_client`)
|
||||
|
||||
## Overview
|
||||
|
||||
The `sal::zinit_client` module provides a Rust interface for interacting with a [Zinit](https://github.com/systeminit/zinit) process supervisor daemon. Zinit is a process and service manager for Linux systems, designed for simplicity and robustness.
|
||||
|
||||
This SAL module allows Rust applications and `herodo` Rhai scripts to:
|
||||
- List and manage Zinit services (get status, start, stop, restart, monitor, forget, kill).
|
||||
- Define and manage service configurations (create, delete, get).
|
||||
- Retrieve logs from Zinit.
|
||||
|
||||
The client communicates with the Zinit daemon over a Unix domain socket. All operations are performed asynchronously.
|
||||
|
||||
## Key Design Points
|
||||
|
||||
- **Async Operations**: Leverages `tokio` for asynchronous communication with the Zinit daemon, ensuring non-blocking calls suitable for concurrent applications.
|
||||
- **Unix Socket Communication**: Connects to the Zinit daemon via a specified Unix domain socket path (e.g., `/var/run/zinit.sock`).
|
||||
- **Global Client Instance**: Manages a global, lazily-initialized `Arc<ZinitClientWrapper>` to reuse the Zinit client connection across multiple calls within the same process, improving efficiency.
|
||||
- **Comprehensive Service Management**: Exposes a wide range of Zinit's service management capabilities, from basic lifecycle control to service definition and log retrieval.
|
||||
- **Rhai Scriptability**: A significant portion of the Zinit client's functionality is exposed to Rhai scripts via `herodo` through the `sal::rhai::zinit` bridge, enabling automation of service management tasks.
|
||||
- **Error Handling**: Converts errors from the underlying `zinit_client` crate into `zinit_client::ClientError`, which are then translated to `EvalAltResult` for Rhai, providing clear feedback.
|
||||
- **Simplified Rhai Interface**: For some operations like service creation, the Rhai interface offers a simplified parameter set compared to the direct Rust API for ease of use in scripts.
|
||||
|
||||
## Rhai Scripting with `herodo`
|
||||
|
||||
The `sal::zinit_client` module is scriptable via `herodo`. The following functions are available in Rhai, prefixed with `zinit_`. All functions require `socket_path` (String) as their first argument, specifying the path to the Zinit Unix domain socket.
|
||||
|
||||
- `zinit_list(socket_path: String) -> Map`
|
||||
- Lists all services managed by Zinit and their states.
|
||||
- Returns a map where keys are service names and values are their current states (e.g., "Running", "Stopped").
|
||||
|
||||
- `zinit_status(socket_path: String, name: String) -> Map`
|
||||
- Retrieves the detailed status of a specific service.
|
||||
- `name`: The name of the service.
|
||||
- Returns a map containing status details like PID, state, target state, and dependencies.
|
||||
|
||||
- `zinit_start(socket_path: String, name: String) -> bool`
|
||||
- Starts the specified service.
|
||||
- Returns `true` on success.
|
||||
|
||||
- `zinit_stop(socket_path: String, name: String) -> bool`
|
||||
- Stops the specified service.
|
||||
- Returns `true` on success.
|
||||
|
||||
- `zinit_restart(socket_path: String, name: String) -> bool`
|
||||
- Restarts the specified service.
|
||||
- Returns `true` on success.
|
||||
|
||||
- `zinit_monitor(socket_path: String, name: String) -> bool`
|
||||
- Enables monitoring for the specified service (Zinit will attempt to keep it running).
|
||||
- Returns `true` on success.
|
||||
|
||||
- `zinit_forget(socket_path: String, name: String) -> bool`
|
||||
- Disables monitoring for the specified service (Zinit will no longer attempt to restart it if it stops).
|
||||
- Returns `true` on success.
|
||||
|
||||
- `zinit_kill(socket_path: String, name: String, signal: String) -> bool`
|
||||
- Sends a specific signal (e.g., "TERM", "KILL", "HUP") to the specified service.
|
||||
- Returns `true` on success.
|
||||
|
||||
- `zinit_create_service(socket_path: String, name: String, exec: String, oneshot: bool) -> String`
|
||||
- Creates a new service configuration in Zinit.
|
||||
- `name`: The name for the new service.
|
||||
- `exec`: The command to execute for the service.
|
||||
- `oneshot`: A boolean indicating if the service is a one-shot task (true) or a long-running process (false).
|
||||
- Returns a confirmation message or an error.
|
||||
|
||||
- `zinit_delete_service(socket_path: String, name: String) -> String`
|
||||
- Deletes the specified service configuration from Zinit.
|
||||
- Returns a confirmation message or an error.
|
||||
|
||||
- `zinit_get_service(socket_path: String, name: String) -> Dynamic`
|
||||
- Retrieves the configuration of the specified service as a dynamic map.
|
||||
|
||||
- `zinit_logs(socket_path: String, filter: String) -> Array`
|
||||
- Retrieves logs for a specific service or component matching the filter.
|
||||
- `filter`: The name of the service/component to get logs for.
|
||||
- Returns an array of log lines.
|
||||
|
||||
- `zinit_logs_all(socket_path: String) -> Array`
|
||||
- Retrieves all available logs from Zinit.
|
||||
- Returns an array of log lines.
|
||||
|
||||
### Rhai Example
|
||||
|
||||
```rhai
|
||||
// Default Zinit socket path
|
||||
let zinit_socket = "/var/run/zinit.sock";
|
||||
|
||||
// Ensure Zinit is running and socket exists before running this script.
|
||||
|
||||
// List all services
|
||||
print("Listing Zinit services...");
|
||||
let services = zinit_list(zinit_socket);
|
||||
if services.is_ok() {
|
||||
print(`Services: ${services}`);
|
||||
} else {
|
||||
print(`Error listing services: ${services}`);
|
||||
// exit(); // Or handle error appropriately
|
||||
}
|
||||
|
||||
// Define a test service
|
||||
let service_name = "my_test_app";
|
||||
let service_exec = "/usr/bin/sleep 300"; // Example command
|
||||
|
||||
// Try to get service info first, to see if it exists
|
||||
let existing_service = zinit_get_service(zinit_socket, service_name);
|
||||
if !existing_service.is_ok() { // Assuming error means it doesn't exist or can't be fetched
|
||||
print(`\nService '${service_name}' not found or error. Attempting to create...`);
|
||||
let create_result = zinit_create_service(zinit_socket, service_name, service_exec, false);
|
||||
if create_result.is_ok() {
|
||||
print(`Service '${service_name}' created successfully.`);
|
||||
} else {
|
||||
print(`Error creating service '${service_name}': ${create_result}`);
|
||||
// exit();
|
||||
}
|
||||
} else {
|
||||
print(`\nService '${service_name}' already exists: ${existing_service}`);
|
||||
}
|
||||
|
||||
// Get status of the service
|
||||
print(`\nFetching status for '${service_name}'...`);
|
||||
let status = zinit_status(zinit_socket, service_name);
|
||||
if status.is_ok() {
|
||||
print(`Status for '${service_name}': ${status}`);
|
||||
// Example: Start if not running (simplified check)
|
||||
if status.state != "Running" && status.state != "Starting" {
|
||||
print(`Attempting to start '${service_name}'...`);
|
||||
zinit_start(zinit_socket, service_name);
|
||||
}
|
||||
} else {
|
||||
print(`Error fetching status for '${service_name}': ${status}`);
|
||||
}
|
||||
|
||||
// Get some logs for the service (if it produced any)
|
||||
// Note: Logs might be empty if service just started or hasn't output anything.
|
||||
print(`\nFetching logs for '${service_name}'...`);
|
||||
let logs = zinit_logs(zinit_socket, service_name);
|
||||
if logs.is_ok() {
|
||||
if logs.len() > 0 {
|
||||
print(`Logs for '${service_name}':`);
|
||||
for log_line in logs {
|
||||
print(` ${log_line}`);
|
||||
}
|
||||
} else {
|
||||
print(`No logs found for '${service_name}'.`);
|
||||
}
|
||||
} else {
|
||||
print(`Error fetching logs for '${service_name}': ${logs}`);
|
||||
}
|
||||
|
||||
// Example: Stop and delete the service (cleanup)
|
||||
// print(`\nStopping service '${service_name}'...`);
|
||||
// zinit_stop(zinit_socket, service_name);
|
||||
// print(`Forgetting service '${service_name}'...`);
|
||||
// zinit_forget(zinit_socket, service_name); // Stop monitoring before delete
|
||||
// print(`Deleting service '${service_name}'...`);
|
||||
// zinit_delete_service(zinit_socket, service_name);
|
||||
|
||||
print("\nZinit Rhai script finished.");
|
||||
```
|
||||
|
||||
This module provides a powerful way to automate service management and interaction with Zinit-supervised systems directly from Rust or `herodo` scripts.
|
@@ -1,209 +0,0 @@
|
||||
use lazy_static::lazy_static;
|
||||
use serde_json::{Map, Value};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, Once};
|
||||
use zinit_client::{ServiceState, ServiceStatus as Status, ZinitClient, ZinitError};
|
||||
|
||||
// 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<(), ZinitError> {
|
||||
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, ServiceState>, ZinitError> {
|
||||
self.client.list().await
|
||||
}
|
||||
|
||||
// Get status of a service
|
||||
pub async fn status(&self, name: &str) -> Result<Status, ZinitError> {
|
||||
self.client.status(name).await
|
||||
}
|
||||
|
||||
// Start a service
|
||||
pub async fn start(&self, name: &str) -> Result<(), ZinitError> {
|
||||
self.client.start(name).await
|
||||
}
|
||||
|
||||
// Stop a service
|
||||
pub async fn stop(&self, name: &str) -> Result<(), ZinitError> {
|
||||
self.client.stop(name).await
|
||||
}
|
||||
|
||||
// Restart a service
|
||||
pub async fn restart(&self, name: &str) -> Result<(), ZinitError> {
|
||||
self.client.restart(name).await
|
||||
}
|
||||
|
||||
// Monitor a service
|
||||
pub async fn monitor(&self, name: &str) -> Result<(), ZinitError> {
|
||||
self.client.monitor(name).await
|
||||
}
|
||||
|
||||
// Forget a service
|
||||
pub async fn forget(&self, name: &str) -> Result<(), ZinitError> {
|
||||
self.client.forget(name).await
|
||||
}
|
||||
|
||||
// Send a signal to a service
|
||||
pub async fn kill(&self, name: &str, signal: &str) -> Result<(), ZinitError> {
|
||||
self.client.kill(name, signal).await
|
||||
}
|
||||
|
||||
// Create a new service
|
||||
pub async fn create_service(
|
||||
&self,
|
||||
name: &str,
|
||||
content: Map<String, Value>,
|
||||
) -> Result<(), ZinitError> {
|
||||
self.client
|
||||
.create_service(name, Value::Object(content))
|
||||
.await
|
||||
}
|
||||
|
||||
// Delete a service
|
||||
pub async fn delete_service(&self, name: &str) -> Result<(), ZinitError> {
|
||||
self.client.delete_service(name).await
|
||||
}
|
||||
|
||||
// Get a service configuration
|
||||
pub async fn get_service(&self, name: &str) -> Result<Value, ZinitError> {
|
||||
self.client.get_service(name).await
|
||||
}
|
||||
|
||||
// Shutdown the system
|
||||
pub async fn shutdown(&self) -> Result<(), ZinitError> {
|
||||
self.client.shutdown().await
|
||||
}
|
||||
|
||||
// Reboot the system
|
||||
pub async fn reboot(&self) -> Result<(), ZinitError> {
|
||||
self.client.reboot().await
|
||||
}
|
||||
|
||||
// Get logs (simplified implementation - returns empty for now due to LogStream complexity)
|
||||
pub async fn logs(&self, _filter: Option<String>) -> Result<Vec<String>, ZinitError> {
|
||||
// TODO: Implement proper LogStream handling when tokio-stream is available
|
||||
// For now, return empty logs to avoid compilation errors
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
// Get the Zinit client instance
|
||||
pub async fn get_zinit_client(socket_path: &str) -> Result<Arc<ZinitClientWrapper>, ZinitError> {
|
||||
// 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>, ZinitError> {
|
||||
// Connect via Unix socket - use new() instead of unix_socket()
|
||||
let client = ZinitClient::new(socket_path);
|
||||
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<(), ZinitError> {
|
||||
// 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 - convert ServiceState to String for compatibility
|
||||
pub async fn list(socket_path: &str) -> Result<HashMap<String, String>, ZinitError> {
|
||||
let client = get_zinit_client(socket_path).await?;
|
||||
let services = client.list().await?;
|
||||
|
||||
// Convert HashMap<String, ServiceState> to HashMap<String, String>
|
||||
let mut result = HashMap::new();
|
||||
for (name, state) in services {
|
||||
result.insert(name, format!("{:?}", state));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// Get status of a service
|
||||
pub async fn status(socket_path: &str, name: &str) -> Result<Status, ZinitError> {
|
||||
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<(), ZinitError> {
|
||||
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<(), ZinitError> {
|
||||
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<(), ZinitError> {
|
||||
let client = get_zinit_client(socket_path).await?;
|
||||
client.restart(name).await
|
||||
}
|
Reference in New Issue
Block a user