Checkpoint Releases
Some releases cannot be skipped. A release may migrate data to a new format, change the partition layout, or update the bootloader, and later releases may rely on that change. A device that jumps from an older version straight to a newer one would miss the change. Checkpoint releases ensure that every device passes through such a release, no matter how far behind it is.
Checkpoints are an optional feature of an integration. Integrations that
support them list checkpoints in the capabilities of their status. Other
integrations ignore base versions and install the target directly, so make sure
that all devices of a package support checkpoints before relying on them.
Base Versions
Each package version can name a base version, a version of the same package that a device must have run before it installs this version. A version that serves as the base of other versions is a checkpoint. Versions without a base have no such requirement.
For example, release 1.4 of acme/gateway-os converts devices to a new
partition layout, which all 2.x releases require. All 2.x releases therefore
name 1.4 as their base. Later, release 3.0 drops support for an old data
format, which 2.2 migrates, so all 3.x releases name 2.2 as their base:
| Versions | Base |
|---|---|
1.2, 1.4 | none |
2.0, 2.1, 2.2 | 1.4 |
3.0, 3.1 | 2.2 |
The checkpoints form a chain: the 3.x releases require 2.2, which requires
1.4. Bases belong to the releases themselves, so they apply however a device
receives its desired state.
Planning the Update Path
On each check, the integration decides which version to install next. A device can install a version directly if the version’s base is the running version, or if both versions have the same base. Two versions without a base count as having the same base. In both cases, the device has already passed through all checkpoints that the version requires.
If the device cannot install the target directly, the target’s base becomes the candidate, and the device applies the same rule to it. The device follows the chain of bases until it finds a candidate that it can install directly. If the chain ends without such a candidate, there is no update path.
next_version(current, target):
if current == target:
return nothing to install
candidate = target
loop:
if base(candidate) == current or base(candidate) == base(current):
return candidate
if base(candidate) is none:
return no update path
candidate = base(candidate)
Each step is a complete update: the device installs the version, reboots into
it, and commits it. On its next check, the device plans again from the version
it now runs. For example, a device running 1.2 with the target 3.1 takes
three steps:
| Running | Candidates | Installs |
|---|---|---|
1.2 | 3.1 (base 2.2), 2.2 (base 1.4), 1.4 (no base) | 1.4 |
1.4 | 3.1 (base 2.2), 2.2 (base 1.4) | 2.2 |
2.2 | 3.1 (base 2.2) | 3.1 |
The same rule covers other situations:
| Running | Target | Result |
|---|---|---|
2.0 | 2.2 | Installs 2.2 directly, as both have base 1.4. |
2.1 | 3.1 | Installs 2.2, then 3.1. |
3.1 | 3.0 | Installs 3.0 directly, as both have base 2.2. |
3.1 | 2.1 | No update path. |
During a multi-step update, targetVersionId holds the configured target, and
nextVersionId holds the version of the current step. For example, during the
second step of the update from 1.2 to 3.1:
{
"capabilities": ["checkpoints"],
"currentVersion": "1.4",
"currentVersionId": "pkg_v_9pVnC4hTx6QbWm8KfLs2Rd",
"targetVersionId": "pkg_v_Bf4kTzR8wq2NcYh6mVxJ3p",
"nextVersionId": "pkg_v_3sLgP9eKd7XwHn2QaRt5Zm",
"state": "installing"
}Each step is a separate attempt under the retry policy and a separate activation under the maintenance window.
Going Back
The planning rule allows going back to an earlier version with the same base,
such as from 3.1 to 3.0. Going back across a checkpoint, such as from 3.1
to 2.1, would undo changes that later releases rely on, and the planning rule
finds no update path. In that case, the device keeps its version and reports
the state blocked with the code no-update-path.
Nexigon does not restrict going back between versions with the same base. This
includes going back from a checkpoint to an earlier version with the same base,
such as from 1.4 to 1.2, which would undo the conversion of the partition
layout. If going back is unsafe, the integration must refuse the installation
itself and report the state blocked with a code of its own, such as
downgrade-unsupported.
Once a device runs a version with a base, it cannot install versions without a base. Give every release after the first checkpoint a base, usually the latest checkpoint.
Unknown Running Versions
To plan a path, the integration needs the base of the running version, which it
finds through currentVersionId. If currentVersion does not resolve to a
package version, the integration can still install a target without a base. For
a target with a base, the device reports the state blocked with the code
unknown-current-version, as it cannot tell which checkpoints it has passed. Tag
every release with the version string built into its image to avoid this.
Managing Base Versions
Set the base when you create a version:
nexigon-cli repositories versions create acme/gateway-os \
--tag 2.1,locked --base acme/gateway-os/1.4
--tag 2.1,locked adds the locked tag 2.1, as described in
Publish Software Artifacts. The base accepts a
version path or a version ID. Nexigon Hub stores the ID of
the version that the path resolves to when you run the command. Moving a tag
later does not change the base.
To change or clear the base of an existing version:
nexigon-cli repositories versions set-base acme/gateway-os/2.1 acme/gateway-os/1.4
nexigon-cli repositories versions set-base acme/gateway-os/2.1 --clear
The first command sets the base, and the second clears it. Nexigon Hub enforces these rules:
- The base must belong to the same package.
- Following the bases of a version must never lead back to the version itself.
- A version that is the base of another version cannot be deleted or scheduled for deletion, and package retention skips it.
- A version scheduled for deletion cannot become a base.
- The base of a sealed version cannot change. Set the base before you seal a version.
Version details and version lists include the base as baseVersionId. Through
the API, repositories_CreateVersion accepts baseVersionId, and
repositories_SetVersionBase changes it.
Tracking the Latest Checkpoint
Release pipelines usually base every new release on the latest checkpoint. A
floating tag, for example checkpoint, can keep track of it. Assign the tag to
the first checkpoint, such as 1.4, when you introduce it, and create releases
as follows:
# Regular release, based on the latest checkpoint.
nexigon-cli repositories versions create acme/gateway-os \
--tag 2.1,locked --base acme/gateway-os/checkpoint
# Checkpoint release, based on the previous checkpoint. Afterward, it becomes
# the latest checkpoint.
nexigon-cli repositories versions create acme/gateway-os \
--tag 2.2,locked --base acme/gateway-os/checkpoint
nexigon-cli repositories versions tag acme/gateway-os/2.2 --tag checkpoint,reassign
reassign moves the existing checkpoint tag to the new version.
Replacing a Faulty Checkpoint
Suppose checkpoint 1.4 fails on some hardware revisions, and 1.5 fixes the
problem while keeping the migration of 1.4. Devices running 1.2 would still
pass through 1.4 on their way to 2.x. To route them through 1.5 instead,
publish 1.5 without a base, like 1.4, and change the base of the versions
that are based on 1.4:
nexigon-cli repositories versions set-base acme/gateway-os/2.0 acme/gateway-os/1.5
nexigon-cli repositories versions set-base acme/gateway-os/2.1 acme/gateway-os/1.5
nexigon-cli repositories versions set-base acme/gateway-os/2.2 acme/gateway-os/1.5
Devices plan their path on every check, so the change also applies to devices
that are already on their way. Devices running 1.4 now install 1.5 before
continuing, as both have no base. Devices running a 2.x release are unaffected.
This requires that the versions based on 1.4 are not sealed, as the base of a
sealed version cannot change.