feat: migrate Redis installer and integrate into coordinator
- Created coordinator installer - Migrated Redis installer to new modular pattern (_model.v, _actions.v, _factory_.v) - Fixed Redis config template for 7.0.15 compatibility (commented out unsupported directives) - Added Redis dependency check to coordinator installer - Coordinator now auto-installs and starts Redis if not available - Added progress indicators to coordinator build process - Consolidated Redis example scripts - All tests passing: Redis installation, coordinator build, and idempotency verified
This commit is contained in:
39
examples/installers/base/redis.vsh
Executable file
39
examples/installers/base/redis.vsh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.base
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import time
|
||||
|
||||
println('=== Redis Installer Example ===\n')
|
||||
|
||||
// Check if redis is already installed
|
||||
if osal.cmd_exists_profile('redis-server') {
|
||||
println('✅ redis-server is already installed')
|
||||
|
||||
// Check if it's running
|
||||
if base.check(port: 6379) {
|
||||
println('✅ Redis is running and responding')
|
||||
} else {
|
||||
println('⚠️ Redis is installed but not running, starting it...')
|
||||
// Use systemctl to start redis
|
||||
osal.exec(cmd: 'systemctl start redis-server')!
|
||||
|
||||
// Wait a moment for redis to start
|
||||
time.sleep(1000)
|
||||
|
||||
if base.check(port: 6379) {
|
||||
println('✅ Redis started successfully')
|
||||
} else {
|
||||
println('❌ Failed to start redis')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println('Redis not found, installing...')
|
||||
|
||||
// Install and start redis
|
||||
base.redis_install(port: 6379, start: true)!
|
||||
|
||||
println('✅ Redis installed and started successfully')
|
||||
}
|
||||
|
||||
println('\n✅ Done!')
|
||||
24
examples/installers/base/redis_test_template.vsh
Executable file
24
examples/installers/base/redis_test_template.vsh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.base
|
||||
|
||||
println('=== Testing Redis Template Application ===\n')
|
||||
|
||||
// Stop redis first
|
||||
println('Stopping redis...')
|
||||
base.stop() or {}
|
||||
|
||||
// Start redis (this will apply the template)
|
||||
println('Starting redis with template...')
|
||||
base.start(port: 6379, datadir: '/root/hero/var/redis')!
|
||||
|
||||
println('✅ Redis started')
|
||||
|
||||
// Verify it's running
|
||||
if base.check(port: 6379) {
|
||||
println('✅ Redis is responding to ping')
|
||||
} else {
|
||||
println('❌ Redis is not responding')
|
||||
}
|
||||
|
||||
println('\n✅ Done!')
|
||||
BIN
examples/installers/infra/herocoordinator
Executable file
BIN
examples/installers/infra/herocoordinator
Executable file
Binary file not shown.
30
examples/installers/infra/herocoordinator.vsh
Executable file
30
examples/installers/infra/herocoordinator.vsh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
|
||||
import incubaid.herolib.installers.infra.herocoordinator
|
||||
|
||||
// Example usage of herocoordinator installer
|
||||
// This will:
|
||||
// 1. Check and install Redis if not running (required dependency)
|
||||
// 2. Install Rust if not already installed
|
||||
// 3. Clone the horus repository
|
||||
// 4. Build the herocoordinator binary
|
||||
|
||||
// Build and install herocoordinator
|
||||
// This will automatically check and install Redis and Rust if needed
|
||||
println('Building coordinator from horus repository...')
|
||||
println('(This will install Redis and Rust if not already installed)\n')
|
||||
|
||||
// Call build_coordinator - it will handle all dependencies
|
||||
herocoordinator.build_coordinator()!
|
||||
|
||||
println('\n✅ Herocoordinator built and installed successfully!')
|
||||
println('Binary location: /hero/var/bin/coordinator')
|
||||
|
||||
// Note: To start the service, uncomment the lines below
|
||||
// (requires proper zinit or screen session setup)
|
||||
// herocoordinator.start()!
|
||||
// if herocoordinator.running()! {
|
||||
// println('Herocoordinator is running!')
|
||||
// }
|
||||
// herocoordinator.stop()!
|
||||
// herocoordinator.destroy()!
|
||||
12
lib/installers/base/redis/.heroscript
Normal file
12
lib/installers/base/redis/.heroscript
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'RedisInstall'
|
||||
singleton:0
|
||||
templates:1
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
startupmanager:1
|
||||
hasconfig:1
|
||||
build:0
|
||||
80
lib/installers/base/redis/MIGRATION_NOTES.md
Normal file
80
lib/installers/base/redis/MIGRATION_NOTES.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Redis Installer Migration Notes
|
||||
|
||||
## Old Installer Logic (redis.v)
|
||||
|
||||
### Key Behaviors:
|
||||
1. **datadir**: `${os.home_dir()}/hero/var/redis` (NOT `/var/lib/redis`)
|
||||
2. **Template Usage**: YES - always applies template before starting
|
||||
3. **macOS Support**: YES - uses `--daemonize yes` flag
|
||||
4. **Linux Support**: YES - uses startupmanager
|
||||
5. **Check before start**: Returns early if already running
|
||||
6. **Config path**:
|
||||
- Linux: `/etc/redis/redis.conf`
|
||||
- macOS: `${datadir}/redis.conf`
|
||||
|
||||
### Flow:
|
||||
```
|
||||
redis_install()
|
||||
→ checks if running (unless reset)
|
||||
→ installs package (redis-server on Linux, redis on macOS)
|
||||
→ creates datadir
|
||||
→ calls start()
|
||||
|
||||
start()
|
||||
→ returns if already running
|
||||
→ configure() - applies template
|
||||
→ kills existing processes
|
||||
→ macOS: starts with daemonize
|
||||
→ Linux: uses startupmanager
|
||||
→ waits for ping response
|
||||
```
|
||||
|
||||
## New Installer Logic (redis/)
|
||||
|
||||
### Matching Behaviors:
|
||||
1. ✅ **datadir**: `${os.home_dir()}/hero/var/redis` - FIXED
|
||||
2. ✅ **Template Usage**: YES - `configure()` called in `start_pre()`
|
||||
3. ✅ **macOS Support**: YES - handled in `start_pre()`
|
||||
4. ✅ **Linux Support**: YES - via `startupcmd()` and startupmanager
|
||||
5. ✅ **Check before start**: Added in `start_pre()`
|
||||
6. ✅ **Config path**: Same logic in `configfilepath()`
|
||||
|
||||
### Flow:
|
||||
```
|
||||
install()
|
||||
→ checks if installed
|
||||
→ installs package (redis-server on Linux, redis on macOS)
|
||||
→ creates datadir
|
||||
|
||||
start()
|
||||
→ calls start_pre()
|
||||
→ on Linux: uses startupmanager with startupcmd()
|
||||
→ calls start_post()
|
||||
|
||||
start_pre()
|
||||
→ returns if already running
|
||||
→ configure() - applies template
|
||||
→ kills existing processes
|
||||
→ macOS: starts with daemonize
|
||||
|
||||
start_post()
|
||||
→ waits for ping response
|
||||
```
|
||||
|
||||
## Template Fixes
|
||||
|
||||
Fixed incompatible directives for Redis 7.0.15:
|
||||
- ✅ Commented out `locale-collate ""`
|
||||
- ✅ Commented out `set-max-listpack-entries 128`
|
||||
- ✅ Commented out `set-max-listpack-value 64`
|
||||
- ✅ Commented out `zset-max-listpack-entries 128`
|
||||
- ✅ Commented out `zset-max-listpack-value 64`
|
||||
|
||||
## Verification
|
||||
|
||||
The new installer now matches the old installer's logic exactly:
|
||||
- Same default datadir
|
||||
- Same template usage
|
||||
- Same platform handling
|
||||
- Same startup flow
|
||||
- Template is compatible with Redis 7.0.15
|
||||
44
lib/installers/base/redis/readme.md
Normal file
44
lib/installers/base/redis/readme.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# redis
|
||||
|
||||
|
||||
|
||||
To get started
|
||||
|
||||
```v
|
||||
|
||||
|
||||
import incubaid.herolib.installers.something.redis as redis_installer
|
||||
|
||||
heroscript:="
|
||||
!!redis.configure name:'test'
|
||||
password: '1234'
|
||||
port: 7701
|
||||
|
||||
!!redis.start name:'test' reset:1
|
||||
"
|
||||
|
||||
redis_installer.play(heroscript=heroscript)!
|
||||
|
||||
//or we can call the default and do a start with reset
|
||||
//mut installer:= redis_installer.get()!
|
||||
//installer.start(reset:true)!
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## example heroscript
|
||||
|
||||
|
||||
```hero
|
||||
!!redis.configure
|
||||
homedir: '/home/user/redis'
|
||||
username: 'admin'
|
||||
password: 'secretpassword'
|
||||
title: 'Some Title'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
|
||||
```
|
||||
|
||||
123
lib/installers/base/redis/redis_actions.v
Normal file
123
lib/installers/base/redis/redis_actions.v
Normal file
@@ -0,0 +1,123 @@
|
||||
module redis
|
||||
|
||||
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.systemd
|
||||
import incubaid.herolib.osal.startupmanager
|
||||
import incubaid.herolib.installers.ulist
|
||||
import incubaid.herolib.core
|
||||
import time
|
||||
import os
|
||||
|
||||
fn startupcmd() ![]startupmanager.ZProcessNewArgs {
|
||||
mut cfg := get()!
|
||||
mut res := []startupmanager.ZProcessNewArgs{}
|
||||
|
||||
res << startupmanager.ZProcessNewArgs{
|
||||
name: 'redis'
|
||||
cmd: 'redis-server ${configfilepath(cfg)}'
|
||||
env: {
|
||||
'HOME': os.home_dir()
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
fn running() !bool {
|
||||
mut cfg := get()!
|
||||
res := os.execute('redis-cli -c -p ${cfg.port} ping > /dev/null 2>&1')
|
||||
if res.exit_code == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn start_pre() ! {
|
||||
// Check if already running
|
||||
if running()! {
|
||||
return
|
||||
}
|
||||
|
||||
mut cfg := get()!
|
||||
|
||||
// Configure redis before starting (applies template)
|
||||
configure()!
|
||||
|
||||
// Kill any existing redis processes
|
||||
osal.process_kill_recursive(name: 'redis-server')!
|
||||
|
||||
// On macOS, start redis with daemonize (not via startupmanager)
|
||||
if core.platform()! == .osx {
|
||||
osal.exec(cmd: 'redis-server ${configfilepath(cfg)} --daemonize yes')!
|
||||
}
|
||||
}
|
||||
|
||||
fn start_post() ! {
|
||||
// Wait for redis to be ready
|
||||
for _ in 0 .. 100 {
|
||||
if running()! {
|
||||
console.print_debug('redis started.')
|
||||
return
|
||||
}
|
||||
time.sleep(100)
|
||||
}
|
||||
return error("Redis did not start properly could not do:'redis-cli -c ping'")
|
||||
}
|
||||
|
||||
fn stop_pre() ! {
|
||||
osal.execute_silent('redis-cli shutdown') or {}
|
||||
}
|
||||
|
||||
fn stop_post() ! {
|
||||
}
|
||||
|
||||
//////////////////// following actions are not specific to instance of the object
|
||||
|
||||
// checks if redis-server is installed
|
||||
fn installed() !bool {
|
||||
return osal.cmd_exists_profile('redis-server')
|
||||
}
|
||||
|
||||
// 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: 'redis'
|
||||
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/redis'
|
||||
// )!
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install redis')
|
||||
|
||||
if installed()! {
|
||||
console.print_debug('redis-server already installed')
|
||||
return
|
||||
}
|
||||
|
||||
// Install redis-server via package manager
|
||||
if core.is_linux()! {
|
||||
osal.package_install('redis-server')!
|
||||
} else {
|
||||
osal.package_install('redis')!
|
||||
}
|
||||
|
||||
mut cfg := get()!
|
||||
osal.execute_silent('mkdir -p ${cfg.datadir}')!
|
||||
|
||||
console.print_debug('redis-server installed successfully')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
mut cfg := get()!
|
||||
cfg.stop()!
|
||||
osal.process_kill_recursive(name: 'redis-server')!
|
||||
}
|
||||
307
lib/installers/base/redis/redis_factory_.v
Normal file
307
lib/installers/base/redis/redis_factory_.v
Normal file
@@ -0,0 +1,307 @@
|
||||
module redis
|
||||
|
||||
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 (
|
||||
redis_global map[string]&RedisInstall
|
||||
redis_default string
|
||||
)
|
||||
|
||||
/////////FACTORY
|
||||
|
||||
@[params]
|
||||
pub struct ArgsGet {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
fromdb bool // will load from filesystem
|
||||
create bool // default will not create if not exist
|
||||
}
|
||||
|
||||
pub fn new(args ArgsGet) !&RedisInstall {
|
||||
mut obj := RedisInstall{
|
||||
name: args.name
|
||||
}
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
}
|
||||
|
||||
pub fn get(args ArgsGet) !&RedisInstall {
|
||||
mut context := base.context()!
|
||||
redis_default = args.name
|
||||
if args.fromdb || args.name !in redis_global {
|
||||
mut r := context.redis()!
|
||||
if r.hexists('context:redis', args.name)! {
|
||||
data := r.hget('context:redis', args.name)!
|
||||
if data.len == 0 {
|
||||
print_backtrace()
|
||||
return error('RedisInstall with name: ${args.name} does not exist, prob bug.')
|
||||
}
|
||||
mut obj := json.decode(RedisInstall, data)!
|
||||
set_in_mem(obj)!
|
||||
} else {
|
||||
if args.create {
|
||||
new(args)!
|
||||
} else {
|
||||
print_backtrace()
|
||||
return error("RedisInstall with name '${args.name}' does not exist")
|
||||
}
|
||||
}
|
||||
return get(name: args.name)! // no longer from db nor create
|
||||
}
|
||||
return redis_global[args.name] or {
|
||||
print_backtrace()
|
||||
return error('could not get config for redis with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o RedisInstall) ! {
|
||||
mut o2 := set_in_mem(o)!
|
||||
redis_default = o2.name
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hset('context:redis', 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:redis', args.name)!
|
||||
}
|
||||
|
||||
pub fn delete(args ArgsGet) ! {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hdel('context:redis', 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) ![]&RedisInstall {
|
||||
mut res := []&RedisInstall{}
|
||||
mut context := base.context()!
|
||||
if args.fromdb {
|
||||
// reset what is in mem
|
||||
redis_global = map[string]&RedisInstall{}
|
||||
redis_default = ''
|
||||
}
|
||||
if args.fromdb {
|
||||
mut r := context.redis()!
|
||||
mut l := r.hkeys('context:redis')!
|
||||
|
||||
for name in l {
|
||||
res << get(name: name, fromdb: true)!
|
||||
}
|
||||
return res
|
||||
} else {
|
||||
// load from memory
|
||||
for _, client in redis_global {
|
||||
res << client
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o RedisInstall) !RedisInstall {
|
||||
mut o2 := obj_init(o)!
|
||||
redis_global[o2.name] = &o2
|
||||
redis_default = o2.name
|
||||
return o2
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'redis.') {
|
||||
return
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'redis.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: 'redis.')!
|
||||
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 redis.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action redis.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut redis_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${redis_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action redis.${other_action.name}')
|
||||
redis_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action redis.${other_action.name}')
|
||||
redis_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action redis.${other_action.name}')
|
||||
redis_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: redis' startupmanager get screen")
|
||||
return startupmanager.get(.screen)!
|
||||
}
|
||||
.zinit {
|
||||
console.print_debug("installer: redis' startupmanager get zinit")
|
||||
return startupmanager.get(.zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug("installer: redis' startupmanager get systemd")
|
||||
return startupmanager.get(.systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug("installer: redis' startupmanager get auto")
|
||||
return startupmanager.get(.auto)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self RedisInstall) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self RedisInstall) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('installer: redis start')
|
||||
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('installer: redis 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('redis did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self RedisInstall) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self RedisInstall) 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 RedisInstall) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self RedisInstall) 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 RedisInstall) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self RedisInstall) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for redis
|
||||
pub fn switch(name string) {
|
||||
redis_default = name
|
||||
}
|
||||
62
lib/installers/base/redis/redis_model.v
Normal file
62
lib/installers/base/redis/redis_model.v
Normal file
@@ -0,0 +1,62 @@
|
||||
module redis
|
||||
|
||||
import incubaid.herolib.data.paramsparser
|
||||
import incubaid.herolib.data.encoderhero
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.core.pathlib
|
||||
import incubaid.herolib.core
|
||||
import os
|
||||
|
||||
pub const version = '7.0.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 RedisInstall {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
port int = 6379
|
||||
datadir string = '${os.home_dir()}/hero/var/redis'
|
||||
ipaddr string = 'localhost' // can be more than 1, space separated
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ RedisInstall) !RedisInstall {
|
||||
mut mycfg := mycfg_
|
||||
if mycfg.name == '' {
|
||||
mycfg.name = 'default'
|
||||
}
|
||||
if mycfg.port == 0 {
|
||||
mycfg.port = 6379
|
||||
}
|
||||
if mycfg.datadir == '' {
|
||||
mycfg.datadir = '${os.home_dir()}/hero/var/redis'
|
||||
}
|
||||
if mycfg.ipaddr == '' {
|
||||
mycfg.ipaddr = 'localhost'
|
||||
}
|
||||
return mycfg
|
||||
}
|
||||
|
||||
fn configfilepath(args RedisInstall) string {
|
||||
if core.is_linux() or { panic(err) } {
|
||||
return '/etc/redis/redis.conf'
|
||||
} else {
|
||||
return '${args.datadir}/redis.conf'
|
||||
}
|
||||
}
|
||||
|
||||
// called before start if done
|
||||
fn configure() ! {
|
||||
mut args := get()!
|
||||
c := $tmpl('../templates/redis_config.conf')
|
||||
pathlib.template_write(c, configfilepath(args), true)!
|
||||
}
|
||||
|
||||
/////////////NORMALLY NO NEED TO TOUCH
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !RedisInstall {
|
||||
mut obj := encoderhero.decode[RedisInstall](heroscript)!
|
||||
return obj
|
||||
}
|
||||
5
lib/installers/base/redis/templates/atemplate.yaml
Normal file
5
lib/installers/base/redis/templates/atemplate.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ${cfg.configpath}
|
||||
|
||||
|
||||
2320
lib/installers/base/redis/templates/redis_config.conf
Normal file
2320
lib/installers/base/redis/templates/redis_config.conf
Normal file
File diff suppressed because it is too large
Load Diff
@@ -414,7 +414,7 @@ proc-title-template "{title} {listen-addr} {server-mode}"
|
||||
# Set the local environment which is used for string comparison operations, and
|
||||
# also affect the performance of Lua scripts. Empty String indicates the locale
|
||||
# is derived from the environment variables.
|
||||
locale-collate ""
|
||||
# locale-collate "" # Not supported in Redis 7.0.15
|
||||
|
||||
################################ SNAPSHOTTING ################################
|
||||
|
||||
@@ -1973,14 +1973,14 @@ set-max-intset-entries 512
|
||||
# data structure when they have a small number of entries, and the biggest entry
|
||||
# does not exceed a given threshold. These thresholds can be configured using
|
||||
# the following directives.
|
||||
set-max-listpack-entries 128
|
||||
set-max-listpack-value 64
|
||||
# set-max-listpack-entries 128 # Not supported in Redis 7.0.15
|
||||
# set-max-listpack-value 64 # Not supported in Redis 7.0.15
|
||||
|
||||
# Similarly to hashes and lists, sorted sets are also specially encoded in
|
||||
# order to save a lot of space. This encoding is only used when the length and
|
||||
# elements of a sorted set are below the following limits:
|
||||
zset-max-listpack-entries 128
|
||||
zset-max-listpack-value 64
|
||||
# zset-max-listpack-entries 128 # Not supported in Redis 7.0.15
|
||||
# zset-max-listpack-value 64 # Not supported in Redis 7.0.15
|
||||
|
||||
# HyperLogLog sparse representation bytes limit. The limit includes the
|
||||
# 16 bytes header. When a HyperLogLog using the sparse representation crosses
|
||||
|
||||
12
lib/installers/infra/herocoordinator/.heroscript
Normal file
12
lib/installers/infra/herocoordinator/.heroscript
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
!!hero_code.generate_installer
|
||||
name:''
|
||||
classname:'HerocoordinatorServer'
|
||||
singleton:0
|
||||
templates:1
|
||||
default:1
|
||||
title:''
|
||||
supported_platforms:''
|
||||
startupmanager:1
|
||||
hasconfig:1
|
||||
build:1
|
||||
253
lib/installers/infra/herocoordinator/herocoordinator_actions.v
Normal file
253
lib/installers/infra/herocoordinator/herocoordinator_actions.v
Normal file
@@ -0,0 +1,253 @@
|
||||
module herocoordinator
|
||||
|
||||
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: 'herocoordinator'
|
||||
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: 'herocoordinator'
|
||||
// source: '${gitpath}/target/x86_64-unknown-linux-musl/release/herocoordinator'
|
||||
// )!
|
||||
}
|
||||
|
||||
fn install() ! {
|
||||
console.print_header('install herocoordinator')
|
||||
// For herocoordinator, we build from source instead of downloading
|
||||
build()!
|
||||
}
|
||||
|
||||
// Public function to build herocoordinator without requiring factory/redis
|
||||
pub fn build_coordinator() ! {
|
||||
console.print_header('build herocoordinator')
|
||||
println('📦 Starting herocoordinator build process...\n')
|
||||
|
||||
// Use default config instead of getting from factory
|
||||
println('⚙️ Initializing configuration...')
|
||||
mut cfg := HerocoordinatorServer{}
|
||||
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...')
|
||||
|
||||
// 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(coderoot: '/root/code')!
|
||||
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 coordinator binary from the horus workspace
|
||||
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('\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/coordinator'
|
||||
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🎉 Herocoordinator built successfully!')
|
||||
println('📍 Binary location: ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn build() ! {
|
||||
console.print_header('build herocoordinator')
|
||||
|
||||
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(coderoot: '/root/code')!
|
||||
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)...')
|
||||
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_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/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('herocoordinator built successfully at ${cfg.binary_path}')
|
||||
}
|
||||
|
||||
fn destroy() ! {
|
||||
mut server := get()!
|
||||
server.stop()!
|
||||
|
||||
osal.process_kill_recursive(name: 'herocoordinator')!
|
||||
|
||||
// Remove the built binary
|
||||
osal.rm(server.binary_path)!
|
||||
}
|
||||
324
lib/installers/infra/herocoordinator/herocoordinator_factory_.v
Normal file
324
lib/installers/infra/herocoordinator/herocoordinator_factory_.v
Normal file
@@ -0,0 +1,324 @@
|
||||
module herocoordinator
|
||||
|
||||
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 (
|
||||
herocoordinator_global map[string]&HerocoordinatorServer
|
||||
herocoordinator_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) !&HerocoordinatorServer {
|
||||
mut obj := HerocoordinatorServer{
|
||||
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) !&HerocoordinatorServer {
|
||||
mut context := base.context()!
|
||||
herocoordinator_default = args.name
|
||||
if args.fromdb || args.name !in herocoordinator_global {
|
||||
mut r := context.redis()!
|
||||
if r.hexists('context:herocoordinator', args.name)! {
|
||||
data := r.hget('context:herocoordinator', args.name)!
|
||||
if data.len == 0 {
|
||||
print_backtrace()
|
||||
return error('HerocoordinatorServer with name: ${args.name} does not exist, prob bug.')
|
||||
}
|
||||
mut obj := json.decode(HerocoordinatorServer, data)!
|
||||
set_in_mem(obj)!
|
||||
} else {
|
||||
if args.create {
|
||||
new(args)!
|
||||
} else {
|
||||
print_backtrace()
|
||||
return error("HerocoordinatorServer with name '${args.name}' does not exist")
|
||||
}
|
||||
}
|
||||
return get(name: args.name)! // no longer from db nor create
|
||||
}
|
||||
return herocoordinator_global[args.name] or {
|
||||
print_backtrace()
|
||||
return error('could not get config for herocoordinator with name:${args.name}')
|
||||
}
|
||||
}
|
||||
|
||||
// register the config for the future
|
||||
pub fn set(o HerocoordinatorServer) ! {
|
||||
mut o2 := set_in_mem(o)!
|
||||
herocoordinator_default = o2.name
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hset('context:herocoordinator', 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:herocoordinator', args.name)!
|
||||
}
|
||||
|
||||
pub fn delete(args ArgsGet) ! {
|
||||
mut context := base.context()!
|
||||
mut r := context.redis()!
|
||||
r.hdel('context:herocoordinator', 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) ![]&HerocoordinatorServer {
|
||||
mut res := []&HerocoordinatorServer{}
|
||||
mut context := base.context()!
|
||||
if args.fromdb {
|
||||
// reset what is in mem
|
||||
herocoordinator_global = map[string]&HerocoordinatorServer{}
|
||||
herocoordinator_default = ''
|
||||
}
|
||||
if args.fromdb {
|
||||
mut r := context.redis()!
|
||||
mut l := r.hkeys('context:herocoordinator')!
|
||||
|
||||
for name in l {
|
||||
res << get(name: name, fromdb: true)!
|
||||
}
|
||||
return res
|
||||
} else {
|
||||
// load from memory
|
||||
for _, client in herocoordinator_global {
|
||||
res << client
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// only sets in mem, does not set as config
|
||||
fn set_in_mem(o HerocoordinatorServer) !HerocoordinatorServer {
|
||||
mut o2 := obj_init(o)!
|
||||
herocoordinator_global[o2.name] = &o2
|
||||
herocoordinator_default = o2.name
|
||||
return o2
|
||||
}
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'herocoordinator.') {
|
||||
return
|
||||
}
|
||||
mut install_actions := plbook.find(filter: 'herocoordinator.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: 'herocoordinator.')!
|
||||
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 herocoordinator.destroy')
|
||||
destroy()!
|
||||
}
|
||||
if other_action.name == 'install' {
|
||||
console.print_debug('install action herocoordinator.install')
|
||||
install()!
|
||||
}
|
||||
}
|
||||
if other_action.name in ['start', 'stop', 'restart'] {
|
||||
mut p := other_action.params
|
||||
name := p.get('name')!
|
||||
mut herocoordinator_obj := get(name: name)!
|
||||
console.print_debug('action object:\n${herocoordinator_obj}')
|
||||
if other_action.name == 'start' {
|
||||
console.print_debug('install action herocoordinator.${other_action.name}')
|
||||
herocoordinator_obj.start()!
|
||||
}
|
||||
|
||||
if other_action.name == 'stop' {
|
||||
console.print_debug('install action herocoordinator.${other_action.name}')
|
||||
herocoordinator_obj.stop()!
|
||||
}
|
||||
if other_action.name == 'restart' {
|
||||
console.print_debug('install action herocoordinator.${other_action.name}')
|
||||
herocoordinator_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: herocoordinator' startupmanager get screen")
|
||||
return startupmanager.get(.screen)!
|
||||
}
|
||||
.zinit {
|
||||
console.print_debug("installer: herocoordinator' startupmanager get zinit")
|
||||
return startupmanager.get(.zinit)!
|
||||
}
|
||||
.systemd {
|
||||
console.print_debug("installer: herocoordinator' startupmanager get systemd")
|
||||
return startupmanager.get(.systemd)!
|
||||
}
|
||||
else {
|
||||
console.print_debug("installer: herocoordinator' startupmanager get auto")
|
||||
return startupmanager.get(.auto)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load from disk and make sure is properly intialized
|
||||
pub fn (mut self HerocoordinatorServer) reload() ! {
|
||||
switch(self.name)
|
||||
self = obj_init(self)!
|
||||
}
|
||||
|
||||
pub fn (mut self HerocoordinatorServer) start() ! {
|
||||
switch(self.name)
|
||||
if self.running()! {
|
||||
return
|
||||
}
|
||||
|
||||
console.print_header('installer: herocoordinator start')
|
||||
|
||||
if !installed()! {
|
||||
install()!
|
||||
}
|
||||
|
||||
configure()!
|
||||
|
||||
start_pre()!
|
||||
|
||||
for zprocess in startupcmd()! {
|
||||
mut sm := startupmanager_get(zprocess.startuptype)!
|
||||
|
||||
console.print_debug('installer: herocoordinator 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('herocoordinator did not install properly.')
|
||||
}
|
||||
|
||||
pub fn (mut self HerocoordinatorServer) install_start(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
self.install(args)!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self HerocoordinatorServer) 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 HerocoordinatorServer) restart() ! {
|
||||
switch(self.name)
|
||||
self.stop()!
|
||||
self.start()!
|
||||
}
|
||||
|
||||
pub fn (mut self HerocoordinatorServer) 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 HerocoordinatorServer) install(args InstallArgs) ! {
|
||||
switch(self.name)
|
||||
if args.reset || (!installed()!) {
|
||||
install()!
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut self HerocoordinatorServer) build() ! {
|
||||
switch(self.name)
|
||||
build()!
|
||||
}
|
||||
|
||||
pub fn (mut self HerocoordinatorServer) destroy() ! {
|
||||
switch(self.name)
|
||||
self.stop() or {}
|
||||
destroy()!
|
||||
}
|
||||
|
||||
// switch instance to be used for herocoordinator
|
||||
pub fn switch(name string) {
|
||||
herocoordinator_default = name
|
||||
}
|
||||
69
lib/installers/infra/herocoordinator/herocoordinator_model.v
Normal file
69
lib/installers/infra/herocoordinator/herocoordinator_model.v
Normal file
@@ -0,0 +1,69 @@
|
||||
module herocoordinator
|
||||
|
||||
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 HerocoordinatorServer {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
binary_path string = '/hero/var/bin/coordinator'
|
||||
redis_addr string = '127.0.0.1:6379'
|
||||
http_port int = 8081
|
||||
ws_port int = 9653
|
||||
log_level string = 'info'
|
||||
repo_path string = '/root/code/git.ourworld.tf/herocode/horus'
|
||||
}
|
||||
|
||||
// your checking & initialization code if needed
|
||||
fn obj_init(mycfg_ HerocoordinatorServer) !HerocoordinatorServer {
|
||||
mut mycfg := mycfg_
|
||||
if mycfg.name == '' {
|
||||
mycfg.name = 'default'
|
||||
}
|
||||
if mycfg.binary_path == '' {
|
||||
mycfg.binary_path = '/hero/var/bin/coordinator'
|
||||
}
|
||||
if mycfg.redis_addr == '' {
|
||||
mycfg.redis_addr = '127.0.0.1:6379'
|
||||
}
|
||||
if mycfg.http_port == 0 {
|
||||
mycfg.http_port = 8081
|
||||
}
|
||||
if mycfg.ws_port == 0 {
|
||||
mycfg.ws_port = 9653
|
||||
}
|
||||
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 HerocoordinatorServer) !string {
|
||||
return encoderhero.encode[HerocoordinatorServer](obj)!
|
||||
}
|
||||
|
||||
pub fn heroscript_loads(heroscript string) !HerocoordinatorServer {
|
||||
mut obj := encoderhero.decode[HerocoordinatorServer](heroscript)!
|
||||
return obj
|
||||
}
|
||||
143
lib/installers/infra/herocoordinator/readme.md
Normal file
143
lib/installers/infra/herocoordinator/readme.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Herocoordinator 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.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Rust Installation**: Installs Rust toolchain if not present
|
||||
- **Git Repository Management**: Clones and manages the horus repository
|
||||
- **Binary Building**: Compiles the coordinator 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/incubaid/herolib/examples/installers/infra
|
||||
./herocoordinator.vsh
|
||||
```
|
||||
|
||||
### Manual Usage
|
||||
|
||||
```v
|
||||
import incubaid.herolib.installers.infra.herocoordinator as herocoordinator_installer
|
||||
|
||||
mut herocoordinator := herocoordinator_installer.get()!
|
||||
herocoordinator.install()!
|
||||
herocoordinator.start()!
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
!!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'
|
||||
```
|
||||
|
||||
### Configuration Fields
|
||||
|
||||
- **name**: Instance name (default: 'default')
|
||||
- **binary_path**: Path where the coordinator binary will be installed (default: '/hero/var/bin/coordinator')
|
||||
- **redis_addr**: Redis server address (default: '127.0.0.1:6379')
|
||||
- **http_port**: HTTP API port (default: 8081)
|
||||
- **ws_port**: WebSocket API port (default: 9653)
|
||||
- **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 coordinator 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 coordinator binary with `cargo build -p hero-coordinator --release`
|
||||
|
||||
```bash
|
||||
hero herocoordinator.install
|
||||
```
|
||||
|
||||
### Start
|
||||
Starts the herocoordinator service using zinit:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.start
|
||||
```
|
||||
|
||||
### Stop
|
||||
Stops the running service:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.stop
|
||||
```
|
||||
|
||||
### Restart
|
||||
Restarts the service:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.restart
|
||||
```
|
||||
|
||||
### Destroy
|
||||
Stops the service and removes all files:
|
||||
|
||||
```bash
|
||||
hero herocoordinator.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:
|
||||
|
||||
- **herocoordinator_model.v**: Configuration structure and initialization
|
||||
- **herocoordinator_actions.v**: Build, install, start, stop, destroy logic
|
||||
- **herocoordinator_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 incubaid.herolib.installers.infra.herocoordinator as hc
|
||||
|
||||
// Get installer instance
|
||||
mut coordinator := hc.get()!
|
||||
|
||||
// Customize configuration
|
||||
coordinator.redis_addr = '127.0.0.1:6379'
|
||||
coordinator.http_port = 8081
|
||||
coordinator.log_level = 'debug'
|
||||
hc.set(coordinator)!
|
||||
|
||||
// Build and start
|
||||
coordinator.install()!
|
||||
coordinator.start()!
|
||||
|
||||
// Check status
|
||||
if coordinator.running()! {
|
||||
println('Coordinator is running on port ${coordinator.http_port}')
|
||||
}
|
||||
|
||||
// Later: cleanup
|
||||
coordinator.destroy()!
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
name: ${cfg.configpath}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user