-
Notifications
You must be signed in to change notification settings - Fork 380
VIP20: Varnish ABI and packaging
Make Varnish upgrades safe when VMODs are installed from packages.
Why would anyone want unsafe upgrades? :)
This VIP focuses on RPM because its authors has little knowledge of DPKG, but he is convinced that similar mechanisms exist. There are no packages officially supported outside of the linux world, which is nice for the VIP author because he wears linux blinders that prevents him from seeing the other platforms.
With RPM, dependency management is not limited to package names (eg. Requires: varnish >= x.y) and instead RPM operates on capabilities or virtual dependencies. For example, Requires: java >= 1.8
could bring any package that Provides: java = 1.8
(or later versions).
You can list everything that a package provides:
$ rpm -q --provides java-1.8.0-openjdk | grep 'java ='
java = 1.8.0
A more familiar example:
$ rpm -q --provides varnish-devel | grep pkgconfig
pkgconfig(varnishapi) = 5.1.3
We, as the Varnish project, don't specify that we provide a "pkgconfig" capability in the varnish-devel package. This is done by RPM, the installation is scanned during the package build and the presence of the varnishapi.pc
file in the right directory is enough for RPM to add the capability.
RPM ships with more "auto provide" scripts than just pkg-config
files detection. One of them analyzes ELF binaries and can see the installed libraries and their soname. There is also an "auto require" side of it, for what's dynamically linked at build time.
For example, on Fedora 26 there is a varnish-libs package:
$ rpm -q --provides varnish-libs | grep ^lib
libvarnishapi.so.1()(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.0)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.1)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.2)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.3)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.4)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.5)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.6)(64bit)
libvmod_directors.so()(64bit)
libvmod_std.so()(64bit)
It can obviously also take symbol versioning into account. It's not very useful to expose VMODs, but if we added the possibility to link VMODs to others (wink wink) it would come in handy too. Except that we build VMODs without any version at all, but that's a different discussion.
And since the varnish package ships with utilities, a natural dependency exists:
$ rpm -q --requires varnish | grep varnish # look again, this time it's --requires ;)
config(varnish) = 5.1.3-2.fc26
libvarnishapi.so.1()(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.0)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.1)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.2)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.3)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.4)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.5)(64bit)
libvarnishapi.so.1(LIBVARNISHAPI_1.6)(64bit)
varnish-libs(x86-64) = 5.1.3-2.fc26
There is an explicit dependency to the varnish-libs package, but only because it was added manually, it is not needed. Installing varnish would always bring varnish-libs, but it's better to ensure that we bring the same exact version:
https://src.fedoraproject.org/rpms/varnish/blob/f26/f/varnish.spec#_50
The link shows:
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
Basically, a strict dependency for the varnish-libs package for the same exact version on the same exact architecture (isa: instruction set architecture, mostly for x86 repositories shipping both 32bit and 64bit packages).
While this works great for a VUT-like package, or basically anything that relies on libvarnishapi
, the story is different for VMODs.
Before the "How?" section, let's recap how VMODs have related to the Varnish ABI over time.
VMODs were built against the Varnish source tree and varnishd
would only load them if the commit shortened hash matched.
The required headers are installed alongside Varnish, and shipped by RPM varnish-devel and DPKG varnish-dev packages. VMODs can now be built against a regular Varnish installation (provided that development files are present) and pkg-config
helps us find where the useful bits are.
VMOD authors have full access to libvarnish
because varnishd
already links to it. As such, they have access to all symbols, even those not exposed when the symbol version script (libvarnish.map
) is in effect. Utility authors have access to some libvarnish
symbols via libvarnishapi
when the version script is in effect.
The result is satisfying for libvarnishapi
consumers.
VMOD authors also have full access to varnishd
symbols, but an unclear subset of those symbols are blessed: the Varnish RunTime. It consists in symbols accessible to VCL once turned into C by (excluding inline C).
A new VRT version is introduced, VMODs will now include the VRT major.minor in their metadata so that varnishd
can reject a VMOD when its target is not compatible. However this was implemented with two flaws:
- building Varnish from master falls back to a commit shortened hash match
- VMODs couldn't express VRT vs strict ABI compliance
For an example of the master branch problem: https://github.com/varnish/varnish-modules/issues/70
We maintain an incomplete list of the reasons behind minor or major bumps in vrt.h
.
libvarnish
is statistically linked into its consumers.
An $ABI
stanza is added for VCC descriptors. It lets VMOD authors express their level of compliance, but the VRT scope is still unclear. The rule of thumb being whatever symbols pure VCL has access to and associated data structures.
See https://github.com/varnishcache/varnish-cache/pull/2330
The result is a safer upgrade path: Varnish could still upgrade and become incompatible with installed VMODs, but VMODs requiring a strict ABI compliance would no longer load based on the fact that VRT major.minor is still compatible with what they expect. VMODs built before 5.2 would get strict or VRT compliance depending on how Varnish was built as explained in the Varnish 4.0.1 section.
There is an ongoing effort to clearly define the scope of VRT, and possibly a new "cache" scope in-between VRT and strict (full ABI) compliance.
As of Varnish 6, supported Varnish packages will expose virtual provides.
For RPM packages:
Provides: varnishd(abi)(x86-64) = sha1
Provides: varnishd(vrt)(x86-64) = x.y
For Deb packages:
Provides:
varnishd-abi-sha1
varnishd-vrt (= x.y)
This means that someone packaging VMODs as Debs or RPMs can manually adjust requirements to ensure that a package upgrade doesn't occur if compatibility is no longer guaranteed. For example, a VMOD relying strictly on symbols and data structures covered by the VRT version could have the following requirements:
Requires: varnishd(vrt)%{?_isa} >= 6.2
Requires: varnishd(vrt)%{?_isa} < 7
Assuming that the package was originally built against a varnish package exposing Provides: varnishd(vrt) = 6.2
.
The next step to complete this VIP would be to ship an RPM find-provides
script (and the Deb equivalent) to automatically require either strict ABI or loose VRT compatibility based on metadata found in installed VMODs. Even better, as this is currently done outside of Varnish itself and lives in pkg-varnish-cache
, move the ABI detection in Varnish so that other downstream distributors could implement a similar mechanism for package managers that support it.