Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

...

...

...

...

...

...

...

...

...

...

maxLevel3

Questions

Assuming at least some of the packages involved belong to repo modules, how can/should we handle:

pkglist + packages.yaml => building tarball/ISO

Can the pkg/tar/ISO build process still create a usable ISO as it currently does, or will something have to change?

  • filtering an OS ISO down to its pkglist

Note

Possible (but involved)

We can probably use dir2module to re-package just the modules we need and mergerepo_c to build them into a slimmed-down AppStream based on pkglist.txt (or a SIMP/ repo for packages.yml)

Details at the end of the RPM module header examples

Warning

Currently failing:

Currently, an AppStream repo mirrored with dnf reposync fails dnf repoclosure rather badly, while the real AppStream repo only has two unresolved deps (both ursine).

If it can be done: How?

If not: What alternatives are there?

Impacts to unpack_dvd & kickstart repositories

How should unpack_dvd handle repos that do/may contain modules?

Tip

Recommendation:
Assuming we can limit any module editing to the tarball/ISO build process (above), unpack_dvd should just mount the ISO as a loopback device and sync everything with dnf reposync --download-metadata … .

How does SELinux support this?

Where do the policies for the packages in various module streams come from?

Notes:

Supporting questions

  • Can we repackage a “thinned-out” module with only select packages?

    • probably:

      • EL8 RPM prereqs: createrepo_c (appstream) + modulemd-tools (epel)

      • Process:

        Code Block
        # Create initial repo with ursine modules
        mkdir -p $NEW_REPO_DIR/Packages/ursine
        cp "${URSINE_PACKAGE_FILES[@]}" "$NEW_REPO_DIR/Packages/ursine/"
        cd "$NEW_REPO_DIR"
        createrepo_c .

Modularity CLI examples

Simple dnf module commands

View all modules

Code Block
dnf module list --all

View a module’s available streams

Code Block
breakoutModewide
# dnf module list --available nodejs
Waiting for process with pid 79896 to finish.
Last metadata expiration check: 0:00:01 ago on Fri 12 Mar 2021 02:03:02 AM UTC.
CentOS Linux 8 - AppStream
Name                   Stream                 Profiles                                             Summary
nodejs                 10 [d]                 common [d], development, minimal, s2i                Javascript runtime
nodejs                 12                     common [d], development, minimal, s2i                Javascript runtime
nodejs                 14                     common [d], development, minimal, s2i                Javascript runtime

Extra Packages for Enterprise Linux Modular 8 - x86_64
Name                   Stream                 Profiles                                             Summary
nodejs                 13                     default, development, minimal                        Javascript runtime

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

View packages in a module

Code Block
dnf module repoquery nodejs
Info

It looks dnf module repoquery only reports on the default stream, regardless of the module-spec specified (tested on EL8.3)

View modules/profiles that provide a package

Code Block
dnf module provides nodejs
Info

This is one of the few (two?) module commands that reports which DNF Repo hosts the module

View the packages in each profile (for a specific stream)

Code Block
languages
]# dnf module info --profile  nodejs:12
Last metadata expiration check: 2:59:10 ago on Thu 11 Mar 2021 11:02:32 PM UTC.
Name        : nodejs:12:8030020210304194546:30b713e6:x86_64
common      : nodejs
            : npm
development : nodejs
            : nodejs-devel
            : npm
minimal     : nodejs
s2i         : nodejs
            : nodejs-nodemon
            : npm

...

Find all modular RPM files that under a directory and print their module headers

This could identify dnf modules that would need to be re-created when shipping modular RPMs independently from their base repos:

Code Block
languagebash
find "$DIR_WITH_RPMS" -name \*.rpm \
  -exec rpm -qp {} --qf '%{NVRA}   %{ModularityLabel}\n' \; \
  | grep -v '(none)' \
  | tee modular_rpms.txt

The %{ModularityLabel} macro is returned in (module)name:stream:version:context:arch (NSVCA) format , so the result looks like this:

Code Block
389-ds-base-1.4.3.8-5.module_el8.3.0+473+53682548.x86_64   389-ds:1.4:8030020200831174107:618f7055
389-ds-base-devel-1.4.3.8-5.module_el8.3.0+473+53682548.x86_64   389-ds:1.4:8030020200831174107:618f7055
389-ds-base-legacy-tools-1.4.3.8-5.module_el8.3.0+473+53682548.x86_64   389-ds:1.4:8030020200831174107:618f7055
389-ds-base-libs-1.4.3.8-5.module_el8.3.0+473+53682548.x86_64   389-ds:1.4:8030020200831174107:618f7055
389-ds-base-snmp-1.4.3.8-5.module_el8.3.0+473+53682548.x86_64   389-ds:1.4:8030020200831174107:618f7055
HdrHistogram-2.1.11-2.module_el8.2.0+460+6583c1d0.noarch   jmc:rhel8:8020020200731165725:21dc74c6
HdrHistogram-javadoc-2.1.11-2.module_el8.2.0+460+6583c1d0.noarch   jmc:rhel8:8020020200731165725:21dc74c6
Judy-1.0.5-18.module_el8.1.0+217+4d875839.x86_64   mariadb:10.3:8010020191115015915:cdc1202b
ant-1.10.5-1.module_el8.0.0+47+197dca37.noarch   ant:1.10:8000020190624202340:f7e686af
ant-lib-1.10.5-1.module_el8.0.0+47+197dca37.noarch   ant:1.10:8000020190624202340:f7e686af
aopalliance-1.0-17.module_el8.0.0+39+6a9b6e22.noarch   maven:3.5:8000020190624140656:f7e686af
aopalliance-1.0-20.module_el8.3.0+568+0c23fd64.noarch   maven:3.6:8030020201104064112:a623df05

It may be possible to re-create specific module/streams based on the RPM’s unique headers. However, there will need to be a second data source, because the RPM ModularityLabel has significant limitations:

Note

By convention, RPMs' ModularityLabel string is in NVSC format, which doesn’t include profile information. Any module streams constructed solely on the RPM header would lose all the original streams' profiles.

Note that the repo2module tool just creates a default profile called everything.

...

We can’t rely on RPMs' ModularityLabel string to be in NVSC format, either!

...

Identify all unique modules/streams from a collection of RPM files

Code Block
find "$DIR_WITH_RPMS" -name \*.rpm \
  -exec rpm -qp {} --qf '%{ModularityLabel}\n' \; \
  | grep -v '^(none)' | sort | uniq -c \
  | sort -nk1,1 \
  | tee unique_rpm_module_streams.txt

...

Mirror the contents of a DNF repository, preserving all modules and package groups. 

The current state of CentOS 8 createrepo_c + modulemd-tools (EPEL8) allow us to:

(Works from CentOS 8.3 and CentOS 7.8, requires packages dnf and dnf-plugin-core )

An example of this mirroring a mounted CentOS 8.3 ISO’s AppStream repository:

Code Block
languagebash
PATH_TO_LOCAL_MIRROR=/path/to/Appstream
PATH_TO_SOURCE_REPO=/mnt/AppStream

dnf reposync \
  --download-metadata --downloadcomps \
  --download-path "$PATH_TO_LOCAL_MIRROR" \
  --repofrompath iso,"$PATH_TO_SOURCE_REPO" \
  --repoid iso

# Useful EL8-only options: --remote-time --norepopath

dnf reposync should probably be the only kind of modular-capable mirroring that on-site tools like unpack_dvd should use.

DNF repoclosure

The ISO build process (really the tar build process) runs repoclosure to make sure the packages on the ISO will be self-contained.

Code Block
dnf repoclosure --repofrompath iso,"$PWD" --repo appstream --repo baseos
Info

Judging by the bug report at https://bugzilla.redhat.com/show_bug.cgi?id=1547041, dnf repoclosure is not module-aware, in the sense that its resolver does not consider packages in all available modules/streams.

The current behavior only resolves module packages using default or enabled streams & profiles.

Clean DNF install --downloadonly with all deps, without needing mock

Make sure you enable/disable the repos to match what you intend for resolution; --config is an option, too.

Code Block
languagebash
DNF_DLONLY_TARGET_PACKAGE=httpd
DNF_DLONLY_INSTALL_ROOT=/root/fake-install-dir
DNF_DLONLY_PACKAGE_DIR=/root/downloadonly

dnf install --downloadonly \
  --installroot="$DNF_DLONLY_INSTALL_ROOT" \
  --downloaddir="$DNF_DLONLY_PACKAGE_DIR" \
  --enablerepo=appstream \
  --disablerepo=test \
  --releasever=8 \
  "$DNF_DLONLY_TARGET_PACKAGE"

Documentation

...

https://docs.fedoraproject.org/en-US/modularity/

...

https://dnf.readthedocs.io/en/latest/

...

https://dnf-plugins-core.readthedocs.io/en/latest/

...

https://sgallagh.wordpress.com/2019/08/14/sausage-factory-modules-fake-it-till-you-make-it/

  • Notes on building modules independently of Fedora’s hosted build system

https://pagure.io/modularity/issue/141

...

Table of Contents
maxLevel3

Questions

Assuming at least some of packages we need to ship with SIMP already belong to modular repository streams, how can/should we handle:

Building SIMP RPM distributions (pkglist.txt + packages.yaml, repositories, ISOs, etc.,)

Note

80% Confidence it’s Possible — but it will be complicated and hacky

  • The current methods of downloading external packages AND pruning OS ISOs down to a minimal set of packages will both result in broken modular repositories.

    • This could be solved by recreating the RPM’s module streams with exactly the same NSVCA values as the source repository—but practically every step of that will be hacky and require unsupported tooling or several workarounds.

  • The repo manipulation methods (createrepo, repoclosure) aren’t module-aware

Current methods of building/sourcing RPM packages

A SIMP RPM distribution is a self-contained collection of RPM packages:

  • SIMP packages are built by the SIMP team, from the repos under src/ in Puppetfile.pinned.

  • Minimal Base OS packages are filtered from a base OS’s installation media (usually a DVD ISO), down to the files DVD/<os.release.major>-pkglist.txt.

  • External packages are RPMs downloaded based on the data in yum_data/packages.yaml, and come from additional sources (e.g., EPEL, Puppet, base OS extras, various vendors).

  • (Optionally): Custom packages placed manually into either the directory yum_data/packages/ (which locates a yum source like External packages) or yum_data/packages/aux_packages/ (included but not checked).

These packages are distributed by the SIMP ISO. Together, these RPMs MUST self-resolve (enough) to install a basic SIMP system on a network-isolated host.

Current process to build and distribute SIMP ISO release

Here’s a quick summary of the SIMP 6.5.0 build process to illustrate where the problems with modularity will arise:

  1. Build a DVD overlay directory (and tarball) containing the SIMP packages.

  2. ➡1️⃣ Download External packages (rake build:yum:sync) into yum_data/packages/

  3. Unpack the base OS installation media into an ISO staging directory.

    1. ➡1️⃣2️⃣ Prune the unpacked ISO RPMs down to the Minimal Base OS packages.

  4. Merge the DVD overlay contents & External packages on top of the files already in the ISO staging directory.

    1. ➡3️⃣ Create new yum repositories using createrepo

    2. ➡4️⃣ Verify that all ISO staging directory’s RPMs can self-resolve by running repoclosure (rake pkg:repoclosure).

  5. Build a SIMP distribution ISO from the contents of the ISO staging directory

  6. ➡1️⃣ Host yum mirrors containing subsets of External package repositories (like EPEL) on the SIMP download service (ex: https://download.simp-project.com/SIMP/yum/releases/6.5.0-1/el/7/x86_64/epel/).

Pain points with modularity + the current process

  1. The DVD overlay directory (and tarball)should not be affected, because it won’t contain modular RPMs (the SIMP project doesn’t create them).

  2. Downloading External packages (rake build:yum:sync) into yum_data/packages/ has modularity problems:

    1. 1️⃣: You can’t simply [download the modular RPMs you want → remove the modular RPMS you don’t need → collect various modular RPMs together in a directory] and run createrepo to re-host them in a new repository. You need the correct metadata (called modulemd metadata) for all downloaded modular RPMs' streams, and special new commands, like createrepo_c.

      1. This metadata can only be obtained from the modular RPMs' source repo.

    2. There isn’t a “roll-your-own” solution to create arbitrary modular repositories yet The supported DNF repository tooling is mostly meant to mirror existing repositories.

      1. There are community tools (dir2module, repo2module), but they are incomplete, unsupported, and buggy. Some of these tools have been rolled into later versions of createrepo_c, but not the version on EL8 (and AFAIK, they may still be buggy).

      2. Pulp is the closest tool, because it supports some flavors of modular subsets

  3. Unpack the base OS installation media into an ISO staging directory runs into problems with pruning:

    1. 2️⃣: https://simp-project.atlassian.net/browse/SIMP-9644
      The naive pruning strategy of “rm RPM is it’s not in a *pkglist.txt file” may work with very specific RPMs, but it will still need to the original modularity metadata and additional information to ensure the correct modules are used

  4. Merging the DVD overlay

    1. 3️⃣: createrepo_c is needed to create a useable modular repository.
      However, it requires quite a few things before it can work:

      • ⚠➡5️⃣ The correct modulemd data (see format specs) for modules.yaml from the original repository.

      • ➡5️⃣ Some way of generating the modulemd YAML data for each module.

      • ➡5️⃣ The ability to add all the modules' modulemd data into a single modules.yaml file for the entire repository.

      • ⚠➡5️⃣ The correct tools to create/merge the repo and the module metadata (createrepo_mod or the *_c commands it runs)

    2. 4️⃣: yum-utilsprovides a CLI compatibility layer with the newer DNF sub-commands (including repoclosure), but may require specific arguments (documented further below)

      • dnf repoclosureis sort of module-aware (bz#1547041), and may need extra logic to module enable non-default streams that need to be considered while depsolving.

    3. ➡6️⃣: To generate the modulemd YAML data for each module and combine it into a single modulemd.yaml file for createrepo_c to consume, we will need to roll either:

      1. build logic to do it in one shot

      2. build logic to glue together a process that combines the buggy community tools from modulemd-tools.

  5. Build a SIMP distribution ISO from the contents of the ISO staging directory

  6. Host yum mirrors containing subsets of External package repositories (like EPEL) on the SIMP download serv

Creating repos with useable modularity streams

⚠5️⃣ We can re-package specific packages into slimmer versions of their source modules and then use mergerepo_c (or creatrepo_mod) to collect them into a slimmed-down AppStream/ repo (based on pkglist.txt) or SIMP/ (based on packages.yml). However, there are several issues that make this complicated:

tools like dir2module and repo2module[note] to re-package specific packages into slimmer versions of their source modules and then use mergerepo_c (or creatrepo_mod) to collect them into a slimmed-down AppStream/ repo (based on pkglist.txt) or SIMP/ (based on packages.yml). However, there are several issues that make this complicated:

  • RPM packages from repo modules contain a ModularityLabel header that is unique to the module’s name + stream + version + context + architecture (NSVCA). DNF must install these RPMs from modules (with modular metadata)—and will refuse to install them as ursine packages.

    • dir2module and repo2modules require a complete NSVCA string generating module metadata for a directory of RPMs.

    • 6️⃣ The dir2module script provided by the EPEL8 RPM modulemd-tools-0.7-1.el8.noarch does not create a default (or any) profile for the module it creates. It is effectively useless. repo2module is better (which is still missing the Arch in NSVCA).

  • A repackaged (slim) module’s name and stream and context and architecture must match the values in the source repo’s metadata for the original module.

    • This is required so the packages maintain continuity with the complete upstream repo (e.g., receiving update from the complete AppStream repo, epel-modular, etc)

    • The versionnumber must evaluate to more than the earlier (module versions) and less than the version of later modules in the full source repository. This number (like stream and context) is an arbitrary string set by the build platform, so we have to get it

    • For practical purposes, you need to mirror the repo’s metadata YAML at the same time as you retrieve the packages—it might be updated later, even if the packages you see hosted there are the same

  • The ModularityLabel header is string unique to a module builds' NSVCfor a particular platform. The header data in RPMs packaged by RHEL/CentOS build system looks useful because a string it’s in NSVC format, however this data is actually arbitrary and cannot be relied uponto provide accurate NSVC data for the module.

  • The only canonical source for a module’s correct NSVCA/P data is the source repo’s metadata (generally under repodata/{XXXXX}-modules.yaml.gz, and defined by repodata/repomd.xml under <data type="modules">`)

  • 🎉 I’ve tested a repacked “slim” repo alongside a repo with the full module w/identical NSVCA details, and it successfully resolved its metadata/packages with the full module.

    • This should also behave correctly with updated modules, but I haven’t been able to stage that yet.

Testing ISO RPMs' completeness with a repoclosure

Tip

Recommendation: Use dnf repoclosure (see below for arguments)

To run a repoclosure against ISO-bound/based repos that are staged on the local filesystem:

Code Block
dnf repoclosure 
  --disablerepo=\* \
  --repofrompath=base,/mnt/BaseOS \
  --repofrompath=test1,/opt/dnf_reposync_from_iso \
  --arch x86_64 \
  --check base \
  --check tes1

Notes:

  • Make sure there is a --repofrompath= for each local repository directory that should be considered during resolution.

  • Using --disablerepo=\* --repofrompath=, the repoclosure doesn’t require purpose-built *.repo files and doesn’t have to run inside a chroot/container/mock.

  • The --arch x86_64 may be needed to keep the closure focused on the ISO’s target arch.

    • (info) As released, the EL 8.3 ISO’s AppStream/ repo doesn’t close without it.

Impacts to the ISO installation

Note

Recommendation: A minimum, change the createrepo . line in auto.cfg on systems installing modular repositories

This is effectively impossible to test until we have a SIMP ISO with modular repositories.

Impacts to unpack_dvd & self-hosted DNF repositories (for kickstarts, etc)

How should unpack_dvd handle repos that do/may contain modules?

Tip

Recommendation: The unpack_dvd tool should assume that all ISO DNF repositories are complete enough to mirror.

(warning) Prerequisites:

  • Any necessary dnf module surgery MUST be completed during the tarball/ISO’s build process (described above)

  • Any “slimmed” dnf modules SHOULD be upgrade-compatible with an unpacked repository than contains the complete modules

If these recommendations are implemented, then a site’s unpack_dvd tool should only need to copy/reposync the files on the ISO.

This has been tested by mounting an ISO as a loopback device and syncing everything (including module metadata) with dnf reposync --download-metadata […] .

  • On a stock EL8 server, dnf reposync needs the dnf-plugins-core package

  • On a stock EL7 server, dnf reposync needs the dnf and dnf-plugins-core packages

How does SELinux support this?

Where do the policies for the packages in various module streams come from?

Info

Notes:

Modularity CLI examples

Simple dnf module commands

View all modules and streams

Code Block
dnf module list --all

View a module’s available streams

Code Block
breakoutModewide
# dnf module list --available nodejs

Waiting for process with pid 79896 to finish.
Last metadata expiration check: 0:00:01 ago on Fri 12 Mar 2021 02:03:02 AM UTC.
CentOS Linux 8 - AppStream
Name                   Stream                 Profiles                                             Summary
nodejs                 10 [d]                 common [d], development, minimal, s2i                Javascript runtime
nodejs                 12                     common [d], development, minimal, s2i                Javascript runtime
nodejs                 14                     common [d], development, minimal, s2i                Javascript runtime

Extra Packages for Enterprise Linux Modular 8 - x86_64
Name                   Stream                 Profiles                                             Summary
nodejs                 13                     default, development, minimal                        Javascript runtime

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

View all default streams

The 21 and 11 are the first two field widths. These may vary depending on the length of the stream names.

Code Block
dnf module list \
  | awk -v FIELDWIDTHS="21 11" '{printf "%-20s %s\n", $1, $2}' \
  | grep '\[d\]'

A stab at at more automation-friendly formatting

Code Block
dnf module list \
  | egrep -v '^(Extra|Name|Hint:|CentOS|Last)' \
  | sed -e '/^$/d' \
  | awk '{printf "%-20s %s\n", $1, $2}' \
  | sort
  
389-directory-server next
389-directory-server stable
389-directory-server testing
389-ds               1.4 
ant                  1.10
avocado              82lts
avocado              latest
cobbler              3
container-tools      1.0
container-tools      2.0
container-tools      rhel8
dwm                  latest
freeradius           3.0
gimp                 2.8
go-toolset           rhel8
httpd                2.4
idm                  DL1
idm                  client
inkscape             0.92.3
javapackages-runtime 201801
jmc                  rhel8
libselinux-python    2.8
libuv                epel8-buildroot
llvm-toolset         rhel8
  

View packages in a module

Code Block
dnf module repoquery nodejs
Info

It looks dnf module repoquery only reports on the default stream, regardless of the module-spec specified (tested on EL8.3)

View modules/profiles that provide a package

Code Block
dnf module provides 389-ds-base
Info

This is one of the few (two?) module commands that reports which DNF Repo hosts the module

Info

Note that it also looks for packages across multiple repos and module:streams

Example:

Code Block
# dnf module provides 389-ds-base
Last metadata expiration check: 1:20:30 ago on Wed Mar 24 18:09:28 2021.
389-ds-base-1.4.3.17-1.module_el8+10764+2b5f8656.x86_64
Module   : 389-directory-server:stable:820201201092549:9edba152:x86_64
Profiles : default legacy minimal
Repo     : epel-modular
Summary  : 389 Directory Server

389-ds-base-1.4.3.8-6.module_el8.3.0+604+ab7bf9cc.x86_64
Module   : 389-ds:1.4:8030020201222185615:618f7055:x86_64
Profiles :
Repo     : appstream
Summary  : 389 Directory Server (base)

389-ds-base-1.4.4.9-1.module_el8+10763+39cf6b48.x86_64
Module   : 389-directory-server:testing:820201201092622:9edba152:x86_64
Profiles : default legacy minimal
Repo     : epel-modular
Summary  : 389 Directory Server

389-ds-base-2.0.1-1.module_el8+10522+e95198da.x86_64
Module   : 389-directory-server:next:820201104083723:9edba152:x86_64
Profiles : default minimal
Repo     : epel-modular
Summary  : 389 Directory Server

View the packages in each profile (for a specific stream)

Code Block
languages
]# dnf module info --profile  nodejs:12
Last metadata expiration check: 2:59:10 ago on Thu 11 Mar 2021 11:02:32 PM UTC.
Name        : nodejs:12:8030020210304194546:30b713e6:x86_64
common      : nodejs
            : npm
development : nodejs
            : nodejs-devel
            : npm
minimal     : nodejs
s2i         : nodejs
            : nodejs-nodemon
            : npm

Anchor
module-headers
module-headers

Find all modular RPM files that under a directory and print their module headers

This identifies modular RPMs that would need to have their module streams re-created when distributed independently from their source repos:

Code Block
languagebash
find "$DIR_WITH_RPMS" -name \*.rpm \
  -exec rpm -qp {} --qf '%-62{NVRA}   %{ModularityLabel}\n' \; \
  | grep -v '(none)' \
  | tee modular_rpms.txt

The %{ModularityLabel} header is in (module)name:stream:version:context:arch (N:S:V:C:A) format (on RedHat/CentOS/Fedora-built packages), so the result looks like this:

Code Block
389-ds-base-1.4.3.8-6.module_el8.3.0+604+ab7bf9cc.x86_64         389-ds:1.4:8030020201222185615:618f7055
389-ds-base-libs-1.4.3.8-6.module_el8.3.0+604+ab7bf9cc.x86_64    389-ds:1.4:8030020201222185615:618f7055
python3-distro-1.4.0-2.module_el8.3.0+562+e162826a.noarch        python36:3.6:8030020201104034153:24f1489c
python3-lib389-1.4.3.8-6.module_el8.3.0+604+ab7bf9cc.noarch      389-ds:1.4:8030020201222185615:618f7055
ruby-2.5.5-106.module_el8.3.0+571+bab7c6bc.i686                  ruby:2.5:8030020201104071226:30b713e6
ruby-2.5.5-106.module_el8.3.0+571+bab7c6bc.x86_64                ruby:2.5:8030020201104071226:30b713e6

However, it isn’t possible to re-create specific module/streams based on the RPM’s ModularityLabel headers.

  1. A stream’s full modulemd metadata is only found in the source repo’s *metadata.yaml.gz

  2. The content of ModularityLabel is unique to a build, but its contents cannot be trusted.

Note

By convention, RPMs' ModularityLabel string is in N:S:V:C format, which doesn’t include profile information. Any module streams constructed solely on the RPM header would lose all the original streams' profiles.

Note that the repo2module tool just creates a default profile called everything.

Warning

We can’t rely on RPMs' ModularityLabel string to be in N:S:V:C format, either!

“The ModularityLabel can be any string at all. In Fedora, we have a convention to use name:stream:version:context to indicate from which build the RPM originally came from, but this is not to be relied upon. It may change at any time and it also may not be accurately reflective of the module in which it currently resides, due to component-reuse in the Module Build System.”

https://sgallagh.wordpress.com/2019/08/14/sausage-factory-modules-fake-it-till-you-make-it/

Identify all unique modules/streams from a collection of RPM files

Code Block
find "$DIR_WITH_RPMS" -name \*.rpm \
  -exec rpm -qp {} --qf '%{ModularityLabel}\n' \; \
  | grep -v '^(none)' | sort | uniq -c \
  | sort -nk1,1 \
  | tee unique_rpm_module_streams.txt

Anchor
reposync
reposync

Mirror the contents of a DNF repository, preserving all modules and package groups. 

The current state of CentOS 8 createrepo_c + modulemd-tools (EPEL8) allow us to:

(Works from CentOS 8.3 and CentOS 7.8, requires packages dnf and dnf-plugins-core )

An example of this mirroring a mounted CentOS 8.3 ISO’s AppStream repository:

Code Block
languagebash
PATH_TO_LOCAL_MIRROR=/path/to/Appstream
PATH_TO_SOURCE_REPO=/mnt/AppStream

dnf reposync \
  --download-metadata --downloadcomps \
  --download-path "$PATH_TO_LOCAL_MIRROR" \
  --repofrompath iso,"$PATH_TO_SOURCE_REPO" \
  --repoid iso

# Useful EL8-only options: --remote-time --norepopath

dnf reposync should probably be the only kind of modular-capable mirroring that on-site tools like unpack_dvd should use.

DNF repoclosure

The ISO build process (really the tar build process) runs repoclosure to make sure the packages on the ISO will be self-contained.

Code Block
dnf repoclosure --repofrompath iso,"$PWD" --repo appstream --repo baseos
Info

Judging by the bug report at https://bugzilla.redhat.com/show_bug.cgi?id=1547041, dnf repoclosure is not module-aware, in the sense that its resolver does not consider packages in all available modules/streams.

The current behavior only resolves module packages using default or enabled streams & profiles.

Clean DNF install --downloadonly with all deps

Make sure you enable/disable the repos to match what you intend for resolution; --config is an option, too.

Code Block
languagebash
DNF_DLONLY_TARGET_PACKAGE=httpd
DNF_DLONLY_INSTALL_ROOT=/root/fake-install-dir
DNF_DLONLY_PACKAGE_DIR=/root/downloadonly

dnf install --downloadonly \
  --setopt=install_weak_deps=False \
  --installroot="$DNF_DLONLY_INSTALL_ROOT" \
  --downloaddir="$DNF_DLONLY_PACKAGE_DIR" \
  --disablerepo=\* \
  --enablerepo=baseos \
  --enablerepo=appstream \
  --releasever=8 \
  "$DNF_DLONLY_TARGET_PACKAGE"

Notes:

  • An an --enablerepo= for each repo to consider during resolution.

  • Pointing to an empty --installroot= will cause dnf install --downloadonly to download EVERY dependency, including the packages for the baseos.

  • In this case, --setopt=install_weak_deps=False may be useful to ignore weak RPM dependencies and cut down on the download size.

Create initial repo with ursine modules

Code Block
mkdir -p $NEW_REPO_DIR/Packages/ursine
cp "${URSINE_PACKAGE_FILES[@]}" "$NEW_REPO_DIR/Packages/ursine/"
cd "$NEW_REPO_DIR"
createrepo_c .
  • Create a modular repo from packages that already have a common module header
    (warning)

    Status
    colourYellow
    titleWARNING
    The approach of taking N:S:V:C:A from the RPM headers below is a.) incomplete and b.) cannot be relied upon to be accurate or present—the ModularityLabel header can contain any String.

    (warning)

    Status
    colourRed
    titleDO NOT PRODUCTIZE
    Use another means to obtain N:V:S:C:A data; preferably from the repo itself (the data is sourced from the source repository’s {XXXXX}-modules.yaml.gz file)

    Code Block
    # Get the ModularityLabel from the RPMs
    ##########################################################
    #### UPDATE: DO NOT USE OR PRODUCTIZE THIS TECHNIQUE #####
    ##########################################################
    
    # Notes: 
    #  - All RPMs in the module must have a SINGLE and IDENTICAL ModularityLabel
    #  - The only thing required of this String is that it is unique to RPMs 
    #    from other modules (and different versions/contexts of this module)
    find "$DIR_WITH_RPMS" -name \*.rpm  \
      -exec rpm -qp {} --qf '%{ModularityLabel}\n' \;  \
      | sort -u
    
    ### WARNING: the ModularityLabel headers in RPMs build by EL and EPEL 
    ####         are (currently) in N:S:V:C format by convention, but in 
    #####        reality this string is arbitrary and cannot be relied upon
    #####        to reflect the actual source module's metadata.
    #####        The actual NVSCA/P data can *ONLY* be obtained from the 
    #####        original repo's metadata
    MODULE_HEADER=nodejs:10:8020020200707141642:6a468ee4
    
    

Documentation

Modularity direction

In early 2020, modularity development moved from Fedora’s Modularity WG to RedHat’s internal DNF team. Fedora development and governance was very open and public, which made it relatively easy to hunt down clarifying information about otherwise obscure details in pagure or the WG’s meeting logs.

Since RedHat took over, it has become very difficult to find up-to-date documentation or discussions, even (especially?) in BZ.

  • Why Modularity? An explanation and list of links.

  • RHEL 9 and modularity (18 June 2020, Fedora devel@ mailing list)

    • Discussion with official RedHat rep, after taking modularity from Fedora and moving its development to an internal DNF team.

    • There’s also a side-discussion in the middle about the “inevitability” of moving the modular “namespacing” (N:S?) into RPM’s headers/tags (like ModularityLabel), where several of Fedora’s former modular WG members chime in with some educational

Projects

More esoteric things that may come in handy in the future:

  • https://github.com/fedora-modularity/fus — 'This tool attempts to produce a viable "depsolved" collection of packages.

    Unlike earlier tools of this type, it understands the concept of modules and module streams and can incorporate them into the dependency solving algorithm.'

    • See NOTES in README on limitations

  • https://github.com/fedora-modularity/depchase — “[a] simple script to lookup runtime (and/or buildtime) requirements of package(s).”