Constraint packages are a mechanism that will limit the versions of the software that can be installed on a system.
An important example of this is the Oracle Solaris 11.3 constraint package. It is simply called 'pkg:/release/constraint/solaris-11.3'.
When this constraint package is installed it prevents 'pkg update' from moving the system onto the next update release, ie Solaris 11.4, while still allowing installation of any SRUs for the current update release. This is important for when the beta and GA of Solaris 11.4 is present in the repository but you need a machine to stay on the 11.3 SRU release.
There will be a constraint package for each update release going forward, this allows you to control when you move to the update release will still allowing 'pkg update' for either SRUs or your own or 3rd party packages in your repositories.
Additionally such constraints can be used to ensure compatible versions of software packages are installed.
A simple example — you need to ensure that the installed libc is compatible with the kernel — if it is not that lots of horrible things will occur, most of these errors will be up front. More insidious is the installation of a library to support some application, you hope that the version of the library is the one that the application has been verified with, if not then strange and hard to diagnose things may occur.
Therefore we need to be able to ensure that when software is installed the correct version is installed, but additionally any dependencies are correctly selected now and in the future. The packaging system allows us to define exactly the versions of the various packages that can be installed by using a variety of dependencies within the packages.
The packaging system has a rich set of dependency types — these are all documented in pkg(5). We will have a look at a couple of them.
'require' — this says that a package is needed and, if a version is specified, the version to be installed will be equal or greater than that version.
pkg:/constA has depend fmri=pkg:/appB type=require depend fmri=pkg:/appC@1.1 type=require
This means when 'constA' is installed then 'appB' will be installed. The version of 'appB' will be the highest that will install on the system.
'appC' will also be installed but with a minimum version of 1.1. If 'appC' was available but only at version 1.0 then 'constA' would fail to install. Additionally if 'appC' versions 1.1 and 1.2 were available then version 1.2 would be installed.
'incorporate' — this says that if a package is to be installed then the version selected will be the version that satisfies this dependency. Note by itself this dependency DOES NOT cause a package to install, it is only specifying a version to use if the package is to be installed.
pkg:/constA has depend fmri=pkg:/appB type=require depend fmri=pkg:/appB@1.1 type=incorporate depend fmri=pkg:/appC@1.1 type=incorporate
When 'constA' is installed then 'appB' will be installed (due to the 'require' dependency) but it will be installed with version 1.1 due to the incorporate dependency.
The package 'appC' will not be installed because there is no 'require' dependency, however if it is installed later then the version to be installed will be 1.1.
Now one interesting, and important, aspect of the values associated in the above dependencies is that it is not required to specify the full version of the package.
Using the last example. If there were the following 'appC' versions available:
appC@1.1 appC@1.1.1 appC@1.2
The first two satisfy the 'incorporate' dependency and ultimately appC@1.1.1 will be installed as the pkg system assumes that missing version numbers are 0.
So what ? This allows us to build what we call constraint packages.
A constraint package is one that delivers, as part of its content, a set of dependencies that will use the 'incorporate' dependency and optionally the 'require' one. Such a package will ensure that only particular versions of packages can be installed on a system.
Again so what ? One simple use case is to define a package that will be prevent updates of the system from major version jumps while still maintaining the capability to update the system for simple package updates.
Initially, we have available:
appB@1.1.0 appB@2.0 appC@1.1.0 appC@2.0
A system installed with appB@1.1.0 and appC@1.1.0 when a 'pkg update' is run will update 'appB' and 'appC' to the 2.0 versions. This is potentially problematic because we have moved to major versions and those could deliver unwanted/unexpected changes.
Now if the vendor then released, as patch builds:
It would be nice to be able to update to those simply. We could specify the exact required versions on the command line:
pkg update appB@1.1.1 appC@1.1.1
This is awkward because it means we have to know the versions and type them in correctly and from an maintenance perspective simply makes life harder.
This is where a constraint package could be used. That is we have 'constA' (version 1.0):
pkg:/constA@1.0 has depend fmri=pkg:/appB@1.1 type=incorporate depend fmri=pkg:/appC@1.1 type=incorporate
Now installing this package onto the system now means we can do a
and we know that only the patch versions of 'appB' and 'appC' will be installed (assuming of course the 3rd digit represents a patch release).
As 'constA' has it's own version space we could actually decide at some point that constA@2.0 allows for the update of 'appB' and 'appC' to the versions 2.0. This means constA@2.0 can be released and it can contain:
pkg:/constA@2.0 has depend fmri=pkg:/appB@2.0 type=incorporate depend fmri=pkg:/appC@2.0 type=incorporate
Allowing for the system to update 'constA', 'appB' and 'appC' using the simple:
So how does Oracle Solaris use this capability ?
In the first instance the 'entire' package can be considered a high level constraint package as it defines, ultimately, what versions of software can be installed as it simply has a list of 'incorporate' dependencies.
As menationed previously Oracle Solaris, as of 11.3, provides a 'constraint' package that will prevent the system updating to a later version of the operating system, if it is available in the package repositories. Such a package will be delivered for each and every Solaris Update (called solaris-11.4, solaris-11.5 and so on).
A more advanced use of this capability is with 'Interim Diagnostic Relief' (IDR) packages. These packages deliver intermin bug fixes. An IDR delivers:
A constraint package: idrXYZ where XYZ is the number of the IDR. A set of packages being patched with a special version number.
IDR2903 has, within it's manifest:
set name=pkg.fmri value=pkg://solaris/idr2903@1 depend fmri=pkg:/email@example.com,5.11-0.175.3.15.0.3.0.2903.1 type=incorporate depend fmri=pkg:/firstname.lastname@example.org,5.11-0.175.3.15.0.3.0.2903.1 type=incorporate
Meaning that when IDR2903 is installed on a system then if either of the named packages are installed then they must be at the versions specified. If IDR2903 is installed on a system then if pkg:/system/header is later installed it will be at the version specified.
Constraint packages offer the ability to control the versions of the software on systems while providing for the ability to maintain the simple updating of the system.