diff --git a/lib/core/redisclient/rpc_test.v b/lib/core/redisclient/rpc_test.v index 5dbc5713..59823af4 100644 --- a/lib/core/redisclient/rpc_test.v +++ b/lib/core/redisclient/rpc_test.v @@ -5,7 +5,7 @@ fn setup() !&redisclient.Redis { mut redis := redisclient.core_get()! // Select db 10 to be away from default one '0' redis.selectdb(10) or { panic(err) } - return &redis + return redis } fn cleanup(mut redis redisclient.Redis) ! { @@ -25,7 +25,8 @@ fn test_rpc() { mut r := redis.rpc_get('testrpc') r.call(cmd: 'test.cmd', data: 'this is my data, normally json', wait: false)! - returnqueue := r.process(10000, process_test)! + + returnqueue := r.process(process_test, timeout: 10000)! mut res := r.result(10000, returnqueue)! console.print_debug(res) diff --git a/lib/installers/infra/screen/.heroscript b/lib/installers/infra/screen/.heroscript new file mode 100644 index 00000000..5b8239b8 --- /dev/null +++ b/lib/installers/infra/screen/.heroscript @@ -0,0 +1,13 @@ + +!!hero_code.generate_installer + name:'screen' + classname:'Screen' + singleton:0 + templates:0 + default:1 + title:'' + supported_platforms:'' + reset:0 + startupmanager:0 + hasconfig:0 + build:0 \ No newline at end of file diff --git a/lib/installers/infra/screen/readme.md b/lib/installers/infra/screen/readme.md new file mode 100644 index 00000000..f8480f1f --- /dev/null +++ b/lib/installers/infra/screen/readme.md @@ -0,0 +1,44 @@ +# screen + + + +To get started + +```vlang + + +import freeflowuniverse.herolib.installers.something.screen as screen_installer + +heroscript:=" +!!screen.configure name:'test' + password: '1234' + port: 7701 + +!!screen.start name:'test' reset:1 +" + +screen_installer.play(heroscript=heroscript)! + +//or we can call the default and do a start with reset +//mut installer:= screen_installer.get()! +//installer.start(reset:true)! + + + + +``` + +## example heroscript + +```hero +!!screen.configure + homedir: '/home/user/screen' + username: 'admin' + password: 'secretpassword' + title: 'Some Title' + host: 'localhost' + port: 8888 + +``` + + diff --git a/lib/installers/infra/screen/screen_actions.v b/lib/installers/infra/screen/screen_actions.v new file mode 100644 index 00000000..616be036 --- /dev/null +++ b/lib/installers/infra/screen/screen_actions.v @@ -0,0 +1,63 @@ +module screen + +import freeflowuniverse.herolib.core +import freeflowuniverse.herolib.ui.console +import freeflowuniverse.herolib.installers.ulist +import os + +//////////////////// following actions are not specific to instance of the object + +// checks if a certain version or above is installed +fn installed() !bool { + res := os.execute('screen --version') + if res.exit_code != 0 { + return false + } + + return true +} + +// get the Upload List of the files +fn ulist_get() !ulist.UList { + // optionally build a UList which is all paths which are result of building, is then used e.g. in upload + return ulist.UList{} +} + +// uploads to S3 server if configured +fn upload() ! { +} + +fn install() ! { + console.print_header('install screen') + + if core.is_ubuntu()! { + res := os.execute('sudo apt install screen -y') + if res.exit_code != 0 { + return error('failed to install screen: ${res.output}') + } + } else if core.is_osx()! { + res := os.execute('sudo brew install screen') + if res.exit_code != 0 { + return error('failed to install screen: ${res.output}') + } + } else { + return error('unsupported platform: ${core.platform()!}') + } +} + +fn destroy() ! { + console.print_header('uninstall screen') + if core.is_ubuntu()! { + res := os.execute('sudo apt remove screen -y') + if res.exit_code != 0 { + return error('failed to uninstall screen: ${res.output}') + } + } else if core.is_osx()! { + res := os.execute('sudo brew uninstall screen') + if res.exit_code != 0 { + return error('failed to uninstall screen: ${res.output}') + } + } else { + return error('unsupported platform: ${core.platform()!}') + } +} diff --git a/lib/installers/infra/screen/screen_factory_.v b/lib/installers/infra/screen/screen_factory_.v new file mode 100644 index 00000000..fff3aaa7 --- /dev/null +++ b/lib/installers/infra/screen/screen_factory_.v @@ -0,0 +1,71 @@ +module screen + +import freeflowuniverse.herolib.ui.console +import freeflowuniverse.herolib.sysadmin.startupmanager +import freeflowuniverse.herolib.osal.zinit + +__global ( + screen_global map[string]&Screen + screen_default string +) + +/////////FACTORY + +@[params] +pub struct ArgsGet { +pub mut: + name string +} + +pub fn get(args_ ArgsGet) !&Screen { + return &Screen{} +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS /////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager { + // unknown + // screen + // zinit + // tmux + // systemd + match cat { + .zinit { + console.print_debug('startupmanager: zinit') + return startupmanager.get(cat: .zinit)! + } + .systemd { + console.print_debug('startupmanager: systemd') + return startupmanager.get(cat: .systemd)! + } + else { + console.print_debug('startupmanager: auto') + return startupmanager.get()! + } + } +} + +@[params] +pub struct InstallArgs { +pub mut: + reset bool +} + +pub fn (mut self Screen) install(args InstallArgs) ! { + switch(self.name) + if args.reset || (!installed()!) { + install()! + } +} + +pub fn (mut self Screen) destroy() ! { + switch(self.name) + destroy()! +} + +// switch instance to be used for screen +pub fn switch(name string) { + screen_default = name +} diff --git a/lib/installers/infra/screen/screen_model.v b/lib/installers/infra/screen/screen_model.v new file mode 100644 index 00000000..2c727f13 --- /dev/null +++ b/lib/installers/infra/screen/screen_model.v @@ -0,0 +1,22 @@ +module screen + +const singleton = false +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 Screen { +pub mut: + name string = 'default' +} + +fn obj_init(obj_ Screen) !Screen { + // never call get here, only thing we can do here is work on object itself + mut obj := obj_ + return obj +} + +// called before start if done +fn configure() ! { + // mut installer := get()! +} diff --git a/lib/osal/package_test.v b/lib/osal/package_test.v index f635abaa..540996fe 100644 --- a/lib/osal/package_test.v +++ b/lib/osal/package_test.v @@ -13,24 +13,23 @@ fn test_package_management() { } } - // First ensure wget is not installed - package_remove('wget') or {} + is_wget_installed := cmd_exists('wget') - // Verify wget is not installed - assert !cmd_exists('wget') + if is_wget_installed { + // Clean up - remove wget + package_remove('wget') or { assert false, 'Failed to remove wget: ${err}' } + assert !cmd_exists('wget') + // Reinstalling wget as it was previously installed + package_install('wget') or { assert false, 'Failed to install wget: ${err}' } + assert cmd_exists('wget') + return + } - // Update package list - package_refresh() or { assert false, 'Failed to refresh package list: ${err}' } - - // Install wget + // Intstall wget and verify it is installed package_install('wget') or { assert false, 'Failed to install wget: ${err}' } - - // Verify wget is now installed assert cmd_exists('wget') // Clean up - remove wget package_remove('wget') or { assert false, 'Failed to remove wget: ${err}' } - - // Verify wget is removed assert !cmd_exists('wget') } diff --git a/lib/osal/screen/screen.v b/lib/osal/screen/screen.v index f91e98ad..f1e5d31d 100644 --- a/lib/osal/screen/screen.v +++ b/lib/osal/screen/screen.v @@ -31,17 +31,25 @@ pub enum ScreenStatus { pub fn (self Screen) status() !ScreenStatus { // Command to list screen sessions cmd := 'screen -ls' - response := osal.execute_silent(cmd)! + + ls_response := os.execute(cmd) + if ls_response.exit_code != 0 { + if ls_response.output.contains('No Sockets found') { + return .inactive + } + + return error('failed to list screen sessions: ${ls_response.output}') + } // Check if the screen session exists by looking for the session name in the output - if !response.contains(self.name) { + if !ls_response.output.contains(self.name) { return .inactive } // Command to send a dummy command to the screen session and check response - cmd_check := "screen -S ${self.name} -X eval \"stuff \\\"\\003\\\"; sleep 0.1; stuff \\\"ps\\n\\\"\"" + cmd_check := "screen -S ${self.name} -X stuff $'\003' && sleep 0.1 && screen -S ${self.name} -X stuff $'ps\n'" osal.execute_silent(cmd_check)! - + time.sleep(100 * time.millisecond) // Command to check if there is an active process in the screen session cmd_ps := 'screen -S ${self.name} -X hardcopy -h /tmp/screen_output; cat /tmp/screen_output' ps_response := osal.execute_silent(cmd_ps)! diff --git a/lib/osal/screen/screen_test.v b/lib/osal/screen/screen_test.v index f56cd77a..18278a8f 100644 --- a/lib/osal/screen/screen_test.v +++ b/lib/osal/screen/screen_test.v @@ -29,11 +29,6 @@ pub fn testsuite_begin() ! { cleanup_test_screens()! } -// Cleanup after all tests -pub fn testsuite_end() ! { - cleanup_test_screens()! -} - fn cleanup_test_screens() ! { mut screen_factory := new(reset: false)! screen_factory.scan()! @@ -80,6 +75,9 @@ fn create_and_verify_screen(mut screen_factory ScreensFactory, name string, cmd // Test screen creation and basic status pub fn test_screen_creation() ! { + defer { + cleanup_test_screens() or { panic('failed to cleanup test screens: ${err}') } + } mut screen_factory := new(reset: false)! mut screen := create_and_verify_screen(mut &screen_factory, test_screen_name, '/bin/bash')! @@ -90,6 +88,9 @@ pub fn test_screen_creation() ! { // Test command sending functionality pub fn test_screen_cmd_send() ! { + defer { + cleanup_test_screens() or { panic('failed to cleanup test screens: ${err}') } + } mut screen_factory := new(reset: false)! mut screen := create_and_verify_screen(mut &screen_factory, test_screen_name, '/bin/bash')! @@ -106,6 +107,9 @@ pub fn test_screen_cmd_send() ! { // Test error cases pub fn test_screen_errors() ! { + defer { + cleanup_test_screens() or { panic('failed to cleanup test screens: ${err}') } + } mut screen_factory := new(reset: false)! // Test invalid screen name @@ -127,6 +131,9 @@ pub fn test_screen_errors() ! { // Test multiple screens pub fn test_multiple_screens() ! { + defer { + cleanup_test_screens() or { panic('failed to cleanup test screens: ${err}') } + } mut screen_factory := new(reset: false)! screen1_name := '${test_screen_name}_1' diff --git a/lib/osal/tmux/testdata/tmux_window_test.v b/lib/osal/tmux/testdata/tmux_window_test.v deleted file mode 100644 index 686d742e..00000000 --- a/lib/osal/tmux/testdata/tmux_window_test.v +++ /dev/null @@ -1,67 +0,0 @@ -module tmux - -import freeflowuniverse.herolib.osal -import freeflowuniverse.herolib.installers.tmux -import freeflowuniverse.herolib.ui.console - -// uses single tmux instance for all tests -__global ( - tmux Tmux -) - -fn init() { - tmux = get_remote('185.69.166.152')! - - // reset tmux for tests - if tmux.is_running() { - tmux.stop() or { panic('Cannot stop tmux') } - } -} - -fn testsuite_end() { - if tmux.is_running() { - tmux.stop()! - } -} - -fn test_window_new() { - tmux.start() or { panic("can't start tmux: ${err}") } - - // test window new with only name arg - window_args := WindowArgs{ - name: 'TestWindow' - } - - assert !tmux.sessions.keys().contains('main') - - mut window := tmux.window_new(window_args) or { panic("Can't create new window: ${err}") } - assert tmux.sessions.keys().contains('main') - window.delete() or { panic('Cant delete window') } -} - -// // tests creating duplicate windows -// fn test_window_new0() { - -// -// installer := tmux.get_install( - -// mut tmux := Tmux { -// node: node_ssh -// } - -// window_args := WindowArgs { -// name: 'TestWindow0' -// } - -// // console.print_debug(tmux) -// mut window := tmux.window_new(window_args) or { -// panic("Can't create new window: $err") -// } -// assert tmux.sessions.keys().contains('main') -// mut window_dup := tmux.window_new(window_args) or { -// panic("Can't create new window: $err") -// } -// console.print_debug(node_ssh.exec('tmux ls') or { panic("fail:$err")}) -// window.delete() or { panic("Cant delete window") } -// // console.print_debug(tmux) -// } diff --git a/lib/osal/tmux/tmux.v b/lib/osal/tmux/tmux.v index 7dbb3686..3c34e9e6 100644 --- a/lib/osal/tmux/tmux.v +++ b/lib/osal/tmux/tmux.v @@ -23,20 +23,20 @@ pub fn new(args TmuxNewArgs) !Tmux { mut t := Tmux{ sessionid: args.sessionid } - t.load()! + // t.load()! t.scan()! return t } -// loads tmux session, populate the object -pub fn (mut tmux Tmux) load() ! { - isrunning := tmux.is_running()! - if !isrunning { - tmux.start()! - } - // console.print_debug("SCAN") - tmux.scan()! -} +// // loads tmux session, populate the object +// pub fn (mut tmux Tmux) load() ! { +// // isrunning := tmux.is_running()! +// // if !isrunning { +// // tmux.start()! +// // } +// // console.print_debug("SCAN") +// tmux.scan()! +// } pub fn (mut t Tmux) stop() ! { $if debug { @@ -91,19 +91,18 @@ pub fn (mut t Tmux) windows_get() []&Window { // checks whether tmux server is running pub fn (mut t Tmux) is_running() !bool { - res := osal.exec(cmd: 'tmux info', stdout: false, name: 'tmux_info', raise_error: false) or { - panic('bug') - } - if res.error.contains('no server running') { - // console.print_debug(" TMUX NOT RUNNING") - return false - } - if res.error.contains('no current client') { - return true - } - if res.exit_code > 0 { - return error('could not execute tmux info.\n${res}') + res := os.execute('tmux info') + if res.exit_code != 0 { + if res.output.contains('no server running') { + // console.print_debug(" TMUX NOT RUNNING") + return false + } + if res.output.contains('no current client') { + return true + } + return error('could not execute tmux info.\n${res.output}') } + return true } diff --git a/lib/osal/tmux/tmux_scan.v b/lib/osal/tmux/tmux_scan.v index f385262d..bbe2ae33 100644 --- a/lib/osal/tmux/tmux_scan.v +++ b/lib/osal/tmux/tmux_scan.v @@ -58,6 +58,9 @@ pub fn (mut t Tmux) scan() ! { cmd_list_session := "tmux list-sessions -F '#{session_name}'" exec_list := osal.exec(cmd: cmd_list_session, stdout: false, name: 'tmux_list') or { + if err.msg().contains('no server running') { + return + } return error('could not execute list sessions.\n${err}') } @@ -80,7 +83,7 @@ pub fn (mut t Tmux) scan() ! { } console.print_debug(t) - + println('t: ${t}') // mut done := map[string]bool{} cmd := "tmux list-panes -a -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'" out := osal.execute_silent(cmd) or { return error("Can't execute ${cmd} \n${err}") } diff --git a/lib/osal/tmux/testdata/tmux_session_test.v b/lib/osal/tmux/tmux_session_test.v similarity index 95% rename from lib/osal/tmux/testdata/tmux_session_test.v rename to lib/osal/tmux/tmux_session_test.v index 5be65c1d..e4822c41 100644 --- a/lib/osal/tmux/testdata/tmux_session_test.v +++ b/lib/osal/tmux/tmux_session_test.v @@ -1,7 +1,7 @@ module tmux import freeflowuniverse.herolib.osal -import freeflowuniverse.herolib.installers.tmux +// import freeflowuniverse.herolib.installers.tmux // fn testsuite_end() { @@ -26,13 +26,13 @@ fn test_session_create() { mut s := Session{ tmux: &tmux - windows: map[string]&Window{} + windows: []&Window{} name: 'testsession' } mut s2 := Session{ tmux: &tmux - windows: map[string]&Window{} + windows: []&Window{} name: 'testsession2' } diff --git a/lib/osal/tmux/tmux_window.v b/lib/osal/tmux/tmux_window.v index ad60d4c0..de047078 100644 --- a/lib/osal/tmux/tmux_window.v +++ b/lib/osal/tmux/tmux_window.v @@ -191,7 +191,7 @@ pub fn (mut w Window) stop() ! { stdout: false name: 'tmux_kill-window' // die: false - ) or { return error("Can't kill window with id:${w.id}") } + ) or { return error("Can't kill window with id:${w.id}: ${err}") } w.pid = 0 w.active = false } diff --git a/lib/osal/tmux/tmux_window_test.v b/lib/osal/tmux/tmux_window_test.v new file mode 100644 index 00000000..5bb228d0 --- /dev/null +++ b/lib/osal/tmux/tmux_window_test.v @@ -0,0 +1,67 @@ +module tmux + +import freeflowuniverse.herolib.osal +import freeflowuniverse.herolib.ui.console +import time + +// uses single tmux instance for all tests + +fn testsuite_begin() { + muttmux := new() or { panic('Cannot create tmux: ${err}') } + + // reset tmux for tests + is_running := tmux.is_running() or { panic('cannot check if tmux is running: ${err}') } + if is_running { + tmux.stop() or { panic('Cannot stop tmux: ${err}') } + } +} + +fn testsuite_end() { + is_running := is_running() or { panic('cannot check if tmux is running: ${err}') } + if is_running { + stop() or { panic('Cannot stop tmux: ${err}') } + } +} + +fn test_window_new() ! { + mut tmux_ := new()! + + // test window new with only name arg + window_args := WindowArgs{ + name: 'TestWindow' + } + + assert tmux_.sessions.filter(it.name == 'main').len == 0 + + mut window := tmux_.window_new(window_args)! + assert tmux_.sessions.filter(it.name == 'main').len > 0 + // time.sleep(1000 * time.millisecond) + // window.stop()! +} + +// tests creating duplicate windows +fn test_window_new0() { + + + installer := tmux.get_install( + + mut tmux := Tmux { + node: node_ssh + } + + window_args := WindowArgs { + name: 'TestWindow0' + } + + // console.print_debug(tmux) + mut window := tmux.window_new(window_args) or { + panic("Can't create new window: $err") + } + assert tmux.sessions.keys().contains('main') + mut window_dup := tmux.window_new(window_args) or { + panic("Can't create new window: $err") + } + console.print_debug(node_ssh.exec('tmux ls') or { panic("fail:$err")}) + window.delete() or { panic("Cant delete window") } + // console.print_debug(tmux) +} diff --git a/lib/osal/zinit/rpc_test.v b/lib/osal/zinit/rpc_test.v index 003a953d..bd9c6bcd 100644 --- a/lib/osal/zinit/rpc_test.v +++ b/lib/osal/zinit/rpc_test.v @@ -2,13 +2,38 @@ module zinit import os import time +import freeflowuniverse.herolib.core +import freeflowuniverse.herolib.osal fn test_zinit() { + if !core.is_linux()! { + // zinit is only supported on linux + return + } + + // TODO: use zinit installer to install zinit + // this is a workaround since we can't import zinit installer due to circular dependency + zinit_version := os.execute('zinit --version') + if zinit_version.exit_code != 0 { + release_url := 'https://github.com/threefoldtech/zinit/releases/download/v0.2.14/zinit' + + mut dest := osal.download( + url: release_url + minsize_kb: 2000 + reset: true + dest: '/tmp/zinit' + )! + + chmod_cmd := os.execute('chmod +x /tmp/zinit') + assert chmod_cmd.exit_code == 0, 'failed to chmod +x /tmp/zinit: ${chmod_cmd.output}' + } + + this_dir := os.dir(@FILE) // you need to have zinit in your path to run this test - spawn os.execute('zinit -s herolib/osal/zinit/zinit/zinit.sock init -c herolib/osal/zinit/zinit') + spawn os.execute('/tmp/zinit -s ${this_dir}/zinit/zinit.sock init -c ${this_dir}/zinit') time.sleep(time.second) - client := new_rpc_client('herolib/osal/zinit/zinit/zinit.sock') + client := new_rpc_client(socket_path: '${this_dir}/zinit/zinit.sock') mut ls := client.list()! mut want_ls := { @@ -57,4 +82,7 @@ fn test_zinit() { time.sleep(time.millisecond * 10) st = client.status('service_1')! assert st.state.contains('SIGTERM') + + // Remove the socet file + os.rm('${this_dir}/zinit/zinit.sock')! } diff --git a/test_basic.vsh b/test_basic.vsh index 6290dc7c..98b150b0 100755 --- a/test_basic.vsh +++ b/test_basic.vsh @@ -185,11 +185,6 @@ clients/livekit ' tests_error := ' -net_test.v -osal/package_test.v -rpc_test.v -screen_test.v -tmux_session_test.v tmux_window_test.v tmux_test.v startupmanager_test.v