feat: Add service manager support

- Add a new service manager crate for dynamic service management
- Integrate service manager with Rhai for scripting
- Provide examples for circle worker management and basic usage
- Add comprehensive tests for service lifecycle and error handling
- Implement cross-platform support for macOS and Linux (zinit/systemd)
This commit is contained in:
Mahmoud-Emad
2025-07-01 18:00:21 +03:00
parent 46ad848e7e
commit 131d978450
28 changed files with 3562 additions and 192 deletions

View File

@@ -0,0 +1,116 @@
# Service Manager Examples
This directory contains examples demonstrating the SAL service manager functionality for dynamically launching and managing services across platforms.
## Overview
The service manager provides a unified interface for managing system services:
- **macOS**: Uses `launchctl` for service management
- **Linux**: Uses `zinit` for service management (systemd also available as alternative)
## Examples
### 1. Circle Worker Manager (`circle_worker_manager.rhai`)
**Primary Use Case**: Demonstrates dynamic circle worker management for freezone residents.
This example shows:
- Creating service configurations for circle workers
- Complete service lifecycle management (start, stop, restart, remove)
- Status monitoring and log retrieval
- Error handling and cleanup
```bash
# Run the circle worker management example
herodo examples/service_manager/circle_worker_manager.rhai
```
### 2. Basic Usage (`basic_usage.rhai`)
**Learning Example**: Simple demonstration of the core service manager API.
This example covers:
- Creating and configuring services
- Starting and stopping services
- Checking service status
- Listing managed services
- Retrieving service logs
```bash
# Run the basic usage example
herodo examples/service_manager/basic_usage.rhai
```
## Prerequisites
### Linux (zinit)
Make sure zinit is installed and running:
```bash
# Start zinit with default socket
zinit -s /tmp/zinit.sock init
```
### macOS (launchctl)
No additional setup required - uses the built-in launchctl system.
## Service Manager API
The service manager provides these key functions:
- `create_service_manager()` - Create platform-appropriate service manager
- `start(manager, config)` - Start a new service
- `stop(manager, service_name)` - Stop a running service
- `restart(manager, service_name)` - Restart a service
- `status(manager, service_name)` - Get service status
- `logs(manager, service_name, lines)` - Retrieve service logs
- `list(manager)` - List all managed services
- `remove(manager, service_name)` - Remove a service
- `exists(manager, service_name)` - Check if service exists
- `start_and_confirm(manager, config, timeout)` - Start with confirmation
## Service Configuration
Services are configured using a map with these fields:
```rhai
let config = #{
name: "my-service", // Service name
binary_path: "/usr/bin/my-app", // Executable path
args: ["--config", "/etc/my-app.conf"], // Command arguments
working_directory: "/var/lib/my-app", // Working directory (optional)
environment: #{ // Environment variables
"VAR1": "value1",
"VAR2": "value2"
},
auto_restart: true // Auto-restart on failure
};
```
## Real-World Usage
The circle worker example demonstrates the exact use case requested by the team:
> "We want to be able to launch circle workers dynamically. For instance when someone registers to the freezone, we need to be able to launch a circle worker for the new resident."
The service manager enables:
1. **Dynamic service creation** - Create services on-demand for new residents
2. **Cross-platform support** - Works on both macOS and Linux
3. **Lifecycle management** - Full control over service lifecycle
4. **Monitoring and logging** - Track service status and retrieve logs
5. **Cleanup** - Proper service removal when no longer needed
## Error Handling
All service manager functions can throw errors. Use try-catch blocks for robust error handling:
```rhai
try {
sm::start(manager, config);
print("✅ Service started successfully");
} catch (error) {
print(`❌ Failed to start service: ${error}`);
}
```

View File

@@ -0,0 +1,81 @@
// Basic Service Manager Usage Example
//
// This example demonstrates the basic API of the service manager.
// It works on both macOS (launchctl) and Linux (zinit).
//
// Prerequisites:
//
// Linux: Make sure zinit is running:
// zinit -s /tmp/zinit.sock init
//
// macOS: No additional setup required (uses launchctl).
//
// Usage:
// herodo examples/service_manager/basic_usage.rhai
// Service Manager Basic Usage Example
// This example uses the SAL service manager through Rhai integration
print("🚀 Basic Service Manager Usage Example");
print("======================================");
// Create a service manager for the current platform
let manager = create_service_manager();
print("🍎 Using service manager for current platform");
// Create a simple service configuration
let config = #{
name: "example-service",
binary_path: "/bin/echo",
args: ["Hello from service manager!"],
working_directory: "/tmp",
environment: #{
"EXAMPLE_VAR": "hello_world"
},
auto_restart: false
};
print("\n📝 Service Configuration:");
print(` Name: ${config.name}`);
print(` Binary: ${config.binary_path}`);
print(` Args: ${config.args}`);
// Start the service
print("\n🚀 Starting service...");
start(manager, config);
print("✅ Service started successfully");
// Check service status
print("\n📊 Checking service status...");
let status = status(manager, "example-service");
print(`Status: ${status}`);
// List all services
print("\n📋 Listing all managed services...");
let services = list(manager);
print(`Found ${services.len()} services:`);
for service in services {
print(` - ${service}`);
}
// Get service logs
print("\n📄 Getting service logs...");
let logs = logs(manager, "example-service", 5);
if logs.trim() == "" {
print("No logs available");
} else {
print(`Logs:\n${logs}`);
}
// Stop the service
print("\n🛑 Stopping service...");
stop(manager, "example-service");
print("✅ Service stopped");
// Remove the service
print("\n🗑 Removing service...");
remove(manager, "example-service");
print("✅ Service removed");
print("\n🎉 Example completed successfully!");

View File

@@ -0,0 +1,141 @@
// Circle Worker Manager Example
//
// This example demonstrates how to use the service manager to dynamically launch
// circle workers for new freezone residents. This is the primary use case requested
// by the team.
//
// Usage:
//
// On macOS (uses launchctl):
// herodo examples/service_manager/circle_worker_manager.rhai
//
// On Linux (uses zinit - requires zinit to be running):
// First start zinit: zinit -s /tmp/zinit.sock init
// herodo examples/service_manager/circle_worker_manager.rhai
// Circle Worker Manager Example
// This example uses the SAL service manager through Rhai integration
print("🚀 Circle Worker Manager Example");
print("=================================");
// Create the appropriate service manager for the current platform
let service_manager = create_service_manager();
print("✅ Created service manager for current platform");
// Simulate a new freezone resident registration
let resident_id = "resident_12345";
let worker_name = `circle-worker-${resident_id}`;
print(`\n📝 New freezone resident registered: ${resident_id}`);
print(`🔧 Creating circle worker service: ${worker_name}`);
// Create service configuration for the circle worker
let config = #{
name: worker_name,
binary_path: "/bin/sh",
args: [
"-c",
`echo 'Circle worker for ${resident_id} starting...'; sleep 30; echo 'Circle worker for ${resident_id} completed'`
],
working_directory: "/tmp",
environment: #{
"RESIDENT_ID": resident_id,
"WORKER_TYPE": "circle",
"LOG_LEVEL": "info"
},
auto_restart: true
};
print("📋 Service configuration created:");
print(` Name: ${config.name}`);
print(` Binary: ${config.binary_path}`);
print(` Args: ${config.args}`);
print(` Auto-restart: ${config.auto_restart}`);
print(`\n🔄 Demonstrating service lifecycle for: ${worker_name}`);
// 1. Check if service already exists
print("\n1⃣ Checking if service exists...");
if exists(service_manager, worker_name) {
print("⚠️ Service already exists, removing it first...");
remove(service_manager, worker_name);
print("🗑️ Existing service removed");
} else {
print("✅ Service doesn't exist, ready to create");
}
// 2. Start the service
print("\n2⃣ Starting the circle worker service...");
start(service_manager, config);
print("✅ Service started successfully");
// 3. Check service status
print("\n3⃣ Checking service status...");
let status = status(service_manager, worker_name);
print(`📊 Service status: ${status}`);
// 4. List all services to show our service is there
print("\n4⃣ Listing all managed services...");
let services = list(service_manager);
print(`📋 Managed services (${services.len()}):`);
for service in services {
let marker = if service == worker_name { "👉" } else { " " };
print(` ${marker} ${service}`);
}
// 5. Wait a moment and check status again
print("\n5⃣ Waiting 3 seconds and checking status again...");
sleep(3000); // 3 seconds in milliseconds
let status = status(service_manager, worker_name);
print(`📊 Service status after 3s: ${status}`);
// 6. Get service logs
print("\n6⃣ Retrieving service logs...");
let logs = logs(service_manager, worker_name, 10);
if logs.trim() == "" {
print("📄 No logs available yet (this is normal for new services)");
} else {
print("📄 Recent logs:");
let log_lines = logs.split('\n');
for i in 0..5 {
if i < log_lines.len() {
print(` ${log_lines[i]}`);
}
}
}
// 7. Demonstrate start_and_confirm with timeout
print("\n7⃣ Testing start_and_confirm (should succeed quickly since already running)...");
start_and_confirm(service_manager, config, 5);
print("✅ Service confirmed running within timeout");
// 8. Stop the service
print("\n8⃣ Stopping the service...");
stop(service_manager, worker_name);
print("🛑 Service stopped");
// 9. Check status after stopping
print("\n9⃣ Checking status after stop...");
let status = status(service_manager, worker_name);
print(`📊 Service status after stop: ${status}`);
// 10. Restart the service
print("\n🔟 Restarting the service...");
restart(service_manager, worker_name);
print("🔄 Service restarted successfully");
// 11. Final cleanup
print("\n🧹 Cleaning up - removing the service...");
remove(service_manager, worker_name);
print("🗑️ Service removed successfully");
// 12. Verify removal
print("\n✅ Verifying service removal...");
if !exists(service_manager, worker_name) {
print("✅ Service successfully removed");
} else {
print("⚠️ Service still exists after removal");
}
print("\n🎉 Circle worker management demonstration complete!");