Client

Differences between revisions 6 and 49 (spanning 43 versions)
Revision 6 as of 2013-05-24 13:52:00
Size: 1982
Editor: lool
Comment:
Revision 49 as of 2014-10-23 15:33:55
Size: 8832
Editor: 38
Comment:
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
The client tool is used to calclate the update, download the files, validate them and then set the needed flags for the upgrader to pick them up and apply. The client tool is used to calculate the update, download the files, validate them and then set the needed flags for the upgrader to pick them up and apply.
Line 36: Line 36:
== DBus API ==

The client will export a D-Bus API on the system bus which will allow for a [[SoftwareUpdates#Phone|u/i in the System Settings]] to query, begin, cancel, and apply a system update. This service starts via D-Bus activation and exits automatically after a configurable amount of time (i.e. it does not run forever). It maintains state such that the client can exit and get restarted to continue the update, even across reboots. The client uses the [[../Downloader|download service]] to manage all file downloads. See the [[http://manpages.ubuntu.com/manpages/utopic/man8/system-image-dbus.8.html|manpage]] for the up-to-date description of the D-Bus interface.
Line 37: Line 41:
 * https://code.launchpad.net/~barry/+junk/resolver  * https://code.launchpad.net/~ubuntu-system-image/ubuntu-system-image/client

== Mock scenarios ==

=== Normal update in auto mode ===

option: --testing=update-auto-success
 1. !CheckForUpdate() is received
 2. wait for 3 seconds
 3. !UpdateAvailableStatus(is_available: true, downloading: true, available_version: 42, update_size: 1337*1024*1024, last_update_date: 1983-09-13T12:13:14, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "") is sent by the daemon
 4. immediately the daemon sends !UpdateProgress(0, 50)
 5. then every half a second, send !UpdateProgress(+1, -0.5)
 6. daemon sends !UpdateDownloaded()
 7. client sends !ApplyUpdate(), daemon gives back an empty string (success)

Note that while download is in progress, calling:
 * !PauseDownload will pause the emission of !UpdateProgress(). It's a noop otherwise (even if called when not download).
 * !DownloadUpdate will resume the emission of this signal. It's a noop otherwise (even if called when not download).
 * !CancelUpdate will cancel the update. It's a noop otherwise (even if called when not download). Next !CheckForUpdate will restart from 1.
 * !CheckForUpdate will immediately resends !UpdateSignal and !UpdateProgress (if a download is in progress). Then, it will eventually send !UpdateDownload if the update was already done, as per spec.

General:
 * !GetSetting("auto_download") returns "1"
 * !SetSetting("auto_download", ''int'') will set the value in memory and then !GetSetting() will return it
 * !GetSetting("foo") returns ""

=== Normal update in manual mode ===

--testing=update-manual-success
 1. !CheckForUpdate() is received
 2. wait for 3 seconds
 3. !UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "") is sent by the daemon
 4. clients sends !DownloadUpdate
 5. immediately the daemon sends !UpdateProgress(0, 50)
 6. then every half a second, send !UpdateProgress(+1, -0.5)
 7. daemon sends !UpdateDownloaded()
 8. client sends !ApplyUpdate, daemon gives back an empty string (success)

=== Failing update ===

--testing=update-failed
The daemon is already in failure mode
 1. the client will send !CheckForUpdate
 2. the daemon answers with !UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "You need some network for downloading.")
 3. the daemon then send !UpdateFailed(9, "You need some network for downloading.")
 4. the client will then send !CancelUpdate
 5. the client will send !CheckForUpdate
 6. the daemon answers with !UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")

=== Failing apply ===

option: --testing=fail-apply
the daemon is in !UpdateDownloaded mode
 1. the client will send !CheckForUpdate
 2. the daemon answers with !UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")
 3. then the daemon sends the !UpdateDownloaded signal
 4. client sends !ApplyUpdate and receive "Not enough battery, you need to plug your phone." string

=== Failing Resume / Manual download ===
--testing=fail-resume
the daemon is paused, 42% of download is done
 1. the client will send !CheckForUpdate
 2. the daemon answers with !UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")
 3. the daemons sends the !UpdatePaused(42) signal
 4. client tries to call !DownloadUpdate() and receive an !UpdateFailed(9, "You need some network for downloading.")

=== Failing pause ===
--testing=fail-pause
the daemon is downloading, initially, 10% is downloaded, there is no known ETA.
 1. the client will send !CheckForUpdate
 2. the daemon answers with !UpdateAvailableStatus(is_available: true, downloading: true, available_version: 42, update_size: 1337*1024*1024, last_update_date: '1983-09-13T12:13:14', descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")
 3. immediately the daemon sends !UpdateProgress(10, 0) (note! 0 for no ETA)
 4. the clients then sends !PauseDownload() and will receive "no no, not now" as a string from the daemon.

=== No Update ===
--testing=no-update
Nothing is downloading and no check is in progress.
 1. client sends !CheckForUpdate
 2. daemon answers with !UpdateAvailableStatus(is_available: false, downlading: false, ? ?, last_update_date: '1983-09-13T12:13:14', ", ")

Client side

Introduction

The client tool is used to calculate the update, download the files, validate them and then set the needed flags for the upgrader to pick them up and apply.

Requirements

  • Secure download of the indexes (HTTPS + GPG)
  • Support for everything described in the GPG spec (revocation list, multiple keyrings, ...)

  • Resolution of the best upgrade path based on different policies (total download size, least number of reboots, ...)
  • Download and validation of the files
  • Flexible implementation to allow different upgrader setups with minimal changes required
  • Support for suspend/resume of downloads

Nice to have

  • Bandwidth limiting is a nice to have and might be tricky to implement.

Implementation

The current implementation is a command line tool, however it's expected to be turned into a DBus service that the Touch UI can drive.

Step-by-step example for an update

Whenever called the client does the following:

  1. Grab https://server/channels.json and lookup the index for the current channel. If present, also grab the device GPG keyring.

  2. Grab https://server/<channel>/<model>/index.json

  3. Read the current version number of the device (ubuntu-build file)
  4. Look for the most recent version available
  5. Resolve an upgrade path to it, minimizing download size and number of reboots
  6. Download any file needed up until the next reboot
  7. Validate all the files
  8. Write them to the cache partition
  9. Write the list of updates for the upgrader to use
  10. Reboot into the upgrader

Those steps don't include all of the specific GPG validation bits required to ensure the authenticity of all files. Those are detailed in the separate GPG wiki page.

Security (e.g. what to download over https/http) is outlined in the server security section.

DBus API

The client will export a D-Bus API on the system bus which will allow for a u/i in the System Settings to query, begin, cancel, and apply a system update. This service starts via D-Bus activation and exits automatically after a configurable amount of time (i.e. it does not run forever). It maintains state such that the client can exit and get restarted to continue the update, even across reboots. The client uses the download service to manage all file downloads. See the manpage for the up-to-date description of the D-Bus interface.

Code

Mock scenarios

Normal update in auto mode

option: --testing=update-auto-success

  1. CheckForUpdate() is received

  2. wait for 3 seconds
  3. UpdateAvailableStatus(is_available: true, downloading: true, available_version: 42, update_size: 1337*1024*1024, last_update_date: 1983-09-13T12:13:14, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "") is sent by the daemon

  4. immediately the daemon sends UpdateProgress(0, 50)

  5. then every half a second, send UpdateProgress(+1, -0.5)

  6. daemon sends UpdateDownloaded()

  7. client sends ApplyUpdate(), daemon gives back an empty string (success)

Note that while download is in progress, calling:

  • PauseDownload will pause the emission of UpdateProgress(). It's a noop otherwise (even if called when not download).

  • DownloadUpdate will resume the emission of this signal. It's a noop otherwise (even if called when not download).

  • CancelUpdate will cancel the update. It's a noop otherwise (even if called when not download). Next CheckForUpdate will restart from 1.

  • CheckForUpdate will immediately resends UpdateSignal and UpdateProgress (if a download is in progress). Then, it will eventually send UpdateDownload if the update was already done, as per spec.

General:

  • GetSetting("auto_download") returns "1"

  • SetSetting("auto_download", int) will set the value in memory and then GetSetting() will return it

  • GetSetting("foo") returns ""

Normal update in manual mode

--testing=update-manual-success

  1. CheckForUpdate() is received

  2. wait for 3 seconds
  3. UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "") is sent by the daemon

  4. clients sends DownloadUpdate

  5. immediately the daemon sends UpdateProgress(0, 50)

  6. then every half a second, send UpdateProgress(+1, -0.5)

  7. daemon sends UpdateDownloaded()

  8. client sends ApplyUpdate, daemon gives back an empty string (success)

Failing update

--testing=update-failed The daemon is already in failure mode

  1. the client will send CheckForUpdate

  2. the daemon answers with UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "You need some network for downloading.")

  3. the daemon then send UpdateFailed(9, "You need some network for downloading.")

  4. the client will then send CancelUpdate

  5. the client will send CheckForUpdate

  6. the daemon answers with UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")

Failing apply

option: --testing=fail-apply the daemon is in UpdateDownloaded mode

  1. the client will send CheckForUpdate

  2. the daemon answers with UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")

  3. then the daemon sends the UpdateDownloaded signal

  4. client sends ApplyUpdate and receive "Not enough battery, you need to plug your phone." string

Failing Resume / Manual download

--testing=fail-resume the daemon is paused, 42% of download is done

  1. the client will send CheckForUpdate

  2. the daemon answers with UpdateAvailableStatus(is_available: true, downloading: false, available_version: 42, update_size: 1337*1024*1024, last_update_date: 83-09-13, descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")

  3. the daemons sends the UpdatePaused(42) signal

  4. client tries to call DownloadUpdate() and receive an UpdateFailed(9, "You need some network for downloading.")

Failing pause

--testing=fail-pause the daemon is downloading, initially, 10% is downloaded, there is no known ETA.

  1. the client will send CheckForUpdate

  2. the daemon answers with UpdateAvailableStatus(is_available: true, downloading: true, available_version: 42, update_size: 1337*1024*1024, last_update_date: '1983-09-13T12:13:14', descriptions: [{'description': "Ubuntu Edge support", 'description-en_GB': 'change the background colour', 'description-fr': "Support d'Ubuntu Edge"}, {'description': "Flipped container with 200% boot speed improvement"}], error_reason: "")

  3. immediately the daemon sends UpdateProgress(10, 0) (note! 0 for no ETA)

  4. the clients then sends PauseDownload() and will receive "no no, not now" as a string from the daemon.

No Update

--testing=no-update Nothing is downloading and no check is in progress.

  1. client sends CheckForUpdate

  2. daemon answers with UpdateAvailableStatus(is_available: false, downlading: false, ? ?, last_update_date: '1983-09-13T12:13:14', ", ")

ImageBasedUpgrades/Client (last edited 2016-10-31 14:09:03 by localhost)