Skip to content

Commit

Permalink
Add update agent example application (eclipse-kanto#8)
Browse files Browse the repository at this point in the history
[eclipse-kanto#4] Add update agent example application

Created update agent example application similar to the containers update agent:
- uses the update agent API
- manages files in a directory, message to feature contains filenames and URLs
- has Dockerfile similar to the vehicle-simulator application
- mount points are used both for adding needed certificates and for specifying the directory to be managed by the update agent
- backup functionality for rollback
- deployment.json provided and tested with the -f flag used by kanto-cm
- readme.md provided with basic information about the update agent and instruction on how to install it (standard/containerized)

Signed-off-by: Gabriela Yoncheva <[email protected]>
Signed-off-by: Kristiyan Gostev <[email protected]>
Co-authored-by: Kristiyan Gostev <[email protected]>
  • Loading branch information
gabriela-yoncheva and k-gostev authored Feb 8, 2024
1 parent 9a4d59c commit f254b19
Show file tree
Hide file tree
Showing 18 changed files with 1,437 additions and 2 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ on:
- "main"
paths:
- vehicle-simulator/**/*.go
- custom-update-agent/**/*.go
- .github/**/*.yaml
pull_request:
paths:
- vehicle-simulator/**/*.go
- custom-update-agent/**/*.go
- .github/**/*.yaml

jobs:
call-go-validation:
call-go-validation-vs:
uses: eclipse-kanto/example-applications/.github/workflows/go-validation.yaml@main
with:
work-dir-path: ./vehicle-simulator
work-dir-path: ./vehicle-simulator
call-go-validation-cua:
uses: eclipse-kanto/example-applications/.github/workflows/go-validation.yaml@main
with:
work-dir-path: ./custom-update-agent
17 changes: 17 additions & 0 deletions custom-update-agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM --platform=$BUILDPLATFORM golang:alpine AS builder
RUN apk update && apk add --no-cache git
WORKDIR /build
COPY . .
RUN go mod download

ARG TARGETOS TARGETARCH
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o app .
RUN mkdir ./fileagent

FROM scratch
WORKDIR /tmp
COPY --from=builder /tmp .
WORKDIR /bin
COPY --from=builder /build/fileagent .
COPY --from=builder /build/app .
CMD ["/bin/app"]
138 changes: 138 additions & 0 deletions custom-update-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
![Kanto logo](https://github.com/eclipse-kanto/kanto/raw/main/logo/kanto.svg)

# Eclipse Kanto - Files Update Agent

# Introduction

This is an example application for demonstrating how an update agent works in implementing the Update Agent API to update a certain domain inside the target device. In the case of the Files Update Agent application it is used to manage the files inside of a directory on the target device.

# Desired state

The update process is initiated by sending the desired state specification as an MQTT message towards the device, which is handled by the Update Manager component.

The desired state specification in the scope of the Update Manager is a JSON-based document, which consists of multiple component definitions per domain. The Files Update Agent is responsible for the files domain. Below is an example desired state that contains the Eclipse Kanto logo.

```json
{
"desiredState": {
"domains": [
{
"id": "files",
"config": [],
"components": [
{
"config": [
{
"key": "file_name",
"value": "kantoLogo.svg"
},
{
"key": "download_url",
"value": "https://github.com/eclipse-kanto/kanto/raw/main/logo/kanto.svg"
}
]
}
]
}
]
}
}
```

# Commands

Based on the received desired state the update agent can do the following changes to the provided directory:

- Download file
- Remove file
- Replace file

# Installation

## Prerequisites
You must have an installed and working instance of:
* Eclipse Kanto Update Manager
* Eclipse Kanto Container Management

Regardless if you are running the update agent as a standard or containerized application, you will need to add the following to the update manager configuration, located at `/etc/kanto-update-manager/config.json`:

```json
{
"domain": "device",
"agents": {
"files": {
"rebootRequired": false,
"readTimeout": "20s"
}
}
}
```
After that reboot the service by executing:

```
$ sudo systemctl restart kanto-update-manager.service
```
## Standard service
Replace the directory provided with `-dir` flag in `custom-update-agent.service` with the desired file directory.
``` Ini
[Unit]
Description=Eclipse Kanto - Files Update Agent
[Service]
Type=simple
ExecStart=/usr/bin/custom-update-agent --dir "<files-directory>"
Restart=always
TimeoutSec=300

[Install]
WantedBy=multi-user.target
```
After that execute the provided `install.sh` script:
```
$ sudo ./install.sh
```
To check the status of the `custom-update-agent.service` execute:
```
$ systemctl status custom-update-agent.service
```
## Containerized application
A containerized instance of the Files Update Agent can be built using Eclipse Kanto Container Management.
Replace the fields marked with <> in `deployment.json` accordingly
```json
{
"container_name": "files-update-agent",
"image": {
"name": "<host>:<port>/<image>:<version>"
},
"host_config": {
"network_mode": "host"
},
"mount_points": [
{
"destination": "<certificates-directory>",
"source": "<certificates-directory>",
"propagation_mode": "rprivate"
},
{
"destination": "/bin/fileagent",
"source": "<files-directory>",
"propagation_mode": "shared"
}
]
}
```
Create the container by executing:
```
$ sudo kanto-cm create -f deployment.json
```
You can check the status of the container by executing:
```
$ sudo kanto-cm list
```
Start the containerized application by executing:
```
$ sudo kanto-cm start <container-id>
```
You can check the logs of the container by executing:
```
$ sudo kanto-cm logs --debug <container-id>
```
11 changes: 11 additions & 0 deletions custom-update-agent/custom-update-agent.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Unit]
Description=Eclipse Kanto - Files Update Agent

[Service]
Type=simple
ExecStart=/usr/bin/custom-update-agent --dir "<files-directory>"
Restart=always
TimeoutSec=300

[Install]
WantedBy=multi-user.target
21 changes: 21 additions & 0 deletions custom-update-agent/deployment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"container_name": "files-update-agent",
"image": {
"name": "<host>:<port>/<image>:<version>"
},
"host_config": {
"network_mode": "host"
},
"mount_points": [
{
"destination": "<certificates-directory>",
"source": "<certificates-directory>",
"propagation_mode": "rprivate"
},
{
"destination": "/bin/fileagent",
"source": "<files-directory>",
"propagation_mode": "shared"
}
]
}
24 changes: 24 additions & 0 deletions custom-update-agent/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module github.com/eclipse-kanto/example-applications/custom-update-agent

go 1.21

replace github.com/docker/docker => github.com/moby/moby v23.0.3+incompatible

require (
github.com/eclipse-kanto/container-management v0.1.0-M4
github.com/eclipse-kanto/update-manager v0.1.0-M4.0.20240112143913-bbeef46051af
github.com/pkg/errors v0.9.1
github.com/rickar/props v1.0.0
)

require (
github.com/eclipse/ditto-clients-golang v0.0.0-20230504175246-3e6e17510ac4 // indirect
github.com/eclipse/paho.mqtt.golang v1.4.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.13.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
)
75 changes: 75 additions & 0 deletions custom-update-agent/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eclipse-kanto/container-management v0.1.0-M4 h1:061GDDWH+3pW7rn5OHiJ5VT9fodjz3pVnGW59j1qcnQ=
github.com/eclipse-kanto/container-management v0.1.0-M4/go.mod h1:CpoavUZrXKGNYrRNGnvSLZEBkx/K3gl4DpY61YSpAdU=
github.com/eclipse-kanto/update-manager v0.1.0-M4.0.20240112143913-bbeef46051af h1:ChZkNmAMM0kT6D6Y+BRodzAdaMJqqKq6tKZC4rLFUXQ=
github.com/eclipse-kanto/update-manager v0.1.0-M4.0.20240112143913-bbeef46051af/go.mod h1:JtTUDoVwEHSI1QYlzn1AZSVNfUazMo+um8LM/rbRbbI=
github.com/eclipse/ditto-clients-golang v0.0.0-20230504175246-3e6e17510ac4 h1:Z3jNhQFfkUmwyFv8JRnGRn3WCJ9+teLeFhh7rGHYtUo=
github.com/eclipse/ditto-clients-golang v0.0.0-20230504175246-3e6e17510ac4/go.mod h1:ey7YwfHSQJsinGkGbgeEgqZA7qJnoB0YiFVTFEY50Jg=
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
github.com/eclipse/paho.mqtt.golang v1.4.1 h1:tUSpviiL5G3P9SZZJPC4ZULZJsxQKXxfENpMvdbAXAI=
github.com/eclipse/paho.mqtt.golang v1.4.1/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rickar/props v1.0.0 h1:3C3j+wF2/XbQ/sCGRK8DkCLwuRvzqToMvDzmdxHwCsg=
github.com/rickar/props v1.0.0/go.mod h1:VVywBJXdOY3IwDtBmgAMIZs/XM/CtMKSJzu5dsHYwEY=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
19 changes: 19 additions & 0 deletions custom-update-agent/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
#
# Copyright (c) 2024 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

go build
cp custom-update-agent /usr/bin/custom-update-agent
cp custom-update-agent.service /etc/systemd/system/custom-update-agent.service
systemctl daemon-reload
systemctl restart custom-update-agent.service
54 changes: 54 additions & 0 deletions custom-update-agent/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2024 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

package main

import (
"context"
"flag"
"log/slog"
"os"
"os/signal"
"syscall"

"github.com/eclipse-kanto/example-applications/custom-update-agent/updateagent"
"github.com/eclipse-kanto/example-applications/custom-update-agent/util"

"github.com/eclipse-kanto/update-manager/api"
"github.com/eclipse-kanto/update-manager/mqtt"
)

func main() {
logger := util.ConfigLogger(slog.LevelDebug, os.Stdout)
slog.SetDefault(&logger)

flag.StringVar(&updateagent.FileDirectory, "dir", "./fileagent", "the path to the directory where file agent will manage files")
flag.Parse()

updateAgent, err := updateagent.Init(mqtt.NewDefaultConfig(), "files")
if err != nil {
slog.Error("could not initialize an Update Agent service! got", "error", err)
os.Exit(1)
}
if err := updateAgent.(api.UpdateAgent).Start(context.Background()); err != nil {
slog.Error("could not start Update Agent service! got", "error", err)
os.Exit(2)
}
slog.Info("successfully started Update Agent service")

var signalChan = make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGHUP)

sig := <-signalChan
slog.Info("Exiting!, received", "signal", sig)
updateAgent.(api.UpdateAgent).Stop()
}
Loading

0 comments on commit f254b19

Please sign in to comment.