From 7001e8a2a6c812907c101431ae35bfdc0c5bba5d Mon Sep 17 00:00:00 2001 From: Mahmoud-Emad Date: Sun, 7 Sep 2025 16:42:34 +0300 Subject: [PATCH] refactor: externalize container and image base directories - Add `base_dir` field to `ContainerFactory` - Initialize `base_dir` from `CONTAINERS_DIR` env or user home - Replace hardcoded `/containers` paths with `base_dir` variable - Update image `created_at` retrieval to use `os.stat` --- examples/virt/heropods/heropods.vsh | 13 +++++++++++++ lib/virt/heropods/container_create.v | 12 ++++++------ lib/virt/heropods/container_image.v | 16 ++++++++-------- lib/virt/heropods/factory.v | 7 +++++-- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/examples/virt/heropods/heropods.vsh b/examples/virt/heropods/heropods.vsh index a200e4ac..794fb363 100755 --- a/examples/virt/heropods/heropods.vsh +++ b/examples/virt/heropods/heropods.vsh @@ -1,3 +1,16 @@ #!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run import freeflowuniverse.herolib.virt.heropods +// Initialize factory + +mut factory := heropods.new( + reset: false + use_podman: true +) or { panic('Failed to init ContainerFactory: ${err}') } + +container := factory.new( + name: 'myalpine' + image: .custom + custom_image_name: 'alpine_3_20' + docker_url: 'docker.io/library/alpine:3.20' +)! diff --git a/lib/virt/heropods/container_create.v b/lib/virt/heropods/container_create.v index 8cdff471..97b816de 100644 --- a/lib/virt/heropods/container_create.v +++ b/lib/virt/heropods/container_create.v @@ -36,22 +36,22 @@ pub fn (mut self ContainerFactory) new(args ContainerNewArgs) !&Container { match args.image { .alpine_3_20 { image_name = 'alpine' - rootfs_path = '/containers/images/alpine/rootfs' + rootfs_path = '${self.base_dir}/images/alpine/rootfs' } .ubuntu_24_04 { image_name = 'ubuntu_24_04' - rootfs_path = '/containers/images/ubuntu/24.04/rootfs' + rootfs_path = '${self.base_dir}/images/ubuntu/24.04/rootfs' } .ubuntu_25_04 { image_name = 'ubuntu_25_04' - rootfs_path = '/containers/images/ubuntu/25.04/rootfs' + rootfs_path = '${self.base_dir}/images/ubuntu/25.04/rootfs' } .custom { if args.custom_image_name == '' { return error('custom_image_name is required when using custom image type') } image_name = args.custom_image_name - rootfs_path = '/containers/images/${image_name}/rootfs' + rootfs_path = '${self.base_dir}/images/${image_name}/rootfs' // Check if image exists, if not and docker_url provided, create it if !os.is_dir(rootfs_path) && args.docker_url != '' { @@ -75,7 +75,7 @@ pub fn (mut self ContainerFactory) new(args ContainerNewArgs) !&Container { // Create container using crun osal.exec( - cmd: 'crun create --bundle /containers/configs/${args.name} ${args.name}' + cmd: 'crun create --bundle ${self.base_dir}/configs/${args.name} ${args.name}' stdout: true )! @@ -89,7 +89,7 @@ pub fn (mut self ContainerFactory) new(args ContainerNewArgs) !&Container { } fn (self ContainerFactory) create_container_config(container_name string, rootfs_path string) ! { - config_dir := '/containers/configs/${container_name}' + config_dir := '${self.base_dir}/configs/${container_name}' osal.exec(cmd: 'mkdir -p ${config_dir}', stdout: false)! // Generate OCI config.json using template diff --git a/lib/virt/heropods/container_image.v b/lib/virt/heropods/container_image.v index 6d1810ec..ab8d4037 100644 --- a/lib/virt/heropods/container_image.v +++ b/lib/virt/heropods/container_image.v @@ -10,7 +10,7 @@ import json @[heap] pub struct ContainerImage { pub mut: - image_name string @[required] // image is located in /containers/images//rootfs + image_name string @[required] // image is located in ${self.factory.base_dir}/images//rootfs docker_url string // optional docker image URL rootfs_path string // path to the extracted rootfs size_mb f64 // size in MB @@ -21,7 +21,7 @@ pub mut: @[params] pub struct ContainerImageArgs { pub mut: - image_name string @[required] // image is located in /containers/images//rootfs + image_name string @[required] // image is located in ${self.factory.base_dir}/images//rootfs docker_url string // docker image URL like "alpine:3.20" or "ubuntu:24.04" reset bool } @@ -43,7 +43,7 @@ pub mut: // Create new image or get existing pub fn (mut self ContainerFactory) image_new(args ContainerImageArgs) !&ContainerImage { mut image_name := texttools.name_fix(args.image_name) - rootfs_path := '/containers/images/${image_name}/rootfs' + rootfs_path := '${self.base_dir}/images/${image_name}/rootfs' // Check if image already exists if image_name in self.images && !args.reset { @@ -84,7 +84,7 @@ fn (mut self ContainerImage) download_from_docker(docker_url string, reset bool) console.print_header('Downloading image: ${docker_url}') // Clean image name for local storage - image_dir := '/containers/images/${self.image_name}' + image_dir := '${self.factory.base_dir}/images/${self.image_name}' // Remove existing if reset is true if reset && os.is_dir(image_dir) { @@ -133,15 +133,15 @@ fn (mut self ContainerImage) update_metadata() ! { self.size_mb = size_str.f64() // Get creation time - stat_result := osal.exec(cmd: 'stat -c "%Y" ${self.rootfs_path}', stdout: false)! - self.created_at = stat_result.output.trim_space() // TODO: should this be ourtime? + info := os.stat(self.rootfs_path) or { return error('stat failed: ${err}') } + self.created_at = info.ctime.str() // or mtime.str(), depending on what you want } // List all available images pub fn (mut self ContainerFactory) images_list() ![]&ContainerImage { mut images := []&ContainerImage{} - images_base_dir := '/containers/images' + images_base_dir := '${self.base_dir}/images' if !os.is_dir(images_base_dir) { return images } @@ -206,7 +206,7 @@ pub fn (mut self ContainerFactory) image_import(args ImageImportArgs) !&Containe console.print_header('Importing image from ${args.source_path}') - image_dir := '/containers/images/${image_name_clean}' + image_dir := '${self.base_dir}/images/${image_name_clean}' rootfs_path := '${image_dir}/rootfs' // Check if image already exists diff --git a/lib/virt/heropods/factory.v b/lib/virt/heropods/factory.v index 50604258..ef6516c1 100644 --- a/lib/virt/heropods/factory.v +++ b/lib/virt/heropods/factory.v @@ -11,6 +11,7 @@ pub mut: tmux_session string containers map[string]&Container images map[string]&ContainerImage + base_dir string } @[params] @@ -28,8 +29,10 @@ pub fn new(args FactoryInitArgs) !ContainerFactory { fn (mut self ContainerFactory) init(args FactoryInitArgs) ! { // Ensure base directories exist + self.base_dir = os.getenv_opt('CONTAINERS_DIR') or { os.home_dir() + '/.containers' } + osal.exec( - cmd: 'mkdir -p /containers/images /containers/configs /containers/runtime' + cmd: 'mkdir -p ${self.base_dir}/images ${self.base_dir}/configs ${self.base_dir}/runtime' stdout: false )! @@ -70,7 +73,7 @@ fn (mut self ContainerFactory) setup_default_images(reset bool) ! { // Load existing images from filesystem into cache fn (mut self ContainerFactory) load_existing_images() ! { - images_base_dir := '/containers/images' + images_base_dir := '${self.base_dir}/containers/images' if !os.is_dir(images_base_dir) { return }