diff --git a/examples/installers/base/redis.vsh b/examples/installers/base/redis.vsh index 808a740d..52542ffa 100755 --- a/examples/installers/base/redis.vsh +++ b/examples/installers/base/redis.vsh @@ -1,39 +1,46 @@ #!/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 +import incubaid.herolib.installers.base.redis 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') +// Create configuration +// You can customize port, datadir, and ipaddr as needed +config := redis.RedisInstall{ + port: 6379 // Redis port + datadir: '/var/lib/redis' // Data directory (standard location) + ipaddr: 'localhost' // Bind address } -println('\n✅ Done!') +// Check if Redis is already running +if redis.check(config) { + println('INFO: Redis is already running on port ${config.port}') + println(' To reinstall, stop Redis first: redis.stop()!') +} else { + // Install and start Redis + println('Installing and starting Redis...') + println(' Port: ${config.port}') + println(' Data directory: ${config.datadir}') + println(' Bind address: ${config.ipaddr}\n') + + redis.redis_install(config)! + + // Verify installation + if redis.check(config) { + println('\nSUCCESS: Redis installed and started successfully!') + println(' You can now connect to Redis on port ${config.port}') + println(' Test with: redis-cli ping') + } else { + println('\nERROR: Redis installation completed but failed to start') + println(' Check logs: journalctl -u redis-server -n 20') + } +} + +println('\n=== Available Functions ===') +println(' redis.redis_install(config)! - Install and start Redis') +println(' redis.start(config)! - Start Redis') +println(' redis.stop()! - Stop Redis') +println(' redis.restart(config)! - Restart Redis') +println(' redis.check(config) - Check if running') + +println('\nDone!') diff --git a/examples/installers/horus/coordinator b/examples/installers/horus/coordinator new file mode 100755 index 00000000..49e9b260 Binary files /dev/null and b/examples/installers/horus/coordinator differ diff --git a/examples/installers/horus/coordinator.vsh b/examples/installers/horus/coordinator.vsh new file mode 100755 index 00000000..ffc3c521 --- /dev/null +++ b/examples/installers/horus/coordinator.vsh @@ -0,0 +1,31 @@ +#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run + +import incubaid.herolib.installers.horus.coordinator + +// Example usage of coordinator 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 coordinator binary + +println('Building coordinator from horus repository...') +println('(This will install Redis and Rust if not already installed)\n') + +// Create coordinator instance - will auto-install Redis if needed +mut coord := coordinator.new()! + +// Build and install +coord.install()! + +println('\nCoordinator built and installed successfully!') +println('Binary location: ${coord.binary_path}') + +// Note: To start the service, uncomment the lines below +// (requires proper zinit or screen session setup) +// coord.start()! +// if coord.running()! { +// println('Coordinator is running!') +// } +// coord.stop()! +// coord.destroy()! diff --git a/examples/installers/infra/herocoordinator.vsh b/examples/installers/infra/herocoordinator.vsh deleted file mode 100755 index 124dd689..00000000 --- a/examples/installers/infra/herocoordinator.vsh +++ /dev/null @@ -1,30 +0,0 @@ -#!/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()! diff --git a/lib/installers/base/redis/MIGRATION_NOTES.md b/lib/installers/base/redis/MIGRATION_NOTES.md deleted file mode 100644 index 613c29a7..00000000 --- a/lib/installers/base/redis/MIGRATION_NOTES.md +++ /dev/null @@ -1,80 +0,0 @@ -# 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 diff --git a/lib/installers/base/redis/readme.md b/lib/installers/base/redis/readme.md index fad1225f..c95d478f 100644 --- a/lib/installers/base/redis/readme.md +++ b/lib/installers/base/redis/readme.md @@ -1,44 +1,119 @@ -# redis +# Redis Installer +A modular Redis installer that works across multiple platforms (Ubuntu, Debian, Alpine, Arch, macOS, containers). +## Features -To get started +- Cross-platform support (systemd and non-systemd systems) +- Automatic package installation via package managers +- Configurable data directory, port, and IP address +- Smart startup (uses systemctl when available, falls back to direct start) +- No circular dependencies (works without Redis being pre-installed) + +## Quick Start + +### Simple Installation ```v +import incubaid.herolib.installers.base.redis +// Create configuration +config := redis.RedisInstall{ + port: 6379 + datadir: '/var/lib/redis' + ipaddr: 'localhost' +} -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)! - - - +// Install and start Redis +redis.redis_install(config)! +// Check if running +if redis.check(config) { + println('Redis is running!') +} ``` -## example heroscript +### Using Individual Functions +```v +import incubaid.herolib.installers.base.redis -```hero -!!redis.configure - homedir: '/home/user/redis' - username: 'admin' - password: 'secretpassword' - title: 'Some Title' - host: 'localhost' - port: 8888 +config := redis.RedisInstall{ + port: 6379 + datadir: '/var/lib/redis' + ipaddr: 'localhost' +} +// Install package only (doesn't start) +redis.redis_install(config)! + +// Start Redis +redis.start(config)! + +// Stop Redis +redis.stop()! + +// Restart Redis +redis.restart(config)! + +// Check if running +is_running := redis.check(config) ``` +## Configuration Options + +```v +pub struct RedisInstall { +pub mut: + name string = 'default' // Instance name + port int = 6379 // Redis port + datadir string = '/var/lib/redis' // Data directory + ipaddr string = 'localhost' // Bind address (space-separated for multiple) +} +``` + +## Platform Support + +| Platform | Package Manager | Startup Method | +|----------|----------------|----------------| +| Ubuntu/Debian | apt (redis-server) | systemctl | +| Alpine | apk (redis) | direct start | +| Arch | pacman (redis) | systemctl | +| Fedora | dnf (redis) | systemctl | +| macOS | brew (redis) | direct start | +| Containers | varies | direct start | + +## Using with Factory (Advanced) + +For applications that need Redis state management: + +```v +import incubaid.herolib.installers.base.redis + +// Create and store in factory +mut installer := redis.new(name: 'myredis')! + +// Install and start +installer.install(reset: false)! +installer.start()! + +// Check status +if installer.running()! { + println('Redis is running') +} + +// Stop +installer.stop()! +``` + +## Example Script + +See `examples/installers/base/redis.vsh` for a complete working example. + +## Notes + +- Default data directory is `/var/lib/redis` (standard location) +- On systemd systems, uses the package's systemd service +- On non-systemd systems, starts Redis directly with `--daemonize yes` +- Automatically handles permissions for the Redis user +- Config file location: `/etc/redis/redis.conf` (Linux) or `${datadir}/redis.conf` (macOS) diff --git a/lib/installers/base/redis/redis_actions.v b/lib/installers/base/redis/redis_actions.v index b08028c1..b1b9d513 100644 --- a/lib/installers/base/redis/redis_actions.v +++ b/lib/installers/base/redis/redis_actions.v @@ -2,9 +2,6 @@ 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 @@ -43,6 +40,14 @@ fn start_pre() ! { mut cfg := get()! + // Ensure data directory exists with proper permissions before configuring + osal.execute_silent('mkdir -p ${cfg.datadir}')! + if core.is_linux()! { + // On Linux, ensure redis user can access the directory + osal.execute_silent('chown -R redis:redis ${cfg.datadir}')! + osal.execute_silent('chmod 755 ${cfg.datadir}')! + } + // Configure redis before starting (applies template) configure()! @@ -95,29 +100,110 @@ fn upload() ! { // )! } -fn install() ! { - console.print_header('install redis') - - if installed()! { - console.print_debug('redis-server already installed') +// Install and start Redis with the given configuration +// This is the main entry point for installing Redis without using the factory +pub fn redis_install(args RedisInstall) ! { + // Check if already running + if check(args) { + console.print_debug('Redis already running on port ${args.port}') return } - // Install redis-server via package manager - if core.is_linux()! { - osal.package_install('redis-server')! - } else { - osal.package_install('redis')! + console.print_header('install redis') + + // Install Redis package if not already installed + if !installed()! { + if core.is_linux()! { + osal.package_install('redis-server')! // Ubuntu/Debian + } else { + osal.package_install('redis')! // macOS, Alpine, Arch, etc. + } } - mut cfg := get()! - osal.execute_silent('mkdir -p ${cfg.datadir}')! + // Create data directory with correct permissions + osal.execute_silent('mkdir -p ${args.datadir}')! + osal.execute_silent('chown -R redis:redis ${args.datadir}') or {} + osal.execute_silent('chmod 755 ${args.datadir}') or {} - console.print_debug('redis-server installed successfully') + // Configure and start Redis + start(args)! +} + +// Check if Redis is running +pub fn check(args RedisInstall) bool { + res := os.execute('redis-cli -c -p ${args.port} ping > /dev/null 2>&1') + if res.exit_code == 0 { + return true + } + return false +} + +// Start Redis with the given configuration +// Writes config file, kills any existing processes, and starts Redis +pub fn start(args RedisInstall) ! { + if check(args) { + console.print_debug('Redis already running on port ${args.port}') + return + } + + // Write Redis configuration file + configure_with_args(args)! + + // Kill any existing Redis processes (including package auto-started ones) + osal.process_kill_recursive(name: 'redis-server')! + + if core.platform()! == .osx { + // macOS: start directly with daemonize + osal.exec(cmd: 'redis-server ${configfilepath(args)} --daemonize yes')! + } else { + // Linux: prefer systemctl if available, otherwise start directly + if osal.cmd_exists('systemctl') { + // Ensure permissions are correct for systemd-managed Redis + osal.execute_silent('chown -R redis:redis ${args.datadir}') or {} + osal.execute_silent('chmod 755 ${args.datadir}') or {} + // Reset any failed state from previous kills + osal.execute_silent('systemctl reset-failed redis-server') or {} + osal.exec(cmd: 'systemctl start redis-server')! + } else { + // No systemctl (Alpine, containers, etc.) + // Set permissions for redis user before starting + osal.execute_silent('chown -R redis:redis ${args.datadir}') or {} + osal.execute_silent('chmod 755 ${args.datadir}') or {} + osal.exec(cmd: 'redis-server ${configfilepath(args)} --daemonize yes')! + } + } + + // Wait for Redis to be ready + for _ in 0 .. 100 { + if check(args) { + console.print_debug('Redis started successfully') + return + } + time.sleep(100) + } + + return error('Redis did not start properly after 10 seconds - could not ping on port ${args.port}') +} + +// Stop Redis +pub fn stop() ! { + osal.execute_silent('redis-cli shutdown')! +} + +// Restart Redis +pub fn restart(args RedisInstall) ! { + stop()! + time.sleep(500) // Give Redis time to shut down + start(args)! +} + +// Private install function for factory-based usage +fn install() ! { + mut cfg := get()! + redis_install(cfg)! } fn destroy() ! { - mut cfg := get()! - cfg.stop()! + stop()! osal.process_kill_recursive(name: 'redis-server')! } diff --git a/lib/installers/base/redis/redis_model.v b/lib/installers/base/redis/redis_model.v index c3b5c8f7..e86f9bb9 100644 --- a/lib/installers/base/redis/redis_model.v +++ b/lib/installers/base/redis/redis_model.v @@ -17,7 +17,7 @@ pub struct RedisInstall { pub mut: name string = 'default' port int = 6379 - datadir string = '${os.home_dir()}/hero/var/redis' + datadir string = '/var/lib/redis' ipaddr string = 'localhost' // can be more than 1, space separated } @@ -31,7 +31,7 @@ fn obj_init(mycfg_ RedisInstall) !RedisInstall { mycfg.port = 6379 } if mycfg.datadir == '' { - mycfg.datadir = '${os.home_dir()}/hero/var/redis' + mycfg.datadir = '/var/lib/redis' } if mycfg.ipaddr == '' { mycfg.ipaddr = 'localhost' @@ -47,11 +47,17 @@ fn configfilepath(args RedisInstall) string { } } -// called before start if done +// Configure with args passed directly (like old installer) +fn configure_with_args(args RedisInstall) ! { + // Use V's template macro like the old installer + c := $tmpl('templates/redis_config.conf') + pathlib.template_write(c, configfilepath(args), true)! +} + +// called before start if done (uses factory) fn configure() ! { mut args := get()! - c := $tmpl('../templates/redis_config.conf') - pathlib.template_write(c, configfilepath(args), true)! + configure_with_args(args)! } /////////////NORMALLY NO NEED TO TOUCH diff --git a/lib/installers/infra/herocoordinator/.heroscript b/lib/installers/horus/coordinator/.heroscript similarity index 100% rename from lib/installers/infra/herocoordinator/.heroscript rename to lib/installers/horus/coordinator/.heroscript diff --git a/lib/installers/horus/coordinator/coordinator_actions.v b/lib/installers/horus/coordinator/coordinator_actions.v new file mode 100644 index 00000000..67284d4f --- /dev/null +++ b/lib/installers/horus/coordinator/coordinator_actions.v @@ -0,0 +1,185 @@ +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 + +// Helper function to ensure Redis is installed and running +fn ensure_redis_running() ! { + redis_config := redis.RedisInstall{ + port: 6379 + datadir: '/var/lib/redis' + ipaddr: 'localhost' + } + + if !redis.check(redis_config) { + println('Installing and starting Redis...') + redis.redis_install(redis_config)! + } else { + println('Redis is already running') + } +} + +fn startupcmd() ![]startupmanager.ZProcessNewArgs { + mut cfg := get()! + mut res := []startupmanager.ZProcessNewArgs{} + + res << startupmanager.ZProcessNewArgs{ + name: 'coordinator' + 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: 'coordinator' + // source: '${gitpath}/target/x86_64-unknown-linux-musl/release/coordinator' + // )! +} + +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() ! { + console.print_header('build coordinator') + 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') + 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...') + ensure_redis_running()! + println('Redis is ready\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('WARNING: 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') + + // 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('\nCoordinator built successfully!') + println('Binary location: ${cfg.binary_path}') +} + +fn destroy() ! { + mut server := get()! + server.stop()! + + osal.process_kill_recursive(name: 'coordinator')! + + // Remove the built binary + osal.rm(server.binary_path)! +} diff --git a/lib/installers/infra/herocoordinator/herocoordinator_factory_.v b/lib/installers/horus/coordinator/coordinator_factory_.v similarity index 58% rename from lib/installers/infra/herocoordinator/herocoordinator_factory_.v rename to lib/installers/horus/coordinator/coordinator_factory_.v index b6422904..f1e74ef4 100644 --- a/lib/installers/infra/herocoordinator/herocoordinator_factory_.v +++ b/lib/installers/horus/coordinator/coordinator_factory_.v @@ -1,4 +1,4 @@ -module herocoordinator +module coordinator import incubaid.herolib.core.base import incubaid.herolib.core.playbook { PlayBook } @@ -8,8 +8,8 @@ import incubaid.herolib.osal.startupmanager import time __global ( - herocoordinator_global map[string]&HerocoordinatorServer - herocoordinator_default string + coordinator_global map[string]&CoordinatorServer + coordinator_default string ) /////////FACTORY @@ -28,8 +28,8 @@ pub mut: create bool // default will not create if not exist } -pub fn new(args ArgsGet) !&HerocoordinatorServer { - mut obj := HerocoordinatorServer{ +pub fn new(args ArgsGet) !&CoordinatorServer { + mut obj := CoordinatorServer{ name: args.name binary_path: args.binary_path redis_addr: args.redis_addr @@ -38,59 +38,67 @@ pub fn new(args ArgsGet) !&HerocoordinatorServer { log_level: args.log_level repo_path: args.repo_path } - set(obj)! + + // 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)! + } + return get(name: args.name)! } -pub fn get(args ArgsGet) !&HerocoordinatorServer { +pub fn get(args ArgsGet) !&CoordinatorServer { mut context := base.context()! - herocoordinator_default = args.name - if args.fromdb || args.name !in herocoordinator_global { + coordinator_default = args.name + if args.fromdb || args.name !in coordinator_global { mut r := context.redis()! - if r.hexists('context:herocoordinator', args.name)! { - data := r.hget('context:herocoordinator', args.name)! + if r.hexists('context:coordinator', args.name)! { + data := r.hget('context:coordinator', args.name)! if data.len == 0 { print_backtrace() - return error('HerocoordinatorServer with name: ${args.name} does not exist, prob bug.') + return error('CoordinatorServer with name: ${args.name} does not exist, prob bug.') } - mut obj := json.decode(HerocoordinatorServer, data)! + mut obj := json.decode(CoordinatorServer, 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 error("CoordinatorServer with name '${args.name}' does not exist") } } return get(name: args.name)! // no longer from db nor create } - return herocoordinator_global[args.name] or { + return coordinator_global[args.name] or { print_backtrace() - return error('could not get config for herocoordinator with name:${args.name}') + return error('could not get config for coordinator with name:${args.name}') } } // register the config for the future -pub fn set(o HerocoordinatorServer) ! { +pub fn set(o CoordinatorServer) ! { mut o2 := set_in_mem(o)! - herocoordinator_default = o2.name + coordinator_default = o2.name mut context := base.context()! mut r := context.redis()! - r.hset('context:herocoordinator', o2.name, json.encode(o2))! + r.hset('context:coordinator', 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)! + return r.hexists('context:coordinator', args.name)! } pub fn delete(args ArgsGet) ! { mut context := base.context()! mut r := context.redis()! - r.hdel('context:herocoordinator', args.name)! + r.hdel('context:coordinator', args.name)! } @[params] @@ -100,17 +108,17 @@ pub mut: } // 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{} +pub fn list(args ArgsList) ![]&CoordinatorServer { + mut res := []&CoordinatorServer{} mut context := base.context()! if args.fromdb { // reset what is in mem - herocoordinator_global = map[string]&HerocoordinatorServer{} - herocoordinator_default = '' + coordinator_global = map[string]&CoordinatorServer{} + coordinator_default = '' } if args.fromdb { mut r := context.redis()! - mut l := r.hkeys('context:herocoordinator')! + mut l := r.hkeys('context:coordinator')! for name in l { res << get(name: name, fromdb: true)! @@ -118,7 +126,7 @@ pub fn list(args ArgsList) ![]&HerocoordinatorServer { return res } else { // load from memory - for _, client in herocoordinator_global { + for _, client in coordinator_global { res << client } } @@ -126,18 +134,18 @@ pub fn list(args ArgsList) ![]&HerocoordinatorServer { } // only sets in mem, does not set as config -fn set_in_mem(o HerocoordinatorServer) !HerocoordinatorServer { +fn set_in_mem(o CoordinatorServer) !CoordinatorServer { mut o2 := obj_init(o)! - herocoordinator_global[o2.name] = &o2 - herocoordinator_default = o2.name + coordinator_global[o2.name] = &o2 + coordinator_default = o2.name return o2 } pub fn play(mut plbook PlayBook) ! { - if !plbook.exists(filter: 'herocoordinator.') { + if !plbook.exists(filter: 'coordinator.') { return } - mut install_actions := plbook.find(filter: 'herocoordinator.configure')! + mut install_actions := plbook.find(filter: 'coordinator.configure')! if install_actions.len > 0 { for mut install_action in install_actions { heroscript := install_action.heroscript() @@ -146,37 +154,37 @@ pub fn play(mut plbook PlayBook) ! { install_action.done = true } } - mut other_actions := plbook.find(filter: 'herocoordinator.')! + mut other_actions := plbook.find(filter: 'coordinator.')! 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') + console.print_debug('install action coordinator.destroy') destroy()! } if other_action.name == 'install' { - console.print_debug('install action herocoordinator.install') + console.print_debug('install action coordinator.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}') + mut coordinator_obj := get(name: name)! + console.print_debug('action object:\n${coordinator_obj}') if other_action.name == 'start' { - console.print_debug('install action herocoordinator.${other_action.name}') - herocoordinator_obj.start()! + console.print_debug('install action coordinator.${other_action.name}') + coordinator_obj.start()! } if other_action.name == 'stop' { - console.print_debug('install action herocoordinator.${other_action.name}') - herocoordinator_obj.stop()! + console.print_debug('install action coordinator.${other_action.name}') + coordinator_obj.stop()! } if other_action.name == 'restart' { - console.print_debug('install action herocoordinator.${other_action.name}') - herocoordinator_obj.restart()! + console.print_debug('install action coordinator.${other_action.name}') + coordinator_obj.restart()! } } other_action.done = true @@ -195,37 +203,37 @@ fn startupmanager_get(cat startupmanager.StartupManagerType) !startupmanager.Sta // systemd match cat { .screen { - console.print_debug("installer: herocoordinator' startupmanager get screen") + console.print_debug("installer: coordinator' startupmanager get screen") return startupmanager.get(.screen)! } .zinit { - console.print_debug("installer: herocoordinator' startupmanager get zinit") + console.print_debug("installer: coordinator' startupmanager get zinit") return startupmanager.get(.zinit)! } .systemd { - console.print_debug("installer: herocoordinator' startupmanager get systemd") + console.print_debug("installer: coordinator' startupmanager get systemd") return startupmanager.get(.systemd)! } else { - console.print_debug("installer: herocoordinator' startupmanager get auto") + console.print_debug("installer: coordinator' startupmanager get auto") return startupmanager.get(.auto)! } } } // load from disk and make sure is properly intialized -pub fn (mut self HerocoordinatorServer) reload() ! { +pub fn (mut self CoordinatorServer) reload() ! { switch(self.name) self = obj_init(self)! } -pub fn (mut self HerocoordinatorServer) start() ! { +pub fn (mut self CoordinatorServer) start() ! { switch(self.name) if self.running()! { return } - console.print_header('installer: herocoordinator start') + console.print_header('installer: coordinator start') if !installed()! { install()! @@ -238,7 +246,7 @@ pub fn (mut self HerocoordinatorServer) start() ! { for zprocess in startupcmd()! { mut sm := startupmanager_get(zprocess.startuptype)! - console.print_debug('installer: herocoordinator starting with ${zprocess.startuptype}...') + console.print_debug('installer: coordinator starting with ${zprocess.startuptype}...') sm.new(zprocess)! @@ -253,16 +261,16 @@ pub fn (mut self HerocoordinatorServer) start() ! { } time.sleep(100 * time.millisecond) } - return error('herocoordinator did not install properly.') + return error('coordinator did not install properly.') } -pub fn (mut self HerocoordinatorServer) install_start(args InstallArgs) ! { +pub fn (mut self CoordinatorServer) install_start(args InstallArgs) ! { switch(self.name) self.install(args)! self.start()! } -pub fn (mut self HerocoordinatorServer) stop() ! { +pub fn (mut self CoordinatorServer) stop() ! { switch(self.name) stop_pre()! for zprocess in startupcmd()! { @@ -272,13 +280,13 @@ pub fn (mut self HerocoordinatorServer) stop() ! { stop_post()! } -pub fn (mut self HerocoordinatorServer) restart() ! { +pub fn (mut self CoordinatorServer) restart() ! { switch(self.name) self.stop()! self.start()! } -pub fn (mut self HerocoordinatorServer) running() !bool { +pub fn (mut self CoordinatorServer) running() !bool { switch(self.name) // walk over the generic processes, if not running return @@ -300,25 +308,25 @@ pub mut: reset bool } -pub fn (mut self HerocoordinatorServer) install(args InstallArgs) ! { +pub fn (mut self CoordinatorServer) install(args InstallArgs) ! { switch(self.name) if args.reset || (!installed()!) { install()! } } -pub fn (mut self HerocoordinatorServer) build() ! { +pub fn (mut self CoordinatorServer) build() ! { switch(self.name) build()! } -pub fn (mut self HerocoordinatorServer) destroy() ! { +pub fn (mut self CoordinatorServer) destroy() ! { switch(self.name) self.stop() or {} destroy()! } -// switch instance to be used for herocoordinator +// switch instance to be used for coordinator pub fn switch(name string) { - herocoordinator_default = name + coordinator_default = name } diff --git a/lib/installers/infra/herocoordinator/herocoordinator_model.v b/lib/installers/horus/coordinator/coordinator_model.v similarity index 79% rename from lib/installers/infra/herocoordinator/herocoordinator_model.v rename to lib/installers/horus/coordinator/coordinator_model.v index fabbc17d..67f323b2 100644 --- a/lib/installers/infra/herocoordinator/herocoordinator_model.v +++ b/lib/installers/horus/coordinator/coordinator_model.v @@ -1,4 +1,4 @@ -module herocoordinator +module coordinator import incubaid.herolib.data.paramsparser import incubaid.herolib.data.encoderhero @@ -11,7 +11,7 @@ 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 struct CoordinatorServer { pub mut: name string = 'default' binary_path string = '/hero/var/bin/coordinator' @@ -23,7 +23,7 @@ pub mut: } // your checking & initialization code if needed -fn obj_init(mycfg_ HerocoordinatorServer) !HerocoordinatorServer { +fn obj_init(mycfg_ CoordinatorServer) !CoordinatorServer { mut mycfg := mycfg_ if mycfg.name == '' { mycfg.name = 'default' @@ -59,11 +59,11 @@ fn configure() ! { /////////////NORMALLY NO NEED TO TOUCH -pub fn heroscript_dumps(obj HerocoordinatorServer) !string { - return encoderhero.encode[HerocoordinatorServer](obj)! +pub fn heroscript_dumps(obj CoordinatorServer) !string { + return encoderhero.encode[CoordinatorServer](obj)! } -pub fn heroscript_loads(heroscript string) !HerocoordinatorServer { - mut obj := encoderhero.decode[HerocoordinatorServer](heroscript)! +pub fn heroscript_loads(heroscript string) !CoordinatorServer { + mut obj := encoderhero.decode[CoordinatorServer](heroscript)! return obj } diff --git a/lib/installers/infra/herocoordinator/readme.md b/lib/installers/horus/coordinator/readme.md similarity index 100% rename from lib/installers/infra/herocoordinator/readme.md rename to lib/installers/horus/coordinator/readme.md diff --git a/lib/installers/infra/herocoordinator/templates/atemplate.yaml b/lib/installers/horus/coordinator/templates/atemplate.yaml similarity index 100% rename from lib/installers/infra/herocoordinator/templates/atemplate.yaml rename to lib/installers/horus/coordinator/templates/atemplate.yaml diff --git a/lib/installers/infra/herocoordinator/herocoordinator_actions.v b/lib/installers/infra/herocoordinator/herocoordinator_actions.v deleted file mode 100644 index 214cc7ec..00000000 --- a/lib/installers/infra/herocoordinator/herocoordinator_actions.v +++ /dev/null @@ -1,253 +0,0 @@ -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)! -}