diff --git a/lib/virt/heropods/.heroscript b/lib/virt/heropods/.heroscript new file mode 100644 index 00000000..75fd311f --- /dev/null +++ b/lib/virt/heropods/.heroscript @@ -0,0 +1,7 @@ + +!!hero_code.generate_client + name:'' + classname:'HeroPods' + singleton:0 + default:1 + hasconfig:1 \ No newline at end of file diff --git a/lib/virt/heropods/container.v b/lib/virt/heropods/container.v index 4a3d5306..f774a3b5 100644 --- a/lib/virt/heropods/container.v +++ b/lib/virt/heropods/container.v @@ -15,7 +15,7 @@ pub mut: node ?&builder.Node tmux_pane ?&tmux.Pane crun_config ?&crun.CrunConfig - factory &ContainerFactory + factory &HeroPods } // Struct to parse JSON output of `crun state` diff --git a/lib/virt/heropods/container_create.v b/lib/virt/heropods/container_create.v index 015b15cd..c47608d6 100644 --- a/lib/virt/heropods/container_create.v +++ b/lib/virt/heropods/container_create.v @@ -24,7 +24,7 @@ pub: reset bool } -pub fn (mut self ContainerFactory) new(args ContainerNewArgs) !&Container { +pub fn (mut self HeroPods) container_new(args ContainerNewArgs) !&Container { if args.name in self.containers && !args.reset { return self.containers[args.name] or { panic('bug: container should exist') } } @@ -88,7 +88,7 @@ pub fn (mut self ContainerFactory) new(args ContainerNewArgs) !&Container { } // Create crun configuration using the crun module -fn (mut self ContainerFactory) create_crun_config(container_name string, rootfs_path string) !&crun.CrunConfig { +fn (mut self HeroPods) create_crun_config(container_name string, rootfs_path string) !&crun.CrunConfig { // Create crun configuration using the factory pattern mut config := crun.new(mut self.crun_configs, name: container_name)! @@ -120,7 +120,7 @@ fn (mut self ContainerFactory) create_crun_config(container_name string, rootfs_ } // Use podman to pull image and extract rootfs -fn (self ContainerFactory) podman_pull_and_export(docker_url string, image_name string, rootfs_path string) ! { +fn (self HeroPods) podman_pull_and_export(docker_url string, image_name string, rootfs_path string) ! { // Pull image osal.exec( cmd: 'podman pull ${docker_url}' diff --git a/lib/virt/heropods/container_image.v b/lib/virt/heropods/container_image.v index d0db37f6..ff355f30 100644 --- a/lib/virt/heropods/container_image.v +++ b/lib/virt/heropods/container_image.v @@ -2,10 +2,8 @@ module heropods import incubaid.herolib.ui.console import incubaid.herolib.osal.core as osal -import incubaid.herolib.core.pathlib import incubaid.herolib.core.texttools import os -import json @[heap] pub struct ContainerImage { @@ -15,7 +13,7 @@ pub mut: rootfs_path string // path to the extracted rootfs size_mb f64 // size in MB created_at string // creation timestamp - factory &ContainerFactory @[skip; str: skip] + factory &HeroPods @[skip; str: skip] } @[params] @@ -41,7 +39,7 @@ pub mut: } // Create new image or get existing -pub fn (mut self ContainerFactory) image_new(args ContainerImageArgs) !&ContainerImage { +pub fn (mut self HeroPods) image_new(args ContainerImageArgs) !&ContainerImage { mut image_name := texttools.name_fix(args.image_name) rootfs_path := '${self.base_dir}/images/${image_name}/rootfs' @@ -138,7 +136,7 @@ fn (mut self ContainerImage) update_metadata() ! { } // List all available images -pub fn (mut self ContainerFactory) images_list() ![]&ContainerImage { +pub fn (mut self HeroPods) images_list() ![]&ContainerImage { mut images := []&ContainerImage{} images_base_dir := '${self.base_dir}/images' @@ -194,7 +192,7 @@ pub fn (mut self ContainerImage) export(args ImageExportArgs) ! { } // Import image from .tgz file -pub fn (mut self ContainerFactory) image_import(args ImageImportArgs) !&ContainerImage { +pub fn (mut self HeroPods) image_import(args ImageImportArgs) !&ContainerImage { if !os.exists(args.source_path) { return error('Source file not found: ${args.source_path}') } diff --git a/lib/virt/heropods/heropods_factory_.v b/lib/virt/heropods/heropods_factory_.v new file mode 100644 index 00000000..08022a80 --- /dev/null +++ b/lib/virt/heropods/heropods_factory_.v @@ -0,0 +1,143 @@ +module heropods + +import incubaid.herolib.core.base +import incubaid.herolib.core.playbook { PlayBook } +import json + +__global ( + heropods_global map[string]&HeroPods + heropods_default string +) + +/////////FACTORY + +@[params] +pub struct ArgsGet { +pub mut: + name string = 'default' // name of the heropods + fromdb bool // will load from filesystem + create bool // default will not create if not exist + reset bool // will reset the heropods + use_podman bool = true // will use podman for image management +} + +pub fn new(args ArgsGet) !&HeroPods { + mut obj := HeroPods{ + name: args.name + reset: args.reset + use_podman: args.use_podman + } + set(obj)! + return get(name: args.name)! +} + +pub fn get(args ArgsGet) !&HeroPods { + mut context := base.context()! + heropods_default = args.name + if args.fromdb || args.name !in heropods_global { + mut r := context.redis()! + if r.hexists('context:heropods', args.name)! { + data := r.hget('context:heropods', args.name)! + if data.len == 0 { + print_backtrace() + return error('HeroPods with name: ${args.name} does not exist, prob bug.') + } + mut obj := json.decode(HeroPods, data)! + set_in_mem(obj)! + } else { + if args.create { + new(args)! + } else { + print_backtrace() + return error("HeroPods with name '${args.name}' does not exist") + } + } + return get(args)! // no longer from db nor create + } + return heropods_global[args.name] or { + print_backtrace() + return error('could not get config for heropods with name:${args.name}') + } +} + +// register the config for the future +pub fn set(o HeroPods) ! { + mut o2 := set_in_mem(o)! + heropods_default = o2.name + mut context := base.context()! + mut r := context.redis()! + r.hset('context:heropods', 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:heropods', args.name)! +} + +pub fn delete(args ArgsGet) ! { + mut context := base.context()! + mut r := context.redis()! + r.hdel('context:heropods', 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) ![]&HeroPods { + mut res := []&HeroPods{} + mut context := base.context()! + if args.fromdb { + // reset what is in mem + heropods_global = map[string]&HeroPods{} + heropods_default = '' + } + if args.fromdb { + mut r := context.redis()! + mut l := r.hkeys('context:heropods')! + + for name in l { + res << get(name: name, fromdb: true)! + } + return res + } else { + // load from memory + for _, client in heropods_global { + res << client + } + } + return res +} + +// only sets in mem, does not set as config +fn set_in_mem(o HeroPods) !HeroPods { + mut o2 := obj_init(o)! + heropods_global[o2.name] = &o2 + heropods_default = o2.name + return o2 +} + +pub fn play(mut plbook PlayBook) ! { + if !plbook.exists(filter: 'heropods.') { + return + } + mut install_actions := plbook.find(filter: 'heropods.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 + } + } +} + +// switch instance to be used for heropods +pub fn switch(name string) { + heropods_default = name +} diff --git a/lib/virt/heropods/factory.v b/lib/virt/heropods/heropods_model.v similarity index 67% rename from lib/virt/heropods/factory.v rename to lib/virt/heropods/heropods_model.v index 494d5f42..cdd898cc 100644 --- a/lib/virt/heropods/factory.v +++ b/lib/virt/heropods/heropods_model.v @@ -1,39 +1,39 @@ module heropods -import incubaid.herolib.ui.console +import incubaid.herolib.data.encoderhero import incubaid.herolib.osal.core as osal +import incubaid.herolib.ui.console import incubaid.herolib.virt.crun import os +pub const version = '0.0.0' +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 ContainerFactory { +pub struct HeroPods { pub mut: - tmux_session string - containers map[string]&Container - images map[string]&ContainerImage - crun_configs map[string]&crun.CrunConfig - base_dir string + tmux_session string // tmux session name + containers map[string]&Container // name -> container mapping + images map[string]&ContainerImage // name -> image mapping + crun_configs map[string]&crun.CrunConfig // name -> crun config mapping + base_dir string // base directory for all container data + reset bool // will reset the heropods + use_podman bool = true // will use podman for image management + name string // name of the heropods } -@[params] -pub struct FactoryInitArgs { -pub: - reset bool - use_podman bool = true -} +// your checking & initialization code if needed +fn obj_init(mycfg_ HeroPods) !HeroPods { + mut args := mycfg_ -pub fn new(args FactoryInitArgs) !ContainerFactory { - mut f := ContainerFactory{} - f.init(args)! - return f -} - -fn (mut self ContainerFactory) init(args FactoryInitArgs) ! { // Ensure base directories exist - self.base_dir = os.getenv_opt('CONTAINERS_DIR') or { os.home_dir() + '/.containers' } + args.base_dir = os.getenv_opt('CONTAINERS_DIR') or { os.home_dir() + '/.containers' } osal.exec( - cmd: 'mkdir -p ${self.base_dir}/images ${self.base_dir}/configs ${self.base_dir}/runtime' + cmd: 'mkdir -p ${args.base_dir}/images ${args.base_dir}/configs ${args.base_dir}/runtime' stdout: false )! @@ -46,21 +46,41 @@ fn (mut self ContainerFactory) init(args FactoryInitArgs) ! { } } + mut heropods := HeroPods{ + tmux_session: args.name + containers: map[string]&Container{} + images: map[string]&ContainerImage{} + crun_configs: map[string]&crun.CrunConfig{} + base_dir: args.base_dir + reset: args.reset + use_podman: args.use_podman + name: args.name + } + // Clean up any leftover crun state if reset is requested if args.reset { - self.cleanup_crun_state()! + heropods.cleanup_crun_state()! } // Load existing images into cache - self.load_existing_images()! + heropods.load_existing_images()! // Setup default images if not using podman if !args.use_podman { - self.setup_default_images(args.reset)! + heropods.setup_default_images(args.reset)! } + + return args } -fn (mut self ContainerFactory) setup_default_images(reset bool) ! { +/////////////NORMALLY NO NEED TO TOUCH + +pub fn heroscript_loads(heroscript string) !HeroPods { + mut obj := encoderhero.decode[HeroPods](heroscript)! + return obj +} + +fn (mut self HeroPods) setup_default_images(reset bool) ! { console.print_header('Setting up default images...') default_images := [ContainerImageType.alpine_3_20, .ubuntu_24_04, .ubuntu_25_04] @@ -78,7 +98,7 @@ fn (mut self ContainerFactory) setup_default_images(reset bool) ! { } // Load existing images from filesystem into cache -fn (mut self ContainerFactory) load_existing_images() ! { +fn (mut self HeroPods) load_existing_images() ! { images_base_dir := '${self.base_dir}/containers/images' if !os.is_dir(images_base_dir) { return @@ -106,7 +126,7 @@ fn (mut self ContainerFactory) load_existing_images() ! { } } -pub fn (mut self ContainerFactory) get(args ContainerNewArgs) !&Container { +pub fn (mut self HeroPods) get(args ContainerNewArgs) !&Container { if args.name !in self.containers { return error('Container "${args.name}" does not exist. Use factory.new() to create it first.') } @@ -114,7 +134,7 @@ pub fn (mut self ContainerFactory) get(args ContainerNewArgs) !&Container { } // Get image by name -pub fn (mut self ContainerFactory) image_get(name string) !&ContainerImage { +pub fn (mut self HeroPods) image_get(name string) !&ContainerImage { if name !in self.images { return error('Image "${name}" not found in cache. Try importing or downloading it.') } @@ -122,7 +142,7 @@ pub fn (mut self ContainerFactory) image_get(name string) !&ContainerImage { } // List all containers currently managed by crun -pub fn (self ContainerFactory) list() ![]Container { +pub fn (self HeroPods) list() ![]Container { mut containers := []Container{} result := osal.exec(cmd: 'crun list --format json', stdout: false)! @@ -144,7 +164,7 @@ pub fn (self ContainerFactory) list() ![]Container { } // Clean up any leftover crun state -fn (mut self ContainerFactory) cleanup_crun_state() ! { +fn (mut self HeroPods) cleanup_crun_state() ! { console.print_debug('Cleaning up leftover crun state...') crun_root := '${self.base_dir}/runtime' diff --git a/lib/virt/heropods/readme.md b/lib/virt/heropods/readme.md index e69de29b..4eded1ce 100644 --- a/lib/virt/heropods/readme.md +++ b/lib/virt/heropods/readme.md @@ -0,0 +1,30 @@ +# heropods + + + +To get started + +```v + + +import incubaid.herolib.clients. heropods + +mut client:= heropods.get()! + +client... + + + + +``` + +## example heroscript + + +```hero +!!heropods.configure + secret: '...' + host: 'localhost' + port: 8888 +``` +