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`
This commit is contained in:
Mahmoud-Emad
2025-09-07 16:42:34 +03:00
parent 16c01b2e0f
commit 7001e8a2a6
4 changed files with 32 additions and 16 deletions

View File

@@ -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'
)!

View File

@@ -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

View File

@@ -10,7 +10,7 @@ import json
@[heap]
pub struct ContainerImage {
pub mut:
image_name string @[required] // image is located in /containers/images/<image_name>/rootfs
image_name string @[required] // image is located in ${self.factory.base_dir}/images/<image_name>/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/<image_name>/rootfs
image_name string @[required] // image is located in ${self.factory.base_dir}/images/<image_name>/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

View File

@@ -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
}