Files
herolib/lib/virt/heropods/factory.v
despiegk cb125e8114 ...
Co-authored-by: Omdanii <mahmmoud.hassanein@gmail.com>
2025-09-07 15:15:41 +04:00

191 lines
5.5 KiB
V

module heropods
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.osal.tmux
import freeflowuniverse.herolib.osal.core as osal
import time
import freeflowuniverse.herolib.builder
import freeflowuniverse.herolib.core.pathlib
import os
pub struct ContainerFactory {
pub mut:
tmux_session string // tmux session name if used
containers map[string]&Container
images map[string]&ContainerImage // Added images map
}
@[params]
pub struct FactoryInitArgs {
pub:
reset bool
use_podman bool = true // Use podman for image management
}
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
osal.exec(cmd: 'mkdir -p /containers/images /containers/configs /containers/runtime', stdout: false)!
if args.use_podman {
// Check if podman is installed
if !osal.cmd_exists('podman') {
console.print_stderr('Warning: podman not found. Installing podman is recommended for better image management.')
console.print_debug('You can install podman with: apt install podman (Ubuntu) or brew install podman (macOS)')
} else {
console.print_debug('Using podman for image management')
}
}
// Load existing images into cache
self.load_existing_images()!
// Setup default images if they don't exist
if !args.use_podman {
self.setup_default_images_legacy(args.reset)!
}
}
// Load existing images from filesystem into cache
fn (mut self ContainerFactory) load_existing_images() ! {
images_base_dir := '/containers/images'
if !os.is_dir(images_base_dir) {
return
}
dirs := os.ls(images_base_dir) or { return }
for dir in dirs {
full_path := '${images_base_dir}/${dir}'
if os.is_dir(full_path) {
rootfs_path := '${full_path}/rootfs'
if os.is_dir(rootfs_path) {
mut image := &ContainerImage{
image_name: dir
rootfs_path: rootfs_path
factory: &self
}
image.update_metadata() or {
console.print_stderr('Failed to load image metadata for ${dir}')
continue
}
self.images[dir] = image
console.print_debug('Loaded existing image: ${dir}')
}
}
}
}
// Legacy method for downloading images directly (fallback if no podman)
fn (mut self ContainerFactory) setup_default_images_legacy(reset bool) ! {
// Setup for all supported images
images := [ContainerImage.alpine_3_20, .ubuntu_24_04, .ubuntu_25_04]
for image in images {
match image {
.alpine_3_20 {
alpine_ver := '3.20.3'
alpine_file := 'alpine-minirootfs-${alpine_ver}-x86_64.tar.gz'
alpine_url := 'https://dl-cdn.alpinelinux.org/alpine/v${alpine_ver[..4]}/releases/x86_64/${alpine_file}'
alpine_dest := '/containers/images/alpine/${alpine_file}'
alpine_rootfs := '/containers/images/alpine/rootfs'
if reset || !os.exists(alpine_rootfs) {
osal.download(
url: alpine_url
dest: alpine_dest
minsize_kb: 1024
)!
// Extract alpine rootfs
osal.exec(cmd: 'mkdir -p ${alpine_rootfs}', stdout: false)!
osal.exec(cmd: 'tar -xzf ${alpine_dest} -C ${alpine_rootfs}', stdout: false)!
}
console.print_green('Alpine ${alpine_ver} rootfs prepared at ${alpine_rootfs}')
}
.ubuntu_24_04 {
ver := '24.04'
codename := 'noble'
file := 'ubuntu-${ver}-minimal-cloudimg-amd64-root.tar.xz'
url := 'https://cloud-images.ubuntu.com/minimal/releases/${codename}/release/${file}'
dest := '/containers/images/ubuntu/${ver}/${file}'
rootfs := '/containers/images/ubuntu/${ver}/rootfs'
if reset || !os.exists(rootfs) {
osal.download(
url: url
dest: dest
minsize_kb: 10240
)!
// Extract ubuntu rootfs
osal.exec(cmd: 'mkdir -p ${rootfs}', stdout: false)!
osal.exec(cmd: 'tar -xf ${dest} -C ${rootfs}', stdout: false)!
}
console.print_green('Ubuntu ${ver} (${codename}) rootfs prepared at ${rootfs}')
}
.ubuntu_25_04 {
ver := '25.04'
codename := 'plucky'
file := 'ubuntu-${ver}-minimal-cloudimg-amd64-root.tar.xz'
url := 'https://cloud-images.ubuntu.com/daily/minimal/releases/${codename}/release/${file}'
dest := '/containers/images/ubuntu/${ver}/${file}'
rootfs := '/containers/images/ubuntu/${ver}/rootfs'
if reset || !os.exists(rootfs) {
osal.download(
url: url
dest: dest
minsize_kb: 10240
)!
// Extract ubuntu rootfs
osal.exec(cmd: 'mkdir -p ${rootfs}', stdout: false)!
osal.exec(cmd: 'tar -xf ${dest} -C ${rootfs}', stdout: false)!
}
console.print_green('Ubuntu ${ver} (${codename}) rootfs prepared at ${rootfs}')
}
}
}
}
pub fn (mut self ContainerFactory) get(args ContainerNewArgs) !&Container {
if args.name !in self.containers {
return error('Container ${args.name} does not exist')
}
return self.containers[args.name]
}
// Get image by name
pub fn (mut self ContainerFactory) image_get(name string) !&ContainerImage {
if name !in self.images {
return error('Image ${name} does not exist')
}
return self.images[name]
}
pub fn (self ContainerFactory) list() ![]Container {
mut containers := []Container{}
result := osal.exec(cmd: 'crun list --format json', stdout: false) or { '[]' }
// Parse crun list output and populate containers
// The output format from crun list is typically tab-separated
lines := result.split_into_lines()
for line in lines {
if line.trim_space() == '' || line.starts_with('ID') {
continue
}
parts := line.split('\t')
if parts.len > 0 {
containers << Container{
name: parts[0]
factory: &self
}
}
}
return containers
}