In order to progressively deploy an image to our users and to be able to block an update in case of an issue, we need the client to support the phased-percentage field.
This field is part of an image definition in index.json and is an integer between 0 and 100.
The client needs to hash the version number with something unique to the device (MAC, EMEI, ...) and use that to seed a random number generator and then get a value between 1 and 100. If that value is higher than phased-percentage the entry needs to be ignored in the upgrade path resolution.
Only the latest version number can have phased-percentage set but the client doesn't need to care about this, the server will enforce it on its side.
All the client needs to do is check phased-percentage against its own random value and drop them from the path resolution if the local value is higher than the one in the index (same algorithm as phased package updates in the archive).
In the case an image is known to be bad, it'll have its phased-percentage value set to "0" so no client will attempt to use it. When an image is ready for general consumption, it'll have phased-percentage set to "100" or simply not set at all (same result).
The relevant function in update-manager appears to be: phased_ update( self, pkg):
def _is_ignored_
""" This will test if the pkg is a phased update and if
it needs to get installed or ignored.
"""
# allow the admin to override this
if apt.apt_
return False
if self.PHASED_ UPDATES_ KEY in pkg.candidate. record: pkg.config. find_b(
self. NEVER_INCLUDE_ PHASED_ UPDATES, False):
logging. info("holding back phased update per configuration")
return True
if apt.apt_
# its important that we always get the same result on
self. random. seed("% s-%s-%s" % (
pkg.candidate .source_ name, pkg.candidate. version,
self. machine_ uniq_id) )
threshold = pkg.candidate. record[ self.PHASED_ UPDATES_ KEY]
percentage = self.random. randint( 0, 100)
logging. info("holding back phased update (%s < %s)" % (
threshold , percentage))
return True
# multiple runs of the update-manager, so we need to
# feed a seed that is a combination of the pkg/ver/machine
if percentage > int(threshold):
return False
The unique id comes from:
UNIQ_MACHINE_ ID_FILE = "/var/lib/ dbus/machine- id" UNIQ_MACHINE_ ID_FILE) as f:
self. machine_ uniq_id = f.read()
with open(self.
Note that we have that file on Ubuntu Touch too and it's writable, persistent and appaears to be indeed unique, so I'd recommend we just use that.