feat: Add Buildah builder API and refactor module
- Introduce `Builder` struct for image creation - Implement `PodmanFactory` methods for builder lifecycle - Rename `herocontainers` module to `podman` - Update `PodmanFactory.new` with platform checks - Revise documentation for `podman` and Buildah usage
This commit is contained in:
@@ -23,7 +23,6 @@ develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
||||
217
lib/virt/podman/builder.v
Normal file
217
lib/virt/podman/builder.v
Normal file
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()!
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module herocontainers
|
||||
module podman
|
||||
|
||||
import freeflowuniverse.herolib.osal.core as osal { exec }
|
||||
import time
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
should make this module compatible with <https://github.com/containerd/nerdctl>
|
||||
|
||||
Reference in New Issue
Block a user