herolib_rust/src/zinit_client/README.md
timurgordon 65e404e517
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
merge branches and document
2025-05-23 21:12:17 +03:00

7.5 KiB

SAL Zinit Client Module (sal::zinit_client)

Overview

The sal::zinit_client module provides a Rust interface for interacting with a 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

// 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.