The IoT Agent enrolls a device with the IoT Identity service and receives credentials to access the MQTT broker. Via MQTT, it establishes communication with an IoT Management service, so the device can be remotely monitored and managed over a secure connection. The state of the device is mirrored in the cloud by the IoT Device Twin service.
The agent is intended to operate on a device running Ubuntu or Ubuntu Core with snapd enabled. The device management features are implemented using the snapd REST API.
The project uses vendorized dependencies using govendor. Development has been done on minimum Go version 1.12.1.
$ go get github.com/CanonicalLtd/iot-agent
$ cd iot-agent
$ ./get-deps.sh
$ go build ./...
The agent runs as a simple daemon in the snap. It uses the snap config value nats.snapd.password
to protect
a partially implemented snapd NATS API that closely mirrors the snapd REST API. The details can be found in
the AsyncAPI here.
The NATS configuration currently needs to be setup separately on the server to support this.
Internal to the agent process there is a service now that is responsible for receiving commands
from DMS instructing it to install required snaps. This happens on a new topic
tree that is filtered by serial, devices/actions/<sanitized serial>/#
. An example would be
devices/actions/ABC123/required-install
.
The command can be a single snap or an array of snaps.
The watchdog service runs and looks for snap changes that have taken too long. The default is one hour. If a snap change is not completed within 1-hour then the watchdog will create a document with the details of all changes that are currently known and then reboot the device. The document will be in $SNAP_DATA and will be named snap_changes_X.json where X is the id of the change that caused the watchdog to reboot the device.
The watchdog daemon includes a sub-service that logs each snap change that is made to the system log. It also logs
each snap change known to the system when it restarts (changes less than 24 hours old, equivalent to snap changes
).
Additionally all known changes over the lifetime of the system are stored in a sqlite database in $SNAP_DATA
.
The maximum number of changes stored here by default is 1000. If that threshold is exceeded older changes will be
removed as newer changes are added.
Parent key: snaplogger
database.location
- (default:$SNAP_DATA/changes.db
) - location to store the changes database, environment variables expandedmaximum.changes.keep
- (default:1000
) - the maximum number of changes to store in the databaseduration.between.checks
- (default:1h
) - the duration between checks for new changes to log / store; specified as a Go duration stringlog.changes.startup
- (default:true
) - specifies whether the service should log each known change when it starts uplog.level
- (default:info
) - The log level specifically for the snap changes service, lower than info will effectively disable change logging but they will still be stored in the sqlite database
unregister is a separate app in the snap that can be used to clear the configuration of the agent related to a Device Management Service it was previously registered/enrolled with. It will:
- Stop the agent
- Remove .secret
- Remove params
- Restart the agent
** CURRENTLY, TEMPORARILY DISABLED **
By default, the snapd.>
subject tree is encrypted and only accessible with the correct username and password. By default, (and with no snap configuration set) the password is accept solve carbon atmosphere
. To change to a different password,
snap set everactive-iot-agent nats.snapd.password="a very long password indeed"
Note that this password must match what is set in everactive-nats
.
go test -v github.com/everactive/iot-agent/pkg/server -testify.m ^Test_NewServer$
A top level Taskfile taskfile.dev is included and drives common tasks.
If you haven't previously installed dependencies, this will install Mockery for you. It will then execute the prebuild task.
Will do all necessary pre-build tasks that are not one-time (like regenerating mocks). Regenerating messages structs is excluded do to manual editing necessary. See generate-message-structs for more information.
NOTE: The current pkg/messages/messages.go has to be hand edited to perserve some types. If you regenerate the file you will need to diff it to identify types
that are translated as string or float64 when their types are time.Time or int64. import "time"
also needs to
be preserved and the empty interface generated is meaningless and is dropped.