Container contracts
Note Container contracts are available for devices running Supervisor versions >= 10.6.17. All prior versions will not enforce any contracts.
Container contracts are used to ensure the compatibility of a device when it comes to run a service. For example, container contracts may be used to ensure that the device is running a specific version of Supervisor or has a specific version of the NVIDIA Tegra Linux Driver Package (L4T).
Container contracts are enforced on the device via the device Supervisor. Each service can define the contract requirements that it enforces, and if a contract's requirements are not met, the release is not deployed to the device, unless it contains services labeled as optional. You only need to define a contract for services that you wish to enforce a contract upon. Currently, you may define contract requirements based on the versions of Supervisor and L4T.
Contract specification
Container contracts are defined as a contract.yml
file and must be placed in the root of the build context for a service. For example, the following multi-container fleet defines two services named first-service
and second-service
, each with a corresponding contract.yml
file.
.
├── docker-compose.yml
├── first-service
│ ├── contract.yml
│ └── Dockerfile.template
└── second-service
├── contract.yml
└── Dockerfile.template
contract.yml
is a YAML file containing type
, slug
, name
, and requires
keys, which define the requirements of the contract. The following example requires the device to be running a version of L4T equal to 28.2 for the contract to pass. For Supervisor versions < 10.10.0, L4T exact versions (either 28.2 or 32.2) must be specified.
type: "sw.container"
slug: "enforce-l4t-version"
name: "Enforce L4T Version"
requires:
- type: "sw.l4t"
version: "28.2"
type
should be set tosw.container
for container contracts.slug
should be a unique identifier to identify the contract.name
should be a descriptive name of the contract.requires
should be a list of requirements to be enforced by the contract defined bytype
andversion
keys. Validtype
keys may be found in the valid contract types table.
Note If type
, slug
, or name
are omitted, the build will fail.
Multiple requirements may be added to a single contract. The following example requires that the Supervisor version is greater than or equal to 10.6.17, the L4T version is greater than or equal to 32.2, and the device type is a Jetson Nano.
type: "sw.container"
slug: "enforce-supervisor-and-l4t"
name: "Enforce supervisor and l4t requirements"
requires:
- type: "sw.supervisor"
version: ">=10.6.17"
- type: "sw.l4t"
version: ">=32.2.0"
- type: "hw.device-type"
slug: "jetson-nano"
Valid contract types
Contract type | Description | Valid from |
---|---|---|
sw.supervisor | Device Supervisor version (specified as version ) |
10.16.17 |
sw.l4t | L4T version (specified as version ) |
10.16.17 |
hw.device-type | The device type as given by BALENA_MACHINE_NAME (specified as slug ) |
11.1.0 |
Note Any requires
type other than those listed in the valid contract types table will result in the contract being invalid.
When deploying a release, if the contract requirements are not met, the release will fail, and all services on the device will remain on their current release. This default behavior can be overridden by including an optional label in the docker-compose.yml file for the release. This optional label specifies which services with unmet requirements will be ignored, with all other services on the device updating to the new release. If there are any existing running services with unmet requirements, they will not be destroyed and continue running the prior release.
Should a contract fail, the dashboard logs will contain detail about the failing contract:
Could not move to new release: Some releases were rejected due to having unmet requirements:
Contracts: Services with unmet requirements: first-service
Note For fleets that contain a mixture of Supervisor and L4T versions, contracts may be used to ensure only compatible devices run specific services, as releases will only be deployed to those meeting the contract requirements.
Optional containers
By default, when a container contract fails, none of the services are deployed to the device. However, in a multi-container release, it is possible to ignore those services that fail the contract requirements with the other services being deployed as normal. To do so, we make use of the io.balena.features.optional: 1
Supervisor label to indicate which services should be considered optional.
In the docker-compose.yml
file, add the io.balena.features.optional: 1
to the labels list for each service you wish to mark as optional. In the following example, even if the first-service
container contract fails, the second-service
service will still be deployed (assuming it doesn’t have any failing contracts of its own).
version: '2'
services:
first-service:
build: ./first-service
labels:
io.balena.features.optional: '1'
second-service:
build: ./second-service
Note When using optional containers, if there are any existing running services of the failed contract, they will be killed and not continue running the old version.
Board support
Container contracts are supported on all devices where the Supervisor version is >= 10.6.17. If a device in the fleet does not support L4T, and the contract specifies an L4T requirement, the device supervisor will reject the release unless the contract is marked as optional. L4T support is available on the following boards:
- Aetina N510 TX2
- Auvidea J120 TX2
- CTI Orbitty TX2
- CTI Spacely TX2
- Nvidia blackboard TX2
- Nvidia D3 TX2
- Nvidia Jetson Nano
- Nvidia Jetson TX1
- Nvidia Jetson TX2
- Nvidia Jetson Xavier
- SKX2