convert to podman

* call podman exec instead of using API:
  - build does not support progress
  - run looks like to lack equivalent dockerpty support
* config: use full uri as short names are not supported on
  all OSes (like debian 11)
This commit is contained in:
Gilles Grandou 2023-12-05 00:02:15 +01:00
parent bd6a026e9c
commit e0b228799f
3 changed files with 78 additions and 111 deletions

View File

@ -21,9 +21,6 @@ debian_base: &debian_base
binds: binds:
- /etc/timezone:ro - /etc/timezone:ro
- /etc/localtime:ro - /etc/localtime:ro
- /etc/passwd:ro
- /etc/group:ro
- /etc/shadow:ro
- "{home}" - "{home}"
environment: environment:
- USER - USER
@ -33,7 +30,7 @@ debian_base: &debian_base
debian9: debian9:
<<: *debian_base <<: *debian_base
image: debian:9 image: docker.io/debian:9
dockerfile: dockerfile:
- ARG DEBIAN_FRONTEND=noninteractive - ARG DEBIAN_FRONTEND=noninteractive
- RUN echo "deb http://archive.debian.org/debian stretch main" > /etc/apt/sources.list - RUN echo "deb http://archive.debian.org/debian stretch main" > /etc/apt/sources.list
@ -46,23 +43,24 @@ debian9:
debian10: debian10:
<<: *debian_base <<: *debian_base
image: debian:10 image: docker.io/debian:10
debian11: debian11:
<<: *debian_base <<: *debian_base
image: debian:11 image: docker.io/debian:11
debian12: debian12:
<<: *debian_base <<: *debian_base
image: debian:12 image: docker.io/debian:12
ubuntu20.04: ubuntu20.04:
<<: *debian_base <<: *debian_base
image: ubuntu:20.04 image: docker.io/ubuntu:20.04
ubuntu22.04: ubuntu22.04:
<<: *debian_base <<: *debian_base
image: ubuntu:22.04 image: docker.io/ubuntu:22.04
rh_base: &rh_base rh_base: &rh_base
dockerfile: dockerfile:
- RUN dnf install -y sudo - RUN dnf install -y sudo
@ -80,9 +78,6 @@ rh_base: &rh_base
binds: binds:
- /etc/timezone:ro - /etc/timezone:ro
- /etc/localtime:ro - /etc/localtime:ro
- /etc/passwd:ro
- /etc/group:ro
- /etc/shadow:ro
- "{home}" - "{home}"
environment: environment:
- USER - USER
@ -93,7 +88,7 @@ rh_base: &rh_base
centos7: centos7:
<<: *rh_base <<: *rh_base
image: centos:7 image: docker.io/centos:7
dockerfile: dockerfile:
- RUN yum install -y sudo - RUN yum install -y sudo
- RUN echo "Defaults lecture = never" >> /etc/sudoers - RUN echo "Defaults lecture = never" >> /etc/sudoers
@ -104,9 +99,9 @@ centos7:
rocky8: rocky8:
<<: *rh_base <<: *rh_base
image: rockylinux:8 image: docker.io/rockylinux:8
rocky9: rocky9:
<<: *rh_base <<: *rh_base
image: rockylinux:9 image: docker.io/rockylinux:9

View File

@ -1,13 +1,8 @@
import sys import sys
import os
import argparse import argparse
import docker
import dockerpty
import io
import getpass import getpass
import pathlib import pathlib
import platform
import os
import re
import xdg.BaseDirectory import xdg.BaseDirectory
import yaml import yaml
import datetime import datetime
@ -80,16 +75,10 @@ def make_osname_link(binpath, osname):
except FileExistsError: except FileExistsError:
pass pass
def make_image_name(osname):
user = getpass.getuser()
name = 'runon-{}-{}'.format(osname, user)
return name
def build_image(conf, update, verbose):
def build_image(client, conf, update, verbose):
osname = conf.get('osname') osname = conf.get('osname')
image_name = 'runon-{}'.format(osname) image_name = 'runon-{}'.format(osname)
tag = make_image_name(conf['osname'])
cache_dir = os.path.join(xdg.BaseDirectory.xdg_cache_home, 'runon') cache_dir = os.path.join(xdg.BaseDirectory.xdg_cache_home, 'runon')
cache_file = os.path.join(cache_dir, image_name) cache_file = os.path.join(cache_dir, image_name)
@ -102,92 +91,80 @@ def build_image(client, conf, update, verbose):
if ts_image and ts_image > ts_conf: if ts_image and ts_image > ts_conf:
if verbose: if verbose:
print('image: {} up-to-date'.format(image_name)) print('image: {} up-to-date'.format(image_name))
image = client.images.get(tag) return image_name
return image
image = conf.get('image') image = conf.get('image')
packages = conf['packages'] dockerfile = conf.get('dockerfile')
dockerfile = conf['dockerfile'] pkginstall = conf.get('pkginstall')
pkginstall = conf['pkginstall'] packages = conf.get('packages')
if not dockerfile:
dockerfile = []
if image: if image:
dockerfile.insert(0, 'FROM {}'.format(image)) dockerfile.insert(0, 'FROM {}'.format(image))
for p in packages: if packages:
dockerfile.append(pkginstall.format(p)) for p in packages:
try: dockerfile.append(pkginstall.format(p))
if verbose:
print('Dockerfile:')
for l in dockerfile:
print(' * {}'.format(l))
print('Building image {} ...'.format(image_name))
cmd = ['podman', 'build']
if update:
cmd.append('--no-cache')
cmd += ['-t', image_name, '-']
ret = subprocess.run(cmd,
input='\n'.join(dockerfile).encode('utf-8'),
stderr=subprocess.STDOUT, check=True)
if not os.path.exists(cache_dir):
os.mkdir(cache_dir)
with open(cache_file, 'w') as file:
if verbose: if verbose:
print('Building image {} ...'.format(tag)) print('cache: {}'.format(cache_file))
cmd = ['docker', 'build'] file.write('')
if update:
cmd.append('--no-cache')
cmd += ['-t', tag, '-']
ret = subprocess.run(cmd,
input='\n'.join(dockerfile).encode('utf-8'),
stderr=subprocess.STDOUT, check=True)
image = client.images.get(tag)
if not os.path.exists(cache_dir): return image_name
os.mkdir(cache_dir)
with open(cache_file, 'w') as file:
if verbose:
print('cache: {}'.format(cache_file))
file.write('')
except (docker.errors.BuildError, KeyboardInterrupt, subprocess.CalledProcessError, docker.errors.ImageNotFound) as e:
print('Build Error: {}'.format(e))
print()
print('with dockerfile:')
for line in dockerfile:
print(' {}'.format(line))
sys.exit(1)
return image
def create_container(client, image, conf, command, verbose): def run_image(name, conf, command, verbose):
volumes = {} volumes = {
}
environment = {} environment = {}
for mnt in conf['binds']: if conf.get('binds'):
mnt = mnt.split(':') for mnt in conf['binds']:
if mnt[-1] in ['ro','rw']: mnt = mnt.split(':')
mode = mnt[-1] if mnt[-1] in ['ro','rw']:
del mnt[-1] mode = mnt[-1]
else: del mnt[-1]
mode = 'rw' else:
mnt = mnt[:2] mode = 'rw'
bind = mnt[-1] mnt = mnt[:2]
vol = mnt[0] bind = mnt[-1]
volumes[vol] = { 'bind': bind, 'mode': mode } vol = mnt[0]
hostname = platform.node() if os.path.exists(vol):
for v in conf['environment']: volumes[vol] = { 'bind': bind, 'mode': mode }
e = v.split('=') if conf.get('environment'):
if len(e) == 1: for v in conf['environment']:
e.append(os.getenv(e[0])) e = v.split('=')
environment[e[0]] = e[1] if len(e) == 1:
#environment['debian_chroot']=conf['osname'] e.append(os.getenv(e[0]))
user='{}:{}'.format(os.getuid(), os.getgid()) environment[e[0]] = e[1]
pwd=os.getcwd()
container = client.containers.create(image, command, cmd = ['podman', 'run', '--rm', '--interactive', '--tty', '--userns=keep-id', '--net=host' ]
detach=False, stdin_open=True, tty=True, for e in environment:
auto_remove=True, cmd += [ '-e', '{}={}'.format(e, environment[e]) ]
hostname=hostname, for v in volumes:
volumes=volumes, cmd += [ '-v', ':'.join([v, volumes[v]['bind'], volumes[v]['mode']]) ]
environment=environment, cmd += [ '--workdir', os.getcwd() ]
user=user, cmd += [ name ]
network_mode='host', if command:
working_dir=pwd cmd += command
) if verbose:
return container print('executing: {}\n'.format(' '.join(cmd)))
ret = subprocess.run(cmd)
return ret.returncode
def run_container(client, container):
try:
dockerpty.start(client.api, container.id)
container.reload() # to update attrs fields
except docker.errors.APIError as e:
print('ERROR: {}'.format(e))
sys.exit(1)
ret = container.attrs['State']['ExitCode']
return ret
def main(): def main():
@ -247,10 +224,7 @@ def main():
make_osname_link(sys.argv[0], args.osname) make_osname_link(sys.argv[0], args.osname)
conf = load_config(conf_file, args.osname) conf = load_config(conf_file, args.osname)
image_name = build_image(conf, args.update, args.verbose)
client = docker.from_env() ret = run_image(image_name, conf, args.command, args.verbose)
image = build_image(client, conf, args.update, args.verbose)
container = create_container(client, image, conf, args.command, args.verbose)
ret = run_container(client, container)
return ret return ret

View File

@ -9,8 +9,6 @@ setup(
packages = find_packages(), packages = find_packages(),
python_requires = ">=3.6", python_requires = ">=3.6",
install_requires = [ install_requires = [
"docker",
"dockerpty",
"pathlib", "pathlib",
"pyxdg", "pyxdg",
"pyyaml", "pyyaml",