Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rootless kvm and podman #1415

Merged
merged 8 commits into from
Oct 4, 2023
67 changes: 38 additions & 29 deletions osc/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# either version 2, or (at your option) any later version.

import fnmatch
import getpass
import glob
import os
import re
Expand Down Expand Up @@ -605,20 +606,35 @@ def calculate_prj_pac(store, opts, descr):
return project, package


def calculate_build_root(apihost, prj, pac, repo, arch):
buildroot = os.environ.get('OSC_BUILD_ROOT', conf.config['build-root']) \
% {'repo': repo, 'arch': arch, 'project': prj, 'package': pac, 'apihost': apihost}
def calculate_build_root_user(vm_type):
if vm_type in ("kvm", "podman"):
return getpass.getuser()
return None


def calculate_build_root(apihost, prj, pac, repo, arch, user=None):
user = user or ""
dash_user = f"-{user:s}" if user else ""
buildroot = conf.config["build-root"] % {
'apihost': apihost,
'project': prj,
'package': pac,
'repo': repo,
'arch': arch,
"user": user,
"dash_user": dash_user,
}
return buildroot


def build_as_user():
if os.environ.get('OSC_SU_WRAPPER', conf.config['su-wrapper']).split():
if conf.config["su-wrapper"]:
return False
return True


def su_wrapper(cmd):
sucmd = os.environ.get('OSC_SU_WRAPPER', conf.config['su-wrapper']).split()
sucmd = conf.config['su-wrapper'].split()
if sucmd:
if sucmd[0] == 'su':
if sucmd[-1] == '-c':
Expand All @@ -633,7 +649,12 @@ def run_build(opts, *args):
cmd = [conf.config['build-cmd']]
cmd += args

cmd = su_wrapper(cmd)
if opts.vm_type:
cmd.extend(["--vm-type", opts.vm_type])

user = calculate_build_root_user(opts.vm_type)
if not user:
cmd = su_wrapper(cmd)

if not opts.userootforbuild:
cmd.append('--norootforbuild')
Expand Down Expand Up @@ -782,18 +803,6 @@ def main(apiurl, store, opts, argv):
if opts.wipe:
buildargs.append("--wipe")

orig_build_root = config['build-root']
# make it possible to override configuration of the rc file
for var in ['OSC_PACKAGECACHEDIR', 'OSC_SU_WRAPPER', 'OSC_BUILD_ROOT']:
val = os.getenv(var)
if val:
if var.startswith('OSC_'):
var = var[4:]
var = var.lower().replace('_', '-')
if var in config:
print('Overriding config value for %s=\'%s\' with \'%s\'' % (var, config[var], val))
config[var] = val

pacname = pac
if pacname == '_repository':
if not opts.local_package:
Expand All @@ -805,15 +814,8 @@ def main(apiurl, store, opts, argv):
pacname = os.path.splitext(os.path.basename(build_descr))[0]
apihost = urlsplit(apiurl)[1]
if not build_root:
build_root = config['build-root']
if build_root == orig_build_root:
# ENV var was not set
build_root = config['api_host_options'][apiurl].get('build-root', build_root)
try:
build_root = build_root % {'repo': repo, 'arch': arch,
'project': prj, 'package': pacname, 'apihost': apihost}
except KeyError:
pass
user = calculate_build_root_user(vm_type)
build_root = calculate_build_root(apihost, prj, pacname, repo, arch, user)

# We configure sccache after pacname, so that in default cases we can have an sccache for each
# package to prevent cross-cache polutions. It helps to make the local-use case a bit nicer.
Expand Down Expand Up @@ -1492,7 +1494,9 @@ def __str__(self):
cmd += specialcmdopts + vm_options + buildargs
cmd += [build_descr]

cmd = su_wrapper(cmd)
# determine if we're building under root (user == None) and use su_wrapper accordingly
if calculate_build_root_user(vm_type) is None:
cmd = su_wrapper(cmd)

# change personality, if needed
if hostarch != bi.buildarch and bi.buildarch in change_personality:
Expand All @@ -1506,7 +1510,12 @@ def __str__(self):
rc = run_external(cmd[0], *cmd[1:])
if rc:
print()
print('The buildroot was:', build_root)
print(f"Build failed with exit code {rc}")
print(f"The buildroot was: {build_root}")
print()
print("Cleaning the build root may fix the problem or allow you to start debugging from a well-defined state:")
print(" - add '--clean' option to your 'osc build' command")
print(" - run 'osc wipe [--vm-type=...]' prior running your 'osc build' command again")
sys.exit(rc)
except KeyboardInterrupt as keyboard_interrupt_exception:
print("keyboard interrupt, killing build ...")
Expand Down
14 changes: 7 additions & 7 deletions osc/commandline.py
Original file line number Diff line number Diff line change
Expand Up @@ -6419,10 +6419,9 @@ def do_localbuildlog(self, subcmd, opts, *args):
raise oscerr.WrongArgs('Wrong number of arguments.')

# TODO: refactor/unify buildroot calculation and move it to core.py
buildroot = os.environ.get('OSC_BUILD_ROOT', conf.config['build-root'])
apihost = urlsplit(self.get_api_url())[1]
buildroot = buildroot % {'project': project, 'package': package,
'repo': repo, 'arch': arch, 'apihost': apihost}
buildroot = osc_build.calculate_build_root(apihost, project, package, repo, arch)

offset = 0
if opts.offset:
offset = int(opts.offset)
Expand Down Expand Up @@ -7268,7 +7267,8 @@ def do_build(self, subcmd, opts, *args):
repo, arch, build_descr = args
prj, pac = osc_build.calculate_prj_pac(store, opts, build_descr)
apihost = urlsplit(self.get_api_url())[1]
build_root = osc_build.calculate_build_root(apihost, prj, pac, repo, arch)
user = osc_build.calculate_build_root_user(opts.vm_type)
build_root = osc_build.calculate_build_root(apihost, prj, pac, repo, arch, user)
print(build_root)
return

Expand All @@ -7281,11 +7281,11 @@ def do_build(self, subcmd, opts, *args):
repo, arch, build_descr = args
prj, pac = osc_build.calculate_prj_pac(store, opts, build_descr)
apihost = urlsplit(self.get_api_url())[1]
build_root = osc_build.calculate_build_root(apihost, prj, pac, repo,
arch)
user = osc_build.calculate_build_root_user(opts.vm_type)
build_root = osc_build.calculate_build_root(apihost, prj, pac, repo, arch, user)
if opts.wipe and not opts.force:
# Confirm delete
print("Really wipe '%s'? [y/N]: " % build_root)
print("Really wipe '%s'? [y/N]: " % build_root, end="")
choice = raw_input().lower()
if choice != 'y':
print('Aborting')
Expand Down
37 changes: 26 additions & 11 deletions osc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -970,17 +970,27 @@ def apiurl_aliases(self):
ini_key="build-jobs",
) # type: ignore[assignment]

build_type: Optional[str] = Field(
vm_type: Optional[str] = Field(
default=None,
description=textwrap.dedent(
"""
Type of the build environment passed the build tool as the ``--vm-type`` option:

- <empty>: chroot build
- kvm: KVM VM build (needs build-device, build-swap, build-memory)
- xen: XEN VM build (needs build-device, build-swap, build-memory)
- qemu: [EXPERIMENTAL] QEMU VM build
- lxc: [EXPERIMENTAL] LXC build
- <empty> chroot build
- kvm KVM VM build (rootless, needs build-device, build-swap, build-memory)
- xen XEN VM build (needs build-device, build-swap, build-memory)
- qemu [EXPERIMENTAL] QEMU VM build
- lxc [EXPERIMENTAL] LXC build
- uml
- zvm
- openstack
- ec2
- docker
- podman (rootless)
- pvm
- nspawn

See ``build --help`` for more details about supported options.
"""
),
ini_key="build-type",
Expand All @@ -997,13 +1007,18 @@ def apiurl_aliases(self):
) # type: ignore[assignment]

build_root: str = Field(
default="/var/tmp/build-root/%(repo)s-%(arch)s",
default="/var/tmp/build-root%(dash_user)s/%(repo)s-%(arch)s",
description=textwrap.dedent(
"""
Path to the build root directory.

Supported substitutions: ``%(repo)s``, ``%(arch)s``, ``%(project)s``, ``%(package)s`` and ``%(apihost)s``
where ``apihost`` is the hostname extracted from the currently used ``apiurl``.
Supported substitutions: ``%(repo)s``, ``%(arch)s``, ``%(project)s``, ``%(package)s``, ``%(apihost)s``, ``%(user)s``, ``%(dash_user)s``
where::

- ``apihost`` is the hostname extracted from the currently used ``apiurl``.
- ``dash_user`` is the username prefixed with a dash. If ``user`` is empty, ``dash_user`` is also empty.

NOTE: The configuration holds the original unexpanded string. Call ``osc.build.get_build_root()`` with proper arguments to retrieve an actual path.

Passed as ``--root <VALUE>`` to the build tool.
"""
Expand Down Expand Up @@ -1745,7 +1760,7 @@ def get_config(override_conffile=None,
Configure osc.

The configuration options are loaded with the following priority:
1. environment variables: OSC_<uppercase-option>
1. environment variables: OSC_<uppercase_option>
2. override arguments provided to get_config()
3. oscrc config file
"""
Expand Down Expand Up @@ -1854,7 +1869,7 @@ def get_config(override_conffile=None,

# priority: env, overrides, config
if env_key in os.environ:
value = os.environ["env_key"]
value = os.environ[env_key]
elif name in overrides:
value = overrides.pop(name)
elif ini_key in overrides:
Expand Down