This commit is contained in:
2025-08-25 09:07:00 +02:00
parent 4c1ed80b85
commit bf999d8fcb
23 changed files with 1337 additions and 620 deletions

View File

@@ -1,9 +0,0 @@
module builder
fn test_nodedb() {
// TODO URGENT create tests for nodedb
}
fn test_nodedone() {
// TODO URGENT create tests for nodedone
}

View File

@@ -0,0 +1,311 @@
module playcmds
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.ui.console
pub fn play_osal_core(mut plbook PlayBook) ! {
if !plbook.exists(filter: 'osal.') {
return
}
play_done(mut plbook)!
play_env(mut plbook)!
play_exec(mut plbook)!
play_package(mut plbook)!
}
fn play_done(mut plbook PlayBook) ! {
// Handle !!osal.done_set actions
mut done_set_actions := plbook.find(filter: 'osal.done_set')!
for mut action in done_set_actions {
mut p := action.params
key := p.get('key')!
value := p.get('value')!
console.print_header('Setting done key: ${key} = ${value}')
osal.done_set(key, value)!
action.done = true
}
// Handle !!osal.done_delete actions
mut done_delete_actions := plbook.find(filter: 'osal.done_delete')!
for mut action in done_delete_actions {
mut p := action.params
key := p.get('key')!
console.print_header('Deleting done key: ${key}')
osal.done_delete(key)!
action.done = true
}
// Handle !!osal.done_reset actions
mut done_reset_actions := plbook.find(filter: 'osal.done_reset')!
for mut action in done_reset_actions {
console.print_header('Resetting all done keys')
osal.done_reset()!
action.done = true
}
// Handle !!osal.done_print actions
mut done_print_actions := plbook.find(filter: 'osal.done_print')!
for mut action in done_print_actions {
console.print_header('Printing all done keys')
osal.done_print()!
action.done = true
}
}
fn play_env(mut plbook PlayBook) ! {
// Handle !!osal.env_set actions
mut env_set_actions := plbook.find(filter: 'osal.env_set')!
for mut action in env_set_actions {
mut p := action.params
key := p.get('key')!
value := p.get('value')!
overwrite := p.get_default_true('overwrite')
console.print_header('Setting environment variable: ${key}')
osal.env_set(key: key, value: value, overwrite: overwrite)
action.done = true
}
// Handle !!osal.env_unset actions
mut env_unset_actions := plbook.find(filter: 'osal.env_unset')!
for mut action in env_unset_actions {
mut p := action.params
key := p.get('key')!
console.print_header('Unsetting environment variable: ${key}')
osal.env_unset(key)
action.done = true
}
// Handle !!osal.env_unset_all actions
mut env_unset_all_actions := plbook.find(filter: 'osal.env_unset_all')!
for mut action in env_unset_all_actions {
console.print_header('Unsetting all environment variables')
osal.env_unset_all()
action.done = true
}
// Handle !!osal.env_set_all actions
mut env_set_all_actions := plbook.find(filter: 'osal.env_set_all')!
for mut action in env_set_all_actions {
mut p := action.params
clear_before_set := p.get_default_false('clear_before_set')
overwrite_if_exists := p.get_default_true('overwrite_if_exists')
// Parse environment variables from parameters
mut env_map := map[string]string{}
param_map := p.get_map()
for key, value in param_map {
if key !in ['clear_before_set', 'overwrite_if_exists'] {
env_map[key] = value
}
}
console.print_header('Setting multiple environment variables')
osal.env_set_all(
env: env_map
clear_before_set: clear_before_set
overwrite_if_exists: overwrite_if_exists
)
action.done = true
}
// Handle !!osal.load_env_file actions
mut load_env_file_actions := plbook.find(filter: 'osal.load_env_file')!
for mut action in load_env_file_actions {
mut p := action.params
file_path := p.get('file_path')!
console.print_header('Loading environment from file: ${file_path}')
osal.load_env_file(file_path)!
action.done = true
}
}
fn play_exec(mut plbook PlayBook) ! {
// Handle !!osal.exec actions
mut exec_actions := plbook.find(filter: 'osal.exec')!
for mut action in exec_actions {
mut p := action.params
cmd := p.get('cmd')!
console.print_header('Executing command: ${cmd}')
mut job := osal.exec(
name: p.get_default('name', '')!
cmd: cmd
description: p.get_default('description', '')!
timeout: p.get_int_default('timeout', 3600)!
stdout: p.get_default_true('stdout')
stdout_log: p.get_default_true('stdout_log')
raise_error: p.get_default_true('raise_error')
ignore_error: p.get_default_false('ignore_error')
work_folder: p.get_default('work_folder', '')!
scriptkeep: p.get_default_false('scriptkeep')
debug: p.get_default_false('debug')
shell: p.get_default_false('shell')
retry: p.get_int_default('retry', 0)!
interactive: p.get_default_true('interactive')
async: p.get_default_false('async')
)!
// Store job output in done if specified
if output_key := p.get_default('output_key', '') {
if output_key != '' {
osal.done_set(output_key, job.output)!
}
}
action.done = true
}
// Handle !!osal.exec_silent actions
mut exec_silent_actions := plbook.find(filter: 'osal.exec_silent')!
for mut action in exec_silent_actions {
mut p := action.params
cmd := p.get('cmd')!
console.print_header('Executing command silently: ${cmd}')
output := osal.execute_silent(cmd)!
// Store output in done if specified
if output_key := p.get_default('output_key', '') {
if output_key != '' {
osal.done_set(output_key, output)!
}
}
action.done = true
}
// Handle !!osal.exec_debug actions
mut exec_debug_actions := plbook.find(filter: 'osal.exec_debug')!
for mut action in exec_debug_actions {
mut p := action.params
cmd := p.get('cmd')!
console.print_header('Executing command with debug: ${cmd}')
output := osal.execute_debug(cmd)!
// Store output in done if specified
if output_key := p.get_default('output_key', '') {
if output_key != '' {
osal.done_set(output_key, output)!
}
}
action.done = true
}
// Handle !!osal.exec_stdout actions
mut exec_stdout_actions := plbook.find(filter: 'osal.exec_stdout')!
for mut action in exec_stdout_actions {
mut p := action.params
cmd := p.get('cmd')!
console.print_header('Executing command to stdout: ${cmd}')
output := osal.execute_stdout(cmd)!
// Store output in done if specified
if output_key := p.get_default('output_key', '') {
if output_key != '' {
osal.done_set(output_key, output)!
}
}
action.done = true
}
// Handle !!osal.exec_interactive actions
mut exec_interactive_actions := plbook.find(filter: 'osal.exec_interactive')!
for mut action in exec_interactive_actions {
mut p := action.params
cmd := p.get('cmd')!
console.print_header('Executing command interactively: ${cmd}')
osal.execute_interactive(cmd)!
action.done = true
}
}
fn play_package(mut plbook PlayBook) ! {
// Handle !!osal.package_refresh actions
mut package_refresh_actions := plbook.find(filter: 'osal.package_refresh')!
for mut action in package_refresh_actions {
console.print_header('Refreshing package lists')
osal.package_refresh()!
action.done = true
}
// Handle !!osal.package_install actions
mut package_install_actions := plbook.find(filter: 'osal.package_install')!
for mut action in package_install_actions {
mut p := action.params
// Get package name(s) - can be a single package or comma-separated list
mut packages := []string{}
if p.exists('name') {
packages << p.get('name')!
}
if p.exists('names') {
packages = p.get_list('names')!
}
// Also check for positional arguments
for i in 0 .. 10 {
if arg := p.get_arg_default(i, '') {
if arg != '' {
packages << arg
}
} else {
break
}
}
if packages.len == 0 {
return error('No packages specified for installation')
}
package_name := packages.join(' ')
console.print_header('Installing packages: ${package_name}')
osal.package_install(package_name)!
action.done = true
}
// Handle !!osal.package_remove actions
mut package_remove_actions := plbook.find(filter: 'osal.package_remove')!
for mut action in package_remove_actions {
mut p := action.params
// Get package name(s) - can be a single package or comma-separated list
mut packages := []string{}
if p.exists('name') {
packages << p.get('name')!
}
if p.exists('names') {
packages = p.get_list('names')!
}
// Also check for positional arguments
for i in 0 .. 10 {
if arg := p.get_arg_default(i, '') {
if arg != '' {
packages << arg
}
} else {
break
}
}
if packages.len == 0 {
return error('No packages specified for removal')
}
package_name := packages.join(' ')
console.print_header('Removing packages: ${package_name}')
osal.package_remove(package_name)!
action.done = true
}
}

View File

@@ -0,0 +1,153 @@
module buildah
import freeflowuniverse.herolib.osal.core as osal
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.builder
import os
import json
// is builderah containers
pub enum ContainerStatus {
up
down
restarting
paused
dead
created
}
pub struct IPAddress {
pub mut:
ipv4 string
ipv6 string
}
// need to fill in what is relevant
@[heap]
pub struct BuildahContainer {
pub mut:
id string
builder bool
imageid string
imagename string
containername string
//TODO: not sure all below is needed
hero_in_container bool //once the hero has been installed this is on, does it once per session
created time.Time
ssh_enabled bool // if yes make sure ssh is enabled to the container
ipaddr IPAddress
forwarded_ports []string
mounts []ContainerVolume
ssh_port int // ssh port on node that is used to get ssh
ports []string
networks []string
labels map[string]string @[str: skip]
image &Image @[str: skip]
engine &CEngine @[str: skip]
status ContainerStatus
memsize int // in MB
command string
factory &BuildAHFactory
node_host &builder.Node
}
@[params]
pub struct RunArgs {
pub mut:
cmd string
// TODO:/..
}
@[params]
pub struct PackageInstallArgs {
pub mut:
names string
// TODO:/..
}
// TODO: mimic osal.package_install('mc,tmux,git,rsync,curl,screen,redis,wget,git-lfs')!
// pub fn (mut self BuildahContainer) package_install(args PackageInstallArgs) !{
// //TODO
// names := texttools.to_array(args.names)
// //now check which OS, need to make platform function on container level so we know which platform it is
// panic("implement")
// }
pub fn (mut self BuildahContainer) copy(src string, dest string) ! {
cmd := 'buildah copy ${self.id} ${src} ${dest}'
self.exec(cmd: cmd, stdout: false)!
}
pub fn (mut self BuildahContainer) shell() ! {
cmd := 'buildah run --terminal --env TERM=xterm ${self.id} /bin/bash'
self.node_host.execute_interactive(cmd)!
}
pub fn (mut self BuildahContainer) clean() ! {
cmd := '
#set -x
set +e
rm -rf /root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/share/doc
#pacman -Rns $(pacman -Qtdq) --noconfirm
#pacman -Scc --noconfirm
rm -rf /var/lib/pacman/sync/*
rm -rf /tmp/*
rm -rf /var/tmp/*
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \\;
rm -rf /home/*/.cache/*
rm -rf /usr/share/doc/*
rm -rf /usr/share/man/*
rm -rf /usr/share/info/*
rm -rf /usr/share/licenses/*
find /usr/share/locale -mindepth 1 -maxdepth 1 ! -name "en*" -exec rm -rf {} \\;
rm -rf /usr/share/i18n
rm -rf /usr/share/icons/*
rm -rf /usr/lib/modules/*
rm -rf /var/cache/pacman
journalctl --vacuum-time=1s
'
self.exec(cmd: cmd, stdout: false)!
}
pub fn (mut self BuildahContainer) delete() ! {
self.engine.builder_delete(self.containername)!
}
pub fn (mut self BuildahContainer) inspect() !BuilderInfo {
cmd := 'buildah inspect ${self.containername}'
out := self.host_exec(cmd:(cmd)!
mut r := json.decode(BuilderInfo, out) or {
return error('Failed to decode JSON for inspect: ${err}')
}
return r
}
// mount the build container to a path and return the path where its mounted
pub fn (mut self BuildahContainer) mount_to_path() !string {
cmd := 'buildah mount ${self.containername}'
out := self.exec(cmd:cmd)!
return out.trim_space()
}
pub fn (mut self BuildahContainer) commit(image_name string) ! {
cmd := 'buildah commit ${self.containername} ${image_name}'
self.exec(cmd: cmd)!
}
pub fn (self BuildahContainer) set_entrypoint(entrypoint string) ! {
cmd := 'buildah config --entrypoint \'${entrypoint}\' ${self.containername}'
self.exec(cmd: cmd)!
}
pub fn (self BuildahContainer) set_workingdir(workdir string) ! {
cmd := 'buildah config --workingdir ${workdir} ${self.containername}'
self.exec(cmd: cmd)!
}
pub fn (self BuildahContainer) set_cmd(command string) ! {
cmd := 'buildah config --cmd ${command} ${self.containername}'
self.exec(cmd: cmd)!
}

View File

@@ -0,0 +1,33 @@
module buildah
import freeflowuniverse.herolib.osal.core as osal
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.core.pathlib
import os
import json
pub fn (mut self BuildahContainer) install_zinit() ! {
// https://github.com/threefoldtech/zinit
self.hero_copy()!
self.hero_play_execute('!!installer.zinit')
// TODO: implement by making sure hero is in the build context and then use hero cmd to install this
self.set_entrypoint('/sbin/zinit init --container')!
}
pub fn (mut self BuildahContainer) install_herodb() ! {
self.install_zinit()!
// the hero database gets installed and put in zinit for automatic start
self.hero_play_execute('!!installer.herodb')
//TODO: the hero_play needs to be implemented
}
// copies the hero from host into guest
pub fn (mut self BuildahContainer) install_mycelium() ! {
self.install_zinit()!
// the mycelium database gets installed and put in zinit for automatic start
self.hero_play_execute('!!installer.mycelium')
//TODO: the hero_play needs to be implemented
}

View File

@@ -0,0 +1,91 @@
module buildah
import freeflowuniverse.herolib.osal.core as osal
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.core.pathlib
import os
import json
@[params]
pub struct Command {
pub mut:
name string // to give a name to your command, good to see logs...
cmd string
description string
timeout int = 3600 // timeout in sec
stdout bool = true
stdout_log bool = true
raise_error bool = true // if false, will not raise an error but still error report
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
work_folder string // location where cmd will be executed
environment map[string]string // env variables
ignore_error_codes []int
scriptpath string // is the path where the script will be put which is executed
scriptkeep bool // means we don't remove the script
debug bool // if debug will put +ex in the script which is being executed and will make sure script stays
shell bool // means we will execute it in a shell interactive
retry int
interactive bool = true
async bool
runtime osal.RunTime
}
pub enum RunTime {
bash
python
heroscript
herocmd
v
}
//should use builders underneith
pub fn (mut self BuildahContainer) exec(cmd Command) ! {
//make sure we have hero in the hostnode of self
self.hero_copy()!
mut rt := RunTime.bash
scriptpath := osal.cmd_to_script_path(cmd: cmd.cmd, runtime: cmd.runtime)!
if cmd.runtime == .heroscript || cmd.runtime == .herocmd {
self.hero_copy()!
}
script_basename := os.base(scriptpath)
script_path_in_container := '/tmp/${script_basename}'
self.copy(scriptpath, script_path_in_container)!
// console.print_debug("copy ${scriptpath} into container '${self.containername}'")
cmd_str := 'buildah run ${self.id} ${script_path_in_container}'
// console.print_debug(cmd_str)
if cmd.runtime == .heroscript || cmd.runtime == .herocmd {
self.hero_copy()!
}
mut node:=self.node()!
//TODO: need to check, the node exec needs to be as much as possible same as osal.exec
node.exec(
name: cmd.name
cmd: cmd_str
description: cmd.description
timeout: cmd.timeout
stdout: cmd.stdout
stdout_log: cmd.stdout_log
raise_error: cmd.raise_error
ignore_error: cmd.ignore_error
ignore_error_codes: cmd.ignore_error_codes
scriptpath: cmd.scriptpath
scriptkeep: cmd.scriptkeep
debug: cmd.debug
shell: cmd.shell
retry: cmd.retry
interactive: cmd.interactive
async: cmd.async
) or {
mut epath := pathlib.get_file(path: scriptpath, create: false)!
c := epath.read()!
return error('cannot execute:\n${c}\nerror:\n${err}')
}
}

View File

@@ -0,0 +1,118 @@
module buildah
import freeflowuniverse.herolib.installers.virt.podman as podman_installer
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.builder
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.ui.console
import json
@[params]
pub struct BuildAHNewArgs {
pub mut:
herocompile bool
reset bool
default_image string = 'docker.io/ubuntu:latest'
install bool = true //make sure buildah is installed
node string //normally empty then localhost, also support root@server.example.com:22
}
//TOD: this to allow crossplatform builds
pub enum BuildPlatformType {
linux_arm64
linux_amd64
}
pub struct BuildAHFactory {
pub mut:
host_node &builder.Node
default_image string
platform BuildPlatformType
}
pub fn new(args BuildAHNewArgs)!BuildAHFactory {
mut b:= builder:builder.new()!
mut n := self.builder.node_local()!
if self.node !=""{
// Create a node for remote execution, need to see if this is cached on builder, otherwise will be too slow
n = b.node_new(ipaddr: args.node)!
}
mut bahf := BuildAHFactory{
host_node: n
default_image: args.default_image
}
if args.reset {
//TODO
panic("implement")
}
// if args.herocompile {
// bahf.builder = builder.hero_compile()!
// }
return bahf
}
@[params]
pub struct BuildAhContainerNewArgs {
pub mut:
name string = 'default'
from string
delete bool = true
}
//TODO: implement, missing parts
//TODO: need to supprot a docker builder if we are on osx or windows, so we use the builders functionality as base for executing, not directly osal
pub fn (mut self BuildAHFactory) new(args_ BuilderNewArgs) !BuildahContainer {
mut args := args_
if args.delete {
self.delete(args.name)!
}
if args.from != "" {
args.from = self.default_image
}
mut c := BuildahContainer{
name: args.name
from: args.from
factory: &self
node_host: self.host_node
}
return c
}
fn (mut self BuildAHFactory) list() ![]string {
panic(implement)
cmd := 'buildah containers --json'
out := self.host_exec(cmd:cmd)!
mut r := json.decode([]BuildahContainer, out) or { return error('Failed to decode JSON: ${err}') }
for mut item in r {
item.engine = &e
}
e.builders = r
}
//delete all builders
pub fn (mut self BuildAHFactory) reset() ! {
console.print_debug('remove all')
osal.execute_stdout('buildah rm -a')!
self.builders_load()!
}
pub fn (mut self BuildAHFactory) delete(name string) ! {
if self.exists(name)! {
console.print_debug('remove ${name}')
osal.execute_stdout('buildah rm ${name}')!
}
}

View File

@@ -0,0 +1,42 @@
module buildah
// copies the hero from host into guest and then execute the heroscript or commandline
pub fn (mut self BuildahContainer) hero_cmd_execute(cmd string) ! {
self.hero_copy()!
self.exec(cmd: cmd, runtime: .herocmd)!
}
// send a hero play command to the buildah container
pub fn (mut self BuildahContainer) hero_play_execute(cmd string) ! {
self.hero_copy()!
panic("implement")
}
pub fn (mut self BuildahContainer) hero_execute_script(cmd string) ! {
self.hero_copy()!
self.exec(cmd: cmd, runtime: .heroscript)!
}
// copies the hero from host into guest
pub fn (mut self BuildahContainer) hero_copy() ! {
//TODO: check we are on linux, check also the platformtype arm or intel, if not right platform then build hero in container
panic("implement")
if !osal.cmd_exists('hero') {
herolib.hero_compile()!
}
heropath := osal.cmd_path('hero')!
self.copy(heropath, '/usr/local/bin/hero')!
}
// get a container where we build hero and export hero from the container so we can use it for hero_copy
pub fn (mut self BuildahContainer) hero_build() ! {
panic("implement")
}

View File

@@ -1,4 +1,4 @@
module herocontainers module buildah
struct BuilderInfo { struct BuilderInfo {
type_ string @[json: 'Type'] type_ string @[json: 'Type']

View File

@@ -0,0 +1,179 @@
module buildah
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.ui.console
import os
@[params]
pub struct GetArgs {
pub mut:
reset bool
}
// builder machine based on arch and install vlang
// TODO need to change, go to ubuntu
pub fn builder_base(args GetArgs) !BuildahContainer {
name := 'base'
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
console.print_header('buildah base build')
mut builder := e.builder_new(name: name, from: 'scratch', delete: true)!
mount_path := builder.mount_to_path()!
if mount_path.len < 4 {
return error('mount_path needs to be +4 chars')
}
self.exec(
cmd: '
export MOUNT_PATH=\'${mount_path}\'
mmdebstrap --variant=minbase --components="main,universe" --include="apt,base-files,base-passwd,bash,coreutils" noble \${MOUNT_PATH}
echo "Binding essential directories..."
mount --bind /dev "\${MOUNT_PATH}/dev"
mount --bind /proc "\${MOUNT_PATH}/proc"
mount --bind /sys "\${MOUNT_PATH}/sys"
echo "tzdata tzdata/Areas select Europe" | chroot "\${MOUNT_PATH}" debconf-set-selections
echo "tzdata tzdata/Zones/Europe select Brussels" | chroot "\${MOUNT_PATH}" debconf-set-selections
chroot \${MOUNT_PATH} apt update
# Set up APT for non-interactive installation
export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true
# Update package lists
echo "Updating package lists in chroot..."
chroot \${MOUNT_PATH} apt-get update -yq
# Install required packages
echo "Installing essential packages..."
chroot \${MOUNT_PATH} apt-get install -yq screen bash coreutils curl mc unzip sudo which openssh-client openssh-server redis wget
echo "Cleaning up..."
umount "\${MOUNT_PATH}/dev" || true
umount "\${MOUNT_PATH}/proc" || true
umount "\${MOUNT_PATH}/sys" || true
'
)!
builder.install_zinit()!
// builder.set_entrypoint('redis-server')!
builder.commit('localhost/${name}')!
return builder
}
//TODO: all below are not good, need to use play cmd over hero remotely. see how we did it with core_installers
// // builder machine based on arch and install vlang
// pub fn (mut e CEngine) builder_go_rust(args GetArgs) !BuildahContainer {
// console.print_header('buildah builder go rust')
// name := 'builder_go_rust'
// e.builder_base(reset: false)!
// if !args.reset && e.builder_exists(name)! {
// return e.builder_get(name)!
// }
// mut builder := e.builder_new(name: name, from: 'localhost/base', delete: true)!
// builder.hero_execute_cmd('installers -n golang,rust')!
// // builder.clean()!
// builder.commit('localhost/${name}')!
// e.load()!
// return builder
// }
// pub fn (mut e CEngine) builder_js(args GetArgs) !BuildahContainer {
// console.print_header('buildah builder js')
// name := 'builder_js'
// e.builder_base(reset: false)!
// if !args.reset && e.builder_exists(name)! {
// return e.builder_get(name)!
// }
// mut builder := e.builder_new(name: name, from: 'localhost/base', delete: true)!
// builder.hero_execute_cmd('installers -n nodejs')!
// // builder.clean()!
// builder.commit('localhost/${name}')!
// e.load()!
// return builder
// }
// pub fn (mut e CEngine) builder_js_python(args GetArgs) !BuildahContainer {
// console.print_header('buildah builder js python')
// name := 'builder_js_python'
// e.builder_js(reset: false)!
// if !args.reset && e.builder_exists(name)! {
// return e.builder_get(name)!
// }
// mut builder := e.builder_new(name: name, from: 'localhost/builder_js', delete: true)!
// builder.hero_execute_cmd('installers -n python')!
// // builder.clean()!
// builder.commit('localhost/${name}')!
// e.load()!
// return builder
// }
// pub fn (mut e CEngine) builder_hero(args GetArgs) !BuildahContainer {
// console.print_header('buildah builder hero dev')
// name := 'builder_hero'
// e.builder_js_python(reset: false)!
// if !args.reset && e.builder_exists(name)! {
// return e.builder_get(name)!
// }
// mut builder := e.builder_new(name: name, from: 'localhost/builder_js_python', delete: true)!
// builder.hero_execute_cmd('installers -n hero')!
// // builder.clean()!
// builder.commit('localhost/${name}')!
// e.load()!
// return builder
// }
// pub fn (mut e CEngine) builder_herodev(args GetArgs) !BuildahContainer {
// console.print_header('buildah builder hero dev')
// name := 'builder_herodev'
// e.builder_js_python(reset: false)!
// if !args.reset && e.builder_exists(name)! {
// return e.builder_get(name)!
// }
// mut builder := e.builder_new(name: name, from: 'localhost/builder_hero', delete: true)!
// builder.hero_execute_cmd('installers -n herodev')!
// // builder.clean()!
// builder.commit('localhost/${name}')!
// e.load()!
// return builder
// }
// pub fn (mut e CEngine) builder_heroweb(args GetArgs) !BuildahContainer {
// console.print_header('buildah builder hero web')
// name := 'builder_heroweb'
// e.builder_go_rust(reset: false)!
// e.builder_hero(reset: false)!
// if !args.reset && e.builder_exists(name)! {
// return e.builder_get(name)!
// }
// mut builder0 := e.builder_new(
// name: 'builder_heroweb_temp'
// from: 'localhost/builder_go_rust'
// delete: true
// )!
// builder0.hero_execute_cmd('installers -n heroweb')!
// // builder0.hero_execute_cmd("installers -n heroweb")!
// mpath := builder0.mount_to_path()!
// // copy the built binary to host
// self.exec(
// cmd: '
// mkdir -p ${os.home_dir()}/hero/var/bin
// cp ${mpath}/usr/local/bin/* ${os.home_dir()}/hero/var/bin/
// '
// )!
// builder0.delete()!
// mut builder2 := e.builder_new(name: name, from: 'localhost/builder_hero', delete: true)!
// builder2.copy('${os.home_dir()}/hero/var/bin/', '/usr/local/bin/')!
// builder2.commit('localhost/${name}')!
// e.load()!
// return builder2
// }

View File

@@ -1,242 +0,0 @@
module herocontainers
import freeflowuniverse.herolib.osal.core as osal
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.core.pathlib
import os
import json
// is builderah containers
// pub enum ContainerStatus {
// up
// down
// restarting
// paused
// dead
// created
// }
// need to fill in what is relevant
@[heap]
pub struct Builder {
pub mut:
id string
builder bool
imageid string
imagename string
containername string
engine &CEngine @[skip; str: skip]
// hero_in_container bool //once the hero has been installed this is on, does it once per session
// created time.Time
// ssh_enabled bool // if yes make sure ssh is enabled to the container
// ipaddr IPAddress
// forwarded_ports []string
// mounts []ContainerVolume
// ssh_port int // ssh port on node that is used to get ssh
// ports []string
// networks []string
// labels map[string]string @[str: skip]
// image &Image @[str: skip]
// engine &CEngine @[str: skip]
// status ContainerStatus
// memsize int // in MB
// command string
}
@[params]
pub struct RunArgs {
pub mut:
cmd string
// TODO:/..
}
@[params]
pub struct PackageInstallArgs {
pub mut:
names string
// TODO:/..
}
// TODO: mimic osal.package_install('mc,tmux,git,rsync,curl,screen,redis,wget,git-lfs')!
// pub fn (mut self Builder) package_install(args PackageInstallArgs) !{
// //TODO
// names := texttools.to_array(args.names)
// //now check which OS, need to make platform function on container level so we know which platform it is
// panic("implement")
// }
@[params]
pub struct Command {
pub mut:
name string // to give a name to your command, good to see logs...
cmd string
description string
timeout int = 3600 // timeout in sec
stdout bool = true
stdout_log bool = true
raise_error bool = true // if false, will not raise an error but still error report
ignore_error bool // means if error will just exit and not raise, there will be no error reporting
work_folder string // location where cmd will be executed
environment map[string]string // env variables
ignore_error_codes []int
scriptpath string // is the path where the script will be put which is executed
scriptkeep bool // means we don't remove the script
debug bool // if debug will put +ex in the script which is being executed and will make sure script stays
shell bool // means we will execute it in a shell interactive
retry int
interactive bool = true
async bool
runtime osal.RunTime
}
pub enum RunTime {
bash
python
heroscript
herocmd
v
}
pub fn (mut self Builder) run(cmd Command) ! {
mut rt := RunTime.bash
scriptpath := osal.cmd_to_script_path(cmd: cmd.cmd, runtime: cmd.runtime)!
if cmd.runtime == .heroscript || cmd.runtime == .herocmd {
self.hero_copy()!
}
script_basename := os.base(scriptpath)
script_path_in_container := '/tmp/${script_basename}'
self.copy(scriptpath, script_path_in_container)!
// console.print_debug("copy ${scriptpath} into container '${self.containername}'")
cmd_str := 'buildah run ${self.id} ${script_path_in_container}'
// console.print_debug(cmd_str)
if cmd.runtime == .heroscript || cmd.runtime == .herocmd {
self.hero_copy()!
}
osal.exec(
name: cmd.name
cmd: cmd_str
description: cmd.description
timeout: cmd.timeout
stdout: cmd.stdout
stdout_log: cmd.stdout_log
raise_error: cmd.raise_error
ignore_error: cmd.ignore_error
ignore_error_codes: cmd.ignore_error_codes
scriptpath: cmd.scriptpath
scriptkeep: cmd.scriptkeep
debug: cmd.debug
shell: cmd.shell
retry: cmd.retry
interactive: cmd.interactive
async: cmd.async
) or {
mut epath := pathlib.get_file(path: scriptpath, create: false)!
c := epath.read()!
return error('cannot execute:\n${c}\nerror:\n${err}')
}
}
pub fn (mut self Builder) copy(src string, dest string) ! {
cmd := 'buildah copy ${self.id} ${src} ${dest}'
osal.exec(cmd: cmd, stdout: false)!
}
// copies the hero from host into guest
pub fn (mut self Builder) hero_copy() ! {
if !osal.cmd_exists('hero') {
herolib.hero_compile()!
}
heropath := osal.cmd_path('hero')!
self.copy(heropath, '/usr/local/bin/hero')!
}
// copies the hero from host into guest and then execute the heroscript or commandline
pub fn (mut self Builder) hero_execute_cmd(cmd string) ! {
self.hero_copy()!
self.run(cmd: cmd, runtime: .herocmd)!
}
pub fn (mut self Builder) hero_execute_script(cmd string) ! {
self.hero_copy()!
self.run(cmd: cmd, runtime: .heroscript)!
}
pub fn (mut self Builder) shell() ! {
cmd := 'buildah run --terminal --env TERM=xterm ${self.id} /bin/bash'
osal.execute_interactive(cmd)!
}
pub fn (mut self Builder) clean() ! {
cmd := '
#set -x
set +e
rm -rf /root/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/share/doc
#pacman -Rns $(pacman -Qtdq) --noconfirm
#pacman -Scc --noconfirm
rm -rf /var/lib/pacman/sync/*
rm -rf /tmp/*
rm -rf /var/tmp/*
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \\;
rm -rf /home/*/.cache/*
rm -rf /usr/share/doc/*
rm -rf /usr/share/man/*
rm -rf /usr/share/info/*
rm -rf /usr/share/licenses/*
find /usr/share/locale -mindepth 1 -maxdepth 1 ! -name "en*" -exec rm -rf {} \\;
rm -rf /usr/share/i18n
rm -rf /usr/share/icons/*
rm -rf /usr/lib/modules/*
rm -rf /var/cache/pacman
journalctl --vacuum-time=1s
'
self.run(cmd: cmd, stdout: false)!
}
pub fn (mut self Builder) delete() ! {
self.engine.builder_delete(self.containername)!
}
pub fn (mut self Builder) inspect() !BuilderInfo {
cmd := 'buildah inspect ${self.containername}'
out := osal.execute_silent(cmd)!
mut r := json.decode(BuilderInfo, out) or {
return error('Failed to decode JSON for inspect: ${err}')
}
return r
}
// mount the build container to a path and return the path where its mounted
pub fn (mut self Builder) mount_to_path() !string {
cmd := 'buildah mount ${self.containername}'
out := osal.execute_silent(cmd)!
return out.trim_space()
}
pub fn (mut self Builder) commit(image_name string) ! {
cmd := 'buildah commit ${self.containername} ${image_name}'
osal.exec(cmd: cmd)!
}
pub fn (self Builder) set_entrypoint(entrypoint string) ! {
cmd := 'buildah config --entrypoint \'${entrypoint}\' ${self.containername}'
osal.exec(cmd: cmd)!
}
pub fn (self Builder) set_workingdir(workdir string) ! {
cmd := 'buildah config --workingdir ${workdir} ${self.containername}'
osal.exec(cmd: cmd)!
}
pub fn (self Builder) set_cmd(command string) ! {
cmd := 'buildah config --cmd ${command} ${self.containername}'
osal.exec(cmd: cmd)!
}

View File

@@ -1,22 +0,0 @@
module herocontainers
import freeflowuniverse.herolib.osal.core as osal
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.core.pathlib
import os
import json
// copies the hero from host into guest
pub fn (mut self Builder) install_zinit() ! {
self.run(
cmd: '
wget https://github.com/threefoldtech/zinit/releases/download/v0.2.5/zinit -O /sbin/zinit
chmod +x /sbin/zinit
touch /etc/environment
mkdir -p /etc/zinit/
'
)!
self.set_entrypoint('/sbin/zinit init --container')!
}

View File

@@ -1,175 +0,0 @@
module herocontainers
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.ui.console
import os
@[params]
pub struct GetArgs {
pub mut:
reset bool
}
// builder machine based on arch and install vlang
pub fn (mut e CEngine) builder_base(args GetArgs) !Builder {
name := 'base'
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
console.print_header('buildah base build')
mut builder := e.builder_new(name: name, from: 'scratch', delete: true)!
mount_path := builder.mount_to_path()!
if mount_path.len < 4 {
return error('mount_path needs to be +4 chars')
}
osal.exec(
cmd: '
export MOUNT_PATH=\'${mount_path}\'
mmdebstrap --variant=minbase --components="main,universe" --include="apt,base-files,base-passwd,bash,coreutils" noble \${MOUNT_PATH}
echo "Binding essential directories..."
mount --bind /dev "\${MOUNT_PATH}/dev"
mount --bind /proc "\${MOUNT_PATH}/proc"
mount --bind /sys "\${MOUNT_PATH}/sys"
echo "tzdata tzdata/Areas select Europe" | chroot "\${MOUNT_PATH}" debconf-set-selections
echo "tzdata tzdata/Zones/Europe select Brussels" | chroot "\${MOUNT_PATH}" debconf-set-selections
chroot \${MOUNT_PATH} apt update
# Set up APT for non-interactive installation
export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true
# Update package lists
echo "Updating package lists in chroot..."
chroot \${MOUNT_PATH} apt-get update -yq
# Install required packages
echo "Installing essential packages..."
chroot \${MOUNT_PATH} apt-get install -yq screen bash coreutils curl mc unzip sudo which openssh-client openssh-server redis wget
echo "Cleaning up..."
umount "\${MOUNT_PATH}/dev" || true
umount "\${MOUNT_PATH}/proc" || true
umount "\${MOUNT_PATH}/sys" || true
'
)!
builder.install_zinit()!
// builder.set_entrypoint('redis-server')!
builder.commit('localhost/${name}')!
return builder
}
// builder machine based on arch and install vlang
pub fn (mut e CEngine) builder_go_rust(args GetArgs) !Builder {
console.print_header('buildah builder go rust')
name := 'builder_go_rust'
e.builder_base(reset: false)!
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
mut builder := e.builder_new(name: name, from: 'localhost/base', delete: true)!
builder.hero_execute_cmd('installers -n golang,rust')!
// builder.clean()!
builder.commit('localhost/${name}')!
e.load()!
return builder
}
pub fn (mut e CEngine) builder_js(args GetArgs) !Builder {
console.print_header('buildah builder js')
name := 'builder_js'
e.builder_base(reset: false)!
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
mut builder := e.builder_new(name: name, from: 'localhost/base', delete: true)!
builder.hero_execute_cmd('installers -n nodejs')!
// builder.clean()!
builder.commit('localhost/${name}')!
e.load()!
return builder
}
pub fn (mut e CEngine) builder_js_python(args GetArgs) !Builder {
console.print_header('buildah builder js python')
name := 'builder_js_python'
e.builder_js(reset: false)!
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
mut builder := e.builder_new(name: name, from: 'localhost/builder_js', delete: true)!
builder.hero_execute_cmd('installers -n python')!
// builder.clean()!
builder.commit('localhost/${name}')!
e.load()!
return builder
}
pub fn (mut e CEngine) builder_hero(args GetArgs) !Builder {
console.print_header('buildah builder hero dev')
name := 'builder_hero'
e.builder_js_python(reset: false)!
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
mut builder := e.builder_new(name: name, from: 'localhost/builder_js_python', delete: true)!
builder.hero_execute_cmd('installers -n hero')!
// builder.clean()!
builder.commit('localhost/${name}')!
e.load()!
return builder
}
pub fn (mut e CEngine) builder_herodev(args GetArgs) !Builder {
console.print_header('buildah builder hero dev')
name := 'builder_herodev'
e.builder_js_python(reset: false)!
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
mut builder := e.builder_new(name: name, from: 'localhost/builder_hero', delete: true)!
builder.hero_execute_cmd('installers -n herodev')!
// builder.clean()!
builder.commit('localhost/${name}')!
e.load()!
return builder
}
pub fn (mut e CEngine) builder_heroweb(args GetArgs) !Builder {
console.print_header('buildah builder hero web')
name := 'builder_heroweb'
e.builder_go_rust(reset: false)!
e.builder_hero(reset: false)!
if !args.reset && e.builder_exists(name)! {
return e.builder_get(name)!
}
mut builder0 := e.builder_new(
name: 'builder_heroweb_temp'
from: 'localhost/builder_go_rust'
delete: true
)!
builder0.hero_execute_cmd('installers -n heroweb')!
// builder0.hero_execute_cmd("installers -n heroweb")!
mpath := builder0.mount_to_path()!
// copy the built binary to host
osal.exec(
cmd: '
mkdir -p ${os.home_dir()}/hero/var/bin
cp ${mpath}/usr/local/bin/* ${os.home_dir()}/hero/var/bin/
'
)!
builder0.delete()!
mut builder2 := e.builder_new(name: name, from: 'localhost/builder_hero', delete: true)!
builder2.copy('${os.home_dir()}/hero/var/bin/', '/usr/local/bin/')!
builder2.commit('localhost/${name}')!
e.load()!
return builder2
}

View File

@@ -1,84 +0,0 @@
module herocontainers
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.ui.console
import json
fn (mut e CEngine) builders_load() ! {
cmd := 'buildah containers --json'
out := osal.execute_silent(cmd)!
mut r := json.decode([]Builder, out) or { return error('Failed to decode JSON: ${err}') }
for mut item in r {
item.engine = &e
}
e.builders = r
}
@[params]
pub struct BuilderNewArgs {
pub mut:
name string = 'default'
from string = 'docker.io/ubuntu:latest'
delete bool = true
}
pub fn (mut e CEngine) builder_new(args_ BuilderNewArgs) !Builder {
mut args := args_
if args.delete {
e.builder_delete(args.name)!
}
osal.exec(cmd: 'buildah --name ${args.name} from ${args.from}')!
e.builders_load()!
return e.builder_get(args.name)
}
// get buildah containers
pub fn (mut e CEngine) builders_get() ![]Builder {
if e.builders.len == 0 {
e.builders_load()!
}
return e.builders
}
pub fn (mut e CEngine) builder_exists(name string) !bool {
r := e.builders_get()!
res := r.filter(it.containername == name)
if res.len == 1 {
return true
}
if res.len > 1 {
panic('bug')
}
return false
}
pub fn (mut e CEngine) builder_get(name string) !Builder {
r := e.builders_get()!
res := r.filter(it.containername == name)
if res.len == 1 {
return res[0]
}
if res.len > 1 {
panic('bug')
}
return error('couldnt find builder with name ${name}')
}
pub fn (mut e CEngine) builders_delete_all() ! {
console.print_debug('remove all')
osal.execute_stdout('buildah rm -a')!
e.builders_load()!
}
pub fn (mut e CEngine) builder_delete(name string) ! {
if e.builder_exists(name)! {
console.print_debug('remove ${name}')
osal.execute_stdout('buildah rm ${name}')!
e.builders_load()!
}
}
pub fn (mut e CEngine) builder_names() ![]string {
r := e.builders_get()!
return r.map(it.containername)
}

View File

@@ -1,39 +0,0 @@
module herocontainers
import freeflowuniverse.herolib.installers.virt.podman as podman_installer
import freeflowuniverse.herolib.installers.lang.herolib
import freeflowuniverse.herolib.core
@[params]
pub struct NewArgs {
pub mut:
install bool = true
reset bool
herocompile bool
}
pub fn new(args_ NewArgs) !CEngine {
mut args := args_
if !core.is_linux()! {
return error('only linux supported as host for now')
}
if args.install {
mut podman_installer0 := podman_installer.get()!
podman_installer0.install()!
}
if args.herocompile {
herolib.check()! // will check if install, if not will do
herolib.hero_compile(reset: true)!
}
mut engine := CEngine{}
engine.init()!
if args.reset {
engine.reset_all()!
}
return engine
}

View File

@@ -22,7 +22,7 @@ pub mut:
networks []string networks []string
labels map[string]string @[str: skip] labels map[string]string @[str: skip]
image &Image @[str: skip] image &Image @[str: skip]
engine &CEngine @[skip; str: skip] engine &PodmanFactory @[skip; str: skip]
status utils.ContainerStatus status utils.ContainerStatus
memsize int // in MB memsize int // in MB
command string command string

View File

@@ -54,7 +54,7 @@ pub mut:
} }
// create a new container from an image // create a new container from an image
pub fn (mut e CEngine) container_create(args_ ContainerCreateArgs) !&Container { pub fn (mut e PodmanFactory) container_create(args_ ContainerCreateArgs) !&Container {
mut args := args_ mut args := args_
mut cmd := 'podman run --systemd=false' mut cmd := 'podman run --systemd=false'

View File

@@ -2,18 +2,17 @@ module herocontainers
import freeflowuniverse.herolib.osal.core as osal { exec } import freeflowuniverse.herolib.osal.core as osal { exec }
import freeflowuniverse.herolib.core import freeflowuniverse.herolib.core
import freeflowuniverse.herolib.installers.virt.podman as podman_installer
@[heap] @[heap]
pub struct CEngine { pub struct PodmanFactory {
pub mut: pub mut:
sshkeys_allowed []string // all keys here have access over ssh into the machine, when ssh enabled // sshkeys_allowed []string // all keys here have access over ssh into the machine, when ssh enabled
images []Image images []Image
containers []Container containers []Container
builders []Builder
buildpath string buildpath string
localonly bool // cache bool = true
cache bool = true // push bool
push bool
// platform []BuildPlatformType // used to build // platform []BuildPlatformType // used to build
// registries []BAHRegistry // one or more supported BAHRegistries // registries []BAHRegistry // one or more supported BAHRegistries
prefix string prefix string
@@ -24,7 +23,22 @@ pub enum BuildPlatformType {
linux_amd64 linux_amd64
} }
fn (mut e CEngine) init() ! { @[params]
pub struct NewArgs {
pub mut:
install bool = true
reset bool
herocompile bool
}
if args.install {
mut podman_installer0 := podman_installer.get()!
podman_installer0.install()!
}
fn (mut e PodmanFactory) init() ! {
if e.buildpath == '' { if e.buildpath == '' {
e.buildpath = '/tmp/builder' e.buildpath = '/tmp/builder'
exec(cmd: 'mkdir -p ${e.buildpath}', stdout: false)! exec(cmd: 'mkdir -p ${e.buildpath}', stdout: false)!
@@ -33,14 +47,14 @@ fn (mut e CEngine) init() ! {
} }
// reload the state from system // reload the state from system
pub fn (mut e CEngine) load() ! { pub fn (mut e PodmanFactory) load() ! {
e.builders_load()! e.builders_load()!
e.images_load()! e.images_load()!
e.containers_load()! e.containers_load()!
} }
// reset all images & containers, CAREFUL! // reset all images & containers, CAREFUL!
pub fn (mut e CEngine) reset_all() ! { pub fn (mut e PodmanFactory) reset_all() ! {
e.load()! e.load()!
for mut container in e.containers.clone() { for mut container in e.containers.clone() {
container.delete()! container.delete()!
@@ -59,7 +73,7 @@ pub fn (mut e CEngine) reset_all() ! {
} }
// Get free port // Get free port
pub fn (mut e CEngine) get_free_port() ?int { pub fn (mut e PodmanFactory) get_free_port() ?int {
mut used_ports := []int{} mut used_ports := []int{}
mut range := []int{} mut range := []int{}

View File

@@ -16,7 +16,7 @@ pub mut:
digest string digest string
size int // size in MB size int // size in MB
created time.Time created time.Time
engine &CEngine @[skip; str: skip] engine &PodmanFactory @[skip; str: skip]
} }
// delete podman image // delete podman image

View File

@@ -6,10 +6,10 @@ import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.virt.utils import freeflowuniverse.herolib.virt.utils
// import freeflowuniverse.herolib.ui.console // import freeflowuniverse.herolib.ui.console
// load all containers, they can be consulted in e.containers // load all containers, they can be consulted in self.containers
// see obj: Container as result in e.containers // see obj: Container as result in self.containers
fn (mut e CEngine) containers_load() ! { fn (mut self PodmanFactory) containers_load() ! {
e.containers = []Container{} self.containers = []Container{}
mut ljob := exec( mut ljob := exec(
// we used || because sometimes the command has | in it and this will ruin all subsequent columns // we used || because sometimes the command has | in it and this will ruin all subsequent columns
cmd: "podman container list -a --no-trunc --size --format '{{.ID}}||{{.Names}}||{{.ImageID}}||{{.Command}}||{{.CreatedAt}}||{{.Ports}}||{{.State}}||{{.Size}}||{{.Mounts}}||{{.Networks}}||{{.Labels}}'" cmd: "podman container list -a --no-trunc --size --format '{{.ID}}||{{.Names}}||{{.ImageID}}||{{.Command}}||{{.CreatedAt}}||{{.Ports}}||{{.State}}||{{.Size}}||{{.Mounts}}||{{.Networks}}||{{.Labels}}'"
@@ -30,9 +30,9 @@ fn (mut e CEngine) containers_load() ! {
if fields[2] == '' { if fields[2] == '' {
continue continue
} }
mut image := e.image_get(id_full: fields[2])! mut image := self.image_get(id_full: fields[2])!
mut container := Container{ mut container := Container{
engine: &e engine: &self
image: &image image: &image
} }
container.id = id container.id = id
@@ -47,7 +47,7 @@ fn (mut e CEngine) containers_load() ! {
container.networks = utils.parse_networks(fields[9])! container.networks = utils.parse_networks(fields[9])!
container.labels = utils.parse_labels(fields[10])! container.labels = utils.parse_labels(fields[10])!
container.ssh_enabled = utils.contains_ssh_port(container.ports) container.ssh_enabled = utils.contains_ssh_port(container.ports)
e.containers << container self.containers << container
} }
} }
@@ -63,14 +63,14 @@ pub mut:
// get containers from memory // get containers from memory
// params: // params:
// name string (can also be a glob e.g. use *,? and []) // name string (can also be a glob self.g. use *,? and [])
// id string // id string
// image_id string // image_id string
pub fn (mut e CEngine) containers_get(args_ ContainerGetArgs) ![]&Container { pub fn (mut self PodmanFactory) containers_get(args_ ContainerGetArgs) ![]&Container {
mut args := args_ mut args := args_
args.name = texttools.name_fix(args.name) args.name = texttools.name_fix(args.name)
mut res := []&Container{} mut res := []&Container{}
for _, c in e.containers { for _, c in self.containers {
if args.name.contains('*') || args.name.contains('?') || args.name.contains('[') { if args.name.contains('*') || args.name.contains('?') || args.name.contains('[') {
if c.name.match_glob(args.name) { if c.name.match_glob(args.name) {
res << &c res << &c
@@ -96,10 +96,10 @@ pub fn (mut e CEngine) containers_get(args_ ContainerGetArgs) ![]&Container {
} }
// get container from memory, can use match_glob see https://modules.vlang.io/index.html#string.match_glob // get container from memory, can use match_glob see https://modules.vlang.io/index.html#string.match_glob
pub fn (mut e CEngine) container_get(args_ ContainerGetArgs) !&Container { pub fn (mut self PodmanFactory) container_get(args_ ContainerGetArgs) !&Container {
mut args := args_ mut args := args_
args.name = texttools.name_fix(args.name) args.name = texttools.name_fix(args.name)
mut res := e.containers_get(args)! mut res := self.containers_get(args)!
if res.len > 1 { if res.len > 1 {
return ContainerGetError{ return ContainerGetError{
args: args args: args
@@ -109,8 +109,8 @@ pub fn (mut e CEngine) container_get(args_ ContainerGetArgs) !&Container {
return res[0] return res[0]
} }
pub fn (mut e CEngine) container_exists(args ContainerGetArgs) !bool { pub fn (mut self PodmanFactory) container_exists(args ContainerGetArgs) !bool {
e.container_get(args) or { self.container_get(args) or {
if err.code() == 1 { if err.code() == 1 {
return false return false
} }
@@ -119,19 +119,19 @@ pub fn (mut e CEngine) container_exists(args ContainerGetArgs) !bool {
return true return true
} }
pub fn (mut e CEngine) container_delete(args ContainerGetArgs) ! { pub fn (mut self PodmanFactory) container_delete(args ContainerGetArgs) ! {
mut c := e.container_get(args)! mut c := self.container_get(args)!
c.delete()! c.delete()!
e.load()! self.load()!
} }
// remove one or more container // remove one or more container
pub fn (mut e CEngine) containers_delete(args ContainerGetArgs) ! { pub fn (mut self PodmanFactory) containers_delete(args ContainerGetArgs) ! {
mut cs := e.containers_get(args)! mut cs := self.containers_get(args)!
for mut c in cs { for mut c in cs {
c.delete()! c.delete()!
} }
e.load()! self.load()!
} }
pub struct ContainerGetError { pub struct ContainerGetError {

View File

@@ -5,8 +5,8 @@ import freeflowuniverse.herolib.osal.core as osal { exec }
import time import time
import freeflowuniverse.herolib.ui.console import freeflowuniverse.herolib.ui.console
fn (mut e CEngine) images_load() ! { fn (mut self PodmanFactory) images_load() ! {
e.images = []Image{} self.images = []Image{}
mut lines := osal.execute_silent("podman images --format '{{.ID}}||{{.Id}}||{{.Repository}}||{{.Tag}}||{{.Digest}}||{{.Size}}||{{.CreatedAt}}'")! mut lines := osal.execute_silent("podman images --format '{{.ID}}||{{.Id}}||{{.Repository}}||{{.Tag}}||{{.Digest}}||{{.Size}}||{{.CreatedAt}}'")!
for line in lines.split_into_lines() { for line in lines.split_into_lines() {
fields := line.split('||').map(utils.clear_str) fields := line.split('||').map(utils.clear_str)
@@ -14,7 +14,7 @@ fn (mut e CEngine) images_load() ! {
panic('podman image needs to output 7 parts.\n${fields}') panic('podman image needs to output 7 parts.\n${fields}')
} }
mut image := Image{ mut image := Image{
engine: &e engine: &self
} }
image.id = fields[0] image.id = fields[0]
image.id_full = fields[1] image.id_full = fields[1]
@@ -23,12 +23,12 @@ fn (mut e CEngine) images_load() ! {
image.digest = utils.parse_digest(fields[4]) or { '' } image.digest = utils.parse_digest(fields[4]) or { '' }
image.size = utils.parse_size_mb(fields[5]) or { 0 } image.size = utils.parse_size_mb(fields[5]) or { 0 }
image.created = utils.parse_time(fields[6]) or { time.now() } image.created = utils.parse_time(fields[6]) or { time.now() }
e.images << image self.images << image
} }
} }
// import herocontainers image back into the local env // import herocontainers image back into the local env
pub fn (mut engine CEngine) image_load(path string) ! { pub fn (mut engine PodmanFactory) image_load(path string) ! {
exec(cmd: 'podman load < ${path}', stdout: false)! exec(cmd: 'podman load < ${path}', stdout: false)!
engine.images_load()! engine.images_load()!
} }
@@ -50,8 +50,8 @@ pub:
// digest string // digest string
// id string // id string
// id_full // id_full
pub fn (mut e CEngine) image_get(args ImageGetArgs) !Image { pub fn (mut self PodmanFactory) image_get(args ImageGetArgs) !Image {
for i in e.images { for i in self.images {
if args.digest != '' && i.digest == args.digest { if args.digest != '' && i.digest == args.digest {
return i return i
} }
@@ -66,7 +66,7 @@ pub fn (mut e CEngine) image_get(args ImageGetArgs) !Image {
if args.repo != '' || args.tag != '' { if args.repo != '' || args.tag != '' {
mut counter := 0 mut counter := 0
mut result_digest := '' mut result_digest := ''
for i in e.images { for i in self.images {
if args.repo != '' && i.repo != args.repo { if args.repo != '' && i.repo != args.repo {
continue continue
} }
@@ -83,7 +83,7 @@ pub fn (mut e CEngine) image_get(args ImageGetArgs) !Image {
toomany: true toomany: true
} }
} }
return e.image_get(digest: result_digest)! return self.image_get(digest: result_digest)!
} }
return ImageGetError{ return ImageGetError{
args: args args: args
@@ -91,8 +91,8 @@ pub fn (mut e CEngine) image_get(args ImageGetArgs) !Image {
} }
} }
pub fn (mut e CEngine) image_exists(args ImageGetArgs) !bool { pub fn (mut self PodmanFactory) image_exists(args ImageGetArgs) !bool {
e.image_get(args) or { self.image_get(args) or {
if err.code() == 1 { if err.code() == 1 {
return false return false
} }
@@ -102,11 +102,11 @@ pub fn (mut e CEngine) image_exists(args ImageGetArgs) !bool {
} }
// get buildah containers // get buildah containers
pub fn (mut e CEngine) images_get() ![]Image { pub fn (mut self PodmanFactory) images_get() ![]Image {
if e.builders.len == 0 { if self.builders.len == 0 {
e.images_load()! self.images_load()!
} }
return e.images return self.images
} }
pub fn (err ImageGetError) msg() string { pub fn (err ImageGetError) msg() string {

117
lib/virt/podman/readme.md Normal file
View File

@@ -0,0 +1,117 @@
# Herocontainers
Tools to work with containers
```go
#!/usr/bin/env -S v -n -cg -w -enable-globals run
import freeflowuniverse.herolib.virt.herocontainers
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.builder
//interative means will ask for login/passwd
console.print_header("BUILDAH Demo.")
//if herocompile on, then will forced compile hero, which might be needed in debug mode for hero
// to execute hero scripts inside build container
mut factory:=herocontainers.new(herocompile=true)!
//mut b:=factory.builder_new(name:"test")!
//create
factory.builderv_create()!
//get the container
//mut b2:=factory.builder_get("builderv")!
//b2.shell()!
```
## buildah tricks
```bash
#find the containers as have been build, these are the active ones you can work with
buildah ls
#see the images
buildah images
```
result is something like
```bash
CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME
a9946633d4e7 * scratch base
86ff0deb00bf * 4feda76296d6 localhost/builder:latest base_go_rust
```
some tricks
```bash
#run interactive in one (here we chose the builderv one)
buildah run --terminal --env TERM=xterm base /bin/bash
#or
buildah run --terminal --env TERM=xterm default /bin/bash
#or
buildah run --terminal --env TERM=xterm base_go_rust /bin/bash
```
to check inside the container about diskusage
```bash
apt install ncdu
ncdu
```
## create container
```go
import freeflowuniverse.herolib.virt.herocontainers
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.builder
//interative means will ask for login/passwd
console.print_header("Get a container.")
mut e:=herocontainers.new()!
//info see https://docs.podman.io/en/latest/markdown/podman-run.1.html
mut c:=e.container_create(
name: 'mycontainer'
image_repo: 'ubuntu'
// Resource limits
memory: '1g'
cpus: 0.5
// Network config
network: 'bridge'
network_aliases: ['myapp', 'api']
// DNS config
dns_servers: ['8.8.8.8', '8.8.4.4']
dns_search: ['example.com']
interactive: true // Keep STDIN open
mounts: [
'type=bind,src=/data,dst=/container/data,ro=true'
]
volumes: [
'/config:/etc/myapp:ro'
]
published_ports: [
'127.0.0.1:8080:80'
]
)!
```
## future
should make this module compatible with https://github.com/containerd/nerdctl

View File

@@ -0,0 +1,230 @@
# OSAL Core Playbook Commands
This document describes the HeroScript commands available for interacting with the Operating System Abstraction Layer (OSAL) core functionalities. These commands allow for managing "done" keys, environment variables, executing commands, and handling package management within HeroScript playbooks.
## Done Management
The `osal.done` commands provide a mechanism to track the completion status of various tasks or conditions within a playbook.
- `!!osal.done_set`
- **Description**: Sets a key-value pair in the done system. This can be used to mark a task as completed or store a specific state.
- **Parameters**:
- `key` (string, required): The unique identifier for the done item.
- `value` (string, required): The value to associate with the key.
- **Example**:
```heroscript
!!osal.done_set
key: 'installation_complete'
value: 'true'
```
- `!!osal.done_delete`
- **Description**: Deletes a specific done key and its associated value.
- **Parameters**:
- `key` (string, required): The key of the done item to delete.
- **Example**:
```heroscript
!!osal.done_delete
key: 'temporary_flag'
```
- `!!osal.done_reset`
- **Description**: Resets (deletes) all currently set done keys. Use with caution.
- **Parameters**: None
- **Example**:
```heroscript
!!osal.done_reset
```
- `!!osal.done_print`
- **Description**: Prints all currently set done keys and their values to the console.
- **Parameters**: None
- **Example**:
```heroscript
!!osal.done_print
```
## Environment Variables
The `osal.env` commands allow for manipulation of environment variables during playbook execution.
- `!!osal.env_set`
- **Description**: Sets a single environment variable.
- **Parameters**:
- `key` (string, required): The name of the environment variable.
- `value` (string, required): The value to set for the variable.
- `overwrite` (bool, optional, default: `true`): If `true`, overwrites the variable if it already exists.
- **Example**:
```heroscript
!!osal.env_set
key: 'MY_APP_PATH'
value: '/opt/my_app'
overwrite: true
```
- `!!osal.env_unset`
- **Description**: Unsets (removes) a single environment variable.
- **Parameters**:
- `key` (string, required): The name of the environment variable to unset.
- **Example**:
```heroscript
!!osal.env_unset
key: 'OLD_VAR'
```
- `!!osal.env_unset_all`
- **Description**: Unsets all environment variables. Use with extreme caution as this can affect subsequent commands.
- **Parameters**: None
- **Example**:
```heroscript
!!osal.env_unset_all
```
- `!!osal.env_set_all`
- **Description**: Sets multiple environment variables from a map of key-value pairs.
- **Parameters**:
- `clear_before_set` (bool, optional, default: `false`): If `true`, all existing environment variables are cleared before setting the new ones.
- `overwrite_if_exists` (bool, optional, default: `true`): If `true`, new variables will overwrite existing ones with the same name.
- Any other named parameter will be treated as an environment variable to set (e.g., `VAR1: 'value1'`).
- **Example**:
```heroscript
!!osal.env_set_all
clear_before_set: false
overwrite_if_exists: true
APP_ENV: 'production'
DEBUG_MODE: 'false'
```
- `!!osal.load_env_file`
- **Description**: Loads environment variables from a specified file (e.g., a `.env` file).
- **Parameters**:
- `file_path` (string, required): The path to the environment file.
- **Example**:
```heroscript
!!osal.load_env_file
file_path: '/etc/my_app/.env'
```
## Command Execution
The `osal.exec` commands provide various ways to execute shell commands, with options for output handling, error management, and interactivity.
- `!!osal.exec`
- **Description**: Executes a command with comprehensive options for control and output capture.
- **Parameters**:
- `cmd` (string, required): The command string to execute.
- `name` (string, optional): A name for the job.
- `description` (string, optional): A description for the job.
- `timeout` (int, optional, default: `3600`): Maximum execution time in seconds.
- `stdout` (bool, optional, default: `true`): If `true`, prints stdout to the console.
- `stdout_log` (bool, optional, default: `true`): If `true`, logs stdout.
- `raise_error` (bool, optional, default: `true`): If `true`, raises an error if the command fails.
- `ignore_error` (bool, optional, default: `false`): If `true`, ignores command execution errors.
- `work_folder` (string, optional): The working directory for the command.
- `scriptkeep` (bool, optional, default: `false`): If `true`, keeps the generated script file.
- `debug` (bool, optional, default: `false`): If `true`, enables debug output for the command.
- `shell` (bool, optional, default: `false`): If `true`, executes the command in a shell.
- `retry` (int, optional, default: `0`): Number of times to retry the command on failure.
- `interactive` (bool, optional, default: `true`): If `true`, allows interactive input/output.
- `async` (bool, optional, default: `false`): If `true`, executes the command asynchronously.
- `output_key` (string, optional): If provided, the command's output will be stored in the done system under this key.
- **Example**:
```heroscript
!!osal.exec
cmd: 'ls -la /var/log'
output_key: 'log_directory_listing'
timeout: 60
raise_error: true
```
- `!!osal.exec_silent`
- **Description**: Executes a command without printing any output to the console.
- **Parameters**:
- `cmd` (string, required): The command string to execute.
- `output_key` (string, optional): If provided, the command's output will be stored in the done system under this key.
- **Example**:
```heroscript
!!osal.exec_silent
cmd: 'systemctl restart my_service'
```
- `!!osal.exec_debug`
- **Description**: Executes a command with debug output enabled.
- **Parameters**:
- `cmd` (string, required): The command string to execute.
- `output_key` (string, optional): If provided, the command's output will be stored in the done system under this key.
- **Example**:
```heroscript
!!osal.exec_debug
cmd: 'my_script --verbose'
output_key: 'script_debug_output'
```
- `!!osal.exec_stdout`
- **Description**: Executes a command and prints its standard output directly to the console.
- **Parameters**:
- `cmd` (string, required): The command string to execute.
- `output_key` (string, optional): If provided, the command's output will be stored in the done system under this key.
- **Example**:
```heroscript
!!osal.exec_stdout
cmd: 'cat /etc/os-release'
output_key: 'os_info'
```
- `!!osal.exec_interactive`
- **Description**: Executes a command in an interactive mode, allowing user input.
- **Parameters**:
- `cmd` (string, required): The command string to execute.
- **Example**:
```heroscript
!!osal.exec_interactive
cmd: 'ssh user@remote_host'
```
## Package Management
The `osal.package` commands provide basic functionalities for managing system packages.
- `!!osal.package_refresh`
- **Description**: Refreshes the local package lists from configured repositories.
- **Parameters**: None
- **Example**:
```heroscript
!!osal.package_refresh
```
- `!!osal.package_install`
- **Description**: Installs one or more packages.
- **Parameters**:
- `name` (string, optional): A single package name to install.
- `names` (list of strings, optional): A comma-separated list of package names to install.
- Positional arguments can also be used for package names.
- **Example**:
```heroscript
!!osal.package_install
name: 'git'
```
```heroscript
!!osal.package_install
names: 'curl,vim,htop'
```
```heroscript
!!osal.package_install
git curl vim
```
- `!!osal.package_remove`
- **Description**: Removes one or more packages.
- **Parameters**:
- `name` (string, optional): A single package name to remove.
- `names` (list of strings, optional): A comma-separated list of package names to remove.
- Positional arguments can also be used for package names.
- **Example**:
```heroscript
!!osal.package_remove
name: 'unwanted_package'
```
```heroscript
!!osal.package_remove
names: 'old_tool,unused_lib'
```
```heroscript
!!osal.package_remove
apache2 php7.4