shlax/podctl/build.py
2020-02-13 15:13:23 +01:00

123 lines
3.8 KiB
Python

import asyncio
import os
import asyncio
import signal
import shlex
import subprocess
import sys
import textwrap
from .proc import Proc, output
from .script import Script
class Build(Script):
"""
The build script iterates over visitors and runs the build functions, it
also provides wrappers around the buildah command.
"""
unshare = True
def __init__(self):
super().__init__()
self.mounts = dict()
self.ctr = None
self.mnt = None
async def config(self, line):
"""Run buildah config."""
return await self.exec(f'buildah config {line} {self.ctr}')
async def copy(self, src, dst):
"""Run buildah copy to copy a file from host into container."""
return await self.exec(f'buildah copy {self.ctr} {src} {dst}')
async def cexec(self, *args, user=None, **kwargs):
"""Execute a command in the container."""
_args = ['buildah', 'run']
if user:
_args += ['--user', user]
_args += [self.ctr, '--', 'sh', '-euc']
return await self.exec(*(_args + [' '.join([str(a) for a in args])]))
async def crexec(self, *args, **kwargs):
"""Execute a command in the container as root."""
kwargs['user'] = 'root'
return await self.cexec(*args, **kwargs)
async def mount(self, src, dst):
"""Mount a host directory into the container."""
target = self.mnt / str(dst)[1:]
await self.exec(f'mkdir -p {src} {target}')
await self.exec(f'mount -o bind {src} {target}')
self.mounts[src] = dst
async def umounts(self):
"""Unmount all mounted directories from the container."""
for src, dst in self.mounts.items():
await self.exec('umount', self.mnt / str(dst)[1:])
async def umount(self):
"""Unmount the buildah container with buildah unmount."""
if self.ctr:
await self.exec(f'buildah unmount {self.ctr}')
async def paths(self):
"""Return the list of $PATH directories"""
return (await self.cexec('echo $PATH')).out.split(':')
async def which(self, cmd):
"""
Return the first path to the cmd in the container.
If cmd argument is a list then it will try all commands.
"""
if not isinstance(cmd, (list, tuple)):
cmd = [cmd]
for path in await self.paths():
for c in cmd:
p = os.path.join(self.mnt, path[1:], c)
if os.path.exists(p):
return p[len(str(self.mnt)):]
def __repr__(self):
return f'Build'
async def run(self, *args, **kwargs):
if os.getuid() == 0:
return await super().run(*args, **kwargs)
from podctl.console_script import console_script
# restart under buildah unshare environment !
argv = [
'buildah', 'unshare',
sys.argv[0], # current podctl location
]
if console_script.options.get('debug') is True:
argv.append('-d')
elif isinstance(console_script.options.get('debug'), str):
argv.append('-d=' + console_script.options.get('debug'))
argv += [
type(self).__name__.lower(), # script name ?
]
output(' '.join(argv), 'EXECUTION', flush=True)
proc = await asyncio.create_subprocess_shell(
shlex.join(argv),
stderr=sys.stderr,
stdin=sys.stdin,
stdout=sys.stdout,
)
await proc.communicate()
console_script.exit_code = proc.returncode
return
pp = subprocess.Popen(
argv,
stderr=sys.stderr,
stdin=sys.stdin,
stdout=sys.stdout,
)
pp.communicate()
console_script.exit_code = pp.returncode