From e280e5cf42d063705ab2f748ac5631aab7f94fd3 Mon Sep 17 00:00:00 2001 From: Kristiyan Gostev Date: Fri, 1 Sep 2023 14:25:22 +0300 Subject: [PATCH] Revert "Prepare How-To Guide documentation page for performing an OTA update via the Update Manager (#264)" This reverts commit 5c7dd673e1f9d08a58377fb89eb120194a3bba70. --- quickstart/hono_commands.py | 2 +- quickstart/hono_commands_um.py | 237 ------------------ quickstart/hono_events.py | 18 +- .../content/docs/getting-started/install.md | 3 +- .../docs/how-to-guides/perform-ota-update.md | 148 ----------- 5 files changed, 7 insertions(+), 401 deletions(-) delete mode 100644 quickstart/hono_commands_um.py delete mode 100644 web/site/content/docs/how-to-guides/perform-ota-update.md diff --git a/quickstart/hono_commands.py b/quickstart/hono_commands.py index 97843236..e9e322be 100644 --- a/quickstart/hono_commands.py +++ b/quickstart/hono_commands.py @@ -53,7 +53,7 @@ def on_message(self, event): print('[got response]') response = json.loads(event.message.body) print(json.dumps(response, indent=2)) - if 200 <= response["status"] <= 299: + if response["status"] == 204: print('[ok]', command) else: print('[error]') diff --git a/quickstart/hono_commands_um.py b/quickstart/hono_commands_um.py deleted file mode 100644 index 9fe0b36b..00000000 --- a/quickstart/hono_commands_um.py +++ /dev/null @@ -1,237 +0,0 @@ -# Copyright (c) 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Eclipse Public License 2.0 which is available at -# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -# which is available at https://www.apache.org/licenses/LICENSE-2.0. -# -# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 - -import getopt -import json -import os -import signal -import sys -import threading -import time -import uuid - -from string import Template -from proton import Message -from proton.handlers import MessagingHandler -from proton.reactor import Container - -alpine_container_template = """ -{ -"id": "alpine", -"version": "latest", -"config": [ - { - "key": "image", - "value": "docker.io/library/alpine:latest" - }, - { - "key": "restartPolicy", - "value": "no" - } - ] -},""" - -containers_desired_state = Template(""" -{ - "topic": "$namespace/$name/things/live/messages/apply", - "headers": { - "content-type": "application/json", - "correlation-id": "$correlation_id", - "response-required": true - }, - "path": "/features/UpdateManager/inbox/messages/apply", - "value": { - "activityId": "$activity_id", - "desiredState": { - "domains": [ - { - "id": "containers", - "config": [], - "components": [ - { - "id": "influxdb", - "version": "2.7.1", - "config": [ - { - "key": "image", - "value": "docker.io/library/influxdb:$influxdb_version" - } - ] - }, - $alpine_container - { - "id": "hello-world", - "version": "latest", - "config": [ - { - "key": "image", - "value": "docker.io/library/hello-world:latest" - }, - { - "key": "restartPolicy", - "value": "no" - } - ] - } - ] - } - ] - } -} -} -""") - -containers_desired_state_clean_up = Template(""" -{ - "topic": "$namespace/$name/things/live/messages/apply", - "headers": { - "content-type": "application/json", - "correlation-id": "$correlation_id", - "response-required": true - }, - "path": "/features/UpdateManager/inbox/messages/apply", - "value": { - "activityId": "$activity_id", - "desiredState": { - "domains": [ - { - "id": "containers", - "config": [], - "components": [] - } - ] - } -} -} -""") - -um_refresh_state = Template(""" -{ - "topic": "$namespace/$name/things/live/messages/apply", - "headers": { - "content-type": "application/json", - "correlation-id": "$correlation_id", - "response-required": true - }, - "path": "/features/UpdateManager/inbox/messages/refresh", - "value": { - "activityId": "$activity_id", - } -} -""") - - -class CommandResponsesHandler(MessagingHandler): - def __init__(self, server, address): - super(CommandResponsesHandler, self).__init__() - self.server = server - self.address = address - - def on_start(self, event): - conn = event.container.connect(self.server, user="consumer@HONO", password="verysecret") - event.container.create_receiver(conn, self.address) - print('[connected]') - - def on_message(self, event): - print('[got response]') - response = json.loads(event.message.body) - print(json.dumps(response, indent=2)) - if response["status"] == 204: - print('[ok]', "um") - else: - print('[error]') - event.receiver.close() - event.connection.close() - - def on_connection_closed(self, event): - print('[connection closed]') - os.kill(os.getpid(), signal.SIGINT) - - -class CommandsInvoker(MessagingHandler): - def __init__(self, server, address): - super(CommandsInvoker, self).__init__() - self.server = server - self.address = address - - def on_start(self, event): - conn = event.container.connect(self.server, sasl_enabled=True, allowed_mechs="PLAIN", allow_insecure_mechs=True, - user="consumer@HONO", password="verysecret") - event.container.create_sender(conn, self.address) - - def on_sendable(self, event): - print('[sending command]') - correlation_id = str(uuid.uuid4()) - namespaced_id = device_id.split(':', 1) - activity_id = str(uuid.uuid4()) - - influxdb_version = "1.8.4" - alpine_container = alpine_container_template - if operation == "update": - influxdb_version = "1.8.5" - alpine_container = "" - if operation == "clean": - payload = containers_desired_state_clean_up.substitute(namespace=namespaced_id[0], - name=namespaced_id[1], - correlation_id=correlation_id, - activity_id=activity_id) - else: - payload = containers_desired_state.substitute(namespace=namespaced_id[0], name=namespaced_id[1], - correlation_id=correlation_id, - influxdb_version=influxdb_version, - alpine_container=alpine_container, - activity_id=activity_id) - print(json.dumps(json.loads(payload), indent=2)) - msg = Message(body=payload, address='{}/{}'.format(address, device_id), - content_type="application/json", - subject="um", reply_to=reply_to_address, correlation_id=correlation_id, id=str(uuid.uuid4())) - event.sender.send(msg) - event.sender.close() - event.connection.close() - print('[sent]') - - -# Parse command line args -options, reminder = getopt.getopt(sys.argv[1:], 't:d:o:') -opts_dict = dict(options) -tenant_id = os.environ.get("TENANT") or opts_dict['-t'] -device_id = os.environ.get("DEVICE_ID") or opts_dict['-d'] -operation = opts_dict['-o'] - -# AMQP global configurations -uri = 'amqps://hono.eclipseprojects.io:15671' -address = 'command/{}'.format(tenant_id) -reply_to_address = 'command_response/{}/replies'.format(tenant_id) -print('[starting] demo update manager app for tenant [{}], device [{}] at [{}]'.format(tenant_id, device_id, uri)) - -# Create command invoker and handler -response_handler = Container(CommandResponsesHandler(uri, reply_to_address)) -commands_invoker = Container(CommandsInvoker(uri, address)) -thread = threading.Thread(target=lambda: response_handler.run(), daemon=True) -thread.start() -# Give it some time to link -time.sleep(2) -# Send the command -commands_invoker.run() - - -def handler(signum, frame): - print('[stopping] demo update manager app for tenant [{}], device [{}] at [{}]'.format(tenant_id, device_id, uri)) - response_handler.stop() - thread.join(timeout=5) - print('[stopped]') - exit(0) - - -signal.signal(signal.SIGINT, handler) -while True: - pass diff --git a/quickstart/hono_events.py b/quickstart/hono_events.py index 1a61def8..ff9115c9 100644 --- a/quickstart/hono_events.py +++ b/quickstart/hono_events.py @@ -32,24 +32,16 @@ def on_start(self, event): print('[connected]') def on_message(self, event): + print('[event received]') if event.message.body is not None: - body = json.loads(event.message.body) - if topic_filter != "" and topic_filter != body['topic']: - return - print('[event received]') - print(json.dumps(body, indent=2)) + print(json.dumps(json.loads(event.message.body), indent=2)) else: - print('[empty event received]') + print('') # Parse command line args -options, reminder = getopt.getopt(sys.argv[1:], 't:f:') -opts_dict = dict(options) -tenant_id = os.environ.get("TENANT") or opts_dict['-t'] -if '-f' in opts_dict: - topic_filter = opts_dict['-f'] -else: - topic_filter = "" +options, reminder = getopt.getopt(sys.argv[1:], 't:') +tenant_id = os.environ.get("TENANT") or dict(options)['-t'] uri = 'amqps://hono.eclipseprojects.io:15671' address = 'event/{}'.format(tenant_id) diff --git a/web/site/content/docs/getting-started/install.md b/web/site/content/docs/getting-started/install.md index d586d8e7..36d17198 100644 --- a/web/site/content/docs/getting-started/install.md +++ b/web/site/content/docs/getting-started/install.md @@ -37,8 +37,7 @@ container-management.service \ software-update.service \ file-upload.service \ file-backup.service \ -system-metrics.service \ -kanto-update-manager.service +system-metrics.service ``` All listed services must be in an active running state. diff --git a/web/site/content/docs/how-to-guides/perform-ota-update.md b/web/site/content/docs/how-to-guides/perform-ota-update.md deleted file mode 100644 index 6cc46c42..00000000 --- a/web/site/content/docs/how-to-guides/perform-ota-update.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -title: "Perform OTA update" -type: docs -description: > - Perform an OTA update on your edge device. -weight: 3 ---- - -By following the steps below you will publish a simple `Desired State` specification via a publicly available Eclipse Hono sandbox and then the specification will be handled by the Eclipse Kanto Update Manager, which will trigger an OTA update on -the edge device. - -A simple monitoring application will track the progress and the status of the update process. -### Before you begin - -To ensure that all steps in this guide can be executed, you need: - -* Debian-based linux distribution and the `apt` command line tool - -* If you don't have an installed and running Eclipse Kanto, follow {{% relrefn "install" %}} Install Eclipse Kanto {{% /relrefn %}} - -* If you don't have a connected Eclipse Kanto to Eclipse Hono sandbox, - follow {{% relrefn "hono" %}} Explore via Eclipse Hono {{% /relrefn %}} - -* The {{% refn "https://github.com/eclipse-kanto/kanto/blob/main/quickstart/hono_commands_um.py" %}} - update manager application {{% /refn %}} - - Navigate to the `quickstart` folder where the resources from the {{% relrefn "hono" %}} Explore via Eclipse Hono {{% /relrefn %}} - guide are located and execute the following script: - - ```shell - wget https://github.com/eclipse-kanto/kanto/raw/main/quickstart/hono_commands_um.py - -* Enable the `containers update agent` service of the `Container Management` by adding the ` "update_agent": {"enable": true}` property to the `container-management` service configuration (by default located at `/etc/container-management/config.json`) - and restart the service: - ```shell - systemctl restart container-management - ``` - -### Publish the `Desired State` specification - -First, start the monitoring application that requires the configured Eclipse Hono tenant (`-t`) and an optional filter parameter (`-f`). It will print all -received feedback events triggered by the device: - -```shell -python3 hono_events.py -t demo -f demo/device/things/live/messages/feedback -``` - -The starting point of the OTA update process is to publish the example `Desired State` specification: -```shell -python3 hono_commands_um.py -t demo -d demo:device -o apply -``` - -The `Desired State` specification in this case consists of single domain section definition for the containers domain and a three container components - `influxdb`, `hello-world` and `alpine` image. - -### Apply `Desired State` specification - -The Update Manager receives the published `Desired State` to the local Mosquitto broker, splits the specification (in this case into single domain) and then -distributes the processed specification to the domain agents which initiates the actual update process logic on the domain agents side. - -The update process is organized into multiple phases, which are triggered by sending specific `Desired State` commands (`DOWNLOAD/UPDATE/ACTIVATE`). - -In the example scenario, the three images for the three container components will be pulled (if not available in the cache locally), created as containers during the `UPDATING` phase and -started in the `ACTIVATING` phase. - -### Monitor OTA update progress - -During the OTA update, the progress can be tracked in the monitoring application fot the `Desired State` feedback messages, started in the prerequisite section above. - -The Update Manager reports at a time interval of a second the status of the active update process. For example: -``` -{ - "activityId":"e5c858cc-2057-41b0-bd5f-83aee0aad22e", - "timestamp":1693201088401, - "desiredStateFeedback":{ - "status":"RUNNING", - "actions":[ - { - "component":{ - "id":"containers:alpine", - "version":"latest" - }, - "status":"UPDATE_SUCCESS", - "message":"New container instance is started." - }, - { - "component":{ - "id":"containers:hello-world", - "version":"latest" - }, - "status":"UPDATE_SUCCESS", - "message":"New container instance is started." - }, - { - "component":{ - "id":"containers:influxdb", - "version":"2.7.1" - }, - "status":"UPDATING", - "message":"New container created." - } - ] - } -} -``` - -### List containers - -After the update process is completed, list the installed containers by executing the command `kanto-cm list` to verify if the `Desired State` is applied correctly. - -The output of the command should display the info about the three containers, described in the `Desired State` specification. The `influxdb` is expected to be in `RUNNING` state and -the other containers in status `EXITED`. For example : -``` -ID |Name |Image |Status |Finished At |Exit Code -|-------------------------------------|-------------|------------------------------------|----------------------|--------- -7fe6b689-eb76-476d-a730-c2f422d6e8ea |influxdb |docker.io/library/influxdb:1.8.4 |Running | |0 -c36523d7-8d17-4255-ae0c-37f11003f658 |hello-world |docker.io/library/hello-world:latest|Exited | |0 -9b99978b-2593-4736-bb52-7a07be4a7ed1 |alpine |docker.io/library/alpine:latest |Exited | |0 -``` - -### Update `Desired State` specification - -To update the existing `Desired State` run the command below. The update changes affect two containers - `alpine` and `influxdb`. Being not present in the updated `Desired State` specification, the `alpine` container will be removed from the system. The `influxdb` will be updated to version 1.8.5. The last container - `hello-world` is not affected and any events will be not reported from the container update agent for this particular container. - -```shell -python3 hono_commands_um.py -t demo -d demo:device -o update -``` - -### List updated containers - -After the update process of the existing `Desired State` is completed, list again the available containers to the verify the `Desired State` is updated correctly. - -The output of the command should display the info about the two containers, described in the `Desired State` specification. The `influxdb` is expected to be updated with the version 1.8.5 and in `RUNNING` state and `hello-world` container to be status `EXITED` with version unchanged. The `alpine` container must be removed and not displayed. -``` -ID |Name |Image |Status |Finished At |Exit Code -|-------------------------------------|-------------|------------------------------------|----------------------|--------- -7fe6b689-eb76-476d-a730-c2f422d6e8ea |influxdb |docker.io/library/influxdb:1.8.5 |Running | |0 -c36523d7-8d17-4255-ae0c-37f11003f658 |hello-world |docker.io/library/hello-world:latest|Exited | |0 -``` - -### Remove all containers - -To remove all containers, publish an empty `Desired State` specification (with empty `components` section): -```shell -python3 hono_commands_um.py -t demo -d demo:device -o clean -``` - -As a final step, execute the command `kanto-cm list` to verify that the containers are actually removed from the Kanto container management. -The expected output is `No found containers.`. \ No newline at end of file