Repository navigation
Conversation
t-8ch
left a comment
There was a problem hiding this comment.
I focused on step 1) for now.
You could also drop the other steps for now and we do one after the other.
3b2caf8 to
90dc4e3
Compare
| for lic in repo_conf.splitlines(): | ||
| if lic.startswith('SignWith'): | ||
| self.keyid = lic.split()[1] | ||
| conf_dist = self.volume.joinpath('conf', 'distributions') |
There was a problem hiding this comment.
In the end we want to have a repository/SBOM for the build environment, even if that wasn't an initvm.
We can probably skip it for now, but will need some solution before making this a stable feature.
There was a problem hiding this comment.
I also thought of that, but wasn't sure how/if to approach it properly, because I am not so sure if copying the exact same approach from initvm is actually useful for the container use case.
Sure, SBOM generation for the container environment should be done. But for that I guess we should first look how we want to approach SBOM generation for ELBE in the first place (as you mentioned debsbom before).
For the packages, do you think it is worth to assemble the Debian packages from the container standalone like we do for initvm? Wouldn't it be more convenient to just archive the whole container including all the configuration / scripting / ...
There was a problem hiding this comment.
Wouldn't it be more convenient to just archive the whole container including all the configuration / scripting /
We'll need a machine-readable description of it, not only the raw artifacts.
There was a problem hiding this comment.
We'll need a machine-readable description of it, not only the raw artifacts.
Yes, definitely! I meant the "archived container" just as replacement for the binary artifacts of the initvm CD, not as replacement for the SBOM.
3ad19f5 to
0cbcbdb
Compare
0cbcbdb to
b415f97
Compare
95af6b2 to
37ead4c
Compare
| ' RAM. Use --basetemp=<dir> or set TMPDIR to select another' | ||
| ' location.', | ||
| ) | ||
| os.environ['TMPDIR'] = str(var_tmp) |
There was a problem hiding this comment.
While I am aware that I asked for this, it is quite complicated...
Maybe we test for the free space instead? That could be done easily in pure python.
There was a problem hiding this comment.
Hmm. If you really want it, I can of course implement it. The change for that would be small (see below). But I am not really sure if just checking for the amount of free space is the correct approach. I see two problems with that
- It introduces an arbitrary constant. How could we know how much space we really need?
- Even if there is enough space in the RAM for the full build, it still leads to excessive RAM usage, leaving fewer RAM for the build as well as parallel processes. My initial issue that triggered the idea to move to /var/tmp was actually not that the build was failing, but that my browser that I had open in parallel got unusable slow. We can then of course keep some buffer, but again, how much buffer is sufficient?
--- a/conftest.py
+++ b/conftest.py
@@ -2,10 +2,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2024 Linutronix GmbH
import os
-import subprocess
+import shutil
import tempfile
from pathlib import Path
import pytest
+# Minimum free space required in the temp dir for large build artifacts.
+MIN_TMP_FREE = 20 * 1024**3
# https://stackoverflow.com/a/61193490
def pytest_addoption(parser):
@@ -22,12 +24,6 @@
)
-def _is_ram_backed(path):
- fstype = subprocess.run(
- ['stat', '-f', '-c', '%T', path],
- capture_output=True, text=True, check=False,
- ).stdout.strip()
- return fstype in ('tmpfs', 'ramfs')
-
-
def pytest_configure(config):
config.addinivalue_line('markers', 'slow: mark test as slow to run')
@@ -46,19 +42,22 @@
os.environ.setdefault('PYTHONWARNINGS', ' '.join(warnings))
- # Avoid filling up the RAM with large build artifacts: unless the user
- # chose a location, move away from the default temp dir if it is RAM-backed.
+ # Avoid filling up the temp dir (often RAM-backed) with large build
+ # artifacts: unless the user chose a location, move away from the default
+ # temp dir if it has too little free space.
if 'TMPDIR' not in os.environ and not config.option.basetemp:
- if _is_ram_backed(Path(tempfile.gettempdir())):
+ if shutil.disk_usage(tempfile.gettempdir()).free < MIN_TMP_FREE:
var_tmp = Path('/var/tmp')
- if not var_tmp.is_dir() or not os.access(var_tmp, os.W_OK | os.X_OK):
+ if (
+ not var_tmp.is_dir()
+ or not os.access(var_tmp, os.W_OK | os.X_OK)
+ or shutil.disk_usage(var_tmp).free < MIN_TMP_FREE
+ ):
raise pytest.UsageError(
- f'{tempfile.gettempdir()} is RAM-backed and {var_tmp} is not a'
- ' writable directory. Large build artifacts would fill up the'
- ' RAM. Use --basetemp=<dir> or set TMPDIR to select another'
- ' location.',
+ f'{tempfile.gettempdir()} has less than'
+ f' {MIN_TMP_FREE // 1024**3} GiB free and {var_tmp} is not a'
+ ' writable directory with enough free space. Use'
+ ' --basetemp=<dir> or set TMPDIR to select another location.',
)
os.environ['TMPDIR'] = str(var_tmp)
# tempfile has cached the old location by now, so reset
tempfile.tempdir = None
|
|
||
| class LocalProjectBackend(ProjectBackend): | ||
| def __init__(self, cache_dir, outdir): | ||
| self.pm = ProjectManager(cache_dir) |
There was a problem hiding this comment.
The database is still stored in /var/cache/elbe. Can we move it into the cache_dir parameter here?
There was a problem hiding this comment.
Refactored the whole handling around the cache dir. This was also needed for native builds (because it otherwise resulted in too long socket paths with the gpg agent).
The user name, full name, password and email are not used anymore since the user table has been removed. Keep accepting the corresponding options of 'elbe db init' for compatibility, but ignore them. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
If the directory of the database can not be created, only an error message was printed and 'elbe db init' still exited successfully, so a missing database went unnoticed. Let the exception propagate instead, as the other database commands do. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Only 'elbe db init' created the directory of the database, so every other user of ElbeDB failed with a cryptic sqlite error if it did not exist yet. Create the directory whenever the database is opened. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
37ead4c to
d36335a
Compare
The update and repository paths, as well as the update scripts, refer to the deployed target system or to the inside of the rootfs that is built, not to the cache directory of the build system. The same holds for the directories the packages for the cdroms are downloaded to. They are inside of the rootfs, but were named like the directories that are used for the packages of the initvm. Prefix them with TARGET_ like TARGET_GNUPG_HOME already is and derive them from a common TARGET_CACHE_DIR, so they are not mistaken for host paths. Name the actual directories of the initvm INITVM_ instead, like the other paths of the initvm. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
The host-side locations below /var/cache/elbe were module level constants, so every user was bound to the system wide cache directory. That is fine for the initvm, but prevents running builds that should not depend on or modify system directories. Collect these locations in a CacheDirs class that is instantiated with a root directory and hand it to ElbeDB, ProjectManager, ElbeProject and the cdrom generation. The default instance DEFAULT_CACHE_DIRS keeps the current behavior. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
ElbeDB created a sqlalchemy engine that was never disposed, so the pooled sqlite connections were closed by the garbage collector at an arbitrary time. This can result in a ResourceWarning. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
The GnuPG home of a project is always located inside the build directory. Since the gpg-agent socket is located inside the GnuPG home, this breaks for deeply nested build directories, because the length of UNIX socket paths is limited to 107 bytes. GnuPG works around that by placing the socket below /run/user/<uid>, but this does not exist without a login session, e.g. when building as root via sudo. Add an optional gnupg_root to CacheDirs and resolve the GnuPG home of the project through CacheDirs.project_gnupg_home() instead of assembling <builddir>/gnupg at each place. Without a gnupg_root, the paths stay unchanged. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Parse the options of an fstab entry into a list instead of keeping the comma separated string. This allows to extend the options without string handling, which is needed for the upcoming rbind support in the mount helper, and removes the need to split them again when evaluating them. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
d36335a to
03eb649
Compare
Hooks from the host system (e.g. apt-listchanges) should not be applied in the target chroot. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
VirtApt modifies the process-global apt_pkg.config (Dir::*, APT::*) to point into its temporary directory but never reverted it. Remember the original values and restore them in delete(). Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Add a new top-level "elbe build" command as alternative to "elbe initvm submit". It drives the project manager directly in-process, without any daemon or SOAP communication. It is meant to be used in an environment that already provides the required isolation. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
…ectory The VM-free build is a one-shot operation and nothing needs the internal signing key once the build finished. Nevertheless, its GnuPG home was located inside the build directory, so the agent socket path exceeded the limit for UNIX socket paths for deeply nested build directories and the build failed with "gpgme_op_genkey: GnuPG: No agent running". Keep the GnuPG homes of the LocalProjectBackend below a short temporary directory instead. On stop(), terminate the agents explicitly and remove the directory, also in case of a failed build. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
If CAP_SYS_ADMIN is missing, re-execute elbe build in a new user and mount namespace. This gives sufficient permissions to perform the (bind) mounts needed for the build process, so running it unprivileged works without any explicit capability. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Support rbind mounts with the mount helper. This is needed to rbind pseudo filesystems in an upcoming commit. For umounting the rbind again, we need a --lazy, because there might be submounts still busy. We anyway were ignoring the result of the umount already before (check=False), so we are not depending on an immediate clean umount and can defer it to the kernel instead to do it in a lazy way. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
…cratch Several operations require a chroot with the pseudo filesystems (/proc, /sys and /dev) in it. Inside a container, the container runtime already protects parts of it (like /proc/kcore) by overlaying it with a null bind mount. Doing a fresh mount from scratch (e.g. mount -t proc None foo) inside a completely new mount and user namespace, would expose the content again, so the kernel has explicit procection against it (-> mount_too_revealing). But we do not need fresh pseudo filesystems. We can just rebind what we already have. The only issue with that is that we need to scope it more tightly around the operations that actually require this setup with chroot and pseudo filesystems, otherwise we would fill up the newly created filesystem with stuff from the pseudo filesystems we do not need. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Several actions require elevated privileges for losetup or mknod. Instead of starting the build and then later failing with an error message, check beforehand for critical elements in the XML and print a respective error message. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
/tmp is usually RAM-backed, so when putting build artifacts there, it will fill up very quickly and exhaust the RAM. Therefore, use /var/tmp per default. On Debian systems /var/tmp will still follow a retention policy, so any potential leftovers will be removed after 30 days (in the default configuration). On systems where /tmp is not RAM-backed, still use it. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Add a build_driver fixture that hides how the XML files under test are
built, so we can in future support multiple different drivers like
initvm and container based.
Use absolute paths since native builds run in-process and the build
changes the working directory of the pytest process (e.g. asyncworker
chdir('/')). Relative XML paths used by later tests then fail to load.
Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
9432645 to
4d50acb
Compare
Split the test_xml to only contain code that is not directly related to the initvm. This will enable us in the next step to add further tests that will use the container-based approach. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Build a simple image without a bootloader to demonstrate an ELBE build that does not require elevated privileges. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
…without udev Setup UUID as needed e.g. for grub, because that is usually also done by udev. So without udev, we need a replacement for that functionality. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Run the XML build tests by calling elbe build directly on the host instead of going through an initvm, using the build_driver abstraction. Tests that need root privileges are skipped when not run as root. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
With the recent additions, it is no longer necessary to spawn an initvm to build ELBE images. Therefore, provide a container that enables this use case and replace the existing legacy containerfile with it. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Provide a test driver to run the same tests as for the initvm and native for the container-based build as well. Detect whether the XML under test actually needs a rootful container by checking xml_needs_rootful(). When it does not, run the container rootless via --userns auto instead of requiring elevated privileges, and only skip the test when a rootful container is required and the tests are not run as root. Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
4d50acb to
2596a54
Compare
| def __init__(self, xml): | ||
|
|
||
| self.xml = xml | ||
| self._saved_config = {} |
There was a problem hiding this comment.
Could you mention in the commit message why we need this?
This supersedes #482 by removing the manual mknod procedures with passing udev and /dev directly into the container for the rootful case.
Also reordered the commits as discussed:
fc80da4
54c6257
da13574
d532e23
9843820
d7f66a3
707b6c6
ea2994e
ec47114
b5b9d22
No additional commits, but requires to manually unshare if not done via container.
0095a8d
43a09d5
41e12bf
76aa59e
79d2fd0
d4cabf0