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:
272
zinit_client/README.md
Normal file
272
zinit_client/README.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# 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
|
Reference in New Issue
Block a user