From 0ae8e227fc0ab8d3723ddc3ea975b18b5bf29730 Mon Sep 17 00:00:00 2001 From: mariobassem Date: Mon, 27 Jan 2025 16:31:54 +0200 Subject: [PATCH] feat: Add screen installer - Add a new installer for the `screen` utility. - This installer supports Ubuntu and macOS. - Includes functionality for installation, uninstallation, and status checking. - Fixed tests for osal.screen Co-authored-by: mahmmoud.hassanein --- lib/installers/infra/screen/.heroscript | 13 ++++ lib/installers/infra/screen/readme.md | 44 ++++++++++++ lib/installers/infra/screen/screen_actions.v | 63 ++++++++++++++++ lib/installers/infra/screen/screen_factory_.v | 71 +++++++++++++++++++ lib/installers/infra/screen/screen_model.v | 22 ++++++ lib/osal/screen/screen.v | 16 +++-- lib/osal/screen/screen_test.v | 17 +++-- test_basic.vsh | 1 - 8 files changed, 237 insertions(+), 10 deletions(-) create mode 100644 lib/installers/infra/screen/.heroscript create mode 100644 lib/installers/infra/screen/readme.md create mode 100644 lib/installers/infra/screen/screen_actions.v create mode 100644 lib/installers/infra/screen/screen_factory_.v create mode 100644 lib/installers/infra/screen/screen_model.v 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/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/test_basic.vsh b/test_basic.vsh index c0d73538..5aa61294 100755 --- a/test_basic.vsh +++ b/test_basic.vsh @@ -185,7 +185,6 @@ clients/livekit ' tests_error := ' -screen_test.v tmux_session_test.v tmux_window_test.v tmux_test.v