add other horus installers, create examples, test startup
This commit is contained in:
47
compare_dirs.sh
Executable file
47
compare_dirs.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: ./compare_dirs.sh <branch1> <branch2> <dir_path>
|
||||
# Example: ./compare_dirs.sh main feature-branch src
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "Usage: $0 <branch1> <branch2> <dir_path>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BRANCH1=$1
|
||||
BRANCH2=$2
|
||||
DIR_PATH=$3
|
||||
|
||||
TMP_DIR1=$(mktemp -d)
|
||||
TMP_DIR2=$(mktemp -d)
|
||||
|
||||
# Ensure we're in a Git repo
|
||||
if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
|
||||
echo "Error: Not inside a Git repository"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fetch branch contents without switching branches
|
||||
git worktree add "$TMP_DIR1" "$BRANCH1" > /dev/null 2>&1
|
||||
git worktree add "$TMP_DIR2" "$BRANCH2" > /dev/null 2>&1
|
||||
|
||||
# Check if the directory exists in both branches
|
||||
if [ ! -d "$TMP_DIR1/$DIR_PATH" ]; then
|
||||
echo "Error: $DIR_PATH does not exist in $BRANCH1"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "$TMP_DIR2/$DIR_PATH" ]; then
|
||||
echo "Error: $DIR_PATH does not exist in $BRANCH2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Compare directories
|
||||
echo "Comparing $DIR_PATH between $BRANCH1 and $BRANCH2..."
|
||||
diff -qr "$TMP_DIR1/$DIR_PATH" "$TMP_DIR2/$DIR_PATH"
|
||||
|
||||
# Detailed differences
|
||||
diff -u -r "$TMP_DIR1/$DIR_PATH" "$TMP_DIR2/$DIR_PATH"
|
||||
|
||||
# Clean up temporary worktrees
|
||||
git worktree remove "$TMP_DIR1" --force
|
||||
git worktree remove "$TMP_DIR2" --force
|
||||
209
examples/installers/horus/README.md
Normal file
209
examples/installers/horus/README.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Horus Installation Examples
|
||||
|
||||
This directory contains example scripts for installing and managing all Horus components using the herolib installer framework.
|
||||
|
||||
## Components
|
||||
|
||||
The Horus ecosystem consists of the following components:
|
||||
|
||||
1. **Coordinator** - Central coordination service (HTTP: 8081, WS: 9653)
|
||||
2. **Supervisor** - Supervision and monitoring service (HTTP: 8082, WS: 9654)
|
||||
3. **Hero Runner** - Command execution runner for Hero jobs
|
||||
4. **Osiris Runner** - Database-backed runner
|
||||
5. **SAL Runner** - System Abstraction Layer runner
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Full Installation and Start
|
||||
|
||||
To install and start all Horus components:
|
||||
|
||||
```bash
|
||||
# 1. Install all components (this will take several minutes)
|
||||
./horus_full_install.vsh
|
||||
|
||||
# 2. Start all services
|
||||
./horus_start_all.vsh
|
||||
|
||||
# 3. Check status
|
||||
./horus_status.vsh
|
||||
```
|
||||
|
||||
### Stop All Services
|
||||
|
||||
```bash
|
||||
./horus_stop_all.vsh
|
||||
```
|
||||
|
||||
## Available Scripts
|
||||
|
||||
### `horus_full_install.vsh`
|
||||
Installs all Horus components:
|
||||
- Checks and installs Redis if needed
|
||||
- Checks and installs Rust if needed
|
||||
- Clones the horus repository
|
||||
- Builds all binaries from source
|
||||
|
||||
**Note:** This script can take 10-30 minutes depending on your system, as it compiles Rust code.
|
||||
|
||||
### `horus_start_all.vsh`
|
||||
Starts all Horus services in the correct order:
|
||||
1. Coordinator
|
||||
2. Supervisor
|
||||
3. Hero Runner
|
||||
4. Osiris Runner
|
||||
5. SAL Runner
|
||||
|
||||
### `horus_stop_all.vsh`
|
||||
Stops all running Horus services in reverse order.
|
||||
|
||||
### `horus_status.vsh`
|
||||
Checks and displays the status of all Horus services.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Operating System**: Linux or macOS
|
||||
- **Dependencies** (automatically installed):
|
||||
- Redis (required for all components)
|
||||
- Rust toolchain (for building from source)
|
||||
- Git (for cloning repositories)
|
||||
|
||||
## Configuration
|
||||
|
||||
All components use default configurations:
|
||||
|
||||
### Coordinator
|
||||
- Binary: `/hero/var/bin/coordinator`
|
||||
- HTTP Port: `8081`
|
||||
- WebSocket Port: `9653`
|
||||
- Redis: `127.0.0.1:6379`
|
||||
|
||||
### Supervisor
|
||||
- Binary: `/hero/var/bin/supervisor`
|
||||
- HTTP Port: `8082`
|
||||
- WebSocket Port: `9654`
|
||||
- Redis: `127.0.0.1:6379`
|
||||
|
||||
### Runners
|
||||
- Hero Runner: `/hero/var/bin/herorunner`
|
||||
- Osiris Runner: `/hero/var/bin/runner_osiris`
|
||||
- SAL Runner: `/hero/var/bin/runner_sal`
|
||||
|
||||
## Custom Configuration
|
||||
|
||||
To customize the configuration, you can use heroscript:
|
||||
|
||||
```v
|
||||
import incubaid.herolib.installers.horus.coordinator
|
||||
|
||||
mut coordinator := herocoordinator.get(create: true)!
|
||||
coordinator.http_port = 9000
|
||||
coordinator.ws_port = 9001
|
||||
coordinator.log_level = 'debug'
|
||||
herocoordinator.set(coordinator)!
|
||||
coordinator.install()!
|
||||
coordinator.start()!
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
After starting the services, you can test them:
|
||||
|
||||
```bash
|
||||
# Test Coordinator HTTP endpoint
|
||||
curl http://127.0.0.1:8081
|
||||
|
||||
# Test Supervisor HTTP endpoint
|
||||
curl http://127.0.0.1:8082
|
||||
|
||||
# Check running processes
|
||||
pgrep -f coordinator
|
||||
pgrep -f supervisor
|
||||
pgrep -f herorunner
|
||||
pgrep -f runner_osiris
|
||||
pgrep -f runner_sal
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Redis Not Running
|
||||
If you get Redis connection errors:
|
||||
```bash
|
||||
# Check if Redis is running
|
||||
redis-cli ping
|
||||
|
||||
# Start Redis (Ubuntu/Debian)
|
||||
sudo systemctl start redis-server
|
||||
|
||||
# Start Redis (macOS with Homebrew)
|
||||
brew services start redis
|
||||
```
|
||||
|
||||
### Build Failures
|
||||
If the build fails:
|
||||
1. Ensure you have enough disk space (at least 5GB free)
|
||||
2. Check that Rust is properly installed: `rustc --version`
|
||||
3. Try cleaning the build: `cd /root/code/git.ourworld.tf/herocode/horus && cargo clean`
|
||||
|
||||
### Port Conflicts
|
||||
If ports 8081 or 8082 are already in use, you can customize the ports in the configuration.
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Individual Component Installation
|
||||
|
||||
You can install components individually:
|
||||
|
||||
```bash
|
||||
# Install only coordinator
|
||||
v run coordinator_only.vsh
|
||||
|
||||
# Install only supervisor
|
||||
v run supervisor_only.vsh
|
||||
```
|
||||
|
||||
### Using with Heroscript
|
||||
|
||||
You can also use heroscript files for configuration:
|
||||
|
||||
```heroscript
|
||||
!!herocoordinator.configure
|
||||
name:'production'
|
||||
http_port:8081
|
||||
ws_port:9653
|
||||
log_level:'info'
|
||||
|
||||
!!herocoordinator.install
|
||||
|
||||
!!herocoordinator.start
|
||||
```
|
||||
|
||||
## Service Management
|
||||
|
||||
Services are managed using the system's startup manager (zinit or systemd):
|
||||
|
||||
```bash
|
||||
# Check service status with systemd
|
||||
systemctl status coordinator
|
||||
|
||||
# View logs
|
||||
journalctl -u coordinator -f
|
||||
```
|
||||
|
||||
## Cleanup
|
||||
|
||||
To completely remove all Horus components:
|
||||
|
||||
```bash
|
||||
# Stop all services
|
||||
./horus_stop_all.vsh
|
||||
|
||||
# Destroy all components (removes binaries)
|
||||
v run horus_destroy_all.vsh
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- Check the main Horus repository: https://git.ourworld.tf/herocode/horus
|
||||
- Review the installer code in `lib/installers/horus/`
|
||||
60
examples/installers/horus/horus_config.heroscript
Normal file
60
examples/installers/horus/horus_config.heroscript
Normal file
@@ -0,0 +1,60 @@
|
||||
// Horus Configuration Heroscript
|
||||
// This file demonstrates how to configure all Horus components using heroscript
|
||||
|
||||
// Configure Coordinator
|
||||
!!herocoordinator.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/coordinator'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
http_port:8081
|
||||
ws_port:9653
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
|
||||
// Configure Supervisor
|
||||
!!supervisor.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/supervisor'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
http_port:8082
|
||||
ws_port:9654
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
|
||||
// Configure Hero Runner
|
||||
!!herorunner.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/herorunner'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
|
||||
// Configure Osiris Runner
|
||||
!!osirisrunner.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/runner_osiris'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
|
||||
// Configure SAL Runner
|
||||
!!salrunner.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/runner_sal'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
|
||||
// Install all components
|
||||
!!herocoordinator.install
|
||||
!!supervisor.install
|
||||
!!herorunner.install
|
||||
!!osirisrunner.install
|
||||
!!salrunner.install
|
||||
|
||||
// Start all services
|
||||
!!herocoordinator.start name:'default'
|
||||
!!supervisor.start name:'default'
|
||||
!!herorunner.start name:'default'
|
||||
!!osirisrunner.start name:'default'
|
||||
!!salrunner.start name:'default'
|
||||
60
examples/installers/horus/horus_full_install.vsh
Executable file
60
examples/installers/horus/horus_full_install.vsh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.horus.coordinator
|
||||
import incubaid.herolib.installers.horus.supervisor
|
||||
import incubaid.herolib.installers.horus.herorunner
|
||||
import incubaid.herolib.installers.horus.osirisrunner
|
||||
import incubaid.herolib.installers.horus.salrunner
|
||||
|
||||
// Full Horus Installation Example
|
||||
// This script installs and configures all Horus components:
|
||||
// - Coordinator (port 8081)
|
||||
// - Supervisor (port 8082)
|
||||
// - Hero Runner
|
||||
// - Osiris Runner
|
||||
// - SAL Runner
|
||||
|
||||
println('🚀 Starting Full Horus Installation')
|
||||
|
||||
// Step 1: Install Coordinator
|
||||
println('\n📦 Step 1/5: Installing Coordinator...')
|
||||
mut coordinator_installer := coordinator.get(create: true)!
|
||||
coordinator_installer.install()!
|
||||
println('✅ Coordinator installed at ${coordinator_installer.binary_path}')
|
||||
|
||||
// Step 2: Install Supervisor
|
||||
println('\n📦 Step 2/5: Installing Supervisor...')
|
||||
mut supervisor_inst := supervisor.get(create: true)!
|
||||
supervisor_inst.install()!
|
||||
println('✅ Supervisor installed at ${supervisor_inst.binary_path}')
|
||||
|
||||
// Step 3: Install Hero Runner
|
||||
println('\n📦 Step 3/5: Installing Hero Runner...')
|
||||
mut hero_runner := herorunner.get(create: true)!
|
||||
hero_runner.install()!
|
||||
println('✅ Hero Runner installed at ${hero_runner.binary_path}')
|
||||
|
||||
// Step 4: Install Osiris Runner
|
||||
println('\n📦 Step 4/5: Installing Osiris Runner...')
|
||||
mut osiris_runner := osirisrunner.get(create: true)!
|
||||
osiris_runner.install()!
|
||||
println('✅ Osiris Runner installed at ${osiris_runner.binary_path}')
|
||||
|
||||
// Step 5: Install SAL Runner
|
||||
println('\n📦 Step 5/5: Installing SAL Runner...')
|
||||
mut sal_runner := salrunner.get(create: true)!
|
||||
sal_runner.install()!
|
||||
println('✅ SAL Runner installed at ${sal_runner.binary_path}')
|
||||
|
||||
println('🎉 All Horus components installed successfully!')
|
||||
|
||||
println('\n📋 Installation Summary:')
|
||||
println(' • Coordinator: ${coordinator_installer.binary_path} (HTTP: ${coordinator_installer.http_port}, WS: ${coordinator_installer.ws_port})')
|
||||
println(' • Supervisor: ${supervisor_inst.binary_path} (HTTP: ${supervisor_inst.http_port}, WS: ${supervisor_inst.ws_port})')
|
||||
println(' • Hero Runner: ${hero_runner.binary_path}')
|
||||
println(' • Osiris Runner: ${osiris_runner.binary_path}')
|
||||
println(' • SAL Runner: ${sal_runner.binary_path}')
|
||||
|
||||
println('\n💡 Next Steps:')
|
||||
println(' To start services, run: ./horus_start_all.vsh')
|
||||
println(' To test individual components, see the other example scripts')
|
||||
76
examples/installers/horus/horus_start_all.vsh
Executable file
76
examples/installers/horus/horus_start_all.vsh
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.horus.coordinator
|
||||
import incubaid.herolib.installers.horus.supervisor
|
||||
import incubaid.herolib.installers.horus.herorunner
|
||||
import incubaid.herolib.installers.horus.osirisrunner
|
||||
import incubaid.herolib.installers.horus.salrunner
|
||||
import time
|
||||
|
||||
// Start All Horus Services
|
||||
// This script starts all Horus components in the correct order
|
||||
|
||||
println('🚀 Starting All Horus Services')
|
||||
|
||||
// Step 1: Start Coordinator
|
||||
println('\n▶️ Step 1/5: Starting Coordinator...')
|
||||
mut coordinator_installer := coordinator.get()!
|
||||
coordinator_installer.start()!
|
||||
if coordinator_installer.running()! {
|
||||
println('✅ Coordinator is running on HTTP:${coordinator_installer.http_port} WS:${coordinator_installer.ws_port}')
|
||||
} else {
|
||||
println('❌ Coordinator failed to start')
|
||||
}
|
||||
|
||||
// Step 2: Start Supervisor
|
||||
println('\n▶️ Step 2/5: Starting Supervisor...')
|
||||
mut supervisor_inst := supervisor.get()!
|
||||
supervisor_inst.start()!
|
||||
if supervisor_inst.running()! {
|
||||
println('✅ Supervisor is running on HTTP:${supervisor_inst.http_port} WS:${supervisor_inst.ws_port}')
|
||||
} else {
|
||||
println('❌ Supervisor failed to start')
|
||||
}
|
||||
|
||||
// Step 3: Start Hero Runner
|
||||
println('\n▶️ Step 3/5: Starting Hero Runner...')
|
||||
mut hero_runner := herorunner.get()!
|
||||
hero_runner.start()!
|
||||
if hero_runner.running()! {
|
||||
println('✅ Hero Runner is running')
|
||||
} else {
|
||||
println('❌ Hero Runner failed to start')
|
||||
}
|
||||
|
||||
// Step 4: Start Osiris Runner
|
||||
println('\n▶️ Step 4/5: Starting Osiris Runner...')
|
||||
mut osiris_runner := osirisrunner.get()!
|
||||
osiris_runner.start()!
|
||||
if osiris_runner.running()! {
|
||||
println('✅ Osiris Runner is running')
|
||||
} else {
|
||||
println('❌ Osiris Runner failed to start')
|
||||
}
|
||||
|
||||
// Step 5: Start SAL Runner
|
||||
println('\n▶️ Step 5/5: Starting SAL Runner...')
|
||||
mut sal_runner := salrunner.get()!
|
||||
sal_runner.start()!
|
||||
if sal_runner.running()! {
|
||||
println('✅ SAL Runner is running')
|
||||
} else {
|
||||
println('❌ SAL Runner failed to start')
|
||||
}
|
||||
|
||||
println('🎉 All Horus services started!')
|
||||
|
||||
println('\n📊 Service Status:')
|
||||
println(' • Coordinator: ${if coordinator_installer.running()! { "✅ Running" } else { "❌ Stopped" }} (http://127.0.0.1:${coordinator_installer.http_port})')
|
||||
println(' • Supervisor: ${if supervisor_inst.running()! { "✅ Running" } else { "❌ Stopped" }} (http://127.0.0.1:${supervisor_inst.http_port})')
|
||||
println(' • Hero Runner: ${if hero_runner.running()! { "✅ Running" } else { "❌ Stopped" }}')
|
||||
println(' • Osiris Runner: ${if osiris_runner.running()! { "✅ Running" } else { "❌ Stopped" }}')
|
||||
println(' • SAL Runner: ${if sal_runner.running()! { "✅ Running" } else { "❌ Stopped" }}')
|
||||
|
||||
println('\n💡 Next Steps:')
|
||||
println(' To stop services, run: ./horus_stop_all.vsh')
|
||||
println(' To check status, run: ./horus_status.vsh')
|
||||
56
examples/installers/horus/horus_status.vsh
Executable file
56
examples/installers/horus/horus_status.vsh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.horus.coordinator
|
||||
import incubaid.herolib.installers.horus.supervisor
|
||||
import incubaid.herolib.installers.horus.herorunner
|
||||
import incubaid.herolib.installers.horus.osirisrunner
|
||||
import incubaid.herolib.installers.horus.salrunner
|
||||
|
||||
// Check Status of All Horus Services
|
||||
|
||||
println('📊 Horus Services Status')
|
||||
println('=' * 60)
|
||||
|
||||
// Get all services
|
||||
mut coordinator := herocoordinator.get()!
|
||||
mut supervisor_inst := supervisor.get()!
|
||||
mut hero_runner := herorunner.get()!
|
||||
mut osiris_runner := osirisrunner.get()!
|
||||
mut sal_runner := salrunner.get()!
|
||||
|
||||
// Check status
|
||||
println('\n🔍 Checking service status...\n')
|
||||
|
||||
coord_running := coordinator.running()!
|
||||
super_running := supervisor_inst.running()!
|
||||
hero_running := hero_runner.running()!
|
||||
osiris_running := osiris_runner.running()!
|
||||
sal_running := sal_runner.running()!
|
||||
|
||||
println('Service Status Details')
|
||||
println('-' * 60)
|
||||
println('Coordinator ${if coord_running { "✅ Running" } else { "❌ Stopped" }} http://127.0.0.1:${coordinator.http_port}')
|
||||
println('Supervisor ${if super_running { "✅ Running" } else { "❌ Stopped" }} http://127.0.0.1:${supervisor_inst.http_port}')
|
||||
println('Hero Runner ${if hero_running { "✅ Running" } else { "❌ Stopped" }}')
|
||||
println('Osiris Runner ${if osiris_running { "✅ Running" } else { "❌ Stopped" }}')
|
||||
println('SAL Runner ${if sal_running { "✅ Running" } else { "❌ Stopped" }}')
|
||||
|
||||
println('\n' + '=' * 60)
|
||||
|
||||
// Count running services
|
||||
mut running_count := 0
|
||||
if coord_running { running_count++ }
|
||||
if super_running { running_count++ }
|
||||
if hero_running { running_count++ }
|
||||
if osiris_running { running_count++ }
|
||||
if sal_running { running_count++ }
|
||||
|
||||
println('Summary: ${running_count}/5 services running')
|
||||
|
||||
if running_count == 5 {
|
||||
println('🎉 All services are running!')
|
||||
} else if running_count == 0 {
|
||||
println('💤 All services are stopped')
|
||||
} else {
|
||||
println('⚠️ Some services are not running')
|
||||
}
|
||||
43
examples/installers/horus/horus_stop_all.vsh
Executable file
43
examples/installers/horus/horus_stop_all.vsh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.horus.coordinator
|
||||
import incubaid.herolib.installers.horus.supervisor
|
||||
import incubaid.herolib.installers.horus.herorunner
|
||||
import incubaid.herolib.installers.horus.osirisrunner
|
||||
import incubaid.herolib.installers.horus.salrunner
|
||||
|
||||
// Stop All Horus Services
|
||||
// This script stops all running Horus components
|
||||
|
||||
println('🛑 Stopping All Horus Services')
|
||||
println('=' * 60)
|
||||
|
||||
// Stop in reverse order
|
||||
println('\n⏹️ Stopping SAL Runner...')
|
||||
mut sal_runner := salrunner.get()!
|
||||
sal_runner.stop()!
|
||||
println('✅ SAL Runner stopped')
|
||||
|
||||
println('\n⏹️ Stopping Osiris Runner...')
|
||||
mut osiris_runner := osirisrunner.get()!
|
||||
osiris_runner.stop()!
|
||||
println('✅ Osiris Runner stopped')
|
||||
|
||||
println('\n⏹️ Stopping Hero Runner...')
|
||||
mut hero_runner := herorunner.get()!
|
||||
hero_runner.stop()!
|
||||
println('✅ Hero Runner stopped')
|
||||
|
||||
println('\n⏹️ Stopping Supervisor...')
|
||||
mut supervisor_inst := supervisor.get()!
|
||||
supervisor_inst.stop()!
|
||||
println('✅ Supervisor stopped')
|
||||
|
||||
println('\n⏹️ Stopping Coordinator...')
|
||||
mut coordinator := herocoordinator.get()!
|
||||
coordinator.stop()!
|
||||
println('✅ Coordinator stopped')
|
||||
|
||||
println('\n' + '=' * 60)
|
||||
println('✅ All Horus services stopped!')
|
||||
println('=' * 60)
|
||||
52
examples/installers/horus/quick_start.vsh
Executable file
52
examples/installers/horus/quick_start.vsh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.horus.coordinator
|
||||
import incubaid.herolib.installers.horus.supervisor
|
||||
|
||||
// Quick Start Example - Install and Start Coordinator and Supervisor
|
||||
// This is a minimal example to get started with Horus
|
||||
|
||||
println('🚀 Horus Quick Start')
|
||||
println('=' * 60)
|
||||
println('This will install and start Coordinator and Supervisor')
|
||||
println('(Runners can be added later using the full install script)')
|
||||
println('=' * 60)
|
||||
|
||||
// Install Coordinator
|
||||
println('\n📦 Installing Coordinator...')
|
||||
mut coordinator := herocoordinator.get(create: true)!
|
||||
coordinator.install()!
|
||||
println('✅ Coordinator installed')
|
||||
|
||||
// Install Supervisor
|
||||
println('\n📦 Installing Supervisor...')
|
||||
mut supervisor_inst := supervisor.get(create: true)!
|
||||
supervisor_inst.install()!
|
||||
println('✅ Supervisor installed')
|
||||
|
||||
// Start services
|
||||
println('\n▶️ Starting Coordinator...')
|
||||
coordinator.start()!
|
||||
if coordinator.running()! {
|
||||
println('✅ Coordinator is running on http://127.0.0.1:${coordinator.http_port}')
|
||||
}
|
||||
|
||||
println('\n▶️ Starting Supervisor...')
|
||||
supervisor_inst.start()!
|
||||
if supervisor_inst.running()! {
|
||||
println('✅ Supervisor is running on http://127.0.0.1:${supervisor_inst.http_port}')
|
||||
}
|
||||
|
||||
println('\n' + '=' * 60)
|
||||
println('🎉 Quick Start Complete!')
|
||||
println('=' * 60)
|
||||
println('\n📊 Services Running:')
|
||||
println(' • Coordinator: http://127.0.0.1:${coordinator.http_port}')
|
||||
println(' • Supervisor: http://127.0.0.1:${supervisor_inst.http_port}')
|
||||
|
||||
println('\n💡 Next Steps:')
|
||||
println(' • Test coordinator: curl http://127.0.0.1:${coordinator.http_port}')
|
||||
println(' • Test supervisor: curl http://127.0.0.1:${supervisor_inst.http_port}')
|
||||
println(' • Install runners: ./horus_full_install.vsh')
|
||||
println(' • Check status: ./horus_status.vsh')
|
||||
println(' • Stop services: ./horus_stop_all.vsh')
|
||||
@@ -17,7 +17,7 @@ pub mut:
|
||||
pub fn (b BizModel) export(args ExportArgs) ! {
|
||||
name := if args.name != '' { args.name } else { texttools.snake_case(args.title) }
|
||||
path := pathlib.get_dir(
|
||||
path: os.join_path(os.home_dir(), '/hero/var/bizmodel/exports/${name}')
|
||||
path: os.join_path(os.home_dir(), 'hero/var/bizmodel/exports/${name}')
|
||||
create: true
|
||||
empty: true
|
||||
)!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'HerocoordinatorServer'
|
||||
classname:'CoordinatorServer'
|
||||
singleton:0
|
||||
templates:1
|
||||
default:1
|
||||
|
||||
@@ -2,11 +2,11 @@ module coordinator
|
||||
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.ui.console
|
||||
import incubaid.herolib.core.texttools
|
||||
import incubaid.herolib.core.pathlib
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import incubaid.herolib.installers.ulist
|
||||
import incubaid.herolib.installers.lang.rust
|
||||
import incubaid.herolib.installers.base.redis
|
||||
import incubaid.herolib.develop.gittools
|
||||
import os
|
||||
|
||||
@@ -34,21 +34,16 @@ fn running() !bool {
|
||||
return res.exit_code == 0
|
||||
}
|
||||
|
||||
// Lifecycle hooks - can be implemented for custom pre/post actions
|
||||
fn start_pre() ! {
|
||||
// Add any pre-start actions here if needed
|
||||
}
|
||||
|
||||
fn start_post() ! {
|
||||
// Add any post-start actions here if needed
|
||||
}
|
||||
|
||||
fn stop_pre() ! {
|
||||
// Add any pre-stop actions here if needed
|
||||
}
|
||||
|
||||
fn stop_post() ! {
|
||||
// Add any post-stop actions here if needed
|
||||
}
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
@@ -80,80 +75,66 @@ fn upload() ! {
|
||||
// )!
|
||||
}
|
||||
|
||||
// Helper function to ensure Redis is installed and running
|
||||
@[params]
|
||||
struct EnsureRedisArgs {
|
||||
pub mut:
|
||||
redis_port int = 6379
|
||||
redis_addr string = 'localhost'
|
||||
datadir string = '/var/lib/redis'
|
||||
}
|
||||
|
||||
fn ensure_redis_running(args EnsureRedisArgs) ! {
|
||||
redis_config := redis.RedisInstall{
|
||||
port: args.redis_port
|
||||
datadir: args.datadir
|
||||
ipaddr: args.redis_addr
|
||||
}
|
||||
|
||||
if !redis.check(redis_config) {
|
||||
println('Installing and starting Redis on ${args.redis_addr}:${args.redis_port}...')
|
||||
redis.redis_install(redis_config)!
|
||||
} else {
|
||||
println('Redis is already running on ${args.redis_addr}:${args.redis_port}')
|
||||
}
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install coordinator')
|
||||
// For coordinator, we build from source instead of downloading
|
||||
build()!
|
||||
}
|
||||
|
||||
// Public build function that works with or without Redis/factory available
|
||||
pub fn build() ! {
|
||||
// Public function to build coordinator without requiring factory/redis
|
||||
pub fn build_coordinator() ! {
|
||||
console.print_header('build coordinator')
|
||||
println('Starting coordinator build process...\n')
|
||||
println('📦 Starting coordinator build process...\n')
|
||||
|
||||
// Try to get config from factory, fallback to default if Redis not available
|
||||
println('Initializing configuration...')
|
||||
mut cfg_ref := get() or {
|
||||
console.print_debug('Factory not available, using default config')
|
||||
mut default_cfg := CoordinatorServer{}
|
||||
_ := set_in_mem(default_cfg)!
|
||||
coordinator_global[default_cfg.name]
|
||||
}
|
||||
mut cfg := *cfg_ref
|
||||
println('Configuration initialized')
|
||||
// Use default config instead of getting from factory
|
||||
println('⚙️ Initializing configuration...')
|
||||
mut cfg := CoordinatorServer{}
|
||||
println('✅ Configuration initialized')
|
||||
println(' - Binary path: ${cfg.binary_path}')
|
||||
println(' - Redis address: ${cfg.redis_addr}')
|
||||
println(' - HTTP port: ${cfg.http_port}')
|
||||
println(' - WS port: ${cfg.ws_port}\n')
|
||||
|
||||
// Ensure Redis is installed and running (required for coordinator)
|
||||
println('Step 1/4: Checking Redis dependency...')
|
||||
// Parse redis_addr to extract host
|
||||
parts := cfg.redis_addr.split(':')
|
||||
redis_host := if parts.len > 0 { parts[0] } else { 'localhost' }
|
||||
ensure_redis_running(redis_port: cfg.redis_port, redis_addr: redis_host)!
|
||||
println('Redis is ready\n')
|
||||
println('🔍 Step 1/4: Checking Redis dependency...')
|
||||
|
||||
// First check if redis-server is installed
|
||||
if !osal.cmd_exists_profile('redis-server') {
|
||||
println('⚠️ Redis is not installed')
|
||||
println('📥 Installing Redis...')
|
||||
osal.package_install('redis-server')!
|
||||
println('✅ Redis installed')
|
||||
} else {
|
||||
println('✅ Redis is already installed')
|
||||
}
|
||||
|
||||
// Now check if it's running
|
||||
println('🔍 Checking if Redis is running...')
|
||||
redis_check := osal.exec(cmd: 'redis-cli -c -p 6379 ping', stdout: false, raise_error: false)!
|
||||
if redis_check.exit_code != 0 {
|
||||
println('⚠️ Redis is not running')
|
||||
println('🚀 Starting Redis...')
|
||||
osal.exec(cmd: 'systemctl start redis-server')!
|
||||
println('✅ Redis started successfully\n')
|
||||
} else {
|
||||
println('✅ Redis is already running\n')
|
||||
}
|
||||
|
||||
// Ensure rust is installed
|
||||
println('Step 2/4: Checking Rust dependency...')
|
||||
println('🔍 Step 2/4: Checking Rust dependency...')
|
||||
mut rust_installer := rust.get()!
|
||||
res := osal.exec(cmd: 'rustc -V', stdout: false, raise_error: false)!
|
||||
if res.exit_code != 0 {
|
||||
println('Installing Rust...')
|
||||
println('📥 Installing Rust...')
|
||||
rust_installer.install()!
|
||||
println('Rust installed\n')
|
||||
println('✅ Rust installed\n')
|
||||
} else {
|
||||
println('Rust is already installed: ${res.output.trim_space()}\n')
|
||||
println('✅ Rust is already installed: ${res.output.trim_space()}\n')
|
||||
}
|
||||
|
||||
// Clone or get the repository
|
||||
println('Step 3/4: Cloning/updating horus repository...')
|
||||
// Use the configured repo_path or default coderoot
|
||||
mut gs := gittools.new(coderoot: '/root/code')!
|
||||
println('🔍 Step 3/4: Cloning/updating horus repository...')
|
||||
mut gs := gittools.new()!
|
||||
mut repo := gs.get_repo(
|
||||
url: 'https://git.ourworld.tf/herocode/horus.git'
|
||||
pull: true
|
||||
@@ -162,40 +143,103 @@ pub fn build() ! {
|
||||
|
||||
// Update the path to the actual cloned repo
|
||||
cfg.repo_path = repo.path()
|
||||
println('Repository ready at: ${cfg.repo_path}\n')
|
||||
println('✅ Repository ready at: ${cfg.repo_path}\n')
|
||||
|
||||
// Build the coordinator binary from the horus workspace
|
||||
println('Step 4/4: Building coordinator binary...')
|
||||
println('WARNING: This may take several minutes (compiling Rust code)...')
|
||||
println('Running: cargo build -p hero-coordinator --release\n')
|
||||
println('🔍 Step 4/4: Building coordinator binary...')
|
||||
println('⚠️ This may take several minutes (compiling Rust code)...')
|
||||
println('📝 Running: cargo build -p hero-coordinator --release\n')
|
||||
|
||||
cmd := 'cd ${cfg.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p hero-coordinator --release'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
println('\nBuild completed successfully')
|
||||
println('\n✅ Build completed successfully')
|
||||
|
||||
// Ensure binary directory exists and copy the binary
|
||||
println('Preparing binary directory: ${cfg.binary_path}')
|
||||
println('📁 Preparing binary directory: ${cfg.binary_path}')
|
||||
mut binary_path_obj := pathlib.get(cfg.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
|
||||
// Copy the built binary to the configured location
|
||||
source_binary := '${cfg.repo_path}/target/release/coordinator'
|
||||
println('Copying binary from: ${source_binary}')
|
||||
println('Copying binary to: ${cfg.binary_path}')
|
||||
|
||||
// Verify source binary exists before copying
|
||||
mut source_file := pathlib.get_file(path: source_binary) or {
|
||||
return error('Built binary not found at ${source_binary}. Build may have failed.')
|
||||
}
|
||||
if !source_file.exists() {
|
||||
return error('Built binary not found at ${source_binary}. Build may have failed.')
|
||||
}
|
||||
|
||||
println('📋 Copying binary from: ${source_binary}')
|
||||
println('📋 Copying binary to: ${cfg.binary_path}')
|
||||
mut source_file := pathlib.get_file(path: source_binary)!
|
||||
source_file.copy(dest: cfg.binary_path, rsync: false)!
|
||||
|
||||
println('\nCoordinator built successfully!')
|
||||
println('Binary location: ${cfg.binary_path}')
|
||||
println('\n🎉 Coordinator built successfully!')
|
||||
println('📍 Binary location: ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn build() ! {
|
||||
console.print_header('build coordinator')
|
||||
|
||||
mut cfg := get()!
|
||||
|
||||
// Ensure Redis is installed and running (required for coordinator)
|
||||
console.print_debug('Checking if Redis is installed and running...')
|
||||
redis_check := osal.exec(cmd: 'redis-cli -c -p 6379 ping', stdout: false, raise_error: false)!
|
||||
if redis_check.exit_code != 0 {
|
||||
console.print_header('Redis is not running, checking if installed...')
|
||||
if !osal.cmd_exists_profile('redis-server') {
|
||||
console.print_header('Installing Redis...')
|
||||
osal.package_install('redis-server')!
|
||||
}
|
||||
console.print_header('Starting Redis...')
|
||||
osal.exec(cmd: 'systemctl start redis-server')!
|
||||
console.print_debug('Redis started successfully')
|
||||
} else {
|
||||
console.print_debug('Redis is already running')
|
||||
}
|
||||
|
||||
// Ensure rust is installed
|
||||
console.print_debug('Checking if Rust is installed...')
|
||||
mut rust_installer := rust.get()!
|
||||
res := osal.exec(cmd: 'rustc -V', stdout: false, raise_error: false)!
|
||||
if res.exit_code != 0 {
|
||||
console.print_header('Installing Rust first...')
|
||||
rust_installer.install()!
|
||||
} else {
|
||||
console.print_debug('Rust is already installed: ${res.output.trim_space()}')
|
||||
}
|
||||
|
||||
// Clone or get the repository
|
||||
console.print_debug('Cloning/updating horus repository...')
|
||||
mut gs := gittools.new()!
|
||||
mut repo := gs.get_repo(
|
||||
url: 'https://git.ourworld.tf/herocode/horus.git'
|
||||
pull: true
|
||||
reset: false
|
||||
)!
|
||||
|
||||
// Update the path to the actual cloned repo
|
||||
cfg.repo_path = repo.path()
|
||||
set(cfg)!
|
||||
console.print_debug('Repository path: ${cfg.repo_path}')
|
||||
|
||||
// Build the coordinator binary from the horus workspace
|
||||
console.print_header('Building coordinator binary (this may take several minutes ${cfg.repo_path})...')
|
||||
console.print_debug('Running: cargo build -p hero-coordinator --release')
|
||||
console.print_debug('Build output:')
|
||||
|
||||
cmd := 'cd ${cfg.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p hero-coordinator --release'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
console.print_debug('Build completed successfully')
|
||||
|
||||
// Ensure binary directory exists and copy the binary
|
||||
console.print_header('Preparing binary directory: ${cfg.binary_path}')
|
||||
mut binary_path_obj := pathlib.get(cfg.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
|
||||
// Copy the built binary to the configured location
|
||||
source_binary := '${cfg.repo_path}/target/release/coordinator'
|
||||
console.print_debug('Copying binary from: ${source_binary}')
|
||||
console.print_debug('Copying binary to: ${cfg.binary_path}')
|
||||
mut source_file := pathlib.get_file(path: source_binary)!
|
||||
source_file.copy(dest: cfg.binary_path, rsync: false)!
|
||||
|
||||
console.print_header('coordinator built successfully at ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
|
||||
@@ -38,15 +38,7 @@ pub fn new(args ArgsGet) !&CoordinatorServer {
|
||||
log_level: args.log_level
|
||||
repo_path: args.repo_path
|
||||
}
|
||||
|
||||
// Try to set in Redis, if it fails (Redis not available), build first
|
||||
set(obj) or {
|
||||
console.print_header('Redis not available, installing dependencies first...')
|
||||
build()! // build() now handles both factory and non-factory cases
|
||||
// Now try again with Redis available
|
||||
set(obj)!
|
||||
}
|
||||
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
}
|
||||
|
||||
@@ -215,8 +207,9 @@ fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.Sta
|
||||
return startupmanager.get(.systemd)!
|
||||
}
|
||||
else {
|
||||
// default to zinit
|
||||
console.print_debug("installer: coordinator' startupmanager get auto")
|
||||
return startupmanager.get(.auto)!
|
||||
return startupmanager.get(.zinit)!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,6 +222,7 @@ pub fn (mut self CoordinatorServer) reload() ! {
|
||||
|
||||
pub fn (mut self CoordinatorServer) start() ! {
|
||||
switch(self.name)
|
||||
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
@@ -242,7 +236,6 @@ pub fn (mut self CoordinatorServer) start() ! {
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module coordinator
|
||||
|
||||
import os
|
||||
import incubaid.herolib.data.paramsparser
|
||||
import incubaid.herolib.data.encoderhero
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.core.pathlib
|
||||
@@ -13,33 +15,26 @@ const default = true
|
||||
pub struct CoordinatorServer {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string = '/hero/var/bin/coordinator'
|
||||
binary_path string = os.join_path(os.home_dir(), 'hero/bin/coordinator')
|
||||
redis_addr string = '127.0.0.1:6379'
|
||||
redis_port int = 6379
|
||||
http_port int = 8081
|
||||
ws_port int = 9653
|
||||
log_level string = 'info'
|
||||
repo_path string = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
|
||||
// Initialize configuration with defaults if not provided
|
||||
// Note: Struct defaults are already set, this ensures runtime overrides work correctly
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ CoordinatorServer) !CoordinatorServer {
|
||||
mut mycfg := mycfg_
|
||||
|
||||
// Only override if explicitly empty (struct defaults should handle most cases)
|
||||
if mycfg.name == '' {
|
||||
mycfg.name = 'default'
|
||||
}
|
||||
if mycfg.binary_path == '' {
|
||||
mycfg.binary_path = '/hero/var/bin/coordinator'
|
||||
mycfg.binary_path = os.join_path(os.home_dir(),'hero/bin/coordinator')
|
||||
}
|
||||
if mycfg.redis_addr == '' {
|
||||
mycfg.redis_addr = '127.0.0.1:6379'
|
||||
}
|
||||
if mycfg.redis_port == 0 {
|
||||
mycfg.redis_port = 6379
|
||||
}
|
||||
if mycfg.http_port == 0 {
|
||||
mycfg.http_port = 8081
|
||||
}
|
||||
@@ -52,7 +47,6 @@ fn obj_init(mycfg_ CoordinatorServer) !CoordinatorServer {
|
||||
if mycfg.repo_path == '' {
|
||||
mycfg.repo_path = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
|
||||
return mycfg
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Herocoordinator Installer
|
||||
# Coordinator Installer
|
||||
|
||||
A V language installer module for building and managing the Herocoordinator service. This installer handles the complete lifecycle of the Herocoordinator binary from the Horus workspace.
|
||||
A V language installer module for building and managing the Coordinator service. This installer handles the complete lifecycle of the Coordinator binary from the Horus workspace.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -16,23 +16,23 @@ A V language installer module for building and managing the Herocoordinator serv
|
||||
|
||||
```bash
|
||||
cd /root/code/github/incubaid/herolib/examples/installers/infra
|
||||
./herocoordinator.vsh
|
||||
./coordinator.vsh
|
||||
```
|
||||
|
||||
### Manual Usage
|
||||
|
||||
```v
|
||||
import incubaid.herolib.installers.infra.herocoordinator as herocoordinator_installer
|
||||
import incubaid.herolib.installers.infra.coordinator as coordinator_installer
|
||||
|
||||
mut herocoordinator := herocoordinator_installer.get()!
|
||||
herocoordinator.install()!
|
||||
herocoordinator.start()!
|
||||
mut coordinator := coordinator_installer.get()!
|
||||
coordinator.install()!
|
||||
coordinator.start()!
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
!!herocoordinator.configure
|
||||
!!coordinator.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/coordinator'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
@@ -61,35 +61,35 @@ Builds the coordinator binary from the horus workspace. This will:
|
||||
3. Build the coordinator binary with `cargo build -p hero-coordinator --release`
|
||||
|
||||
```bash
|
||||
hero herocoordinator.install
|
||||
hero coordinator.install
|
||||
```
|
||||
|
||||
### Start
|
||||
Starts the herocoordinator service using zinit:
|
||||
Starts the coordinator service using zinit:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.start
|
||||
hero coordinator.start
|
||||
```
|
||||
|
||||
### Stop
|
||||
Stops the running service:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.stop
|
||||
hero coordinator.stop
|
||||
```
|
||||
|
||||
### Restart
|
||||
Restarts the service:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.restart
|
||||
hero coordinator.restart
|
||||
```
|
||||
|
||||
### Destroy
|
||||
Stops the service and removes all files:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.destroy
|
||||
hero coordinator.destroy
|
||||
```
|
||||
|
||||
## Requirements
|
||||
@@ -104,9 +104,9 @@ hero herocoordinator.destroy
|
||||
|
||||
The installer follows the standard herolib installer pattern:
|
||||
|
||||
- **herocoordinator_model.v**: Configuration structure and initialization
|
||||
- **herocoordinator_actions.v**: Build, install, start, stop, destroy logic
|
||||
- **herocoordinator_factory_.v**: Factory pattern for instance management
|
||||
- **coordinator_model.v**: Configuration structure and initialization
|
||||
- **coordinator_actions.v**: Build, install, start, stop, destroy logic
|
||||
- **coordinator_factory_.v**: Factory pattern for instance management
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -119,7 +119,7 @@ The installer follows the standard herolib installer pattern:
|
||||
## Example Workflow
|
||||
|
||||
```v
|
||||
import incubaid.herolib.installers.infra.herocoordinator as hc
|
||||
import incubaid.herolib.installers.infra.coordinator as hc
|
||||
|
||||
// Get installer instance
|
||||
mut coordinator := hc.get()!
|
||||
|
||||
12
lib/installers/horus/herorunner/.heroscript
Normal file
12
lib/installers/horus/herorunner/.heroscript
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'HerorunnerServer'
|
||||
singleton:0
|
||||
templates:1
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
startupmanager:1
|
||||
hasconfig:1
|
||||
build:1
|
||||
139
lib/installers/horus/herorunner/herorunner_actions.v
Normal file
139
lib/installers/horus/herorunner/herorunner_actions.v
Normal file
@@ -0,0 +1,139 @@
|
||||
module herorunner
|
||||
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.ui.console
|
||||
import incubaid.herolib.core.pathlib
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import incubaid.herolib.installers.ulist
|
||||
import incubaid.herolib.installers.lang.rust
|
||||
import incubaid.herolib.develop.gittools
|
||||
import os
|
||||
|
||||
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
|
||||
mut cfg := get()!
|
||||
mut res := []startupmanager.ZProcessNewArgs{}
|
||||
|
||||
res << startupmanager.ZProcessNewArgs{
|
||||
name: 'herorunner'
|
||||
cmd: '${cfg.binary_path} --redis-addr ${cfg.redis_addr}'
|
||||
env: {
|
||||
'HOME': os.home_dir()
|
||||
'RUST_LOG': cfg.log_level
|
||||
'RUST_LOG_STYLE': 'never'
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn running() !bool {
|
||||
// Check if the process is running
|
||||
res := osal.exec(cmd: 'pgrep -f herorunner', stdout: false, raise_error: false)!
|
||||
return res.exit_code == 0
|
||||
}
|
||||
|
||||
fn start_pre() ! {
|
||||
}
|
||||
|
||||
fn start_post() ! {
|
||||
}
|
||||
|
||||
fn stop_pre() ! {
|
||||
}
|
||||
|
||||
fn stop_post() ! {
|
||||
}
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed() !bool {
|
||||
mut cfg := get()!
|
||||
|
||||
// Check if the binary exists
|
||||
mut binary := pathlib.get(cfg.binary_path)
|
||||
if !binary.exists() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// get the Upload List of the files
|
||||
fn ulist_get() !ulist.UList {
|
||||
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
|
||||
return ulist.UList{}
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload() ! {
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install herorunner')
|
||||
// For herorunner, we build from source instead of downloading
|
||||
build()!
|
||||
}
|
||||
|
||||
fn build() ! {
|
||||
console.print_header('build herorunner')
|
||||
|
||||
mut cfg := get()!
|
||||
|
||||
// Ensure rust is installed
|
||||
console.print_debug('Checking if Rust is installed...')
|
||||
mut rust_installer := rust.get()!
|
||||
res := osal.exec(cmd: 'rustc -V', stdout: false, raise_error: false)!
|
||||
if res.exit_code != 0 {
|
||||
console.print_header('Installing Rust first...')
|
||||
rust_installer.install()!
|
||||
} else {
|
||||
console.print_debug('Rust is already installed: ${res.output.trim_space()}')
|
||||
}
|
||||
|
||||
// Clone or get the repository
|
||||
console.print_debug('Cloning/updating horus repository...')
|
||||
mut gs := gittools.new()!
|
||||
mut repo := gs.get_repo(
|
||||
url: 'https://git.ourworld.tf/herocode/horus.git'
|
||||
pull: true
|
||||
reset: false
|
||||
)!
|
||||
|
||||
repo_path := repo.path()
|
||||
console.print_debug('Repository path: ${repo_path}')
|
||||
|
||||
// Build the herorunner binary from the horus workspace
|
||||
console.print_header('Building herorunner binary (this may take several minutes)...')
|
||||
console.print_debug('Running: cargo build -p runner-hero --release')
|
||||
console.print_debug('Build output:')
|
||||
|
||||
cmd := 'cd ${repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p runner-hero --release'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
console.print_debug('Build completed successfully')
|
||||
|
||||
// Ensure binary directory exists and copy the binary
|
||||
console.print_debug('Preparing binary directory: ${cfg.binary_path}')
|
||||
mut binary_path_obj := pathlib.get(cfg.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
|
||||
// Copy the built binary to the configured location
|
||||
source_binary := '${repo_path}/target/release/herorunner'
|
||||
console.print_debug('Copying binary from: ${source_binary}')
|
||||
console.print_debug('Copying binary to: ${cfg.binary_path}')
|
||||
mut source_file := pathlib.get_file(path: source_binary)!
|
||||
source_file.copy(dest: cfg.binary_path, rsync: false)!
|
||||
|
||||
console.print_header('herorunner built successfully at ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
mut server := get()!
|
||||
server.stop()!
|
||||
|
||||
osal.process_kill_recursive(name: 'herorunner')!
|
||||
|
||||
// Remove the built binary
|
||||
osal.rm(server.binary_path)!
|
||||
}
|
||||
318
lib/installers/horus/herorunner/herorunner_factory_.v
Normal file
318
lib/installers/horus/herorunner/herorunner_factory_.v
Normal file
@@ -0,0 +1,318 @@
|
||||
module herorunner
|
||||
|
||||
import incubaid.herolib.core.base
|
||||
import incubaid.herolib.core.playbook { PlayBook }
|
||||
import incubaid.herolib.ui.console
|
||||
import json
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import time
|
||||
|
||||
__global (
|
||||
herorunner_global map[string]&HerorunnerServer
|
||||
herorunner_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string
|
||||
redis_addr string
|
||||
log_level string
|
||||
fromdb bool // will load from filesystem
|
||||
create bool // default will not create if not exist
|
||||
}
|
||||
|
||||
pub fn new(args ArgsGet) !&HerorunnerServer {
|
||||
mut obj := HerorunnerServer{
|
||||
name: args.name
|
||||
binary_path: args.binary_path
|
||||
redis_addr: args.redis_addr
|
||||
log_level: args.log_level
|
||||
}
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
}
|
||||
|
||||
pub fn get(args ArgsGet) !&HerorunnerServer {
|
||||
mut context := base.context()!
|
||||
herorunner_default = args.name
|
||||
if args.fromdb || args.name !in herorunner_global {
|
||||
mut r := context.redis()!
|
||||
if r.hexists('context:herorunner', args.name)! {
|
||||
data := r.hget('context:herorunner', args.name)!
|
||||
if data.len == 0 {
|
||||
print_backtrace()
|
||||
return error('HerorunnerServer with name: ${args.name} does not exist, prob bug.')
|
||||
}
|
||||
mut obj := json.decode(HerorunnerServer, data)!
|
||||
set_in_mem(obj)!
|
||||
} else {
|
||||
if args.create {
|
||||
new(args)!
|
||||
} else {
|
||||
print_backtrace()
|
||||
return error("HerorunnerServer with name '${args.name}' does not exist")
|
||||
}
|
||||
}
|
||||
return get(name: args.name)! // no longer from db nor create
|
||||
}
|
||||
return herorunner_global[args.name] or {
|
||||
print_backtrace()
|
||||
return error('could not get config for herorunner with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o HerorunnerServer) ! {
|
||||
mut o2 := set_in_mem(o)!
|
||||
herorunner_default = o2.name
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hset('context:herorunner', o2.name, json.encode(o2))!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
return r.hexists('context:herorunner', args.name)!
|
||||
}
|
||||
|
||||
pub fn delete(args ArgsGet) ! {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hdel('context:herorunner', args.name)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct ArgsList {
|
||||
pub mut:
|
||||
fromdb bool // will load from filesystem
|
||||
}
|
||||
|
||||
// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
|
||||
pub fn list(args ArgsList) ![]&HerorunnerServer {
|
||||
mut res := []&HerorunnerServer{}
|
||||
mut context := base.context()!
|
||||
if args.fromdb {
|
||||
// reset what is in mem
|
||||
herorunner_global = map[string]&HerorunnerServer{}
|
||||
herorunner_default = ''
|
||||
}
|
||||
if args.fromdb {
|
||||
mut r := context.redis()!
|
||||
mut l := r.hkeys('context:herorunner')!
|
||||
|
||||
for name in l {
|
||||
res << get(name: name, fromdb: true)!
|
||||
}
|
||||
return res
|
||||
} else {
|
||||
// load from memory
|
||||
for _, client in herorunner_global {
|
||||
res << client
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o HerorunnerServer) !HerorunnerServer {
|
||||
mut o2 := obj_init(o)!
|
||||
herorunner_global[o2.name] = &o2
|
||||
herorunner_default = o2.name
|
||||
return o2
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'herorunner.') {
|
||||
return
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'herorunner.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for mut install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
install_action.done = true
|
||||
}
|
||||
}
|
||||
mut other_actions := plbook.find(filter: 'herorunner.')!
|
||||
for mut other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action herorunner.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action herorunner.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut herorunner_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${herorunner_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action herorunner.${other_action.name}')
|
||||
herorunner_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action herorunner.${other_action.name}')
|
||||
herorunner_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action herorunner.${other_action.name}')
|
||||
herorunner_obj.restart()!
|
||||
}
|
||||
}
|
||||
other_action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager {
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat {
|
||||
.screen {
|
||||
console.print_debug("installer: herorunner' startupmanager get screen")
|
||||
return startupmanager.get(.screen)!
|
||||
}
|
||||
.zinit {
|
||||
console.print_debug("installer: herorunner' startupmanager get zinit")
|
||||
return startupmanager.get(.zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug("installer: herorunner' startupmanager get systemd")
|
||||
return startupmanager.get(.systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug("installer: herorunner' startupmanager get auto")
|
||||
return startupmanager.get(.auto)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self HerorunnerServer) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('installer: herorunner start')
|
||||
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('installer: herorunner starting with ${zprocess.startuptype}...')
|
||||
|
||||
sm.new(zprocess)!
|
||||
|
||||
sm.start(zprocess.name)!
|
||||
}
|
||||
|
||||
start_post()!
|
||||
|
||||
for _ in 0 .. 50 {
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
return error('herorunner did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) stop() ! {
|
||||
switch(self.name)
|
||||
stop_pre()!
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
sm.stop(zprocess.name)!
|
||||
}
|
||||
stop_post()!
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) running() !bool {
|
||||
switch(self.name)
|
||||
|
||||
// walk over the generic processes, if not running return
|
||||
for zprocess in startupcmd()! {
|
||||
if zprocess.startuptype != .screen {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
r := sm.running(zprocess.name)!
|
||||
if r == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return running()!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn (mut self HerorunnerServer) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for herorunner
|
||||
pub fn switch(name string) {
|
||||
herorunner_default = name
|
||||
}
|
||||
58
lib/installers/horus/herorunner/herorunner_model.v
Normal file
58
lib/installers/horus/herorunner/herorunner_model.v
Normal file
@@ -0,0 +1,58 @@
|
||||
module herorunner
|
||||
|
||||
import incubaid.herolib.data.paramsparser
|
||||
import incubaid.herolib.data.encoderhero
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.core.pathlib
|
||||
import os
|
||||
|
||||
const version = '0.1.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
@[heap]
|
||||
pub struct HerorunnerServer {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string = os.join_path(os.home_dir(), 'hero/bin/herorunner')
|
||||
redis_addr string = '127.0.0.1:6379'
|
||||
log_level string = 'info'
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ HerorunnerServer) !HerorunnerServer {
|
||||
mut mycfg := mycfg_
|
||||
if mycfg.name == '' {
|
||||
mycfg.name = 'default'
|
||||
}
|
||||
if mycfg.binary_path == '' {
|
||||
mycfg.binary_path = os.join_path(os.home_dir(), 'hero/bin/herorunner')
|
||||
}
|
||||
if mycfg.redis_addr == '' {
|
||||
mycfg.redis_addr = '127.0.0.1:6379'
|
||||
}
|
||||
if mycfg.log_level == '' {
|
||||
mycfg.log_level = 'info'
|
||||
}
|
||||
return mycfg
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
mut server := get()!
|
||||
// Ensure the binary directory exists
|
||||
mut binary_path_obj := pathlib.get(server.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj HerorunnerServer) !string {
|
||||
return encoderhero.encode[HerorunnerServer](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !HerorunnerServer {
|
||||
mut obj := encoderhero.decode[HerorunnerServer](heroscript)!
|
||||
return obj
|
||||
}
|
||||
104
lib/installers/horus/herorunner/readme.md
Normal file
104
lib/installers/horus/herorunner/readme.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Herorunner Installer
|
||||
|
||||
A V language installer module for building and managing the Hero Runner service. This installer handles the complete lifecycle of the Herorunner binary from the Horus workspace.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Rust Installation**: Installs Rust toolchain if not present
|
||||
- **Git Repository Management**: Clones and manages the horus repository
|
||||
- **Binary Building**: Compiles the herorunner binary from the horus workspace
|
||||
- **Service Management**: Start/stop/restart via zinit
|
||||
- **Configuration**: Customizable Redis connection
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Manual Usage
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.installers.horus.herorunner as herorunner_installer
|
||||
|
||||
mut herorunner := herorunner_installer.get()!
|
||||
herorunner.install()!
|
||||
herorunner.start()!
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
!!herorunner.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/herorunner'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
```
|
||||
|
||||
### Configuration Fields
|
||||
|
||||
- **name**: Instance name (default: 'default')
|
||||
- **binary_path**: Path where the herorunner binary will be installed (default: '/hero/var/bin/herorunner')
|
||||
- **redis_addr**: Redis server address (default: '127.0.0.1:6379')
|
||||
- **log_level**: Rust log level - trace, debug, info, warn, error (default: 'info')
|
||||
- **repo_path**: Path to clone the horus repository (default: '/root/code/git.ourworld.tf/herocode/horus')
|
||||
|
||||
## Commands
|
||||
|
||||
### Install
|
||||
Builds the herorunner binary from the horus workspace. This will:
|
||||
1. Install Rust if not present
|
||||
2. Clone the horus repository from git.ourworld.tf
|
||||
3. Build the herorunner binary with `cargo build -p runner-hero --release`
|
||||
|
||||
```bash
|
||||
hero herorunner.install
|
||||
```
|
||||
|
||||
### Start
|
||||
Starts the herorunner service using zinit:
|
||||
|
||||
```bash
|
||||
hero herorunner.start
|
||||
```
|
||||
|
||||
### Stop
|
||||
Stops the running service:
|
||||
|
||||
```bash
|
||||
hero herorunner.stop
|
||||
```
|
||||
|
||||
### Restart
|
||||
Restarts the service:
|
||||
|
||||
```bash
|
||||
hero herorunner.restart
|
||||
```
|
||||
|
||||
### Destroy
|
||||
Stops the service and removes all files:
|
||||
|
||||
```bash
|
||||
hero herorunner.destroy
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Dependencies**:
|
||||
- Rust toolchain (automatically installed)
|
||||
- Git (for cloning repository)
|
||||
- Redis (must be running separately)
|
||||
|
||||
## Architecture
|
||||
|
||||
The installer follows the standard herolib installer pattern:
|
||||
|
||||
- **herorunner_model.v**: Configuration structure and initialization
|
||||
- **herorunner_actions.v**: Build, install, start, stop, destroy logic
|
||||
- **herorunner_factory_.v**: Factory pattern for instance management
|
||||
|
||||
## Notes
|
||||
|
||||
- The installer builds from source rather than downloading pre-built binaries
|
||||
- Redis must be running and accessible at the configured address
|
||||
- The binary is built with `RUSTFLAGS="-A warnings"` to suppress warnings
|
||||
- Service management uses zinit by default
|
||||
5
lib/installers/horus/herorunner/templates/atemplate.yaml
Normal file
5
lib/installers/horus/herorunner/templates/atemplate.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ${cfg.configpath}
|
||||
|
||||
|
||||
12
lib/installers/horus/osirisrunner/.heroscript
Normal file
12
lib/installers/horus/osirisrunner/.heroscript
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'OsirisrunnerServer'
|
||||
singleton:0
|
||||
templates:1
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
startupmanager:1
|
||||
hasconfig:1
|
||||
build:1
|
||||
142
lib/installers/horus/osirisrunner/osirisrunner_actions.v
Normal file
142
lib/installers/horus/osirisrunner/osirisrunner_actions.v
Normal file
@@ -0,0 +1,142 @@
|
||||
module osirisrunner
|
||||
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.ui.console
|
||||
import incubaid.herolib.core.pathlib
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import incubaid.herolib.installers.ulist
|
||||
import incubaid.herolib.installers.lang.rust
|
||||
import incubaid.herolib.develop.gittools
|
||||
import os
|
||||
|
||||
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
|
||||
mut cfg := get()!
|
||||
mut res := []startupmanager.ZProcessNewArgs{}
|
||||
|
||||
res << startupmanager.ZProcessNewArgs{
|
||||
name: 'runner_osiris'
|
||||
cmd: '${cfg.binary_path} --redis-addr ${cfg.redis_addr}'
|
||||
env: {
|
||||
'HOME': os.home_dir()
|
||||
'RUST_LOG': cfg.log_level
|
||||
'RUST_LOG_STYLE': 'never'
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn running() !bool {
|
||||
mut cfg := get()!
|
||||
// Check if the process is running
|
||||
res := osal.exec(cmd: 'pgrep -f runner_osiris', stdout: false, raise_error: false)!
|
||||
return res.exit_code == 0
|
||||
}
|
||||
|
||||
fn start_pre() ! {
|
||||
}
|
||||
|
||||
fn start_post() ! {
|
||||
}
|
||||
|
||||
fn stop_pre() ! {
|
||||
}
|
||||
|
||||
fn stop_post() ! {
|
||||
}
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed() !bool {
|
||||
mut cfg := get()!
|
||||
|
||||
// Check if the binary exists
|
||||
mut binary := pathlib.get(cfg.binary_path)
|
||||
if !binary.exists() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// get the Upload List of the files
|
||||
fn ulist_get() !ulist.UList {
|
||||
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
|
||||
return ulist.UList{}
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload() ! {
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install osirisrunner')
|
||||
// For osirisrunner, we build from source instead of downloading
|
||||
build()!
|
||||
}
|
||||
|
||||
fn build() ! {
|
||||
console.print_header('build osirisrunner')
|
||||
|
||||
mut cfg := get()!
|
||||
|
||||
// Ensure rust is installed
|
||||
console.print_debug('Checking if Rust is installed...')
|
||||
mut rust_installer := rust.get()!
|
||||
res := osal.exec(cmd: 'rustc -V', stdout: false, raise_error: false)!
|
||||
if res.exit_code != 0 {
|
||||
console.print_header('Installing Rust first...')
|
||||
rust_installer.install()!
|
||||
} else {
|
||||
console.print_debug('Rust is already installed: ${res.output.trim_space()}')
|
||||
}
|
||||
|
||||
// Clone or get the repository
|
||||
console.print_debug('Cloning/updating horus repository...')
|
||||
mut gs := gittools.new()!
|
||||
mut repo := gs.get_repo(
|
||||
url: 'https://git.ourworld.tf/herocode/horus.git'
|
||||
pull: true
|
||||
reset: false
|
||||
)!
|
||||
|
||||
// Update the path to the actual cloned repo
|
||||
cfg.repo_path = repo.path()
|
||||
set(cfg)!
|
||||
console.print_debug('Repository path: ${cfg.repo_path}')
|
||||
|
||||
// Build the osirisrunner binary from the horus workspace
|
||||
console.print_header('Building osirisrunner binary (this may take several minutes)...')
|
||||
console.print_debug('Running: cargo build -p runner-osiris --release')
|
||||
console.print_debug('Build output:')
|
||||
|
||||
cmd := 'cd ${cfg.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p runner-osiris --release'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
console.print_debug('Build completed successfully')
|
||||
|
||||
// Ensure binary directory exists and copy the binary
|
||||
console.print_debug('Preparing binary directory: ${cfg.binary_path}')
|
||||
mut binary_path_obj := pathlib.get(cfg.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
|
||||
// Copy the built binary to the configured location
|
||||
source_binary := '${cfg.repo_path}/target/release/runner_osiris'
|
||||
console.print_debug('Copying binary from: ${source_binary}')
|
||||
console.print_debug('Copying binary to: ${cfg.binary_path}')
|
||||
mut source_file := pathlib.get_file(path: source_binary)!
|
||||
source_file.copy(dest: cfg.binary_path, rsync: false)!
|
||||
|
||||
console.print_header('osirisrunner built successfully at ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
mut server := get()!
|
||||
server.stop()!
|
||||
|
||||
osal.process_kill_recursive(name: 'runner_osiris')!
|
||||
|
||||
// Remove the built binary
|
||||
osal.rm(server.binary_path)!
|
||||
}
|
||||
320
lib/installers/horus/osirisrunner/osirisrunner_factory_.v
Normal file
320
lib/installers/horus/osirisrunner/osirisrunner_factory_.v
Normal file
@@ -0,0 +1,320 @@
|
||||
module osirisrunner
|
||||
|
||||
import incubaid.herolib.core.base
|
||||
import incubaid.herolib.core.playbook { PlayBook }
|
||||
import incubaid.herolib.ui.console
|
||||
import json
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import time
|
||||
|
||||
__global (
|
||||
osirisrunner_global map[string]&OsirisrunnerServer
|
||||
osirisrunner_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string
|
||||
redis_addr string
|
||||
log_level string
|
||||
repo_path string
|
||||
fromdb bool // will load from filesystem
|
||||
create bool // default will not create if not exist
|
||||
}
|
||||
|
||||
pub fn new(args ArgsGet) !&OsirisrunnerServer {
|
||||
mut obj := OsirisrunnerServer{
|
||||
name: args.name
|
||||
binary_path: args.binary_path
|
||||
redis_addr: args.redis_addr
|
||||
log_level: args.log_level
|
||||
repo_path: args.repo_path
|
||||
}
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
}
|
||||
|
||||
pub fn get(args ArgsGet) !&OsirisrunnerServer {
|
||||
mut context := base.context()!
|
||||
osirisrunner_default = args.name
|
||||
if args.fromdb || args.name !in osirisrunner_global {
|
||||
mut r := context.redis()!
|
||||
if r.hexists('context:osirisrunner', args.name)! {
|
||||
data := r.hget('context:osirisrunner', args.name)!
|
||||
if data.len == 0 {
|
||||
print_backtrace()
|
||||
return error('OsirisrunnerServer with name: ${args.name} does not exist, prob bug.')
|
||||
}
|
||||
mut obj := json.decode(OsirisrunnerServer, data)!
|
||||
set_in_mem(obj)!
|
||||
} else {
|
||||
if args.create {
|
||||
new(args)!
|
||||
} else {
|
||||
print_backtrace()
|
||||
return error("OsirisrunnerServer with name '${args.name}' does not exist")
|
||||
}
|
||||
}
|
||||
return get(name: args.name)! // no longer from db nor create
|
||||
}
|
||||
return osirisrunner_global[args.name] or {
|
||||
print_backtrace()
|
||||
return error('could not get config for osirisrunner with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o OsirisrunnerServer) ! {
|
||||
mut o2 := set_in_mem(o)!
|
||||
osirisrunner_default = o2.name
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hset('context:osirisrunner', o2.name, json.encode(o2))!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
return r.hexists('context:osirisrunner', args.name)!
|
||||
}
|
||||
|
||||
pub fn delete(args ArgsGet) ! {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hdel('context:osirisrunner', args.name)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct ArgsList {
|
||||
pub mut:
|
||||
fromdb bool // will load from filesystem
|
||||
}
|
||||
|
||||
// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
|
||||
pub fn list(args ArgsList) ![]&OsirisrunnerServer {
|
||||
mut res := []&OsirisrunnerServer{}
|
||||
mut context := base.context()!
|
||||
if args.fromdb {
|
||||
// reset what is in mem
|
||||
osirisrunner_global = map[string]&OsirisrunnerServer{}
|
||||
osirisrunner_default = ''
|
||||
}
|
||||
if args.fromdb {
|
||||
mut r := context.redis()!
|
||||
mut l := r.hkeys('context:osirisrunner')!
|
||||
|
||||
for name in l {
|
||||
res << get(name: name, fromdb: true)!
|
||||
}
|
||||
return res
|
||||
} else {
|
||||
// load from memory
|
||||
for _, client in osirisrunner_global {
|
||||
res << client
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o OsirisrunnerServer) !OsirisrunnerServer {
|
||||
mut o2 := obj_init(o)!
|
||||
osirisrunner_global[o2.name] = &o2
|
||||
osirisrunner_default = o2.name
|
||||
return o2
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'osirisrunner.') {
|
||||
return
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'osirisrunner.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for mut install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
install_action.done = true
|
||||
}
|
||||
}
|
||||
mut other_actions := plbook.find(filter: 'osirisrunner.')!
|
||||
for mut other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action osirisrunner.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action osirisrunner.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut osirisrunner_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${osirisrunner_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action osirisrunner.${other_action.name}')
|
||||
osirisrunner_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action osirisrunner.${other_action.name}')
|
||||
osirisrunner_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action osirisrunner.${other_action.name}')
|
||||
osirisrunner_obj.restart()!
|
||||
}
|
||||
}
|
||||
other_action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager {
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat {
|
||||
.screen {
|
||||
console.print_debug("installer: osirisrunner' startupmanager get screen")
|
||||
return startupmanager.get(.screen)!
|
||||
}
|
||||
.zinit {
|
||||
console.print_debug("installer: osirisrunner' startupmanager get zinit")
|
||||
return startupmanager.get(.zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug("installer: osirisrunner' startupmanager get systemd")
|
||||
return startupmanager.get(.systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug("installer: osirisrunner' startupmanager get auto")
|
||||
return startupmanager.get(.auto)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self OsirisrunnerServer) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('installer: osirisrunner start')
|
||||
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('installer: osirisrunner starting with ${zprocess.startuptype}...')
|
||||
|
||||
sm.new(zprocess)!
|
||||
|
||||
sm.start(zprocess.name)!
|
||||
}
|
||||
|
||||
start_post()!
|
||||
|
||||
for _ in 0 .. 50 {
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
return error('osirisrunner did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) stop() ! {
|
||||
switch(self.name)
|
||||
stop_pre()!
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
sm.stop(zprocess.name)!
|
||||
}
|
||||
stop_post()!
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) running() !bool {
|
||||
switch(self.name)
|
||||
|
||||
// walk over the generic processes, if not running return
|
||||
for zprocess in startupcmd()! {
|
||||
if zprocess.startuptype != .screen {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
r := sm.running(zprocess.name)!
|
||||
if r == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return running()!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn (mut self OsirisrunnerServer) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for osirisrunner
|
||||
pub fn switch(name string) {
|
||||
osirisrunner_default = name
|
||||
}
|
||||
62
lib/installers/horus/osirisrunner/osirisrunner_model.v
Normal file
62
lib/installers/horus/osirisrunner/osirisrunner_model.v
Normal file
@@ -0,0 +1,62 @@
|
||||
module osirisrunner
|
||||
|
||||
import os
|
||||
import incubaid.herolib.data.paramsparser
|
||||
import incubaid.herolib.data.encoderhero
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.core.pathlib
|
||||
|
||||
const version = '0.1.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
@[heap]
|
||||
pub struct OsirisrunnerServer {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string = os.join_path(os.home_dir(), 'hero/bin/runner_osiris')
|
||||
redis_addr string = '127.0.0.1:6379'
|
||||
log_level string = 'info'
|
||||
repo_path string = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ OsirisrunnerServer) !OsirisrunnerServer {
|
||||
mut mycfg := mycfg_
|
||||
if mycfg.name == '' {
|
||||
mycfg.name = 'default'
|
||||
}
|
||||
if mycfg.binary_path == '' {
|
||||
mycfg.binary_path = os.join_path(os.home_dir(), 'hero/bin/runner_osiris')
|
||||
}
|
||||
if mycfg.redis_addr == '' {
|
||||
mycfg.redis_addr = '127.0.0.1:6379'
|
||||
}
|
||||
if mycfg.log_level == '' {
|
||||
mycfg.log_level = 'info'
|
||||
}
|
||||
if mycfg.repo_path == '' {
|
||||
mycfg.repo_path = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
return mycfg
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
mut server := get()!
|
||||
// Ensure the binary directory exists
|
||||
mut binary_path_obj := pathlib.get(server.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj OsirisrunnerServer) !string {
|
||||
return encoderhero.encode[OsirisrunnerServer](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !OsirisrunnerServer {
|
||||
mut obj := encoderhero.decode[OsirisrunnerServer](heroscript)!
|
||||
return obj
|
||||
}
|
||||
104
lib/installers/horus/osirisrunner/readme.md
Normal file
104
lib/installers/horus/osirisrunner/readme.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Osirisrunner Installer
|
||||
|
||||
A V language installer module for building and managing the Hero Runner service. This installer handles the complete lifecycle of the Osirisrunner binary from the Horus workspace.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Rust Installation**: Installs Rust toolchain if not present
|
||||
- **Git Repository Management**: Clones and manages the horus repository
|
||||
- **Binary Building**: Compiles the osirisrunner binary from the horus workspace
|
||||
- **Service Management**: Start/stop/restart via zinit
|
||||
- **Configuration**: Customizable Redis connection
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Manual Usage
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.installers.horus.osirisrunner as osirisrunner_installer
|
||||
|
||||
mut osirisrunner := osirisrunner_installer.get()!
|
||||
osirisrunner.install()!
|
||||
osirisrunner.start()!
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
!!osirisrunner.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/osirisrunner'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
```
|
||||
|
||||
### Configuration Fields
|
||||
|
||||
- **name**: Instance name (default: 'default')
|
||||
- **binary_path**: Path where the osirisrunner binary will be installed (default: '/hero/var/bin/osirisrunner')
|
||||
- **redis_addr**: Redis server address (default: '127.0.0.1:6379')
|
||||
- **log_level**: Rust log level - trace, debug, info, warn, error (default: 'info')
|
||||
- **repo_path**: Path to clone the horus repository (default: '/root/code/git.ourworld.tf/herocode/horus')
|
||||
|
||||
## Commands
|
||||
|
||||
### Install
|
||||
Builds the osirisrunner binary from the horus workspace. This will:
|
||||
1. Install Rust if not present
|
||||
2. Clone the horus repository from git.ourworld.tf
|
||||
3. Build the osirisrunner binary with `cargo build -p runner-osiris --release`
|
||||
|
||||
```bash
|
||||
hero osirisrunner.install
|
||||
```
|
||||
|
||||
### Start
|
||||
Starts the osirisrunner service using zinit:
|
||||
|
||||
```bash
|
||||
hero osirisrunner.start
|
||||
```
|
||||
|
||||
### Stop
|
||||
Stops the running service:
|
||||
|
||||
```bash
|
||||
hero osirisrunner.stop
|
||||
```
|
||||
|
||||
### Restart
|
||||
Restarts the service:
|
||||
|
||||
```bash
|
||||
hero osirisrunner.restart
|
||||
```
|
||||
|
||||
### Destroy
|
||||
Stops the service and removes all files:
|
||||
|
||||
```bash
|
||||
hero osirisrunner.destroy
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Dependencies**:
|
||||
- Rust toolchain (automatically installed)
|
||||
- Git (for cloning repository)
|
||||
- Redis (must be running separately)
|
||||
|
||||
## Architecture
|
||||
|
||||
The installer follows the standard herolib installer pattern:
|
||||
|
||||
- **osirisrunner_model.v**: Configuration structure and initialization
|
||||
- **osirisrunner_actions.v**: Build, install, start, stop, destroy logic
|
||||
- **osirisrunner_factory_.v**: Factory pattern for instance management
|
||||
|
||||
## Notes
|
||||
|
||||
- The installer builds from source rather than downloading pre-built binaries
|
||||
- Redis must be running and accessible at the configured address
|
||||
- The binary is built with `RUSTFLAGS="-A warnings"` to suppress warnings
|
||||
- Service management uses zinit by default
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ${cfg.configpath}
|
||||
|
||||
|
||||
12
lib/installers/horus/salrunner/.heroscript
Normal file
12
lib/installers/horus/salrunner/.heroscript
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'SalrunnerServer'
|
||||
singleton:0
|
||||
templates:1
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
startupmanager:1
|
||||
hasconfig:1
|
||||
build:1
|
||||
104
lib/installers/horus/salrunner/readme.md
Normal file
104
lib/installers/horus/salrunner/readme.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Salrunner Installer
|
||||
|
||||
A V language installer module for building and managing the Hero Runner service. This installer handles the complete lifecycle of the Salrunner binary from the Horus workspace.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Rust Installation**: Installs Rust toolchain if not present
|
||||
- **Git Repository Management**: Clones and manages the horus repository
|
||||
- **Binary Building**: Compiles the salrunner binary from the horus workspace
|
||||
- **Service Management**: Start/stop/restart via zinit
|
||||
- **Configuration**: Customizable Redis connection
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Manual Usage
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.installers.horus.salrunner as salrunner_installer
|
||||
|
||||
mut salrunner := salrunner_installer.get()!
|
||||
salrunner.install()!
|
||||
salrunner.start()!
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
!!salrunner.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/salrunner'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
```
|
||||
|
||||
### Configuration Fields
|
||||
|
||||
- **name**: Instance name (default: 'default')
|
||||
- **binary_path**: Path where the salrunner binary will be installed (default: '/hero/var/bin/salrunner')
|
||||
- **redis_addr**: Redis server address (default: '127.0.0.1:6379')
|
||||
- **log_level**: Rust log level - trace, debug, info, warn, error (default: 'info')
|
||||
- **repo_path**: Path to clone the horus repository (default: '/root/code/git.ourworld.tf/herocode/horus')
|
||||
|
||||
## Commands
|
||||
|
||||
### Install
|
||||
Builds the salrunner binary from the horus workspace. This will:
|
||||
1. Install Rust if not present
|
||||
2. Clone the horus repository from git.ourworld.tf
|
||||
3. Build the salrunner binary with `cargo build -p runner-sal --release`
|
||||
|
||||
```bash
|
||||
hero salrunner.install
|
||||
```
|
||||
|
||||
### Start
|
||||
Starts the salrunner service using zinit:
|
||||
|
||||
```bash
|
||||
hero salrunner.start
|
||||
```
|
||||
|
||||
### Stop
|
||||
Stops the running service:
|
||||
|
||||
```bash
|
||||
hero salrunner.stop
|
||||
```
|
||||
|
||||
### Restart
|
||||
Restarts the service:
|
||||
|
||||
```bash
|
||||
hero salrunner.restart
|
||||
```
|
||||
|
||||
### Destroy
|
||||
Stops the service and removes all files:
|
||||
|
||||
```bash
|
||||
hero salrunner.destroy
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Dependencies**:
|
||||
- Rust toolchain (automatically installed)
|
||||
- Git (for cloning repository)
|
||||
- Redis (must be running separately)
|
||||
|
||||
## Architecture
|
||||
|
||||
The installer follows the standard herolib installer pattern:
|
||||
|
||||
- **salrunner_model.v**: Configuration structure and initialization
|
||||
- **salrunner_actions.v**: Build, install, start, stop, destroy logic
|
||||
- **salrunner_factory_.v**: Factory pattern for instance management
|
||||
|
||||
## Notes
|
||||
|
||||
- The installer builds from source rather than downloading pre-built binaries
|
||||
- Redis must be running and accessible at the configured address
|
||||
- The binary is built with `RUSTFLAGS="-A warnings"` to suppress warnings
|
||||
- Service management uses zinit by default
|
||||
142
lib/installers/horus/salrunner/salrunner_actions.v
Normal file
142
lib/installers/horus/salrunner/salrunner_actions.v
Normal file
@@ -0,0 +1,142 @@
|
||||
module salrunner
|
||||
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.ui.console
|
||||
import incubaid.herolib.core.pathlib
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import incubaid.herolib.installers.ulist
|
||||
import incubaid.herolib.installers.lang.rust
|
||||
import incubaid.herolib.develop.gittools
|
||||
import os
|
||||
|
||||
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
|
||||
mut cfg := get()!
|
||||
mut res := []startupmanager.ZProcessNewArgs{}
|
||||
|
||||
res << startupmanager.ZProcessNewArgs{
|
||||
name: 'runner_sal'
|
||||
cmd: '${cfg.binary_path} --redis-addr ${cfg.redis_addr}'
|
||||
env: {
|
||||
'HOME': os.home_dir()
|
||||
'RUST_LOG': cfg.log_level
|
||||
'RUST_LOG_STYLE': 'never'
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn running() !bool {
|
||||
mut cfg := get()!
|
||||
// Check if the process is running
|
||||
res := osal.exec(cmd: 'pgrep -f runner_sal', stdout: false, raise_error: false)!
|
||||
return res.exit_code == 0
|
||||
}
|
||||
|
||||
fn start_pre() ! {
|
||||
}
|
||||
|
||||
fn start_post() ! {
|
||||
}
|
||||
|
||||
fn stop_pre() ! {
|
||||
}
|
||||
|
||||
fn stop_post() ! {
|
||||
}
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed() !bool {
|
||||
mut cfg := get()!
|
||||
|
||||
// Check if the binary exists
|
||||
mut binary := pathlib.get(cfg.binary_path)
|
||||
if !binary.exists() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// get the Upload List of the files
|
||||
fn ulist_get() !ulist.UList {
|
||||
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
|
||||
return ulist.UList{}
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload() ! {
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install salrunner')
|
||||
// For salrunner, we build from source instead of downloading
|
||||
build()!
|
||||
}
|
||||
|
||||
fn build() ! {
|
||||
console.print_header('build salrunner')
|
||||
|
||||
mut cfg := get()!
|
||||
|
||||
// Ensure rust is installed
|
||||
console.print_debug('Checking if Rust is installed...')
|
||||
mut rust_installer := rust.get()!
|
||||
res := osal.exec(cmd: 'rustc -V', stdout: false, raise_error: false)!
|
||||
if res.exit_code != 0 {
|
||||
console.print_header('Installing Rust first...')
|
||||
rust_installer.install()!
|
||||
} else {
|
||||
console.print_debug('Rust is already installed: ${res.output.trim_space()}')
|
||||
}
|
||||
|
||||
// Clone or get the repository
|
||||
console.print_debug('Cloning/updating horus repository...')
|
||||
mut gs := gittools.new()!
|
||||
mut repo := gs.get_repo(
|
||||
url: 'https://git.ourworld.tf/herocode/horus.git'
|
||||
pull: true
|
||||
reset: false
|
||||
)!
|
||||
|
||||
// Update the path to the actual cloned repo
|
||||
cfg.repo_path = repo.path()
|
||||
set(cfg)!
|
||||
console.print_debug('Repository path: ${cfg.repo_path}')
|
||||
|
||||
// Build the salrunner binary from the horus workspace
|
||||
console.print_header('Building salrunner binary (this may take several minutes)...')
|
||||
console.print_debug('Running: cargo build -p runner-sal --release')
|
||||
console.print_debug('Build output:')
|
||||
|
||||
cmd := 'cd ${cfg.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p runner-sal --release'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
console.print_debug('Build completed successfully')
|
||||
|
||||
// Ensure binary directory exists and copy the binary
|
||||
console.print_debug('Preparing binary directory: ${cfg.binary_path}')
|
||||
mut binary_path_obj := pathlib.get(cfg.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
|
||||
// Copy the built binary to the configured location
|
||||
source_binary := '${cfg.repo_path}/target/release/runner_sal'
|
||||
console.print_debug('Copying binary from: ${source_binary}')
|
||||
console.print_debug('Copying binary to: ${cfg.binary_path}')
|
||||
mut source_file := pathlib.get_file(path: source_binary)!
|
||||
source_file.copy(dest: cfg.binary_path, rsync: false)!
|
||||
|
||||
console.print_header('salrunner built successfully at ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
mut server := get()!
|
||||
server.stop()!
|
||||
|
||||
osal.process_kill_recursive(name: 'runner_sal')!
|
||||
|
||||
// Remove the built binary
|
||||
osal.rm(server.binary_path)!
|
||||
}
|
||||
320
lib/installers/horus/salrunner/salrunner_factory_.v
Normal file
320
lib/installers/horus/salrunner/salrunner_factory_.v
Normal file
@@ -0,0 +1,320 @@
|
||||
module salrunner
|
||||
|
||||
import incubaid.herolib.core.base
|
||||
import incubaid.herolib.core.playbook { PlayBook }
|
||||
import incubaid.herolib.ui.console
|
||||
import json
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import time
|
||||
|
||||
__global (
|
||||
salrunner_global map[string]&SalrunnerServer
|
||||
salrunner_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string
|
||||
redis_addr string
|
||||
log_level string
|
||||
repo_path string
|
||||
fromdb bool // will load from filesystem
|
||||
create bool // default will not create if not exist
|
||||
}
|
||||
|
||||
pub fn new(args ArgsGet) !&SalrunnerServer {
|
||||
mut obj := SalrunnerServer{
|
||||
name: args.name
|
||||
binary_path: args.binary_path
|
||||
redis_addr: args.redis_addr
|
||||
log_level: args.log_level
|
||||
repo_path: args.repo_path
|
||||
}
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
}
|
||||
|
||||
pub fn get(args ArgsGet) !&SalrunnerServer {
|
||||
mut context := base.context()!
|
||||
salrunner_default = args.name
|
||||
if args.fromdb || args.name !in salrunner_global {
|
||||
mut r := context.redis()!
|
||||
if r.hexists('context:salrunner', args.name)! {
|
||||
data := r.hget('context:salrunner', args.name)!
|
||||
if data.len == 0 {
|
||||
print_backtrace()
|
||||
return error('SalrunnerServer with name: ${args.name} does not exist, prob bug.')
|
||||
}
|
||||
mut obj := json.decode(SalrunnerServer, data)!
|
||||
set_in_mem(obj)!
|
||||
} else {
|
||||
if args.create {
|
||||
new(args)!
|
||||
} else {
|
||||
print_backtrace()
|
||||
return error("SalrunnerServer with name '${args.name}' does not exist")
|
||||
}
|
||||
}
|
||||
return get(name: args.name)! // no longer from db nor create
|
||||
}
|
||||
return salrunner_global[args.name] or {
|
||||
print_backtrace()
|
||||
return error('could not get config for salrunner with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o SalrunnerServer) ! {
|
||||
mut o2 := set_in_mem(o)!
|
||||
salrunner_default = o2.name
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hset('context:salrunner', o2.name, json.encode(o2))!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
return r.hexists('context:salrunner', args.name)!
|
||||
}
|
||||
|
||||
pub fn delete(args ArgsGet) ! {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hdel('context:salrunner', args.name)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct ArgsList {
|
||||
pub mut:
|
||||
fromdb bool // will load from filesystem
|
||||
}
|
||||
|
||||
// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
|
||||
pub fn list(args ArgsList) ![]&SalrunnerServer {
|
||||
mut res := []&SalrunnerServer{}
|
||||
mut context := base.context()!
|
||||
if args.fromdb {
|
||||
// reset what is in mem
|
||||
salrunner_global = map[string]&SalrunnerServer{}
|
||||
salrunner_default = ''
|
||||
}
|
||||
if args.fromdb {
|
||||
mut r := context.redis()!
|
||||
mut l := r.hkeys('context:salrunner')!
|
||||
|
||||
for name in l {
|
||||
res << get(name: name, fromdb: true)!
|
||||
}
|
||||
return res
|
||||
} else {
|
||||
// load from memory
|
||||
for _, client in salrunner_global {
|
||||
res << client
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o SalrunnerServer) !SalrunnerServer {
|
||||
mut o2 := obj_init(o)!
|
||||
salrunner_global[o2.name] = &o2
|
||||
salrunner_default = o2.name
|
||||
return o2
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'salrunner.') {
|
||||
return
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'salrunner.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for mut install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
install_action.done = true
|
||||
}
|
||||
}
|
||||
mut other_actions := plbook.find(filter: 'salrunner.')!
|
||||
for mut other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action salrunner.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action salrunner.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut salrunner_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${salrunner_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action salrunner.${other_action.name}')
|
||||
salrunner_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action salrunner.${other_action.name}')
|
||||
salrunner_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action salrunner.${other_action.name}')
|
||||
salrunner_obj.restart()!
|
||||
}
|
||||
}
|
||||
other_action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager {
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat {
|
||||
.screen {
|
||||
console.print_debug("installer: salrunner' startupmanager get screen")
|
||||
return startupmanager.get(.screen)!
|
||||
}
|
||||
.zinit {
|
||||
console.print_debug("installer: salrunner' startupmanager get zinit")
|
||||
return startupmanager.get(.zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug("installer: salrunner' startupmanager get systemd")
|
||||
return startupmanager.get(.systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug("installer: salrunner' startupmanager get auto")
|
||||
return startupmanager.get(.auto)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self SalrunnerServer) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('installer: salrunner start')
|
||||
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('installer: salrunner starting with ${zprocess.startuptype}...')
|
||||
|
||||
sm.new(zprocess)!
|
||||
|
||||
sm.start(zprocess.name)!
|
||||
}
|
||||
|
||||
start_post()!
|
||||
|
||||
for _ in 0 .. 50 {
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
return error('salrunner did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) stop() ! {
|
||||
switch(self.name)
|
||||
stop_pre()!
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
sm.stop(zprocess.name)!
|
||||
}
|
||||
stop_post()!
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) running() !bool {
|
||||
switch(self.name)
|
||||
|
||||
// walk over the generic processes, if not running return
|
||||
for zprocess in startupcmd()! {
|
||||
if zprocess.startuptype != .screen {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
r := sm.running(zprocess.name)!
|
||||
if r == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return running()!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn (mut self SalrunnerServer) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for salrunner
|
||||
pub fn switch(name string) {
|
||||
salrunner_default = name
|
||||
}
|
||||
62
lib/installers/horus/salrunner/salrunner_model.v
Normal file
62
lib/installers/horus/salrunner/salrunner_model.v
Normal file
@@ -0,0 +1,62 @@
|
||||
module salrunner
|
||||
|
||||
import os
|
||||
import incubaid.herolib.data.paramsparser
|
||||
import incubaid.herolib.data.encoderhero
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.core.pathlib
|
||||
|
||||
const version = '0.1.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
@[heap]
|
||||
pub struct SalrunnerServer {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string = os.join_path(os.home_dir(), 'hero/bin/runner_sal')
|
||||
redis_addr string = '127.0.0.1:6379'
|
||||
log_level string = 'info'
|
||||
repo_path string = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ SalrunnerServer) !SalrunnerServer {
|
||||
mut mycfg := mycfg_
|
||||
if mycfg.name == '' {
|
||||
mycfg.name = 'default'
|
||||
}
|
||||
if mycfg.binary_path == '' {
|
||||
mycfg.binary_path = os.join_path(os.home_dir(), 'hero/var/bin/runner_sal')
|
||||
}
|
||||
if mycfg.redis_addr == '' {
|
||||
mycfg.redis_addr = '127.0.0.1:6379'
|
||||
}
|
||||
if mycfg.log_level == '' {
|
||||
mycfg.log_level = 'info'
|
||||
}
|
||||
if mycfg.repo_path == '' {
|
||||
mycfg.repo_path = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
return mycfg
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
mut server := get()!
|
||||
// Ensure the binary directory exists
|
||||
mut binary_path_obj := pathlib.get(server.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj SalrunnerServer) !string {
|
||||
return encoderhero.encode[SalrunnerServer](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !SalrunnerServer {
|
||||
mut obj := encoderhero.decode[SalrunnerServer](heroscript)!
|
||||
return obj
|
||||
}
|
||||
5
lib/installers/horus/salrunner/templates/atemplate.yaml
Normal file
5
lib/installers/horus/salrunner/templates/atemplate.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ${cfg.configpath}
|
||||
|
||||
|
||||
12
lib/installers/horus/supervisor/.heroscript
Normal file
12
lib/installers/horus/supervisor/.heroscript
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'SupervisorServer'
|
||||
singleton:0
|
||||
templates:1
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
startupmanager:1
|
||||
hasconfig:1
|
||||
build:1
|
||||
144
lib/installers/horus/supervisor/readme.md
Normal file
144
lib/installers/horus/supervisor/readme.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Supervisor Installer
|
||||
|
||||
A V language installer module for building and managing the Supervisor service. This installer handles the complete lifecycle of the Supervisor binary from the Horus workspace.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Rust Installation**: Installs Rust toolchain if not present
|
||||
- **Git Repository Management**: Clones and manages the horus repository
|
||||
- **Binary Building**: Compiles the supervisor binary from the horus workspace
|
||||
- **Service Management**: Start/stop/restart via zinit
|
||||
- **Configuration**: Customizable Redis, HTTP, and WebSocket ports
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Using the Example Script
|
||||
|
||||
```bash
|
||||
cd /root/code/github/freeflowuniverse/herolib/examples/installers/horus
|
||||
./supervisor.vsh
|
||||
```
|
||||
|
||||
### Manual Usage
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.installers.horus.supervisor as supervisor_installer
|
||||
|
||||
mut supervisor := supervisor_installer.get()!
|
||||
supervisor.install()!
|
||||
supervisor.start()!
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
!!supervisor.configure
|
||||
name:'default'
|
||||
binary_path:'/hero/var/bin/supervisor'
|
||||
redis_addr:'127.0.0.1:6379'
|
||||
http_port:8082
|
||||
ws_port:9654
|
||||
log_level:'info'
|
||||
repo_path:'/root/code/git.ourworld.tf/herocode/horus'
|
||||
```
|
||||
|
||||
### Configuration Fields
|
||||
|
||||
- **name**: Instance name (default: 'default')
|
||||
- **binary_path**: Path where the supervisor binary will be installed (default: '/hero/var/bin/supervisor')
|
||||
- **redis_addr**: Redis server address (default: '127.0.0.1:6379')
|
||||
- **http_port**: HTTP API port (default: 8082)
|
||||
- **ws_port**: WebSocket API port (default: 9654)
|
||||
- **log_level**: Rust log level - trace, debug, info, warn, error (default: 'info')
|
||||
- **repo_path**: Path to clone the horus repository (default: '/root/code/git.ourworld.tf/herocode/horus')
|
||||
|
||||
## Commands
|
||||
|
||||
### Install
|
||||
Builds the supervisor binary from the horus workspace. This will:
|
||||
1. Install Rust if not present
|
||||
2. Clone the horus repository from git.ourworld.tf
|
||||
3. Build the supervisor binary with `cargo build -p hero-supervisor --release`
|
||||
|
||||
```bash
|
||||
hero supervisor.install
|
||||
```
|
||||
|
||||
### Start
|
||||
Starts the supervisor service using zinit:
|
||||
|
||||
```bash
|
||||
hero supervisor.start
|
||||
```
|
||||
|
||||
### Stop
|
||||
Stops the running service:
|
||||
|
||||
```bash
|
||||
hero supervisor.stop
|
||||
```
|
||||
|
||||
### Restart
|
||||
Restarts the service:
|
||||
|
||||
```bash
|
||||
hero supervisor.restart
|
||||
```
|
||||
|
||||
### Destroy
|
||||
Stops the service and removes all files:
|
||||
|
||||
```bash
|
||||
hero supervisor.destroy
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Dependencies**:
|
||||
- Rust toolchain (automatically installed)
|
||||
- Git (for cloning repository)
|
||||
- Redis (must be running separately)
|
||||
- Mycelium (must be installed and running separately)
|
||||
|
||||
## Architecture
|
||||
|
||||
The installer follows the standard herolib installer pattern:
|
||||
|
||||
- **supervisor_model.v**: Configuration structure and initialization
|
||||
- **supervisor_actions.v**: Build, install, start, stop, destroy logic
|
||||
- **supervisor_factory_.v**: Factory pattern for instance management
|
||||
|
||||
## Notes
|
||||
|
||||
- The installer builds from source rather than downloading pre-built binaries
|
||||
- Mycelium is expected to be already installed and running in the environment
|
||||
- Redis must be running and accessible at the configured address
|
||||
- The binary is built with `RUSTFLAGS="-A warnings"` to suppress warnings
|
||||
- Service management uses zinit by default
|
||||
|
||||
## Example Workflow
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.installers.horus.supervisor as sv
|
||||
|
||||
// Get installer instance
|
||||
mut supervisor := sv.get()!
|
||||
|
||||
// Customize configuration
|
||||
supervisor.redis_addr = '127.0.0.1:6379'
|
||||
supervisor.http_port = 8082
|
||||
supervisor.log_level = 'debug'
|
||||
sv.set(supervisor)!
|
||||
|
||||
// Build and start
|
||||
supervisor.install()!
|
||||
supervisor.start()!
|
||||
|
||||
// Check status
|
||||
if supervisor.running()! {
|
||||
println('Supervisor is running on port ${supervisor.http_port}')
|
||||
}
|
||||
|
||||
// Later: cleanup
|
||||
supervisor.destroy()!
|
||||
```
|
||||
0
lib/installers/horus/supervisor/readme_new.md
Normal file
0
lib/installers/horus/supervisor/readme_new.md
Normal file
253
lib/installers/horus/supervisor/supervisor_actions.v
Normal file
253
lib/installers/horus/supervisor/supervisor_actions.v
Normal file
@@ -0,0 +1,253 @@
|
||||
module supervisor
|
||||
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.ui.console
|
||||
import incubaid.herolib.core.texttools
|
||||
import incubaid.herolib.core.pathlib
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import incubaid.herolib.installers.ulist
|
||||
import incubaid.herolib.installers.lang.rust
|
||||
import incubaid.herolib.develop.gittools
|
||||
import os
|
||||
|
||||
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
|
||||
mut cfg := get()!
|
||||
mut res := []startupmanager.ZProcessNewArgs{}
|
||||
|
||||
res << startupmanager.ZProcessNewArgs{
|
||||
name: 'supervisor'
|
||||
cmd: '${cfg.binary_path} --redis-addr ${cfg.redis_addr} --api-http-port ${cfg.http_port} --api-ws-port ${cfg.ws_port}'
|
||||
env: {
|
||||
'HOME': os.home_dir()
|
||||
'RUST_LOG': cfg.log_level
|
||||
'RUST_LOG_STYLE': 'never'
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn running() !bool {
|
||||
mut cfg := get()!
|
||||
// Check if the process is running by checking the HTTP port
|
||||
res := osal.exec(cmd: 'curl -fsSL http://127.0.0.1:${cfg.http_port} || exit 1', stdout: false, raise_error: false)!
|
||||
return res.exit_code == 0
|
||||
}
|
||||
|
||||
fn start_pre() ! {
|
||||
}
|
||||
|
||||
fn start_post() ! {
|
||||
}
|
||||
|
||||
fn stop_pre() ! {
|
||||
}
|
||||
|
||||
fn stop_post() ! {
|
||||
}
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if a certain version or above is installed
|
||||
fn installed() !bool {
|
||||
mut cfg := get()!
|
||||
|
||||
// Check if the binary exists
|
||||
mut binary := pathlib.get(cfg.binary_path)
|
||||
if !binary.exists() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// get the Upload List of the files
|
||||
fn ulist_get() !ulist.UList {
|
||||
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
|
||||
return ulist.UList{}
|
||||
}
|
||||
|
||||
// uploads to S3 server if configured
|
||||
fn upload() ! {
|
||||
// installers.upload(
|
||||
// cmdname: 'supervisor'
|
||||
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/supervisor'
|
||||
// )!
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install supervisor')
|
||||
// For supervisor, we build from source instead of downloading
|
||||
build()!
|
||||
}
|
||||
|
||||
// Public function to build supervisor without requiring factory/redis
|
||||
pub fn build_supervisor() ! {
|
||||
console.print_header('build supervisor')
|
||||
println('📦 Starting supervisor build process...\n')
|
||||
|
||||
// Use default config instead of getting from factory
|
||||
println('⚙️ Initializing configuration...')
|
||||
mut cfg := SupervisorServer{}
|
||||
println('✅ Configuration initialized')
|
||||
println(' - Binary path: ${cfg.binary_path}')
|
||||
println(' - Redis address: ${cfg.redis_addr}')
|
||||
println(' - HTTP port: ${cfg.http_port}')
|
||||
println(' - WS port: ${cfg.ws_port}\n')
|
||||
|
||||
// Ensure Redis is installed and running (required for supervisor)
|
||||
println('🔍 Step 1/4: Checking Redis dependency...')
|
||||
|
||||
// First check if redis-server is installed
|
||||
if !osal.cmd_exists_profile('redis-server') {
|
||||
println('⚠️ Redis is not installed')
|
||||
println('📥 Installing Redis...')
|
||||
osal.package_install('redis-server')!
|
||||
println('✅ Redis installed')
|
||||
} else {
|
||||
println('✅ Redis is already installed')
|
||||
}
|
||||
|
||||
// Now check if it's running
|
||||
println('🔍 Checking if Redis is running...')
|
||||
redis_check := osal.exec(cmd: 'redis-cli -c -p 6379 ping', stdout: false, raise_error: false)!
|
||||
if redis_check.exit_code != 0 {
|
||||
println('⚠️ Redis is not running')
|
||||
println('🚀 Starting Redis...')
|
||||
osal.exec(cmd: 'systemctl start redis-server')!
|
||||
println('✅ Redis started successfully\n')
|
||||
} else {
|
||||
println('✅ Redis is already running\n')
|
||||
}
|
||||
|
||||
// Ensure rust is installed
|
||||
println('🔍 Step 2/4: Checking Rust dependency...')
|
||||
mut rust_installer := rust.get()!
|
||||
res := osal.exec(cmd: 'rustc -V', stdout: false, raise_error: false)!
|
||||
if res.exit_code != 0 {
|
||||
println('📥 Installing Rust...')
|
||||
rust_installer.install()!
|
||||
println('✅ Rust installed\n')
|
||||
} else {
|
||||
println('✅ Rust is already installed: ${res.output.trim_space()}\n')
|
||||
}
|
||||
|
||||
// Clone or get the repository
|
||||
println('🔍 Step 3/4: Cloning/updating horus repository...')
|
||||
mut gs := gittools.new()!
|
||||
mut repo := gs.get_repo(
|
||||
url: 'https://git.ourworld.tf/herocode/horus.git'
|
||||
pull: true
|
||||
reset: false
|
||||
)!
|
||||
|
||||
// Update the path to the actual cloned repo
|
||||
cfg.repo_path = repo.path()
|
||||
println('✅ Repository ready at: ${cfg.repo_path}\n')
|
||||
|
||||
// Build the supervisor binary from the horus workspace
|
||||
println('🔍 Step 4/4: Building supervisor binary...')
|
||||
println('⚠️ This may take several minutes (compiling Rust code)...')
|
||||
println('📝 Running: cargo build -p hero-supervisor --release\n')
|
||||
|
||||
cmd := 'cd ${cfg.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p hero-supervisor --release'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
println('\n✅ Build completed successfully')
|
||||
|
||||
// Ensure binary directory exists and copy the binary
|
||||
println('📁 Preparing binary directory: ${cfg.binary_path}')
|
||||
mut binary_path_obj := pathlib.get(cfg.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
|
||||
// Copy the built binary to the configured location
|
||||
source_binary := '${cfg.repo_path}/target/release/supervisor'
|
||||
println('📋 Copying binary from: ${source_binary}')
|
||||
println('📋 Copying binary to: ${cfg.binary_path}')
|
||||
mut source_file := pathlib.get_file(path: source_binary)!
|
||||
source_file.copy(dest: cfg.binary_path, rsync: false)!
|
||||
|
||||
println('\n🎉 Supervisor built successfully!')
|
||||
println('📍 Binary location: ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn build() ! {
|
||||
console.print_header('build supervisor')
|
||||
|
||||
mut cfg := get()!
|
||||
|
||||
// Ensure Redis is installed and running (required for supervisor)
|
||||
console.print_debug('Checking if Redis is installed and running...')
|
||||
redis_check := osal.exec(cmd: 'redis-cli -c -p 6379 ping', stdout: false, raise_error: false)!
|
||||
if redis_check.exit_code != 0 {
|
||||
console.print_header('Redis is not running, checking if installed...')
|
||||
if !osal.cmd_exists_profile('redis-server') {
|
||||
console.print_header('Installing Redis...')
|
||||
osal.package_install('redis-server')!
|
||||
}
|
||||
console.print_header('Starting Redis...')
|
||||
osal.exec(cmd: 'systemctl start redis-server')!
|
||||
console.print_debug('Redis started successfully')
|
||||
} else {
|
||||
console.print_debug('Redis is already running')
|
||||
}
|
||||
|
||||
// Ensure rust is installed
|
||||
console.print_debug('Checking if Rust is installed...')
|
||||
mut rust_installer := rust.get()!
|
||||
res := osal.exec(cmd: 'rustc -V', stdout: false, raise_error: false)!
|
||||
if res.exit_code != 0 {
|
||||
console.print_header('Installing Rust first...')
|
||||
rust_installer.install()!
|
||||
} else {
|
||||
console.print_debug('Rust is already installed: ${res.output.trim_space()}')
|
||||
}
|
||||
|
||||
// Clone or get the repository
|
||||
console.print_debug('Cloning/updating horus repository...')
|
||||
mut gs := gittools.new()!
|
||||
mut repo := gs.get_repo(
|
||||
url: 'https://git.ourworld.tf/herocode/horus.git'
|
||||
pull: true
|
||||
reset: false
|
||||
)!
|
||||
|
||||
// Update the path to the actual cloned repo
|
||||
cfg.repo_path = repo.path()
|
||||
set(cfg)!
|
||||
console.print_debug('Repository path: ${cfg.repo_path}')
|
||||
|
||||
// Build the supervisor binary from the horus workspace
|
||||
console.print_header('Building supervisor binary (this may take several minutes)...')
|
||||
console.print_debug('Running: cargo build -p hero-supervisor --release')
|
||||
console.print_debug('Build output:')
|
||||
|
||||
cmd := 'cd ${cfg.repo_path} && . ~/.cargo/env && RUSTFLAGS="-A warnings" cargo build -p hero-supervisor --release'
|
||||
osal.execute_stdout(cmd)!
|
||||
|
||||
console.print_debug('Build completed successfully')
|
||||
|
||||
// Ensure binary directory exists and copy the binary
|
||||
console.print_debug('Preparing binary directory: ${cfg.binary_path}')
|
||||
mut binary_path_obj := pathlib.get(cfg.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
|
||||
// Copy the built binary to the configured location
|
||||
source_binary := '${cfg.repo_path}/target/release/supervisor'
|
||||
console.print_debug('Copying binary from: ${source_binary}')
|
||||
console.print_debug('Copying binary to: ${cfg.binary_path}')
|
||||
mut source_file := pathlib.get_file(path: source_binary)!
|
||||
source_file.copy(dest: cfg.binary_path, rsync: false)!
|
||||
|
||||
console.print_header('supervisor built successfully at ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
mut server := get()!
|
||||
server.stop()!
|
||||
|
||||
osal.process_kill_recursive(name: 'supervisor')!
|
||||
|
||||
// Remove the built binary
|
||||
osal.rm(server.binary_path)!
|
||||
}
|
||||
324
lib/installers/horus/supervisor/supervisor_factory_.v
Normal file
324
lib/installers/horus/supervisor/supervisor_factory_.v
Normal file
@@ -0,0 +1,324 @@
|
||||
module supervisor
|
||||
|
||||
import incubaid.herolib.core.base
|
||||
import incubaid.herolib.core.playbook { PlayBook }
|
||||
import incubaid.herolib.ui.console
|
||||
import json
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import time
|
||||
|
||||
__global (
|
||||
supervisor_global map[string]&SupervisorServer
|
||||
supervisor_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string
|
||||
redis_addr string
|
||||
http_port int
|
||||
ws_port int
|
||||
log_level string
|
||||
repo_path string
|
||||
fromdb bool // will load from filesystem
|
||||
create bool // default will not create if not exist
|
||||
}
|
||||
|
||||
pub fn new(args ArgsGet) !&SupervisorServer {
|
||||
mut obj := SupervisorServer{
|
||||
name: args.name
|
||||
binary_path: args.binary_path
|
||||
redis_addr: args.redis_addr
|
||||
http_port: args.http_port
|
||||
ws_port: args.ws_port
|
||||
log_level: args.log_level
|
||||
repo_path: args.repo_path
|
||||
}
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
}
|
||||
|
||||
pub fn get(args ArgsGet) !&SupervisorServer {
|
||||
mut context := base.context()!
|
||||
supervisor_default = args.name
|
||||
if args.fromdb || args.name !in supervisor_global {
|
||||
mut r := context.redis()!
|
||||
if r.hexists('context:supervisor', args.name)! {
|
||||
data := r.hget('context:supervisor', args.name)!
|
||||
if data.len == 0 {
|
||||
print_backtrace()
|
||||
return error('SupervisorServer with name: ${args.name} does not exist, prob bug.')
|
||||
}
|
||||
mut obj := json.decode(SupervisorServer, data)!
|
||||
set_in_mem(obj)!
|
||||
} else {
|
||||
if args.create {
|
||||
new(args)!
|
||||
} else {
|
||||
print_backtrace()
|
||||
return error("SupervisorServer with name '${args.name}' does not exist")
|
||||
}
|
||||
}
|
||||
return get(name: args.name)! // no longer from db nor create
|
||||
}
|
||||
return supervisor_global[args.name] or {
|
||||
print_backtrace()
|
||||
return error('could not get config for supervisor with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o SupervisorServer) ! {
|
||||
mut o2 := set_in_mem(o)!
|
||||
supervisor_default = o2.name
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hset('context:supervisor', o2.name, json.encode(o2))!
|
||||
}
|
||||
|
||||
// does the config exists?
|
||||
pub fn exists(args ArgsGet) !bool {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
return r.hexists('context:supervisor', args.name)!
|
||||
}
|
||||
|
||||
pub fn delete(args ArgsGet) ! {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hdel('context:supervisor', args.name)!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct ArgsList {
|
||||
pub mut:
|
||||
fromdb bool // will load from filesystem
|
||||
}
|
||||
|
||||
// if fromdb set: load from filesystem, and not from mem, will also reset what is in mem
|
||||
pub fn list(args ArgsList) ![]&SupervisorServer {
|
||||
mut res := []&SupervisorServer{}
|
||||
mut context := base.context()!
|
||||
if args.fromdb {
|
||||
// reset what is in mem
|
||||
supervisor_global = map[string]&SupervisorServer{}
|
||||
supervisor_default = ''
|
||||
}
|
||||
if args.fromdb {
|
||||
mut r := context.redis()!
|
||||
mut l := r.hkeys('context:supervisor')!
|
||||
|
||||
for name in l {
|
||||
res << get(name: name, fromdb: true)!
|
||||
}
|
||||
return res
|
||||
} else {
|
||||
// load from memory
|
||||
for _, client in supervisor_global {
|
||||
res << client
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o SupervisorServer) !SupervisorServer {
|
||||
mut o2 := obj_init(o)!
|
||||
supervisor_global[o2.name] = &o2
|
||||
supervisor_default = o2.name
|
||||
return o2
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'supervisor.') {
|
||||
return
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'supervisor.configure')!
|
||||
if install_actions.len > 0 {
|
||||
for mut install_action in install_actions {
|
||||
heroscript := install_action.heroscript()
|
||||
mut obj2 := heroscript_loads(heroscript)!
|
||||
set(obj2)!
|
||||
install_action.done = true
|
||||
}
|
||||
}
|
||||
mut other_actions := plbook.find(filter: 'supervisor.')!
|
||||
for mut other_action in other_actions {
|
||||
if other_action.name in ['destroy', 'install', 'build'] {
|
||||
mut p := other_action.params
|
||||
reset := p.get_default_false('reset')
|
||||
if other_action.name == 'destroy' || reset {
|
||||
console.print_debug('install action supervisor.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action supervisor.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut supervisor_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${supervisor_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action supervisor.${other_action.name}')
|
||||
supervisor_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action supervisor.${other_action.name}')
|
||||
supervisor_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action supervisor.${other_action.name}')
|
||||
supervisor_obj.restart()!
|
||||
}
|
||||
}
|
||||
other_action.done = true
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.StartupManager {
|
||||
// unknown
|
||||
// screen
|
||||
// zinit
|
||||
// tmux
|
||||
// systemd
|
||||
match cat {
|
||||
.screen {
|
||||
console.print_debug("installer: supervisor' startupmanager get screen")
|
||||
return startupmanager.get(.screen)!
|
||||
}
|
||||
.zinit {
|
||||
console.print_debug("installer: supervisor' startupmanager get zinit")
|
||||
return startupmanager.get(.zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug("installer: supervisor' startupmanager get systemd")
|
||||
return startupmanager.get(.systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug("installer: supervisor' startupmanager get auto")
|
||||
return startupmanager.get(.auto)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self SupervisorServer) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('installer: supervisor start')
|
||||
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('installer: supervisor starting with ${zprocess.startuptype}...')
|
||||
|
||||
sm.new(zprocess)!
|
||||
|
||||
sm.start(zprocess.name)!
|
||||
}
|
||||
|
||||
start_post()!
|
||||
|
||||
for _ in 0 .. 50 {
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
return error('supervisor did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) stop() ! {
|
||||
switch(self.name)
|
||||
stop_pre()!
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
sm.stop(zprocess.name)!
|
||||
}
|
||||
stop_post()!
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) running() !bool {
|
||||
switch(self.name)
|
||||
|
||||
// walk over the generic processes, if not running return
|
||||
for zprocess in startupcmd()! {
|
||||
if zprocess.startuptype != .screen {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
r := sm.running(zprocess.name)!
|
||||
if r == false {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return running()!
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct InstallArgs {
|
||||
pub mut:
|
||||
reset bool
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn (mut self SupervisorServer) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for supervisor
|
||||
pub fn switch(name string) {
|
||||
supervisor_default = name
|
||||
}
|
||||
70
lib/installers/horus/supervisor/supervisor_model.v
Normal file
70
lib/installers/horus/supervisor/supervisor_model.v
Normal file
@@ -0,0 +1,70 @@
|
||||
module supervisor
|
||||
|
||||
import os
|
||||
import incubaid.herolib.data.paramsparser
|
||||
import incubaid.herolib.data.encoderhero
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.core.pathlib
|
||||
|
||||
const version = '0.1.0'
|
||||
const singleton = true
|
||||
const default = true
|
||||
|
||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||
@[heap]
|
||||
pub struct SupervisorServer {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string = os.join_path(os.home_dir(), 'hero/bin/supervisor')
|
||||
redis_addr string = '127.0.0.1:6379'
|
||||
http_port int = 8082
|
||||
ws_port int = 9654
|
||||
log_level string = 'info'
|
||||
repo_path string = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ SupervisorServer) !SupervisorServer {
|
||||
mut mycfg := mycfg_
|
||||
if mycfg.name == '' {
|
||||
mycfg.name = 'default'
|
||||
}
|
||||
if mycfg.binary_path == '' {
|
||||
mycfg.binary_path = os.join_path(os.home_dir(), 'hero/bin/supervisor')
|
||||
}
|
||||
if mycfg.redis_addr == '' {
|
||||
mycfg.redis_addr = '127.0.0.1:6379'
|
||||
}
|
||||
if mycfg.http_port == 0 {
|
||||
mycfg.http_port = 8082
|
||||
}
|
||||
if mycfg.ws_port == 0 {
|
||||
mycfg.ws_port = 9654
|
||||
}
|
||||
if mycfg.log_level == '' {
|
||||
mycfg.log_level = 'info'
|
||||
}
|
||||
if mycfg.repo_path == '' {
|
||||
mycfg.repo_path = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
return mycfg
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
mut server := get()!
|
||||
// Ensure the binary directory exists
|
||||
mut binary_path_obj := pathlib.get(server.binary_path)
|
||||
osal.dir_ensure(binary_path_obj.path_dir())!
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_dumps(obj SupervisorServer) !string {
|
||||
return encoderhero.encode[SupervisorServer](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !SupervisorServer {
|
||||
mut obj := encoderhero.decode[SupervisorServer](heroscript)!
|
||||
return obj
|
||||
}
|
||||
5
lib/installers/horus/supervisor/templates/atemplate.yaml
Normal file
5
lib/installers/horus/supervisor/templates/atemplate.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ${cfg.configpath}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ fn startupcmd() ![]startupmanager.ZProcessNewArgs {
|
||||
start: true
|
||||
}
|
||||
}
|
||||
osal.dir_ensure(os.home_dir() + '/hero/cfg/zinit')!
|
||||
osal.dir_ensure(os.join_path(os.home_dir(), 'hero/cfg/zinit'))!
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
@@ -16,19 +16,34 @@ pub fn get(cat StartupManagerType) !StartupManager {
|
||||
mut sm := StartupManager{
|
||||
cat: cat
|
||||
}
|
||||
if sm.cat == .auto {
|
||||
// Try to get a ZinitRPC client and check if it can discover RPC methods.
|
||||
// This implies the zinit daemon is running and accessible via its socket.
|
||||
mut zinit_client_test := zinit.get(create: true)! // 'create:true' ensures a client object is initiated even if the socket isn't active.
|
||||
if _ := zinit_client_test.rpc_discover() {
|
||||
sm.cat = .zinit
|
||||
} else {
|
||||
sm.cat = .screen
|
||||
match sm.cat {
|
||||
.zinit {
|
||||
mut zinit_client_test := zinit.get()! // 'create:true' ensures a client object is initiated even if the socket isn't active.
|
||||
if _ := zinit_client_test.rpc_discover() {
|
||||
sm.cat = .zinit
|
||||
} else {
|
||||
return error('zinit not found ${err}')
|
||||
}
|
||||
|
||||
}
|
||||
.auto {
|
||||
// Try to get a ZinitRPC client and check if it can discover RPC methods.
|
||||
// This implies the zinit daemon is running and accessible via its socket.
|
||||
// Since it's auto doesn't insist and die if it can't find zinit.
|
||||
mut zinit_client_test := zinit.get(create: true)! // 'create:true' ensures a client object is initiated even if the socket isn't active.
|
||||
if _ := zinit_client_test.rpc_discover() {
|
||||
sm.cat = .zinit
|
||||
} else {
|
||||
sm.cat = .screen
|
||||
}
|
||||
}
|
||||
.unknown {
|
||||
print_backtrace()
|
||||
return error("can't determine startup manager type, need to be a known one.")
|
||||
}
|
||||
else {
|
||||
return sm
|
||||
}
|
||||
}
|
||||
if sm.cat == .unknown {
|
||||
print_backtrace()
|
||||
return error("can't determine startup manager type, need to be a known one.")
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ pub fn decode_request(data string) !Request {
|
||||
// Returns:
|
||||
// - A JSON string representation of the Request
|
||||
pub fn (req Request) encode() string {
|
||||
return json2.encode(req, prettify: true)
|
||||
return json2.encode(req)
|
||||
}
|
||||
|
||||
// validate checks if the Request object contains all required fields
|
||||
|
||||
Reference in New Issue
Block a user