Initial commit
This commit is contained in:
commit
13930c94ed
177
runon
Executable file
177
runon
Executable file
@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import docker
|
||||||
|
import dockerpty
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import getpass
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import xdg.BaseDirectory
|
||||||
|
import configparser
|
||||||
|
import subprocess
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(user_confname, osname):
|
||||||
|
ini_list = [ 'runon.conf', '.runon.conf', os.path.join(xdg.BaseDirectory.xdg_config_home, 'runon', 'runon.conf'), '/etc/runon/runon.conf' ]
|
||||||
|
defaults = {
|
||||||
|
'osname': osname,
|
||||||
|
'user': getpass.getuser()
|
||||||
|
}
|
||||||
|
if user_confname:
|
||||||
|
ini_list.insert(0, user_confname)
|
||||||
|
ini = configparser.ConfigParser(defaults=defaults, interpolation=configparser.ExtendedInterpolation())
|
||||||
|
ini.read(ini_list)
|
||||||
|
if not ini.has_section(osname):
|
||||||
|
print('ERROR: cannot find configuration for distribution "{}"'.format(osname))
|
||||||
|
sys.exit(1)
|
||||||
|
conf = {}
|
||||||
|
fields = [ 'dockerfile', 'pkginstall', 'packages', 'environment', 'binds', 'user', 'osname' ]
|
||||||
|
try:
|
||||||
|
for f in fields:
|
||||||
|
conf[f] = ini.get(osname, f)
|
||||||
|
except configparser.NoOptionError as e:
|
||||||
|
print('ERROR: {}'.format(e))
|
||||||
|
sys.exit(1)
|
||||||
|
for f in [ 'dockerfile', 'environment', 'binds' ]:
|
||||||
|
conf[f] = [ i for i in conf[f].split('\n') if i ]
|
||||||
|
for f in [ 'packages' ]:
|
||||||
|
conf[f] = conf[f].split()
|
||||||
|
return conf
|
||||||
|
|
||||||
|
|
||||||
|
def make_image_name(osname):
|
||||||
|
user = getpass.getuser()
|
||||||
|
name = 'runon-{}-{}'.format(osname, user)
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def build_image(client, conf, update, verbose):
|
||||||
|
packages = conf['packages']
|
||||||
|
dockerfile = conf['dockerfile']
|
||||||
|
pkginstall = conf['pkginstall']
|
||||||
|
for p in packages:
|
||||||
|
dockerfile.append(pkginstall.format(p))
|
||||||
|
tag = make_image_name(conf['osname'])
|
||||||
|
try:
|
||||||
|
if verbose:
|
||||||
|
# fallback to external command 'docker build' as there is
|
||||||
|
# no way to follow the build progress with API.
|
||||||
|
print('Building image {} ...'.format(tag))
|
||||||
|
cmd = ['docker', 'build']
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
with io.BytesIO('\n'.join(dockerfile).encode('utf-8')) as fd:
|
||||||
|
image, logs = client.images.build(tag=tag, fileobj=fd, rm=True, nocache=update)
|
||||||
|
if verbose:
|
||||||
|
print('Built image {} / {}'.format(image.tags[0], image.short_id))
|
||||||
|
for l in logs:
|
||||||
|
print(l.get('stream', '').strip('\n'))
|
||||||
|
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):
|
||||||
|
volumes = {}
|
||||||
|
environment = {}
|
||||||
|
for mnt in conf['binds']:
|
||||||
|
mnt = mnt.split(':')
|
||||||
|
if mnt[-1] in ['ro','rw']:
|
||||||
|
mode = mnt[-1]
|
||||||
|
del mnt[-1]
|
||||||
|
else:
|
||||||
|
mode = 'rw'
|
||||||
|
mnt = mnt[:2]
|
||||||
|
bind = mnt[-1]
|
||||||
|
vol = mnt[0]
|
||||||
|
volumes[vol] = { 'bind': bind, 'mode': mode }
|
||||||
|
hostname = platform.node()
|
||||||
|
for v in conf['environment']:
|
||||||
|
e = v.split('=')
|
||||||
|
if len(e) == 1:
|
||||||
|
e.append(os.getenv(e[0]))
|
||||||
|
environment[e[0]] = e[1]
|
||||||
|
#environment['debian_chroot']=conf['osname']
|
||||||
|
user='{}:{}'.format(os.getuid(), os.getgid())
|
||||||
|
pwd=os.getcwd()
|
||||||
|
|
||||||
|
container = client.containers.create(image, command,
|
||||||
|
detach=False, stdin_open=True, tty=True,
|
||||||
|
auto_remove=True,
|
||||||
|
hostname=hostname,
|
||||||
|
volumes=volumes,
|
||||||
|
environment=environment,
|
||||||
|
user=user,
|
||||||
|
network_mode='host',
|
||||||
|
working_dir=pwd
|
||||||
|
)
|
||||||
|
return container
|
||||||
|
|
||||||
|
|
||||||
|
def run_container(client, container):
|
||||||
|
dockerpty.start(client.api, container.id)
|
||||||
|
container.reload() # to update attrs fields
|
||||||
|
ret = container.attrs['State']['ExitCode']
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
osname = None
|
||||||
|
run_name = os.path.basename(os.sys.argv[0])
|
||||||
|
if run_name == 'runos':
|
||||||
|
pass
|
||||||
|
elif run_name.startswith('runon'):
|
||||||
|
m = re.match('runon[-_]?(.*)$', run_name)
|
||||||
|
if m:
|
||||||
|
osname = m[1]
|
||||||
|
else:
|
||||||
|
osname = run_name
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
if osname:
|
||||||
|
parser.description = 'run commands on "{}" distribution'.format(osname)
|
||||||
|
else:
|
||||||
|
parser.description = 'run commands on any distribution'
|
||||||
|
parser.add_argument('osname',
|
||||||
|
help = 'distribution name to run on')
|
||||||
|
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
|
help='verbose output')
|
||||||
|
parser.add_argument('-c', '--config',
|
||||||
|
help='specify config file')
|
||||||
|
parser.add_argument('-u', '--update', action='store_true',
|
||||||
|
help='force image update')
|
||||||
|
parser.add_argument('command', nargs='*', default=None,
|
||||||
|
help = 'command to execute')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
if osname:
|
||||||
|
args.osname = osname
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
conf = load_config(args.config, args.osname)
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ret = main()
|
||||||
|
sys.exit(ret)
|
53
runon.conf
Normal file
53
runon.conf
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
environment =
|
||||||
|
HOME
|
||||||
|
USER
|
||||||
|
DISPLAY
|
||||||
|
debian_chroot=${osname}
|
||||||
|
|
||||||
|
|
||||||
|
binds =
|
||||||
|
/etc/passwd:ro
|
||||||
|
/etc/group:ro
|
||||||
|
/etc/shadow:ro
|
||||||
|
/tmp/.X11-unix:ro
|
||||||
|
/home/${user}
|
||||||
|
|
||||||
|
[centos7]
|
||||||
|
dockerfile =
|
||||||
|
FROM centos:7
|
||||||
|
RUN yum install sudo -y
|
||||||
|
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||||
|
RUN echo "ALL ALL=(ALL) ALL" >> /etc/sudoers
|
||||||
|
pkginstall = RUN yum install {} -y
|
||||||
|
packages = ksh csh xterm xorg-x11-apps xkeyboard-config git
|
||||||
|
|
||||||
|
[centos8]
|
||||||
|
dockerfile =
|
||||||
|
FROM centos:8
|
||||||
|
RUN yum install dnf-plugins-core -y
|
||||||
|
RUN yum config-manager --set-enabled powertools -y
|
||||||
|
RUN yum install sudo -y
|
||||||
|
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||||
|
RUN echo "ALL ALL=(ALL) ALL" >> /etc/sudoers
|
||||||
|
pkginstall = RUN yum install {} -y
|
||||||
|
packages = ksh csh xterm xorg-x11-apps xkeyboard-config git
|
||||||
|
|
||||||
|
[debian9]
|
||||||
|
dockerfile =
|
||||||
|
FROM debian:9
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get -y install sudo
|
||||||
|
RUN echo "Defaults lecture = never" >> /etc/sudoers
|
||||||
|
pkginstall = RUN apt-get -y install {}
|
||||||
|
packages = ksh csh xterm x11-apps libgtk-3-0 build-essential git
|
||||||
|
|
||||||
|
[ubuntu20.04]
|
||||||
|
dockerfile =
|
||||||
|
FROM ubuntu:20.04
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get -y install sudo
|
||||||
|
pkginstall = RUN apt-get -y install {}
|
||||||
|
packages = ksh csh xterm x11-apps build-essential git
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user