diff --git a/lib/develop/codewalker/ignore.v b/lib/develop/codewalker/ignore.v index 1c4aca5e..dd2b8575 100644 --- a/lib/develop/codewalker/ignore.v +++ b/lib/develop/codewalker/ignore.v @@ -23,7 +23,6 @@ develop-eggs/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ diff --git a/lib/virt/podman/builder.v b/lib/virt/podman/builder.v new file mode 100644 index 00000000..14052bba --- /dev/null +++ b/lib/virt/podman/builder.v @@ -0,0 +1,217 @@ +module podman + +import time +import freeflowuniverse.herolib.osal.core as osal { exec } +import freeflowuniverse.herolib.ui.console +import json + +@[heap] +pub struct Builder { +pub mut: + id string + containername string + imageid string + imagename string + created time.Time + engine &PodmanFactory @[skip; str: skip] +} + +pub struct BuilderInfo { +pub: + id string + containername string + imageid string + imagename string + created string +} + +// load all buildah containers/builders +pub fn (mut self PodmanFactory) builders_load() ! { + self.builders = []Builder{} + cmd := 'buildah containers --json' + out := osal.execute_silent(cmd) or { + // If buildah is not installed or no containers, return empty list + console.print_debug('buildah containers command failed: ${err}') + return + } + + if out.trim_space() == '' || out.trim_space() == '[]' { + return + } + + mut r := json.decode([]BuilderInfo, out) or { + console.print_debug('Failed to decode buildah JSON: ${err}') + return + } + + for item in r { + mut builder := Builder{ + engine: &self + id: item.id + containername: item.containername + imageid: item.imageid + imagename: item.imagename + } + // Parse created time if needed + builder.created = time.now() // TODO: parse from item.created + self.builders << builder + } +} + +// delete all buildah containers/builders +pub fn (mut self PodmanFactory) builders_delete_all() ! { + for mut builder in self.builders.clone() { + builder.delete()! + } + self.builders = []Builder{} +} + +@[params] +pub struct BuilderNewArgs { +pub mut: + name string = 'default' + from string = 'docker.io/ubuntu:latest' + delete bool = true +} + +pub fn (mut e PodmanFactory) builder_new(args_ BuilderNewArgs) !Builder { + mut args := args_ + if args.delete { + e.builder_delete(args.name)! + } + 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 PodmanFactory) builders_get() ![]Builder { + if e.builders.len == 0 { + e.builders_load()! + } + return e.builders +} + +pub fn (mut e PodmanFactory) 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: multiple builders with same name') + } + return false +} + +pub fn (mut e PodmanFactory) builder_get(name string) !Builder { + r := e.builders_get()! + res := r.filter(it.containername == name) + if res.len == 0 { + return error('builder with name ${name} not found') + } + if res.len > 1 { + return error('multiple builders found with name ${name}') + } + return res[0] +} + +pub fn (mut e PodmanFactory) builder_delete(name string) ! { + if e.builder_exists(name)! { + exec(cmd: 'buildah rm ${name}', stdout: false) or { + console.print_debug('Failed to delete builder ${name}: ${err}') + } + } + e.builders_load()! +} + +// Builder methods +pub fn (mut self Builder) delete() ! { + self.engine.builder_delete(self.containername)! +} + +pub fn (mut self Builder) run(cmd string) ! { + cmd_str := 'buildah run ${self.id} ${cmd}' + exec(cmd: cmd_str)! +} + +pub fn (mut self Builder) copy(src string, dest string) ! { + cmd := 'buildah copy ${self.id} ${src} ${dest}' + exec(cmd: cmd, stdout: false)! +} + +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) commit(image_name string) ! { + cmd := 'buildah commit ${self.containername} ${image_name}' + exec(cmd: cmd)! +} + +pub fn (self Builder) set_entrypoint(entrypoint string) ! { + cmd := 'buildah config --entrypoint \'${entrypoint}\' ${self.containername}' + exec(cmd: cmd)! +} + +pub fn (self Builder) set_workingdir(workdir string) ! { + cmd := 'buildah config --workingdir ${workdir} ${self.containername}' + exec(cmd: cmd)! +} + +pub fn (self Builder) set_cmd(command string) ! { + cmd := 'buildah config --cmd ${command} ${self.containername}' + exec(cmd: cmd)! +} + +// 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() +} + +// Builder solution methods for common setups +@[params] +pub struct GetArgs { +pub mut: + reset bool +} + +// builder machine based on arch and install vlang +pub fn (mut e PodmanFactory) 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') + } + + // TODO: Add base system setup here + console.print_header('buildah base build done') + return builder +} + +// builder with hero tools +pub fn (mut e PodmanFactory) builder_hero(args GetArgs) !Builder { + name := 'hero' + if !args.reset && e.builder_exists(name)! { + return e.builder_get(name)! + } + console.print_header('buildah hero build') + + mut builder := e.builder_new(name: name, from: 'docker.io/ubuntu:latest', delete: true)! + + // Install basic tools and hero + builder.run('apt-get update && apt-get install -y curl git build-essential')! + // TODO: Add hero installation steps + + console.print_header('buildah hero build done') + return builder +} diff --git a/lib/virt/podman/container.v b/lib/virt/podman/container.v index 6198aa4b..4ce1be9b 100644 --- a/lib/virt/podman/container.v +++ b/lib/virt/podman/container.v @@ -1,4 +1,4 @@ -module herocontainers +module podman import time import freeflowuniverse.herolib.osal.core as osal { exec } @@ -22,7 +22,7 @@ pub mut: networks []string labels map[string]string @[str: skip] image &Image @[str: skip] - engine &PodmanFactory @[skip; str: skip] + engine &PodmanFactory @[skip; str: skip] status utils.ContainerStatus memsize int // in MB command string diff --git a/lib/virt/podman/container_create.v b/lib/virt/podman/container_create.v index 4d25ff1d..5c94ac19 100644 --- a/lib/virt/podman/container_create.v +++ b/lib/virt/podman/container_create.v @@ -1,11 +1,9 @@ -module herocontainers +module podman import time import freeflowuniverse.herolib.osal.core as osal { exec } import freeflowuniverse.herolib.data.ipaddress import freeflowuniverse.herolib.core.texttools -import freeflowuniverse.herolib.virt.utils -import freeflowuniverse.herolib.ui.console // info see https://docs.podman.io/en/latest/markdown/podman-run.1.html diff --git a/lib/virt/podman/factory.v b/lib/virt/podman/factory.v index c76c8a55..940289ee 100644 --- a/lib/virt/podman/factory.v +++ b/lib/virt/podman/factory.v @@ -1,16 +1,18 @@ -module herocontainers +module podman import freeflowuniverse.herolib.osal.core as osal { exec } import freeflowuniverse.herolib.core import freeflowuniverse.herolib.installers.virt.podman as podman_installer +import freeflowuniverse.herolib.installers.lang.herolib @[heap] pub struct PodmanFactory { pub mut: // sshkeys_allowed []string // all keys here have access over ssh into the machine, when ssh enabled - images []Image - containers []Container - buildpath string + images []Image + containers []Container + builders []Builder + buildpath string // cache bool = true // push bool // platform []BuildPlatformType // used to build @@ -31,12 +33,32 @@ pub mut: herocompile bool } +pub fn new(args_ NewArgs) !PodmanFactory { + mut args := args_ + + // Support both Linux and macOS + if !core.is_linux()! && !core.is_osx()! { + return error('only linux and macOS 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 factory := PodmanFactory{} + factory.init()! + if args.reset { + factory.reset_all()! + } + + return factory +} fn (mut e PodmanFactory) init() ! { if e.buildpath == '' { @@ -66,7 +88,8 @@ pub fn (mut e PodmanFactory) reset_all() ! { exec(cmd: 'podman rmi -a -f', stdout: false)! e.builders_delete_all()! osal.done_reset()! - if core.platform()! == core.PlatformType.arch { + // Only check systemctl on Linux + if core.is_linux()! && core.platform()! == core.PlatformType.arch { exec(cmd: 'systemctl status podman.socket', stdout: false)! } e.load()! diff --git a/lib/virt/podman/image.v b/lib/virt/podman/image.v index bf6f3f43..39f127cf 100644 --- a/lib/virt/podman/image.v +++ b/lib/virt/podman/image.v @@ -1,4 +1,4 @@ -module herocontainers +module podman import freeflowuniverse.herolib.osal.core as osal { exec } import time diff --git a/lib/virt/podman/podman_containers.v b/lib/virt/podman/podman_containers.v index fbd71de3..34ef241d 100644 --- a/lib/virt/podman/podman_containers.v +++ b/lib/virt/podman/podman_containers.v @@ -1,4 +1,4 @@ -module herocontainers +module podman import freeflowuniverse.herolib.osal.core as osal { exec } // import freeflowuniverse.herolib.data.ipaddress { IPAddress } @@ -8,7 +8,7 @@ import freeflowuniverse.herolib.virt.utils // load all containers, they can be consulted in self.containers // see obj: Container as result in self.containers -fn (mut self PodmanFactory) containers_load() ! { +pub fn (mut self PodmanFactory) containers_load() ! { self.containers = []Container{} mut ljob := exec( // we used || because sometimes the command has | in it and this will ruin all subsequent columns diff --git a/lib/virt/podman/podman_images.v b/lib/virt/podman/podman_images.v index d19da716..941d63b8 100644 --- a/lib/virt/podman/podman_images.v +++ b/lib/virt/podman/podman_images.v @@ -1,11 +1,11 @@ -module herocontainers +module podman import freeflowuniverse.herolib.virt.utils import freeflowuniverse.herolib.osal.core as osal { exec } import time import freeflowuniverse.herolib.ui.console -fn (mut self PodmanFactory) images_load() ! { +pub fn (mut self PodmanFactory) images_load() ! { self.images = []Image{} mut lines := osal.execute_silent("podman images --format '{{.ID}}||{{.Id}}||{{.Repository}}||{{.Tag}}||{{.Digest}}||{{.Size}}||{{.CreatedAt}}'")! for line in lines.split_into_lines() { diff --git a/lib/virt/podman/readme.md b/lib/virt/podman/readme.md index 05337d3b..05edaaa1 100644 --- a/lib/virt/podman/readme.md +++ b/lib/virt/podman/readme.md @@ -1,32 +1,37 @@ -# Herocontainers +# Podman Module -Tools to work with containers +Tools to work with containers using Podman and Buildah. -```go -#!/usr/bin/env -S v -n -cg -w -enable-globals run +## Platform Support -import freeflowuniverse.herolib.virt.herocontainers +- **Linux**: Full support +- **macOS**: Full support (requires podman installation) +- **Windows**: Not supported + +## Basic Usage + +```v +#!/usr/bin/env -S v -n -w -enable-globals run + +import freeflowuniverse.herolib.virt.podman import freeflowuniverse.herolib.ui.console -import freeflowuniverse.herolib.builder -//interative means will ask for login/passwd +console.print_header("PODMAN Demo") -console.print_header("BUILDAH Demo.") +// Create a new podman factory +// install: true will install podman if not present +// herocompile: true will compile hero for use in containers +mut factory := podman.new(install: false, herocompile: false)! -//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()! +// Create a new builder +mut builder := factory.builder_new(name: 'test', from: 'docker.io/ubuntu:latest')! +// Run commands in the builder +builder.run('apt-get update && apt-get install -y curl')! +// Open interactive shell +builder.shell()! ``` ## buildah tricks @@ -40,7 +45,6 @@ buildah images result is something like - ```bash CONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAME a9946633d4e7 * scratch base @@ -66,25 +70,22 @@ apt install ncdu ncdu ``` -## create container +## Create Container - -```go -import freeflowuniverse.herolib.virt.herocontainers +```v +import freeflowuniverse.herolib.virt.podman import freeflowuniverse.herolib.ui.console -import freeflowuniverse.herolib.builder -//interative means will ask for login/passwd +console.print_header("Create a container") -console.print_header("Get a container.") +mut factory := podman.new()! -mut e:=herocontainers.new()! - -//info see https://docs.podman.io/en/latest/markdown/podman-run.1.html - -mut c:=e.container_create( +// Create a container with advanced options +// See https://docs.podman.io/en/latest/markdown/podman-run.1.html +mut container := factory.container_create( name: 'mycontainer' image_repo: 'ubuntu' + image_tag: 'latest' // Resource limits memory: '1g' cpus: 0.5 @@ -103,15 +104,19 @@ mut c:=e.container_create( ] published_ports: [ '127.0.0.1:8080:80' - ] + ] )! +// Start the container +container.start()! +// Execute commands in the container +container.execute('apt-get update', false)! - +// Open interactive shell +container.shell()! ``` - ## future -should make this module compatible with https://github.com/containerd/nerdctl \ No newline at end of file +should make this module compatible with