]> git.ipfire.org Git - thirdparty/mkosi.git/commit
Rework package manager caching
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 30 Jan 2024 13:50:42 +0000 (14:50 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 31 Jan 2024 13:42:32 +0000 (14:42 +0100)
commited41b60dbd2c07faffe55454e5ee44fe203f6db3
treea38ec82819650d9657af560277e71fbc48ff8704
parent04f0cde0768995cb02e2a807b016ebbe0c9961af
Rework package manager caching

Currently, CacheDirectory= is used for all caching, both incremental
images and package manager cache. While this works great for incremental
images, there's a few issues with using CacheDirectory= for all package
manager caching:

- By default we don't do any caching, this has to be explicitly
  configured by setting CacheDirectory=mkosi.cache or telling users
  to manually create mkosi.cache. This means new users might be frustrated
  by their image builds downloading everything again on subsequent builds.

- By doing package manager caches per individual mkosi project, we
  unnecessarily download multiple copies of the same repo metadata and
  packages.

- When using incremental images, if the post-install or finalize scripts
  install extra packages, these packages can trigger repository metadata
  updates, which will result in the image being built from two repository
  metadata snapshots, one from when the incremental image was built, the
  other from the new packages installed using the refreshed repository
  metadata. Even if the scripts don't trigger repository metadata updates
  themselves, because the cache is shared, the repository metadata could
  already have been updated during another image build.

- When using base trees, any images using the base tree might trigger a
  repository metadata update as well, resulting in the same issue, where
  the image is built from multiple different snapshots of the repository
  metadata.

To fix the first two issues, we introduce a new setting PackageCacheDirectory=
and make its default either a system or per user cache directory depending
on how mkosi is invoked. This makes sure we cache by default and use a shared
package manager cache directory so that we do not unnecessarily download duplicate
copies of repository metadata and packages

To fix the two remaining issues, we need to make sure we only sync repository
metadata once for each image. We opt to do this at the start of each image build
and configure the package manager commands to not do any metadata syncing by
default. To make sure the repository metadata snapshot stays available for
extension images and for incremental images, we copy the repository metadata from
the shared cache into the image itself at /mkosi/cache/<subdir>. This makes sure
that even if the repository metadata in the shared cache is refreshed by another
image build it won't remove the old snapshot for incremental builds or images
intended to be used as base trees.

To make sure the actual packages downloaded during the image build are still
written into the shared package cache, in finalize_package_manager_mounts(), we
bind mounts the relevant directories from the shared package cache instead of
from /mkosi/cache/<subdir> in the image to make sure that other image builds can
take advantage of the downloaded packages.

The /mkosi/ directory is removed from the image at the end of each image build
before packaging up the result unless we're building a directory or tar image
and CleanPackageMetadata= is explicitly disabled.

The initial sync we do at the start of each image build operates on the shared
package cache directory so that repository metadata is only refreshed once and
can be reused by other image builds.

Because package managers now prevent automatic syncing by default, we have to
rework the local package repositories slightly to make sure the local package
repository is still synced whenever it is updated. We get rid of the localrepo()
functions and opt to again write the repo definitions for the local package
repository inline to keep things simple and localized.

To avoid pacman from writing packages from the local package repository to the
shared package cache directory, we configure the local repository itself as an
additional read-only cache directory, which makes sure that pacman will read cached
packages from this directory but won't write any new packages to this directory.

For zypper we disable "keeppackages" for the local package repository to prevent
those packages from getting cached.

For dnf, we don't mount in any directory from the shared package cache for the
mkosi-packages repository to make sure it stays local to the image.

Apt doesn't support any mechanism that allows us to prevent packages from the
local repository from getting cached so we allow these to be written to the
shared package cache.

We also take the opportunity to rename the mkosi-packages repo to the mkosi repo,
and rename the accompanying config files as well;

Because pacman does not employ any sort of cache key for its repository metadata,
when using the default shared package cache directory, we use a subdirectory based
on the distribution, release and architecture that we're building for to prevent
any possible conflicts in the cache directory when different pacman based distributions
use the same repo identifiers.

To avoid issues when two instances of mkosi operate on the same package cache directory,
we take an advisory BSD lock on the cache subdirectory that we're going to sync or copy.

When building the default initrd, we make sure it uses the same repository snapshot
as the associated image.
24 files changed:
NEWS.md
kernel-install/50-mkosi.install
mkosi/__init__.py
mkosi/config.py
mkosi/context.py
mkosi/distributions/__init__.py
mkosi/distributions/arch.py
mkosi/distributions/centos.py
mkosi/distributions/custom.py
mkosi/distributions/debian.py
mkosi/distributions/fedora.py
mkosi/distributions/mageia.py
mkosi/distributions/openmandriva.py
mkosi/distributions/opensuse.py
mkosi/distributions/ubuntu.py
mkosi/installer/__init__.py
mkosi/installer/apt.py
mkosi/installer/dnf.py
mkosi/installer/pacman.py
mkosi/installer/rpm.py
mkosi/installer/zypper.py
mkosi/resources/mkosi.md
mkosi/user.py
tests/test_json.py