sal/zinit_client/README.md
Mahmoud-Emad 511729c477
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
feat: Add zinit_client package to workspace
- 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.
2025-06-22 10:59:19 +03:00

273 lines
7.6 KiB
Markdown

# SAL Zinit Client (`sal-zinit-client`)
A Rust client library for interacting with [Zinit](https://github.com/systeminit/zinit), a process supervisor daemon for Linux systems. This package provides both a Rust API and Rhai scripting integration for comprehensive service management.
## Features
- **Async Operations**: Built on tokio for non-blocking communication
- **Unix Socket Communication**: Connects to Zinit daemon via Unix domain sockets
- **Global Client Management**: Efficient connection reuse with lazy initialization
- **Comprehensive Service Management**: Full lifecycle control (start, stop, restart, monitor, etc.)
- **Service Configuration**: Create, delete, and retrieve service configurations
- **Real-time Log Streaming**: Retrieve logs with filtering support
- **Rhai Integration**: Complete scripting support for automation
- **Production Ready**: Real-world tested with comprehensive error handling
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
sal-zinit-client = "0.1.0"
```
## Quick Start
### Rust API
```rust
use sal_zinit_client::{list, status, create_service, start, stop};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let socket_path = "/var/run/zinit.sock";
// List all services
let services = list(socket_path).await?;
println!("Services: {:?}", services);
// Create a new service
create_service(socket_path, "my-service", "echo 'Hello World'", true).await?;
// Start the service
start(socket_path, "my-service").await?;
// Get service status
let service_status = status(socket_path, "my-service").await?;
println!("Status: {:?}", service_status);
Ok(())
}
```
### Rhai Scripting
```rhai
// Zinit socket path
let socket_path = "/var/run/zinit.sock";
// List all services
let services = zinit_list(socket_path);
print(`Found ${services.len()} services`);
// Create and manage a service
let service_name = "rhai-test-service";
let exec_command = "echo 'Hello from Rhai'";
// Create service
zinit_create_service(socket_path, service_name, exec_command, true);
// Monitor and start
zinit_monitor(socket_path, service_name);
zinit_start(socket_path, service_name);
// Get status
let status = zinit_status(socket_path, service_name);
print(`Service state: ${status.state}`);
// Clean up
zinit_stop(socket_path, service_name);
zinit_forget(socket_path, service_name);
zinit_delete_service(socket_path, service_name);
```
## API Reference
### Core Functions
#### Service Management
- `list(socket_path)` - List all services and their states
- `status(socket_path, name)` - Get detailed status of a specific service
- `start(socket_path, name)` - Start a service
- `stop(socket_path, name)` - Stop a service
- `restart(socket_path, name)` - Restart a service
- `monitor(socket_path, name)` - Start monitoring a service
- `forget(socket_path, name)` - Stop monitoring a service
- `kill(socket_path, name, signal)` - Send a signal to a service
#### Service Configuration
- `create_service(socket_path, name, exec, oneshot)` - Create a simple service
- `create_service_full(socket_path, name, exec, oneshot, after, env, log, test)` - Create service with full options
- `delete_service(socket_path, name)` - Delete a service
- `get_service(socket_path, name)` - Get service configuration
#### Logs
- `logs(socket_path, filter)` - Get logs with optional filtering
- `logs(socket_path, None)` - Get all logs
### Rhai Functions
All Rust functions are available in Rhai with `zinit_` prefix:
- `zinit_list(socket_path)` → Map
- `zinit_status(socket_path, name)` → Map
- `zinit_start(socket_path, name)` → bool
- `zinit_stop(socket_path, name)` → bool
- `zinit_restart(socket_path, name)` → bool
- `zinit_monitor(socket_path, name)` → bool
- `zinit_forget(socket_path, name)` → bool
- `zinit_kill(socket_path, name, signal)` → bool
- `zinit_create_service(socket_path, name, exec, oneshot)` → String
- `zinit_delete_service(socket_path, name)` → String
- `zinit_get_service(socket_path, name)` → Dynamic
- `zinit_logs(socket_path, filter)` → Array
- `zinit_logs_all(socket_path)` → Array
## Configuration
### Socket Paths
Common Zinit socket locations:
- `/var/run/zinit.sock` (default system location)
- `/tmp/zinit.sock` (temporary/testing)
- `/run/zinit.sock` (alternative system location)
### Environment Variables
The client respects standard environment configurations and handles connection failures gracefully.
## Testing
The package includes comprehensive tests that work with real Zinit servers:
```bash
# Run all tests
cargo test
# Run only unit tests
cargo test --test zinit_client_tests
# Run only Rhai integration tests
cargo test --test rhai_integration_tests
```
### Test Requirements
**IMPORTANT**: For full test coverage, you must start a Zinit server before running tests:
```bash
# Start Zinit for testing (recommended for development)
zinit -s /tmp/zinit.sock init
# Alternative: Start with system socket (requires sudo)
sudo zinit --socket /var/run/zinit.sock init
# Or use systemd (if available)
sudo systemctl start zinit
```
**Without a running Zinit server:**
- Tests will gracefully skip when no socket is available
- You'll see messages like "⚠ No Zinit socket found. Tests will be skipped."
- This is expected behavior and not a test failure
**With a running Zinit server:**
- Tests will connect to the server and perform real operations
- Service creation, management, and deletion will be tested
- Log retrieval and signal handling will be validated
## Examples
### Service Lifecycle Management
```rust
use sal_zinit_client::*;
async fn manage_web_server() -> Result<(), Box<dyn std::error::Error>> {
let socket = "/var/run/zinit.sock";
let service = "web-server";
// Create web server service
create_service(socket, service, "python3 -m http.server 8080", false).await?;
// Start monitoring and run
monitor(socket, service).await?;
start(socket, service).await?;
// Check if running
let status = status(socket, service).await?;
println!("Web server PID: {}", status.pid);
// Graceful shutdown
stop(socket, service).await?;
forget(socket, service).await?;
delete_service(socket, service).await?;
Ok(())
}
```
### Log Monitoring
```rust
use sal_zinit_client::logs;
async fn monitor_logs() -> Result<(), Box<dyn std::error::Error>> {
let socket = "/var/run/zinit.sock";
// Get all logs
let all_logs = logs(socket, None).await?;
println!("Total log entries: {}", all_logs.len());
// Get filtered logs
let error_logs = logs(socket, Some("error".to_string())).await?;
println!("Error log entries: {}", error_logs.len());
Ok(())
}
```
## Error Handling
The client provides comprehensive error handling:
```rust
use sal_zinit_client::{list, ZinitError};
async fn handle_errors() {
let socket = "/invalid/path/zinit.sock";
match list(socket).await {
Ok(services) => println!("Services: {:?}", services),
Err(e) => {
eprintln!("Zinit error: {}", e);
// Handle specific error types
}
}
}
```
## Integration with SAL
This package is part of the SAL (System Abstraction Layer) ecosystem:
```rust
use sal::zinit_client;
// Access through SAL
let services = sal::zinit_client::list("/var/run/zinit.sock").await?;
```
## Contributing
This package follows SAL's strict quality standards:
- Real functionality only (no placeholders or stubs)
- Comprehensive test coverage with actual behavior validation
- Production-ready error handling and logging
- Security considerations for credential handling
## License
Apache-2.0