34 Build Constraints #
Build constraints provide a way for the user to specify build worker parameters that the Build Service will use to decide which build workers are "qualified" to undertake a given build.
They are intended to be used for defining known, hard requirements for successfully building a given package (for example, disk space, memory, or certain CPU functionality).
34.1 Build Resource Usage and Statistics #
Ideally, the build constraints should be set to the minimum values that enable a build to succeed, because any higher setting than the minimum might unnecessarily reduce the number of build workers available to build the package.
Now, in the real world, we do not always have a precise idea of what the minimum values are for all the different build worker parameters that can be controlled using OBS build constraints. That need not deter us from setting build constraints, however. It is not necessary to wait for a build failure before setting minimum memory and disk space constraints, for example, because the OBS can give us reasonable values for memory and disk space based on a successful build.
Each successful build produces a file called _statistics
which can be examined to get details of the resources our build consumed. We
can then use this information to set appropriate values for the relevant build
constraints.
34.1.1 Displaying the build statistics #
The information from the _statistics
file can be
found in the Build Service web UI, by clicking on the build target we are
interested in and then clicking on "Show resources". Alternatively, the
_statistics
file itself can be downloaded from the
Build Service, either from the command line or using the OBS API.
34.1.1.1 Downloading the _statistics file using osc
#
Since the _statistics
file is a build artifact
produced by every successful build, it is always included among the build
artifacts downloaded by osc getbinaries
.
34.1.1.2 Downloading the _statistics file using the OBS API #
If you are using the OBS API, the relevant call is:
GET /build/{project_name}/{repository_name}/{architecture_name}/{package_name}/_statistics
When reviewing the build statistics, it's important to be aware that the
numbers can vary significantly from build to build depending on build
parallelism (e.g. make -j
).
34.2 Constraint Qualifiers #
In general, build constraints are specified in terms of a qualifier
and a value. The qualifier expresses "what" - the build worker parameter
that is to be constrained - and the semantics of the value depend on the
qualifier. If the qualifier takes a numeric value, it generally expresses
"how much", or, in other words, the minimum value (of that parameter) that a
given build worker must meet in order to fulfill the constraint. But there
are some qualifiers, such as hostlabel
, that take a simple
string value.
In the simplest cases, the qualifier is just a string. In more complicated cases, the qualifier can include subqualifiers and be modified by attributes. For example:
hardware:disk:size unit=G
The string hardware:disk:size
in this example means
"size, a subqualifier of disk, which is itself a subqualifier of hardware",
and unit=G
means "the value is expressed in units of
Gigabytes.
For a full treatment of constraint syntax, see Section 34.4, “Constraint syntax”.
34.3 Constraint scope and precedence #
Depending on the required scope, constraint qualifiers and their
values can be set at four different levels: instance, project, package, and
build recipe. Setting constraints at the OBS instance level is up to the
administrator of the OBS instance and is covered in the OBS Admin Guide.
Project-wide constraints are defined in the project configuration. Package
constraints are defined in a special file, _constraints
,
in the packages sources. It is also possible to insert constraints directly
in the individual build recipes (RPM spec files, Dockerfiles).
The constraints that are in force for a particular build are determined by merging all constraints defined at all levels: site, project, package, and build recipe. The merging is done in that order, with later settings overwriting earlier ones. That means, for example, that site-wide constraints can be overrided at any of the lower levels (project, package, build recipe), project-level constraints can be overrided at the package and build recipe levels, etc.
34.3.1 Project-scoped constraints #
Build constraints for an entire project, or for specific repositories within it, or for specific architectures within those repositories, are defined in the project config by adding lines as so:
Constraint: <QUALIFIER> <VALUE>
The QUALIFIER syntax is the same as used in RPM spec files, documented
in Section 34.4, “Constraint syntax”. Within the project configuration,
individual Constraint
lines can be enclosed in guards to
make a constraint apply only to certain architectures or repositories. For
example:
%ifarch ppc ppc64 ppc64le Constraint: hardware:cpu:flag power8 %endif
or
%if "%_repository" == "images" Constraint: hardware:disk:size unit=M 4000 %endif
Constraints set in project configuration affect not only the project itself, but also all projects that build against it.
For a full treatment of constraint syntax, see Section 34.4, “Constraint syntax”.
34.3.2 Package-scoped constraints #
Setting constraints at the package level is achieved by including
an XML file called _constraints
in the package sources.
The Build Service will attempt to validate this file when it is committed
(from the osc command line) or saved (in the web UI) to prevent invalid XML
from reaching the Build Service.
Here is a simple example showing what a _constraints
file might look like:
<?xml version="1.0"?> <constraints> <hardware> <physicalmemory> <size unit="M">2000</size> </physicalmemory> <disk> <size unit="G">5</size> </disk> </hardware> <sandbox>kvm</sandbox> <hostlabel exclude="true">SLOW_CPU</hostlabel> </constraints>
For details on constraint qualifiers and how to specify them
in a _constraints
file, see
Section 34.4, “Constraint syntax”.
34.3.3 Build recipe-scoped constraints #
At the build recipe level, constraints are set by embedding lines containing constraint qualifiers and values directly in RPM spec files or Dockerfiles. Such lines take the form
#!BuildConstraint: <QUALIFIER> <VALUE>
The #
character at the beginning of the line
causes the build recipe parser (RPM, Docker, podman) to ignore the
whole line, but in combination with the !
character
signifies an OBS-specific directive that is picked up by a pre-processor
within the OBS back-end.
Instead of specifying subqualifiers by nesting directives like in XML, colons are used. For example:
#!BuildConstraint: linux:version:min 3.0
In this example, "linux:version:min" is the constraint qualifier and "3.0" is the value.
As described for project-scoped conditionals, above,
#!BuildConstraint
lines can be guarded with various
conditionals to limit their effect to certain architectures
or, e.g., multibuild flavors.
Be careful when setting build constraints. The idea is to use them to express minimum values for the various parameters, below which builds are likely to fail. If you set a constraint too high, you risk reducing the pool of compliant build workers down to a very low number, or even to zero. (A low number of compliant build workers means your build may not start for a long time, and no compliant workers at all will cause the build to fail. See Section 34.5, “Constraint Handling” for details.)
By default, constraints applied to build workers regardless of
architecture. However, you may only be interested in certain architectures
and not in others. See Section 34.6, “Checking Constraints with osc
”
for how to get architecture-specific information on which workers satisfy
your constraints.
34.4 Constraint syntax #
This section describes the various constraint qualifiers and their syntax.
In general, it is important to understand that the syntax used will
differ depending on where the constraints are specified. When specified
via a _constraints
file, XML syntax is used, while a
different syntax is used when specifying constraints in a project
configuration or a build recipe (e.g., an RPM spec file). In this section,
both syntaxes are described for each qualifier.
When specifying constraints using XML syntax, attribute values must be
enclosed in double-quotes (unit="G"
,
exclude="true"
, etc.), while in project configurations and
build recipes the values must be given without
quotes (unit=G
, exclude=true
).
34.4.1 hostlabel
#
The "hostlabel" qualifier is any string which can be assigned to
build workers when starting the bs_worker
process. Since
its intended use is to restrict a build to specific workers, it should only
be used after consultation with OBS administrators who have detailed
knowledge of the build farm, and ideally as a negative definition, using the
exclude=true
attribute. Even then, keep in mind that
hostlabel
settings are not portable, since the always
specific to a given OBS instance and should therefore only be used as a last
resort in situations that cannot be addressed by any of the other qualifiers.
An example use case is to run benchmarks in a reproducible way.
Example for _constraints
file:
<constraints exclude="false"> <hostlabel>benchmark_runner</hostlabel> </constraints>
Example for project configuration:
Constraint: hostlabel benchmark_runner
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hostlabel benchmark_runner
34.4.2 sandbox
#
Defines the "sandbox" which is used for the build. The "sandbox" is the virtual environment in which the build takes place: each build worker is configured with a fixed sandbox type.
The configuration of this build constraint is typically left to OBS administrators, and there is usually no reason for a project or package maintainer to set it.
Example for _constraints
file:
<constraints> <sandbox exclude="true">kvm</sandbox> </constraints>
Example for project configuration:
Constraint: sandbox exclude="true" kvm
Example for RPM spec file or Dockerfile:
#!BuildConstraint: sandbox exclude="true" kvm
34.4.3 linux
#
This is a category of constraints specific to the Linux kernel, applied to the kernel running on the build worker.
34.4.3.1 version #
To require a specific Linux kernel version or version range.
Example for the _constraints
file:
<constraints> <linux><version> <min>3.0</min> <max>4.0</max> </version></linux> </constraints>
Example for project configuration:
Constraint: linux:version:min 3.0 Constraint: linux:version:max 4.0
Example for RPM spec file or Dockerfile:
#!BuildConstraint: linux:version:min 3.0 #!BuildConstraint: linux:version:max 4.0
34.4.3.1.1 min
#
Minimum kernel version.
34.4.3.1.2 max
#
Maximum kernel version.
34.4.3.2 flavor
#
A specific kernel flavor, such as default or smp (corresponding to the kernel packages kernel-default and kernel-smp, respectively).
Example for _constraints
file:
<constraints> <linux> <flavor>default</flavor> </linux> </constraints>
Example for project configuration:
Constraint: linux:flavor default
Example for RPM spec file or Dockerfile:
#!BuildConstraint: linux:flavor default
34.4.4 hardware
#
To specify that build workers must meet certain minimum hardware specifications or possess certain hardware features.
34.4.4.1 cpu
#
To require a specific CPU feature.
34.4.4.1.1 flag
#
CPU features which are provided by the hardware. On Linux, these can
be found in /proc/cpuinfo
. The flag element may be
used multiple times to require multiple CPU features.
Example for _constraints
file:
<constraints> <hardware><cpu> <flag>mmx</flag> <flag>sse2</flag> </cpu></hardware> </constraints>
Example for project configuration:
Constraint: hardware:cpu:flag mmx Constraint: hardware:cpu:flag sse2
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hardware:cpu:flag mmx #!BuildConstraint: hardware:cpu:flag sse2
EL0
is a special flag that that can be used on
hardware that only supports level-0 exceptions, such as certain armv8l
systems. This means that VMs or 32-bit kernels are not supported but
userland is supported. This flag can be used to block builds on such
hardware if no 64-bit kernel is available for a project.
Example for project configuration:
Constraint: hardware:cpu:flag exclude=true EL0
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hardware:cpu:flag exclude=true EL0
Additional flags are also reported for effective architecture level of the CPU. This includes the following flags:
power7
power8
power9
x86-64-v2
x86-64-v3
x86-64-v4
34.4.4.2 processors
#
To specify a minimum number of processor cores, virtual or physical
(i.e., as reported by /proc/cpuinfo
, provided by the build
worker and usable for the build
Example for _constraints
file:
<constraints> <hardware> <processors>4</processors> </hardware> </constraints>
Example for project configuration:
Constraint: hardware:processors 4
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hardware:processors 4
34.4.4.3 jobs
#
Each build worker is configured with a given default for the
JOBS
environment variable, which expresses how many parallel
build processes the worker should be able to sustain. To require a minimal
number of pre-confiured parallel jobs for the build, use this qualifier.
This specifies the number of parallel jobs the build tooling should use, so
even though it is under hardware
it is not actually a
hardware requirement.
Example for _constraints
file:
<constraints> <hardware> <jobs>4</jobs> </hardware> </constraints>
Example for project configuration:
Constraint: hardware:jobs 4
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hardware:jobs 4
34.4.4.4 disk
#
Hard disk specific constraints.
34.4.4.4.1 size
#
To specify a minimum amount of free disk space, below which a build on the worker will not be attempted.
Example for _constraints
file:
<constraints> <hardware> <disk> <size unit="G">4</size> </disk> </hardware> </constraints>
Example for project configuration:
Constraint: hardware:disk:size unit=G 4
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hardware:disk:size unit=G 4
34.4.4.5 memory
#
To specify a minimum amount of RAM memory that the worker must be equipped with.
34.4.4.5.1 size
#
To require a minimum memory size, including swap space.
Example for _constraints
file:
<constraints> <hardware> <memory> <size unit="M">1400</size> </memory> </hardware> </constraints>
Example for project configuration:
Constraint: hardware:memory:size unit=M 1400
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hardware:memory:size unit=M 1400
34.4.4.6 physicalmemory
#
Memory specific.
34.4.4.6.1 size
#
To require a minimal memory size. Swap space is not taken into account here.
Example for _constraints file:
<constraints> <hardware> <physicalmemory> <size unit="M">1400</size> </physicalmemory> </hardware> </constraints>
Example for project configuration:
Constraint: hardware:physicalmemory:size unit=M 1400
Example for RPM spec file or Dockerfile:
#!BuildConstraint: hardware:physicalmemory:size unit=M 1400
34.5 Constraint Handling #
What happens when someone sets a constraint so high, that the OBS instance does not have even a single worker that meets it? What happens when just a few workers satisfy all the constraints, but all of them are busy building packages, or have been taken down for maintenance? This section describes how the OBS handles these "low compliant worker" situations.
34.5.1 At least one compliant worker is available #
After determining which build workers satisfy the defined constraints for a given build, the scheduler checks if any of them are available to start building. If at least one is available, the build begins. The rest of this section describes the OBS's behavior when no compliant build workers are free to start building a given package.
34.5.2 More than half of existing workers satisfy the constraints #
The build will stay in state "scheduled" until one of the compliant workers becomes available. No further notification is set.
34.5.3 Less than half of existing workers satisfy the constraints #
The build will stay in state scheduled until one of the compliant workers becomes available. In addition, the dispatch details are set to tell the user that this build might take a long time to complete. The notification looks like this:
waiting for 4 compliant workers (4 down)
In this case, all four compliant workers are down. The notification
helps the user understand why the build is not starting. The dispatch
details can also be retrieved using the OBS API or, e.g., using the command
osc results -v
).
34.5.4 No existing workers satisfy the constraints #
If no worker can handle the constraints defined by the package or project, the build fails. In such cases, the build log will mention why the build failed:
package build was not possible: no compliant workers (constraints mismatch hint: hardware:processors sandbox) Please adapt your constraints.
34.6 Checking Constraints with osc
#
You can check the constraints of a project or package with the osc
tool. You have to be in an osc
working directory.
osc checkconstraints [OPTS] [REPOSITORY] [ARCH] [CONSTRAINTSFILE]
Either you give a repository and an arch or osc will check the constraints for all repository / arch pairs for the package. A few examples:
geeko >
osc checkconstraints
Repository Arch Worker ---------- ---- ------ openSUSE_Leap_42.2 x86_64 1 openSUSE_Leap_42.1 x86_64 1
If no file is given it takes the local _constraints file. If this file does not exist or the --ignore-file switch is set only the project constraints are used.
geeko >
osc checkconstraints openSUSE_Leap_42.1 x86_64
Worker ------ x86_64:worker:1 x86_64:worker:2
If a repository and an arch is given a list of compliant workers is returned.
Another command to verify a worker and display the worker information
is osc workerinfo
.
geeko >
osc workerinfo x86_64:worker:1
<worker hostarch="x86_64" registerserver="http://localhost:5252" workerid="worker:1"> <hostlabel>MY_WORKER_LABEL_1</hostlabel> <sandbox>chroot</sandbox> <linux> <version>4.1.34-33</version> <flavor>default</flavor> </linux> <hardware> <cpu> <flag>fpu</flag> <flag>vme</flag> <flag>de</flag> </cpu> <processors>2</processors> <jobs>1</jobs> </hardware> </worker>
It returns the information of the desired worker.