Table of Contents:

The Apertis distribution provides a number of routes for managing updates to deployed devices. This document discusses utilising hawkBit, a domain independent back-end framework for rolling out software updates.

Design

The hawkBit data model defines a hierarchy of software that starts with a distribution, which can have (sub-)modules and these may have multiple artifacts. The hawkBit Data Model concept can be found here.

For Apertis, we want the device agent to be as simple as possible. So it should only need to check the hawkBit server for an update, download it and trigger installation.

Apertis uses OSTree to provide atomic update management. As part of the automated image generation OSTree static delta files are created which can be used to update existing OSTree installations.

When an OSTree static delta file has been created, a new distribution containing a single module for each build and for each Apertis release, type and architecture is created.

The OSTree static delta file is uploaded to the module as its single artifact.

Distribution and module version are based on the image version.

To be more concrete, when the image pipeline is building the armhf fixedfunction image it also builds a matching full static OSTree bundle named, for instance, apertis_ostree_v2022-fixedfunction-armhf-uboot_v2022.0.delta.

This bundle is uploaded to hawkBit under the apertis_v2022-fixedfunction-armhf-uboot module with version v2022.0. This module is linked to a distribution sharing the same name and version.

HawkBit backend installation

The hawkBit update server can be installed using docker-compose (cf. HawkBit Getting Started).

It needs a docker-compose.yml and an application.properties file to configure the hawkBit server.

docker-compose.yml file (based on docker-compose.yml):

#
# Copyright (c) 2018 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
version: '3'

services:

  # ---------------------
  # MySQL service
  # ---------------------
  mysql:
    image: "mysql:5.7"
    environment:
      MYSQL_DATABASE: "hawkbit"
      MYSQL_USER: "root"
      MYSQL_ALLOW_EMPTY_PASSWORD: "true"
    restart: always
    ports:
      - "3306:3306"
    labels:
      NAME: "mysql"

  # ---------------------
  # HawkBit service
  # ---------------------
  hawkbit:
    image: "hawkbit/hawkbit-update-server:latest-mysql"
    environment:
      - 'SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/hawkbit'
      - 'SPRING_DATASOURCE_USERNAME=root'
    restart: always
    ports:
      - "8080:8080"
    volumes:
      - ./application.properties:/opt/hawkbit/application.properties
    labels:
      NAME: "hawkbit"

application.properties file:

#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#

# User Security
spring.security.user.name=admin
spring.security.user.password={noop}admin
spring.main.allow-bean-definition-overriding=true

# DDI authentication configuration
hawkbit.server.ddi.security.authentication.anonymous.enabled=false
hawkbit.server.ddi.security.authentication.targettoken.enabled=true
hawkbit.server.ddi.security.authentication.gatewaytoken.enabled=true

# Optional events
hawkbit.server.repository.publish-target-poll-event=false

## Configuration for DMF/RabbitMQ integration
#spring.rabbitmq.username=guest
#spring.rabbitmq.password=guest
#spring.rabbitmq.virtual-host=/
#spring.rabbitmq.host=localhost
#spring.rabbitmq.port=5672

# Define own users instead of default "admin" user:
hawkbit.server.im.users[0].username=apertis
hawkbit.server.im.users[0].password={noop}Apertis!
hawkbit.server.im.users[0].firstname=Apertis
hawkbit.server.im.users[0].lastname=HawkBit
hawkbit.server.im.users[0].permissions=ALL

# Enable CORS and specify the allowed origins:
#hawkbit.server.security.cors.enabled=true
#hawkbit.server.security.cors.allowedOrigins=http://localhost

Copy them to a directory and run:

$ docker-compose up -d

You can then connect to the hawkBit Management UI at server address and port 8080, with username apertis and password Apertis!.

Trigger a deployment from Apertis hawkBit update server

Deployment setup

The deployment of OSTree static delta files should be performed as part of the automated Apertis image build process, but can also be done manually.

Manually updating static delta to update server

Using the UI

Create a Software Module in Upload page

This allows to group multiple artifacts.

Click on + in Software Module panel and provide Type, Name, and Version.

Multiple artifacts can be attached to the Software Module using the upload File button.

Create a Distribution in Distributions page

The distribution is a collection of software module(s).

1st create the Distribution (+ sign in Distributions panel), which will require Name and Version info, then drag-and-drop required Software Module(s) on top of the newly created Distribution.

Using the API

1st we need to define some variables which will be used: name, description, version and the delta_file to upload, e.g.:

$ name="apertis_test-fixedfunction-armhf-uboot"
$ description="Apertis test fixedfunction for armhf uboot"
$ version="v1.0"
$ delta_file="apertis_ostree_fixedfunction-armhf-uboot.delta"
Create a Software Module
$ curl --user admin:admin 'http://localhost:8080/rest/v1/softwaremodules' -i -X POST -H 'Content-Type: application/json;charset=UTF-8' -d '[ {"vendor" : "Apertis","name" : "$name","description" : "$description", "type" : "os", "version" : "$version" } ]'
[{"createdBy":"admin","createdAt":1568627145649,"lastModifiedBy":"admin","lastModifiedAt":1568627145649,"name":"apertis_test-fixedfunction-armhf-uboot","description":"Apertis test fixedfunction for armhf uboot","version":"v1.0","type":"os","vendor":"Apertis","deleted":false,"_links":{"self":{"href":"http://localhost:8080/rest/v1/softwaremodules/1"}},"id":1}]
Upload an artifact to a Software Module

This will use the href link returned during the Software Module creation.

$ curl --user admin:admin 'http://localhost:8080/rest/v1/softwaremodules/1/artifacts' -i -X POST -H 'Content-Type: multipart/form-data' -F 'file=@$delta_file'
{"createdBy":"admin","createdAt":1568627195280,"lastModifiedBy":"admin","lastModifiedAt":1568627195280,"hashes":{"sha1":"d3b582709d7574e92e16671c5f097fda7795f832","md5":"c368c561a9279f60f7a1b04eabf63d58"},"providedFilename":"apertis_ostree_fixedfunction-armhf-uboot.delta","size":21,"_links":{"self":{"href":"http://localhost:8080/rest/v1/softwaremodules/1/artifacts/1"},"download":{"href":"http://localhost:8080/rest/v1/softwaremodules/1/artifacts/1/download"}},"id":1}
Create a Distribution

the will use the module id returned during the Software Module creation.

$ curl --user admin:admin 'http://localhost:8080/rest/v1/distributionsets/' -i -X POST -H 'Content-Type: application/json;charset=UTF-8' -d '[ {"requiredMigrationStep" : false,"name" : "$name","description" : "$description", "type" : "os", "version" : "$version", "modules" : [ {"id" : 1} ] } ]'
[{"createdBy":"admin","createdAt":1568627754940,"lastModifiedBy":"admin","lastModifiedAt":1568627754940,"name":"apertis_test-fixedfunction-armhf-uboot","description":"Apertis test fixedfunction for armhf uboot","version":"v1.0","modules":[{"createdBy":"admin","createdAt":1568627145649,"lastModifiedBy":"admin","lastModifiedAt":1568627195295,"name":"apertis_test-fixedfunction-armhf-uboot","description":"Apertis test fixedfunction for armhf uboot","version":"v1.0","type":"os","vendor":"Apertis","deleted":false,"_links":{"self":{"href":"http://localhost:8080/rest/v1/softwaremodules/1"}},"id":1}],"requiredMigrationStep":false,"type":"os","complete":true,"deleted":false,"_links":{"self":{"href":"http://localhost:8080/rest/v1/distributionsets/1"}},"id":1}]

Deployment to target(s)

There is 2 ways two start the distribution deployment: manually for a specific target, or to multiple targets using a filter.

Manually deploy to one target

From the Deployment page, simply drag-n-drop the distribution on top of the desired target.

Rollout deployment to multiple targets

This needs to create a filter in Target Filters page by clicking on + sign, and providing a Name, the filter string (e.g. attribute.isoCode==DE), the Action type and the Start type.

  • Action type is related to how the target should manage the update:
  • forced: immediately
  • soft: based on user approval or device update time plan
  • time forced: soft then forced after a specific time
  • download only: device is supposed to only download the update and not install it
  • Start type is related to the rollout:
  • manual: starts on user action
  • auto: starts immediately after rollout is created
  • scheduled: start as soon as rollout is ready and the set time has passed

:exclamation: Do not forget to save the filter.

Then, we are now able to create the deployment in Rollout page by clicking on + sign, giving it a Name and selecting the Distribution, Filter and the number of group to use.

Target device

Target authentication

Target devices are identified in hawkBit by there Controller Id and Target Token. Any Controller Id can be chosen, but should be unique.

A Target token is generated automatically for each new device added using the UI.

A device can be added manually in the hawkBit management UI, or multiple devices can be added by uploading a csv file containing:

Controller_Id_1,targetName1
Controller_Id_2,targetName2
…

A device can also be added using the Management API, allowing to set the Target Token:

$ curl --user 'admin:admin' 'http://localhost:8080/rest/v1/targets' -i -X POST -H 'Content-Type: application/json;charset=UTF-8' -d '[ {"securityToken" : "2345678DGGDGFTDzztgf","address" : "https://192.168.0.1","controllerId" : "Controller_Id_1","name" : "Controller 1","description" : "Test controller 1"} ]' -v

apertis-hawkbit-agent

The apertis-hawkbit-agent is a component in the OSTree-based images to poll hawkBit update server, and in case of an update is available to download it and trigger apertis-update-manager to apply it.

Installation

Add apertis-hawkbit-agent package using apt action to debos image.

Configuration

The server URL, Controller ID and Target Token are mandatory and are located in /etc/apertis-hawbit-agent.ini on the device, they should be uncommented and set to their respective values.

Controller Id and Target Token can be retrieved from Target details in Hawkbit HMI deployment page.

The final configuration file should look like:

[server]
base-uri=http://<IP_address_or_URL_to_HawkBit_server>:8080
controller-id=<ControllerID>
key-token=<DeviceToken>

The apertis-hawkbit-agent should be enabled by running:

$ sudo systemctl enable apertis-hawkbit-agent

After reboot, the device should be able to connect to hawkBit update server and retrieve updates.

Possible improvements

  • Stabilizing the hawkBit instance
  • Optimize resource consumption by moving away from full static bundles
  • Use hawkBit tags for the distributions, setting type and architecture. This will allow to easily find the right distribution to send to a specific target or set of targets.
  • Improve Apertis hawkBit agent interaction with Apertis Update Manager by calling the RegisterSystemUpgradeHandler DBus method, and calling ApplySystemUpgrade when all artifacts has been applied, which may happen with partial upgrades.
  • Send successful feedback from Apertis hawkBit agent to hawkBit update server only after reboot of the upgraded system went well.