Stephen Gallagher wrote:
One of the recurring themes in the ongoing Modularity threads has
been
that we've made references to the problems we're trying to solve, but we
haven't done a good job of gathering those requirements and use-cases into
a single place. To resolve this, I've written a (fairly long) blog post
describing the set of problems that we are trying to solve.
Unfortunately, the list of requirements is very long (IMHO, unreasonably
long), and some of the requirements are in conflict, as I am going to
explain point by point below. For some of the requirements, it is also
arguable whether they are really relevant, especially for Fedora. I am also
going to address those individually below.
I would also like to point out that none of the requirements you listed
actually require default streams, only modules for alternate versions (and
perhaps not even those), and that in fact the first requirement under
"Critical use cases for consumers" (that simplicity for users should trump
simplicity for packagers) is actually an argument against default streams.
Please note as well that these are goals. There are numerous places
where
the implementation of Modularity at the time of this writing is not yet
fully adherent to them.
Unfortunately, there are some requirements in your list that are mutually
exclusive (see below), so the implementation will never be fully adherent to
them, and in fact no implementation will. So some requirements will have to
go.
This is excellent for the maintainers of the distribution, because
it
allows them to test that everything works together as a cohesive whole. It
means that there’s one authoritative version to align to.
Users, on the other hand, are most concerned about solving their problem.
It matters less to them that the distribution is cohesive and more that
the tools they need are available to them.
It actually matters to users that the distribution is cohesive because they
want to use more than just one application, and those applications need to
work together. And this is the area where the design restrictions of
Modularity come into play.
The “Too Fast/Too Slow” problem is basically this: users want a
solid,
stable, reliable, *unchanging* system. They want it to stay that way for
the life of their application. However, they also want their application
to run using the set of dependencies it was designed for.
Do they really?
I want my applications to run using the dependencies that are part of the
distribution platform (and which, as a result, do not conflict with other
applications I may also want to install). If those dependencies are too new,
the application needs to be ported to the new versions. If they are too old,
then they probably need to be updated systemwide (assuming the newer version
is backwards-compatible, of course), as has been done with Qt more than
once.
Where that is not possible, the next best solution is to have a parallel-
installable compatibility library that provides the version required by the
application. The existing packaging guidelines for compatibility libraries
cover those pretty well: suffixed package name, the runtime packages must
not conflict (ever), the -devel packages should not conflict if it can be
avoided.
I do not care whether the library version is the exact version upstream
happened to have installed. It just needs to make the application work. And
I consider this making things work together with shared dependencies to be
the one value added by the distribution over the upstream software, i.e.,
the distribution's entire purpose.
If that doesn’t happen to be the same version (newer or older) as the
one
selected for the monolithic distribution, the user will now have to resort
to alternative means to get up and running. This may be as simple as
bundling a dependency or as drastic as selecting an entirely different
distribution that better fits their specific need.
However, Modularity does not provide a reasonable way to bundle dependencies
(that is not already possible in ursine RPMs), because as Zbigniew pointed
out, the bundled dependencies are never really private, but still pollute
both the package name namespace and the file system, conflicting with any
other version (ursine or from another module) of the same package.
The only way to prevent these conflicts is to resort to RPM-level conflict-
free bundling techniques such as static linking or the aforementioned
compatibility package pattern. But then you do not need Modularity to begin
with.
A few years back, the Product Management team inside Red Hat
performed a
large-scale survey of customers and potential customers about the user
experience of Red Hat Enterprise Linux. In particular, they asked about
their level of satisfaction with the software available from the
enterprise distribution and their opinion on these Software Collections.
Perhaps unsurprisingly, the overwhelming majority of respondents were
thrilled to have supported versions of software beyond what had shipped
with the base operating system. What the survey team did come away with
that was an epiphany was that the respondents generally did not care about
the parallel installability of the SCLs. For the most part, they
maintained individual userspaces (using bare metal, traditional
virtualization or containers) for each of the applications they cared
about.
And this is a very big assumption in the design of Modularity that just does
not apply in the Fedora world (and maybe not even in the CentOS world). I am
pretty sure that Fedora users typically do not maintain per-app userspaces.
It is a lot of maintenance effort to keep all those userspaces up to date
and secure. Instead, Fedora users want to install an arbitrary choice of
applications and not have them conflict. I know I do. (And, while off topic
here, the CentOS VPS I maintain is also set up like that, so this is not
limited to Fedora nor to desktop setups. All the server applications are on
one single virtual server. The host machine is a black box managed by the
hosting provider. So I administrate only one VPS userspace.)
Please keep in mind that the typical Fedora user is not an enterprise, but
an individual or a small company, where just throwing in dozens of
containers or VMs, let alone physical servers, is not going to scale.
The most common problem reported for Software Collections was that
using
them required changes to the applications they wanted to run. SCLs install
to a separate filesystem location from more traditional RPMs and
applications that rely on them need to know where to look for them. (In
SCL parlance, this is called “activating” the collection.)
The consequence of this relocation on disk is that users were unable to
take existing applications (either FOSS or proprietary) and simply use
them. Instead, they had to modify the projects to first activate the
collections. This was a consistent pain point.
It is clear that SCLs are also a suboptimal solution, which is why they were
never really accepted into Fedora.
The issue mainly boils down to SCLs violating (at least the spirit of) the
FHS by design. The other pain points (such as the requirement to "activate"
the collection that you pointed out) mostly follow from that.
Given this feedback, Red Hat came to the conclusion that parallel
installability, while nice to have, was not a critical user requirement.
But this is a non sequitur. The actual 2 issues are that 1. the
implementation of parallel installability caused problems and 2. the user
group that was queried happened to be the group not having a use for
parallel installability (which, as I explained above, is not generally
true).
I believe that any approach to parallel installability that is based on
relocating packages with minimal specfile changes (i.e., the SCL approach)
is also doomed to fail. Parallel installability should be done only where it
makes sense (i.e., it is pointless for, e.g., most leaf packages) and on a
case by case basis, taking into account the file system layout of the
individual package and honoring the FHS as well as possible without
introducing conflicts.
The compatibility libraries are good examples of that. With some trickery,
even -devel can be made to coexist (e.g., if the .so symlinks conflict, then
put them into a subdirectory of %{_libdir} and use a -L flag to link against
them). Other examples are the compatibility language interpreters or
compilers such as python* or gcc*. E.g., I have found the old RHEL/CentOS
approach of providing suffixed packages such as gcc4 with similarly suffixed
binaries (allowing to just use something like CC=gcc4 ./configure or
cmake -DCMAKE_C_COMPILER=gcc4 .) more convenient than the devtoolset SCL
approach.
Instead, the focus would be on the parallel *availability*. By
dropping
this requirement, it became possible to create a solution that allowed the
different versions to be swapped in and take over the standard locations
on the disk.
Unfortunately, this approach conflicts with your later requirement that
modules can require an arbitrary version of their dependencies. The
combination of the 2 requirements introduces version conflicts that can only
be solved through containerization, which is impractical for the reasons I
already pointed out above (maintenance workload, security) and elsewhere
(system integration issues etc.), and which also has to be done manually
with no help from the tooling (and I strongly doubt that the tooling will
ever be able to automatically containerize conflicting modules in the way
that was advertised in the early Modularity presentations at DevConf in past
years, i.e., that DNF would automatically install a module as a container
rather than directly if it detects that installing it directly would
conflict, a proposed feature which, I think, is really way beyond the scope
of DNF and completely unimplementable in practice).
Meanwhile in Fedora
Of course, it’s not just Red Hat — people in Fedora are also concerned
with solving this Too Fast / Too Slow problem for our users. Efforts
around this kicked off in seriousness with the Fedora.next initiative
<
https://fedoramagazine.org/fedora-present-and-future-a-fedora-next-2014-u...
and
Fedora Project Leader Matthew Miller’s “Rings
<
https://lwn.net/Articles/563395/>” talk at the first Flock conference in
2013.
And already back then, there were doubts about the implementability of those
proposals.
Critical use cases for consumers
First and foremost, our primary driving goal is to make it easy for our
users to understand and interact with alternative software versions. In
any instance where choosing between the packager experience and the user
experience is in conflict, we elect to improve things for the user.
As I wrote in the introduction, this is actually an argument against default
streams, which are mostly about packager experience, while making the user
experience less smooth (due to the leaky abstraction).
Standard Locations
In order to make deployment of users’ applications simpler, we need to
make sure that software can be installed into the common, expected
locations on the system. This includes (but is not limited to):
- Libraries must be installed to /usr/lib[64].
- Headers must be installed to /usr/include.
- Executables must be installed to a location in the default system
$PATH
- Other -devel functionality such as pkgconfig files must be
installed in their standard lookup locations.
- Installed services may own a well-known DBUS address.
- Services may own the appropriate standard TCP/UDP ports or local
socket paths.
*Requirement*: Installation must occur in the same locations as
traditional RPM software delivery.
I think this requirement is too rigid. While it usually makes perfect sense
to not muck around with leaf applications to make them parallel-installable
and honor the default upstream locations, I think it is perfectly fine (and
FHS-compliant) to (e.g.) install compatibility libraries to subdirectories
of /usr/lib(64) and/or /usr/include even if the default version RPM (and the
upstream build system) installs directly into /usr/lib(64) and/or
/usr/include, in order to achieve parallel installability. IMHO, for
libraries, being able to use all versions in parallel (including the -devel
packages!) is well worth the slight hassle of having to muck with -I and -L
flags to compile software against the non-default version.
For leaf applications, modules (which just replace the default version) are
a good fit. But so are Coprs, which are pretty good at replacing leaf
packages with different versions (even older ones, with Epoch), too. For
libraries, parallel-installable suffixed compatibility packages are the
better fit.
Don’t break the app!
It is very common for Fedora to update to the latest major version of
packages at each new semiannual release. This ensures that Fedora remains
at the leading edge of software development, but it can wreak havoc on
anyone trying to maintain a deployment on Fedora. If they are running an
app that is built for PostgreSQL 9.6 and Fedora switches to carrying
PostgreSQL 10 in the next major release, upgrading to that release may
break their app (possibly in ways undetectable by the upgrade process).
However, staying on an old version of Fedora forever has its own problems.
Not least of these is the problem of security updates: Once a release has
been out for about 13 months, it stops receiving errata. Moreover, new
releases of the Fedora platform may have other useful enhancements (better
security defaults, increased performance thanks to compiler improvements,
etc.).
*Requirement*: We need to allow users to “lock” themselves onto certain
dependencies as long as the packager is maintaining them. These
dependencies must continue to receive updates.
This sounds great in theory. In practice, packages are not isolated islands,
so you cannot in general just use the old application on the new system
libraries, and if the package is not a leaf, other packages in the
distribution may require the new version and not work with the old one
anymore. Sure, a lot of this can be solved given enough package maintenance
work, backporting (code and/or packaging) changes to allow the old version
to work on newer system libraries, and rebuilding dependent packages into
the module, but as others have already pointed out, manpower is already thin
enough as it stands, without this extra effort to keep old versions alive.
The "rebuilding dependent packages" approach also breaks down completely if
those same packages also need to be rebuilt by another module, because the
rebuilds will then conflict. It can be solved by putting the affected
packages into yet another, separate, module, but then that other module has
to be built against an exponential number of dependency version
combinations, as was already discussed.
*Requirement*: There must be appropriate and helpful UX for dealing
with
when those dependencies go EOL.
The modules going EOL will always completely screw over their users, no
matter the UX. But of course, old module streams cannot be maintained
forever with the manpower that we have. So those "old version" modules are
only delaying the inevitable.
Support the developers
Developers often want to build their applications using the
latest-and-greatest version of their dependencies. However, that may not
have been released until after the most recent Fedora release. In
non-Modular Fedora, that means waiting up to six months to be able to work
on it there.
*Requirement*: It must be possible to gain access to newer software than
was available at the Fedora release GA.
This is already possible in at least 3 ways:
1. If the newer dependency is backwards compatible, we can simply update the
system version. (See, e.g., Qt, KDE Frameworks, etc.)
2. Compatibility packages can also be used for forward compatibility. (E.g.,
we already had a qt4 package back when Qt 3 was still the default qt.)
3. The newer dependency, and applications depending on it, can be provided
through a Copr.
And failing all three, up to 6 months is not really all that long to wait to
begin with.
Additionally, Dev/Ops people are rapidly switching to a new paradigm
of
development and deployment (containers) to solve the above issue. However,
most containers today are retrieved from public repositories. The public
repositories are generally user-managed and have not been verified and
validated for security.
*Requirement*: Provide a mechanism for building *trusted* container base
and application images with content alternatives.
A trusted container should contain a tested combination of packages (as
provided by a non-modular distribution release), not an untested arbitrary
mix&match of potentially incompatible (at runtime) versions.
Keep it updated
It’s not enough that other versions of software are available to install.
They also need to be kept up to date with bug fixes and security updates.
In non-Modular Fedora, users had the ability to force DNF to lock to a
specific RPM NEVRA, but they wouldn’t get updates from it.
*Requirement*: Alternative software must receive be able to recieve and
apply updates.
Of course. Version-locking old package builds is never a reasonable long-
term solution. Old packages need to come from somewhere that can provide
bugfix and security updates, such as a module (relying on DNF automatically
preferring the modular version to the ursine one) or a Copr (with bumped
Epoch).
Make it discoverable
Having alternative versions available is important but not sufficient. It
is also necessary for users to be able to locate these alternatives. Some
of our early explorations into this area failed this ease-of-use test
because they require the user to have knowledge of external sites and then
to search those sites for what they think they want.
*Requirement*: Users must be able to discover what alternative software
versions are available with tools that are shipped with the OS by default.
Ideally, these should be the same tools that they are already comfortable
with.
This is a requirement that I also consider more rigid than really necessary.
Is it really unreasonable to require locating a Copr to get a non-default
version of something? The default version is the default for a reason, so
IMHO it should be fine if that is the only version shown out of the box, by
default.
Don’t break existing package management workflows
Users are slow to adapt to changes in the way they need to behave.
Requiring them to learn a new set of commands to interact with their
system will likely result in frustration and possibly exodus to other
distributions.
*Requirement*: It must remain possible to continue to operate with only
the package management commands used in traditional Fedora. We may provide
additional commands to support new functionality, but we must not break
the previous workflow.
This is a noble goal, but when the bolted-on functionality modifies the
behavior of the existing tools enough to be confusing (or outright broken),
the whole masquerade breaks down. E.g., that is what happened with the
libgit2 upgrade path issue, where the magic enablement of the default stream
put DNF in a state that was not automatically upgradeable to Fedora 30,
something that would never have happened without the module and default
stream handling that was added to DNF under the hood.
*Requirement*: Existing automation tools such as anaconda’s kickstart
and
Ansible must continue to work.
In principle, the same considerations also apply in this case, though in
this case, I am, at this time, not aware of any concrete issues caused by
the module support in those tools.
Critical use-cases for packagers
Dependencies
Because very little software today is wholly self-contained, it must be
possible for Modules to depend on each other.
*Requirement*: There must be a mechanism for packagers to explicitly list
dependencies on other software, including alternative versions. This
mechanism must support both build-time and run-time dependencies.
Unfortunately, this requirement is inherently incompatible with the design
decision to not support parallel installation, as pointed out above. Users
(including me) will want to install different unrelated applications on the
same system (in the same userspace, not in a container, see above) without
running into random version conflicts (because those applications that look
completely unrelated to the user actually happen to transitively depend on
different versions of one particular module).
At package level, this kind of issues was known as "RPM Hell". It was an
incredibly frustrating user experience. It was (mostly) solved by
centralizing repositories and discouraging the replacement of system
packages by third-party repositories. (Better tooling, which automatically
resolves dependencies, taking them from the correct repositories rather than
some random place the user found with a search engine, and reports why a
conflict is not resolvable, also helped to some extent. But what helped the
most with minimizing those issues was the sane repository structure.) But
now, Modularity introduces something like repositories within a repository
and brings these conflicts back.
In my view, this is the biggest design flaw in Modularity.
Alternative dependencies
Some software is very restrictive about which dependencies it can work
with. Other software may work with several different major releases of a
dependency. For example, a user may ship two Ruby-based web applications,
one which is capable of running on Ruby 2.5 and the other that can run on
either Ruby 2.5 or Ruby 2.6. In non-modular Fedora, only one version of
Ruby would be available. If the system version was 2.5, then both
applications could run fine. But if in the next release of Fedora the Ruby
2.6 release becomes the system copy, one of those applications will have
to be dropped (or patched) to work with it.
Well, in my view (and as I have already pointed out above), making things
work together, patching them where necessary (and using compatibility
packages where that is not possible), is really the whole point of a
distribution.
*Requirement*: It must be possible to build software that can be run
against multiple versions of its dependencies.
*Requirement*: The packaging process for creating software that supports
multiple versions of their dependencies must not be significantly more
difficult than packaging for a single dependency.
As more and more things become modules, there is concern that such things
will grow into an unbounded matrix. For this, we need to establish
policies on when the use of alternative dependencies is preferable or when
it is better to constrain it to a single version or small set.
See, you point out yourself why the two requirements above are problematic.
You end up with exponentially many combinations of dependencies. Supporting
them all definitely does not scale. But supporting only a subset will
inevitably break things for users who, due to hard requirements in other
packages, need a different subset. (And that is assuming that the hard
requirements do not already by themselves conflict.)
Hence, I think creating modules (which are by design not parallel-
installable with different versions of themselves) for non-leaf packages is
a very bad idea and will inevitably lead to breakage and conflicts.
The only 2 ways to prevent those conflicts are those that I have already
pointed out: make the applications work with a common default version of the
dependency (by patching it if necessary) or ship a parallel-installable
compatibility package of the dependency for the non-default version.
*Requirement*: Packaging guidelines need to provide advice on when to
use
multiple alternative dependencies or to select a single one.
As a result of the above, the advice provided under this requirement will
always be wrong one way or the other, you only get to choose between an
unmaintainable combinatorial explosion or broken user systems.
Managing private dependencies
When a person decides that they want Fedora to carry a particular package
and decides to do the work to accomplish this, it is not uncommon to
discover that the package they care about has additional dependencies that
are not yet packaged in Fedora. Traditionally, this has meant that the
packager has needed to package up those dependencies and then continue to
maintain them for anyone who may be using them for other purposes. This
can sometimes be a significant investment in time and energy, all to
support a package they don’t necessarily care about except for how it
supports the primary package.
But this is part of the reality of a community distribution. If your package
depends on libfoo, then you obviously have to package libfoo. So why should
others then not be allowed to benefit from libfoo for other purposes, just
because those purposes do not happen to be the one purpose you cared about?
You can always ask other packagers to comaintain libfoo if it makes sense
(e.g., because their package uses libfoo in a different way than yours).
(E.g., I comaintain kpmcore with the kde-partitionmanager (KPM) maintainer
because I maintain Calamares, which also depends on kpmcore.) But why should
they be discouraged or banned from using your libfoo (and, worse, encouraged
to bundle their own private libfoo build in their module, which then causes
conflicts when users want to use both modules)? (E.g., what should I have
done as the packager of Calamares if kde-partitionmanager (KPM) had been a
module and the kpmcore library private to that module?) It just does not
make sense.
IMHO, private dependencies should be a definite non-goal in Fedora.
Build-time Dependencies
Sometimes, a package is needed only to build the software and is not
required at run-time. In such cases, Modularity should offer the ability
to keep those build-time dependencies entirely private and not exposed to
the Fedora Package Collection at large.
But WHY? The package may well not be required at runtime by your package,
but that does not mean end users will not need it, if only to compile other
packages.
Compiling software is definitely a valid use case of a Free Software
distribution. We ship compilers, -devel subpackages, etc. for this very
purpose, and we do not do that for fun, but because we actually have users
who want to compile software. Developers are explicitly one of the personas
for Fedora Workstation, and of course developers need to compile software!
But in the Free Software world, unlike the proprietary world, even non-
developer users may end up compiling software themselves for various
reasons.
In addition, build-time dependencies may also be useful for other purposes,
e.g., you may consider some LaTeX package to be a build-time-only tool used
during the building of your package's documentation, but I might actually
want to use that package in my scientific documents.
*Requirement*: Build-time only dependencies for an alternative
version may
be excluded from the installable output artifacts. These excluded
artifacts may be preserved by the build-system for other purposes.
As a result of the above, this should be an explicit non-goal.
*Requirement*: All sources used for generating alternative versions,
regardless of final visibility, must be available to the community for
purposes of modification and reproducibility.
And this requirement actually conflicts with the preceding one. The best way
to make those sources available is as part of the SRPMs of the regular
repository, and the best way to ensure easy reproducibility is to actually
also ship the binaries there. If I want to modify or reproduce your build of
bar that depends on libfoo, why should I first have to track down the libfoo
sources from some hidden side repository and then rebuild libfoo from source
before I can actually compile bar, which is the only thing I actually wanted
to compile?
Defining the public API
Similarly, there are times when an application the packager cares about
depends on another package that is required at runtime, but sufficiently
complex that the packager would not want to maintain it for general use.
(For example, an application that links to a complicated library but only
uses a few functions.)
As already explained above, it is then standard practice in a community
distribution that somebody needs to bite the bullet and just maintain this
library with no strings attached. Comaintainership (where different
comaintainers are most familiar with different parts of the library) can be
a solution if there are suitable candidates, but even if there are none,
there needs to be at least one maintainer.
In this case, we want there to be a standard mechanism for the
packager to
be able to indicate that some of the output artifacts are not supported
for use outside this module. If they are needed by others, they should
package it themselves and/or help maintain it in a shared place.
*Requirement*: Packagers must be able to encode whether their output
artifacts are intended for use by other projects or if they are
effectively private to the alternative version. Packagers must also have a
way of finding this information out so they understand what they can and
cannot rely on as a dependency.
Such arbitrary restrictions of the "supported" interface are both a very
anti-social thing to do and a conflict waiting to happen. The latter because
(as already pointed out above) even "private" packages still share the
installation's global package name namespace and file system, so if every
module bundles their own copy of libfoo with a different "supported" subset,
those copies will conflict on end user systems.
In addition, Fedora does not provide commercial support, and as such,
deciding whether to "support" some functionality or not is pretty
meaningless in Fedora.
Hence, no, I do not agree that packagers should be enabled and allowed to
encode such arbitrary "support" restrictions in Fedora.
Use-case-based installation
Since the earliest days of Linux, the “package” has been the fundamental
unit of installable software. If you want to have some functionality on
the system, you need to learn the name of the individual packages that
provide that functionality (not all of which are named obviously). As we
build modules, one of the goals is to try to focus installation around
use-cases rather than around upstream projects. A big piece of this is
that we want to have a way to install a subset of a module that supports
specific use-cases. A common example being “server” and “client” cases.
*Requirement*: It must be possible to install a subset of artifacts from
an alternative version. These installation groups should be easily
discoverable.
*Recommendation*: Installation groups should be named based on the
use-case they are intended to solve. This will provide a better user
experience.
This functionality is almost orthogonal to the parallel availability of
alternative versions. Yet, both are being mingled together in "modules".
We already have two existing mechanisms to encore super-package units: comps
groups and metapackages. Now modules add a third, incompatible way to
provide this same information, and one which is mixed with a choice of
version.
Why can enabling modules not just work like enabling repositories (i.e., it
selects which package versions you get if you install the packages, but does
not actually install anything) and the actual groups be kept in comps with
all the existing ones? I consider the fact that selecting a version of
something also forces a specific set of packages on me, which I may or may
not actually want or need, very confusing. E.g., I may want to enable a
specific stream of a module for git and only get git-core from it and not
whatever git* packages are considered part of the smallest "use case" by the
module maintainer. (In the worst case, there may only be unsuitable "use
cases", such as a "client" use case including some GNOME GUIs that I have
no
use for on my KDE Plasma system, and a "server" use case that includes some
server packages that I have no use for either.)
Lifecycle isolation
Another of the major issues faced by Fedora is maintaining a release
schedule when all of the components within it follow vastly differing
schedules. There are two main aspects to this problem:
- A major version of a popular piece of software is released just after
a Fedora release, so it doesn’t land in Fedora for six months.
- Some software does frequent major revisions (Django, Node.js, etc.)
and swapping them out every six months for the latest one means that
dependent projects are constantly needing to adapt to the new breakage
or find alternative mechanisms for retaining the older, working version
- Some software does not handle multiple-version upgrades (Nextcloud,
for example). Attempting to go from version 15 to verison 19 requires
first upgrading through 16, 17, and 18.
*Requirement*: It must be possible for new alternative versions of
software to become available to the Fedora Package Collection between
release dates.
As already mentioned above, this is already possible in at least 3 different
ways (pushing the new version as an official update, creating a versioned
compatibility package, providing the alternate version in a Copr).
For leaf applications, compatibility packages are not really a good fit.
There, a non-parallel-installable stream selection (as provided by the
module streams) indeed makes more sense. But this was already possible
through Copr.
For frameworks such as Django and Node.js, the lack of parallel
installability is an issue, because it means that some applications will not
be coinstallable because they depend on different versions of the framework.
So modules are a poor fit for those. Compatibility packages (i.e., parallel-
installable packages with version-suffixed names) should be used instead. I
do not see why I should not be allowed to install two unrelated web
applications on the same web server just because they depend on different
versions of Django or Node.js.
*Requirement*: It must be possible for alternative versions of
software to
go end-of-life during a Fedora release. This does not mean that the
software must disappear from the repositories, merely that an assertion
exists somewhere that after a certain date, the package will not receive
updates.
I think that this is a very bad idea, because it is really confusing for
users, and I am not alone in thinking like that, because this idea was
actually already voted down by FESCo. (FESCo decreed that modules are only
allowed to go EOL with a Fedora release EOL.)
*Requirement*: For alternative versions whose lifecycle will
continue
through at least part of the next Fedora release, it must be possible to
upgrade from one release to the next and remain with the fully-compatible
version.
The question, though, is whether that really needs to be the default action
or whether it would not make more sense to upgrade to the new default
version by default and require an explicit downgrade to retain the old
version (and all the more so if the module maintainer is not even going to
maintain that old module stream for the lifetime of the new Fedora release).
I would argue for the latter.
Third-party additions
Some third-party add-on repositories (particularly EPEL) have been limited
in the past by relying on the system copies of packages in the base
distribution of the release. In the particular case of EPEL, little can be
done to upgrade these system copies. In order to be able to package much
of the available FOSS software out there, it may be necessary to override
some of the content shipped in the base system with packages known to work
properly.
*Requirement*: It must be possible for third party repositories to create
alternative versions that override base distribution content at the user’s
explicit choice.
*Requirement*: It must be possible for third party repositories to create
alternative versions of software that exist in the base distribution.
It is not clear to me what the difference between those two requirements
would be. They sound like two different wordings for the same thing to me.
Either way, this is all about bypassing the EPEL policy of not replacing
core RHEL packages. But that policy was enacted for a reason. Doing this for
non-leaf packages can be quite dangerous, potentially breaking both software
included with RHEL and third-party software depending on the original RHEL
version of the library. As long as the replacement only happens at a user's
explicit request, the potential for damage is limited, but the point of
replacing a non-leaf package with a different version is usually to enable
some application depending on that version, so that application will then
drag in the non-default version of the non-leaf package (with the potential
for breakage that that entails).
E.g., the KDE packager for EPEL 8 wants to upgrade Qt to a new version
through a module. Installing the Plasma module will then also upgrade the
system Qt. One can usually get away with that due to Qt's backwards
compatibility, but some packages depend on private APIs or on undocumented
behaviors and will thus not work with a new Qt. If those packages are not
all rebuilt in the module upgrading Qt, they will break. So I am not
convinced that upgrading core RHEL packages through modules is any safer
than just upgrading them through the main repository. If it is decided that
upgrading Qt and rebuilding those packages known to need a rebuild is safe
(after all, this is routinely done in Fedora, though of course Fedora is
less concerned about third-party binary-only software than RHEL), it should
be done EPEL-wide (or ideally in RHEL proper, but that is out of the
community's control), whereas if it is ruled to be unsafe, it should just
not be done anywhere.
For Fedora proper (i.e., outside of EPEL), this requirement is not
applicable at all, because we are speaking about a specific EPEL policy.
Reduce duplication in packaging work
There is plenty of software out in the wild that maintains compatibility
over time and is therefore useful to carry in multiple releases of Fedora.
With traditional packaging, this means carrying and building separate
branches of the packages for each release of Fedora. In the case of
software “stacks” which are tightly bound, this means also manually
building each of its dependencies in each release of Fedora.
*Requirement*: It must be possible to build multiple component software
packages in the same build process.
*Requirement*: It must be possible for the packager to specify the order
in which packages must be built (and to indicate which ones can be built
in parallel).
*Requirement*: It must be possible to be build for all supported platforms
using the same specification and with a single build command.
These make sense as nice-to-haves (I would not call them requirements), but
they all really call for better packaging automation, not Modularity. We do
not need a new delivery mechanism (modules) to achieve that. This can all be
done manually (using git fast-forwards, chain builds, buildroot overrides or
side tags, etc.) now, what is missing is a way to get it done in one command
and directly from git master (to avoid having to fast-forward the branches
each time). But that mechanism can be purely a build-time thing and need not
affect the way the packages are delivered to our users at all.
Non-Goals
Parallel installability
As mentioned in the Background section, the goals of Modularity are
specifically to *not* implement parallel-installability. We recommend
instead that users should rely on other mechanisms such as virtualization
or containerization to accomplish this.
As explained above, that may work for enterprise RHEL customers, but is not
a workable approach in the Fedora world.
If parallel-installation is unavoidable, then Modularity is not the
correct tool for this job.
Agreed, but this essentially disqualifies Modularity for any non-leaf
package, and even for some rare leaf packages (e.g., where old versions are
needed to read old documents).
Arbitrary stream switching
Module streams are intended to be compatible update streams. That means
they must follow the same rules regarding RPM package-level updates within
the stream. By definition, two streams of the same module exist because
upgrades (or downgrades or cross-grades…) are not capable of being done in
a safe, automated fashion.
That does not mean that stream switching should be impossible, but it does
mean that we will not build any tools intended to handle such switching in
a generic manner. Stream switches should be handled on a module-by-module
basis and detailed instructions and/or tools written for each such case.
Sure, a new module stream is by definition incompatible in some way, or it
would not make sense to create a different stream to begin with, I follow
you so far. But in Fedora, we already have well-defined points at which such
incompatibilites may occur: Fedora releases. (I concede that this is more
fluid in the RHEL world where, due to their extremely long lifetime, the
major releases get "update releases" that try to be fully compatible, but
occasionally contain somewhat incompatible changes. But here, we are talking
about Fedora only.) Therefore, I think that it is entirely reasonable for
Fedora release upgrades to default to upgrading to the new release's new
default version of the packages and modules. That would of course only be
the default, it would always be possible to downgrade to a specific module
stream, or ideally to explicitly exclude it from being upgraded during the
upgrade to begin with. But I do not see why the distro upgrade not upgrading
the module needs to be the default behavior.
Kevin Kofler