mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-01-18 02:40:03 +00:00
Rename most of the documentation and variable names from resin to balena
Change-type: minor Signed-off-by: Pablo Carranza Velez <pablo@balena.io>
This commit is contained in:
parent
94ebc67839
commit
8298487a88
@ -1,4 +1,4 @@
|
|||||||
# Changelog for resin-supervisor
|
# Changelog for balena-supervisor
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file
|
All notable changes to this project will be documented in this file
|
||||||
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
||||||
|
42
README.md
42
README.md
@ -1,22 +1,22 @@
|
|||||||
# resin-supervisor [![Tickets in Progress](https://badge.waffle.io/resin-io/resin-supervisor.svg?label=flow/in-progress&title=Tickets%20in%20progress)](https://waffle.io/resin-io/resin-supervisor)
|
# balena-supervisor [![Tickets in Progress](https://badge.waffle.io/resin-io/resin-supervisor.svg?label=flow/in-progress&title=Tickets%20in%20progress)](https://waffle.io/resin-io/resin-supervisor)
|
||||||
|
|
||||||
Join our online chat at [![Gitter chat](https://badges.gitter.im/resin-io/chat.png)](https://gitter.im/resin-io/chat)
|
Join our online chat at [![Gitter chat](https://badges.gitter.im/resin-io/chat.png)](https://gitter.im/resin-io/chat)
|
||||||
|
|
||||||
This is [resin.io](https://resin.io)'s supervisor, a program that runs on IoT devices and has the task of running user Apps (which are Docker containers), and updating them as resin.io's API informs it to.
|
This is [balena](https://balena.io)'s supervisor, a program that runs on IoT devices and has the task of running user Apps (which are Docker containers), and updating them as the balena API informs it to.
|
||||||
|
|
||||||
The supervisor is a Node.js program.
|
The supervisor is a Node.js program.
|
||||||
|
|
||||||
## Running a supervisor locally
|
## Running a supervisor locally
|
||||||
|
|
||||||
This process will allow you to run a development instance of the supervisor on your local computer. It is not recommended for production scenarios, but allows someone developing on the supervisor to test changes quickly.
|
This process will allow you to run a development instance of the supervisor on your local computer. It is not recommended for production scenarios, but allows someone developing on the supervisor to test changes quickly.
|
||||||
The supervisor is run inside a resinOS instance running in a container, so effectively it's a Docker-in-Docker instance (or more precisely, [balena](https://github.com/resin-os/balena)-in-Docker).
|
The supervisor is run inside a resinOS instance running in a container, so effectively it's a Docker-in-Docker instance (or more precisely, [balenaEngine](https://github.com/resin-os/balena-engine)-in-Docker).
|
||||||
|
|
||||||
### Set up `config.json`
|
### Set up `config.json`
|
||||||
|
|
||||||
To configure the supervisor, you'll need a `tools/dind/config.json` file. There's two options on how to get this file:
|
To configure the supervisor, you'll need a `tools/dind/config.json` file. There's two options on how to get this file:
|
||||||
|
|
||||||
* Log in to the [resin dashboard](https://dashboard.resin.io), create or select an application, click "Add device" and on the Advanced section select "Download configuration file only". Make sure you use an x86 or amd64 device type for your application, for example Intel NUC.
|
* Log in to the [balenaCloud dashboard](https://dashboard.balena-cloud.com), create or select an application, click "Add device" and on the Advanced section select "Download configuration file only". Make sure you use an x86 or amd64 device type for your application, for example Intel NUC.
|
||||||
* Install the resin CLI with `npm install -g resin-cli`, then login with `resin login` and finally run `resin config generate --app <appName> -o config.json` (choose the default settings whenever prompted).
|
* Install the balena CLI with `npm install -g balena-cli`, then login with `balena login` and finally run `balena config generate --app <appName> -o config.json` (choose the default settings whenever prompted).
|
||||||
|
|
||||||
The `config.json` file should look something like this:
|
The `config.json` file should look something like this:
|
||||||
|
|
||||||
@ -25,36 +25,36 @@ The `config.json` file should look something like this:
|
|||||||
```
|
```
|
||||||
{
|
{
|
||||||
"applicationId": "2167", /* Id of the app this supervisor will run */
|
"applicationId": "2167", /* Id of the app this supervisor will run */
|
||||||
"apiKey": "supersecretapikey", /* The API key to provision the device on the resin.io API */
|
"apiKey": "supersecretapikey", /* The API key to provision the device on the balena API */
|
||||||
"userId": "141", /* User ID for the user who owns the app */
|
"userId": "141", /* User ID for the user who owns the app */
|
||||||
"username": "gh_pcarranzav", /* User name for the user who owns the app */
|
"username": "gh_pcarranzav", /* User name for the user who owns the app */
|
||||||
"deviceType": "intel-nuc", /* The device type corresponding to the test application */
|
"deviceType": "intel-nuc", /* The device type corresponding to the test application */
|
||||||
"apiEndpoint": "https://api.resinstaging.io", /* Endpoint for the resin.io API */
|
"apiEndpoint": "https://api.balena-cloud.com", /* Endpoint for the balena API */
|
||||||
"deltaEndpoint": "https://delta.resinstaging.io", /* Endpoint for the delta server to download Docker binary diffs */
|
"deltaEndpoint": "https://delta.balena-cloud.com", /* Endpoint for the delta server to download Docker binary diffs */
|
||||||
"vpnEndpoint": "vpn.resinstaging.io", /* Endpoint for the resin.io VPN server */
|
"vpnEndpoint": "vpn.balena-cloud.com", /* Endpoint for the balena VPN server */
|
||||||
"listenPort": 48484, /* Listen port for the supervisor API */
|
"listenPort": 48484, /* Listen port for the supervisor API */
|
||||||
"mixpanelToken": "aaaaaaaaaaaaaaaaaaaaaaaaaa", /* Mixpanel token to report events */
|
"mixpanelToken": "aaaaaaaaaaaaaaaaaaaaaaaaaa", /* Mixpanel token to report events */
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Additionally, the `uuid`, `registered_at` and `deviceId` fields will be added by the supervisor upon registration with the resin API. Other fields may be present (the format has evolved over time and will likely continue to do so) but they are not used by the supervisor.
|
Additionally, the `uuid`, `registered_at` and `deviceId` fields will be added by the supervisor upon registration with the balena API. Other fields may be present (the format has evolved over time and will likely continue to do so) but they are not used by the supervisor.
|
||||||
|
|
||||||
### Start the supervisor instance
|
### Start the supervisor instance
|
||||||
|
|
||||||
Ensure your kernel supports aufs (in Ubuntu, install `linux-image-extra-$(uname -r)`) and the `aufs` module is loaded (if necessary, run `sudo modprobe aufs`).
|
Ensure your kernel supports aufs (in Ubuntu, install `linux-image-extra-$(uname -r)`) and the `aufs` module is loaded (if necessary, run `sudo modprobe aufs`).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dindctl run --image resin/amd64-supervisor:master
|
./dindctl run --image balena/amd64-supervisor:master
|
||||||
```
|
```
|
||||||
|
|
||||||
This will setup a Docker-in-Docker instance with an image that runs the supervisor image. You can replace `:master` for a specific tag (see the [tags in Dockerhub](https://hub.docker.com/r/resin/amd64-supervisor/tags/)) to run
|
This will setup a Docker-in-Docker instance with an image that runs the supervisor image. You can replace `:master` for a specific tag (see the [tags in Dockerhub](https://hub.docker.com/r/balena/amd64-supervisor/tags/)) to run
|
||||||
a supervisor from a branch or specific version. The script will pull the image if it is not already available in your
|
a supervisor from a branch or specific version. The script will pull the image if it is not already available in your
|
||||||
local Docker instance.
|
local Docker instance.
|
||||||
|
|
||||||
If you want to develop and test your changes, you can run:
|
If you want to develop and test your changes, you can run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dindctl run --image resin/amd64-supervisor:master --mount-dist
|
./dindctl run --image balena/amd64-supervisor:master --mount-dist
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Using `--mount-dist` requires a Node.js 6.x installed on your computer.
|
Note: Using `--mount-dist` requires a Node.js 6.x installed on your computer.
|
||||||
@ -67,19 +67,19 @@ This will mount the ./dist folder into the supervisor container and build the co
|
|||||||
|
|
||||||
### Testing with preloaded apps
|
### Testing with preloaded apps
|
||||||
|
|
||||||
To test preloaded apps, run `resin preload` (see the [resin CLI docs](https://docs.resin.io/tools/cli/#preload-60-image-62-) on an OS image for the app you are testing with. Then copy the `apps.json` file from the `resin-data` partition into `tools/dind/apps.json`.
|
To test preloaded apps, run `balena preload` (see the [balena CLI docs](https://docs.balena.io/tools/cli/#preload-60-image-62-) on an OS image for the app you are testing with. Then copy the `apps.json` file from the `resin-data` partition into `tools/dind/apps.json`.
|
||||||
|
|
||||||
This file has a format equivalent to the `local` part of the target state endpoint on the resin API.
|
This file has a format equivalent to the `local` part of the target state endpoint on the balena API.
|
||||||
|
|
||||||
Make sure the `config.json` file doesn't have uuid, registered_at or deviceId populated from a previous run.
|
Make sure the `config.json` file doesn't have uuid, registered_at or deviceId populated from a previous run.
|
||||||
|
|
||||||
Then run the supervisor like this:
|
Then run the supervisor like this:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dindctl run --image resin/amd64-supervisor:master --preload
|
./dindctl run --image balena/amd64-supervisor:master --preload
|
||||||
```
|
```
|
||||||
|
|
||||||
This will make the Docker-in-Docker instance pull the image specified in `apps.json` before running the supervisor, simulating a preloaded resinOS image.
|
This will make the Docker-in-Docker instance pull the image specified in `apps.json` before running the supervisor, simulating a preloaded balenaOS image.
|
||||||
|
|
||||||
### View the supervisor's logs
|
### View the supervisor's logs
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ This will show the output of `journalctl` inside the Docker-in-Docker container.
|
|||||||
additional options, for instance, to see the logs from the supervisor service:
|
additional options, for instance, to see the logs from the supervisor service:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dindctl logs -fn 100 -u resin-supervisor-dind
|
./dindctl logs -fn 100 -u resin-supervisor
|
||||||
```
|
```
|
||||||
|
|
||||||
### Stop the supervisor
|
### Stop the supervisor
|
||||||
@ -102,10 +102,10 @@ additional options, for instance, to see the logs from the supervisor service:
|
|||||||
|
|
||||||
This will stop the container and remove it, also removing its volumes.
|
This will stop the container and remove it, also removing its volumes.
|
||||||
|
|
||||||
## Developing with a resinOS device
|
## Developing with a balenaOS device
|
||||||
|
|
||||||
If you want to test local changes (only changes to the Node.js code are supported) on a real resinOS device, provision
|
If you want to test local changes (only changes to the Node.js code are supported) on a real balenaOS device, provision
|
||||||
a [development OS image](https://docs.resin.io/understanding/understanding-devices/2.0.0/#dev-vs-prod-images) and power up the device. On the resin.io dashboard, take note of the device's IP address. Then run:
|
a [development OS image](https://docs.balena.io/understanding/understanding-devices/2.0.0/#dev-vs-prod-images) and power up the device. On the balenaCloud dashboard, take note of the device's IP address. Then run:
|
||||||
|
|
||||||
```
|
```
|
||||||
./sync.js <device IP>
|
./sync.js <device IP>
|
||||||
|
8
dindctl
8
dindctl
@ -11,15 +11,15 @@
|
|||||||
# Usage: dindctl action [options]
|
# Usage: dindctl action [options]
|
||||||
#
|
#
|
||||||
# Actions:
|
# Actions:
|
||||||
# build build local supervisor image. By default it will be resin/amd64-supervisor:master, you can override the tag with --tag.
|
# build build local supervisor image. By default it will be balena/amd64-supervisor:master, you can override the tag with --tag.
|
||||||
# run [options] build dind host container, run it (with name resin_supervisor_1), which will include the specified supervisor image and run it.
|
# run [options] build dind host container, run it (with name balena_supervisor_1), which will include the specified supervisor image and run it.
|
||||||
# buildrun [options] run 'build' and then immediately 'run' the built container.
|
# buildrun [options] run 'build' and then immediately 'run' the built container.
|
||||||
# refresh recompile sources in './src' and restart supervisor container on dind host - requires --mount-dist in order to work properly.
|
# refresh recompile sources in './src' and restart supervisor container on dind host - requires --mount-dist in order to work properly.
|
||||||
# logs [-f] print out supervisor log files - use '-f' to follow instead, or any other arguments you'd send to journalctl.
|
# logs [-f] print out supervisor log files - use '-f' to follow instead, or any other arguments you'd send to journalctl.
|
||||||
# stop stop dind supervisor host container.
|
# stop stop dind supervisor host container.
|
||||||
# Options:
|
# Options:
|
||||||
# --arch | -a [arch] architecture of the supervisor to build (default: amd64 )
|
# --arch | -a [arch] architecture of the supervisor to build (default: amd64 )
|
||||||
# --image | -i [image] image name for supervisor image to build/use ( default: resin/$ARCH-supervisor:master )
|
# --image | -i [image] image name for supervisor image to build/use ( default: balena/$ARCH-supervisor:master )
|
||||||
# --dind-image [image] image to use for the resinos-in-container host (default: resin/resinos:2.12.5_rev1-intel-nuc)
|
# --dind-image [image] image to use for the resinos-in-container host (default: resin/resinos:2.12.5_rev1-intel-nuc)
|
||||||
# --dind-container [name] container name suffix for the dind host container ( default: "supervisor", which will produce a container named resinos-in-container-supervisor)
|
# --dind-container [name] container name suffix for the dind host container ( default: "supervisor", which will produce a container named resinos-in-container-supervisor)
|
||||||
# --mount-dist bind-mount './dist/' (where webpack stores the built js) from local development environment into supervisor container.
|
# --mount-dist bind-mount './dist/' (where webpack stores the built js) from local development environment into supervisor container.
|
||||||
@ -43,7 +43,7 @@ DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
|||||||
SUPERVISOR_BASE_DIR="${DIR}"
|
SUPERVISOR_BASE_DIR="${DIR}"
|
||||||
|
|
||||||
ARCH="amd64"
|
ARCH="amd64"
|
||||||
SUPERVISOR_IMAGE="resin/${ARCH}-supervisor:master"
|
SUPERVISOR_IMAGE="balena/${ARCH}-supervisor:master"
|
||||||
DIND_IMAGE="resin/resinos:2.12.5_rev1-intel-nuc"
|
DIND_IMAGE="resin/resinos:2.12.5_rev1-intel-nuc"
|
||||||
MOUNT_DIST="false"
|
MOUNT_DIST="false"
|
||||||
MOUNT_NODE_MODULES="false"
|
MOUNT_NODE_MODULES="false"
|
||||||
|
@ -482,7 +482,7 @@ Added in supervisor v6.6.0.
|
|||||||
This endpoint allows setting some configuration values for the host OS. Currently it supports
|
This endpoint allows setting some configuration values for the host OS. Currently it supports
|
||||||
proxy and hostname configuration.
|
proxy and hostname configuration.
|
||||||
|
|
||||||
For proxy configuration, resinOS 2.0.7 and higher provides a transparent proxy redirector (redsocks) that makes all connections be routed to a SOCKS or HTTP proxy. This endpoint allows user applications to modify these proxy settings at runtime.
|
For proxy configuration, balenaOS 2.0.7 and higher provides a transparent proxy redirector (redsocks) that makes all connections be routed to a SOCKS or HTTP proxy. This endpoint allows user applications to modify these proxy settings at runtime.
|
||||||
|
|
||||||
|
|
||||||
#### Request body
|
#### Request body
|
||||||
@ -513,7 +513,7 @@ guaranteed to work, especially if they block connections that the balena service
|
|||||||
Keep in mind that, even if transparent proxy redirection will take effect immediately after the API call (i.e. all new connections will go through the proxy), open connections will not be closed. So, if for example, the device has managed to connect to the balenaCloud VPN without the proxy, it will stay connected directly without trying to reconnect through the proxy, unless the connection breaks - any reconnection attempts will then go through the proxy. To force *all* connections to go through the proxy, the best way is to reboot the device (see the /v1/reboot endpoint). In most networks were no connections to the Internet can be made if not through a proxy, this should not be necessary (as there will be no open connections before configuring the proxy settings).
|
Keep in mind that, even if transparent proxy redirection will take effect immediately after the API call (i.e. all new connections will go through the proxy), open connections will not be closed. So, if for example, the device has managed to connect to the balenaCloud VPN without the proxy, it will stay connected directly without trying to reconnect through the proxy, unless the connection breaks - any reconnection attempts will then go through the proxy. To force *all* connections to go through the proxy, the best way is to reboot the device (see the /v1/reboot endpoint). In most networks were no connections to the Internet can be made if not through a proxy, this should not be necessary (as there will be no open connections before configuring the proxy settings).
|
||||||
|
|
||||||
The "noProxy" setting for the proxy is an optional array of IP addresses/subnets that should not be routed through the
|
The "noProxy" setting for the proxy is an optional array of IP addresses/subnets that should not be routed through the
|
||||||
proxy. Keep in mind that local/reserved subnets are already [excluded by resinOS automatically](https://github.com/resin-os/meta-resin/blob/master/meta-resin-common/recipes-connectivity/resin-proxy-config/resin-proxy-config/resin-proxy-config#L48).
|
proxy. Keep in mind that local/reserved subnets are already [excluded by balenaOS automatically](https://github.com/resin-os/meta-resin/blob/master/meta-resin-common/recipes-connectivity/resin-proxy-config/resin-proxy-config/resin-proxy-config#L48).
|
||||||
|
|
||||||
If either "proxy" or "hostname" are null or empty values (i.e. `{}` for proxy or an empty string for hostname), they will be cleared to their default values (i.e. not using a proxy, and a hostname equal to the first 7 characters of the device's uuid, respectively).
|
If either "proxy" or "hostname" are null or empty values (i.e. `{}` for proxy or an empty string for hostname), they will be cleared to their default values (i.e. not using a proxy, and a hostname equal to the first 7 characters of the device's uuid, respectively).
|
||||||
|
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
# Using the Resin Supervisor to manage dependent applications
|
# Using the balena Supervisor to manage dependent applications
|
||||||
|
|
||||||
Since version 2.5.0 the Resin Supervisor can act as a proxy for dependent apps.
|
Since version 2.5.0 the balena Supervisor can act as a proxy for dependent apps.
|
||||||
|
|
||||||
Only Supervisors after version 2.5.0 have this functionality, and some of the endpoints appeared in later versions (we've noted it down where this is the case).
|
Only Supervisors after version 2.5.0 have this functionality, and some of the endpoints appeared in later versions (we've noted it down where this is the case).
|
||||||
|
|
||||||
## What is a dependent application
|
## What is a dependent application
|
||||||
|
|
||||||
A **dependent application** is a resin application that targets devices not capable of interacting directly with the Resin API - the reasons can be several, the most common are:
|
A **dependent application** is a balena application that targets devices not capable of interacting directly with the balena API - the reasons can be several, the most common are:
|
||||||
|
|
||||||
- no direct Internet capabilities
|
- no direct Internet capabilities
|
||||||
- not able to run Resin OS (being a microcontroller, for example)
|
- not able to run balenaOS (being a microcontroller, for example)
|
||||||
|
|
||||||
The **dependent application** is scoped under a resin application, which gets the definition of **gateway application**.
|
The **dependent application** is scoped under a balena application, which gets the definition of **gateway application**.
|
||||||
|
|
||||||
The **gateway application** is responsible for detecting, provisioning and managing **dependent devices** belonging to one of its **dependent applications**. This is possible leveraging a new set of endpoints exposed by the Resin Supervisor.
|
The **gateway application** is responsible for detecting, provisioning and managing **dependent devices** belonging to one of its **dependent applications**. This is possible leveraging a new set of endpoints exposed by the balena Supervisor.
|
||||||
|
|
||||||
When a new version of the dependent application is git pushed, the supervisor will download the docker image and expose the assets in one of the endpoints detailed below. It is then the gateway application (i.e. the user app that is run by the supervisor) that is responsible for ensuring those assets get deployed to the dependent devices, using the provided endpoints to perform the management.
|
When a new version of the dependent application is git pushed, the supervisor will download the docker image and expose the assets in one of the endpoints detailed below. It is then the gateway application (i.e. the user app that is run by the supervisor) that is responsible for ensuring those assets get deployed to the dependent devices, using the provided endpoints to perform the management.
|
||||||
|
|
||||||
A dependent application follows the same development cycle of a conventional resin application:
|
A dependent application follows the same development cycle of a conventional balena application:
|
||||||
|
|
||||||
- it binds to your git workspace via the **resin remote**
|
- it binds to your git workspace via the **balena remote**
|
||||||
- it consists in a Docker application
|
- it consists in a Docker application
|
||||||
- it offers the same environment and configuration variables management
|
- it offers the same environment and configuration variables management
|
||||||
|
|
||||||
@ -29,13 +29,13 @@ There are some differences:
|
|||||||
- the Dockerfile must target either an `x86` or `amd64` base image
|
- the Dockerfile must target either an `x86` or `amd64` base image
|
||||||
- the actual firmware/business logic must be stored in the `/assets` folder within the built docker image.
|
- the actual firmware/business logic must be stored in the `/assets` folder within the built docker image.
|
||||||
- You can either just `COPY` a pre-built artifact in that folder, or build your artifact at push time and then store it in the `/assets` folder.
|
- You can either just `COPY` a pre-built artifact in that folder, or build your artifact at push time and then store it in the `/assets` folder.
|
||||||
- **a dependent application Docker image is only used to build, package and deliver the firmware on the dependent device via resin-supervisor - it won't be run at any point.**
|
- **a dependent application Docker image is only used to build, package and deliver the firmware on the dependent device via balena-supervisor - it won't be run at any point.**
|
||||||
|
|
||||||
## How a dependent application works
|
## How a dependent application works
|
||||||
|
|
||||||
### Endpoints
|
### Endpoints
|
||||||
|
|
||||||
The supervisor exposes a REST API to interact with the dependent applications and dependent devices models that come from the Resin API - it also allows using a set of hooks to have push functionality, both documented below.
|
The supervisor exposes a REST API to interact with the dependent applications and dependent devices models that come from the balena API - it also allows using a set of hooks to have push functionality, both documented below.
|
||||||
|
|
||||||
# HTTP API reference
|
# HTTP API reference
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ Dependent Applications List
|
|||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X GET $RESIN_SUPERVISOR_ADDRESS/v1/dependent-apps?apikey=$RESIN_SUPERVISOR_API_KEY
|
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/dependent-apps?apikey=$BALENA_SUPERVISOR_API_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response**
|
**Response**
|
||||||
@ -76,7 +76,7 @@ Dependent Application Updates Registry
|
|||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X GET $RESIN_SUPERVISOR_ADDRESS/v1/dependent-apps/<appId>/assets/<commit>?apikey=$RESIN_SUPERVISOR_API_KEY
|
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/dependent-apps/<appId>/assets/<commit>?apikey=$BALENA_SUPERVISOR_API_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response**
|
**Response**
|
||||||
@ -95,7 +95,7 @@ Dependent Devices List
|
|||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X GET $RESIN_SUPERVISOR_ADDRESS/v1/devices?apikey=$RESIN_SUPERVISOR_API_KEY
|
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/devices?apikey=$BALENA_SUPERVISOR_API_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response**
|
**Response**
|
||||||
@ -155,7 +155,7 @@ application with the deprecated `edge` device type.
|
|||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" -X POST --data '{"appId": <appId>,
|
curl -H "Content-Type: application/json" -X POST --data '{"appId": <appId>,
|
||||||
"device_type": "edge"}' /
|
"device_type": "edge"}' /
|
||||||
$RESIN_SUPERVISOR_ADDRESS/v1/devices?apikey=$RESIN_SUPERVISOR_API_KEY
|
$BALENA_SUPERVISOR_ADDRESS/v1/devices?apikey=$BALENA_SUPERVISOR_API_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response**
|
**Response**
|
||||||
@ -178,7 +178,7 @@ Dependent Device Information
|
|||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X GET $RESIN_SUPERVISOR_ADDRESS/v1/devices/<uuid>?apikey=$RESIN_SUPERVISOR_API_KEY
|
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/devices/<uuid>?apikey=$BALENA_SUPERVISOR_API_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response**
|
**Response**
|
||||||
@ -211,7 +211,7 @@ Dependent Device Information Update
|
|||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" -X PUT --data /
|
curl -H "Content-Type: application/json" -X PUT --data /
|
||||||
'{"is_online":true, "status": "Updating", "commit": "339125a7529cb2c2a8c93a0bbd8af69f2d96286ab4f4552cb5cfe99b0d3ee9"}' /
|
'{"is_online":true, "status": "Updating", "commit": "339125a7529cb2c2a8c93a0bbd8af69f2d96286ab4f4552cb5cfe99b0d3ee9"}' /
|
||||||
$RESIN_SUPERVISOR_ADDRESS/v1/devices/<uuid>?apikey=$RESIN_SUPERVISOR_API_KEY
|
$BALENA_SUPERVISOR_ADDRESS/v1/devices/<uuid>?apikey=$BALENA_SUPERVISOR_API_KEY
|
||||||
```
|
```
|
||||||
|
|
||||||
**Response**
|
**Response**
|
||||||
@ -243,18 +243,18 @@ Dependent Device Log
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" -X POST --data '{"message":"detected movement","timestamp":1472142960}' /
|
curl -H "Content-Type: application/json" -X POST --data '{"message":"detected movement","timestamp":1472142960}' /
|
||||||
$RESIN_SUPERVISOR_ADDRESS/v1/devices/<uuid>/logs?apikey=$RESIN_SUPERVISOR_API_KEY
|
$BALENA_SUPERVISOR_ADDRESS/v1/devices/<uuid>/logs?apikey=$BALENA_SUPERVISOR_API_KEY
|
||||||
```
|
```
|
||||||
**Response**
|
**Response**
|
||||||
`HTTP/1.1 202 ACCEPTED`
|
`HTTP/1.1 202 ACCEPTED`
|
||||||
|
|
||||||
## Hooks (the requests the Resin Supervisor performs)
|
## Hooks (the requests the balena Supervisor performs)
|
||||||
|
|
||||||
### Hook configuration
|
### Hook configuration
|
||||||
|
|
||||||
You can point the supervisor where to find the hook server via a configuration variable.
|
You can point the supervisor where to find the hook server via a configuration variable.
|
||||||
|
|
||||||
- `RESIN_DEPENDENT_DEVICES_HOOK_ADDRESS` _(defaults to `http://0.0.0.0:1337/v1/devices/`)_
|
- `BALENA_DEPENDENT_DEVICES_HOOK_ADDRESS` _(defaults to `http://0.0.0.0:1337/v1/devices/`)_
|
||||||
|
|
||||||
It's worth mentioning (as described below) that the supervisor will append the dependent device uuid (`<uuid>` in the hook descriptions) to every hook request URL
|
It's worth mentioning (as described below) that the supervisor will append the dependent device uuid (`<uuid>` in the hook descriptions) to every hook request URL
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "resin-supervisor",
|
"name": "balena-supervisor",
|
||||||
"description": "This is resin.io's Supervisor, a program that runs on IoT devices and has the task of running user Apps (which are Docker containers), and updating them as Resin's API informs it to.",
|
"description": "This is balena's Supervisor, a program that runs on IoT devices and has the task of running user Apps (which are Docker containers), and updating them as the balena API informs it to.",
|
||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -38,8 +38,8 @@ createAPIBinderRouter = (apiBinder) ->
|
|||||||
|
|
||||||
module.exports = class APIBinder
|
module.exports = class APIBinder
|
||||||
constructor: ({ @config, @db, @deviceState, @eventTracker }) ->
|
constructor: ({ @config, @db, @deviceState, @eventTracker }) ->
|
||||||
@resinApi = null
|
@balenaApi = null
|
||||||
@cachedResinApi = null
|
@cachedBalenaApi = null
|
||||||
@lastReportedState = { local: {}, dependent: {} }
|
@lastReportedState = { local: {}, dependent: {} }
|
||||||
@stateForReport = { local: {}, dependent: {} }
|
@stateForReport = { local: {}, dependent: {} }
|
||||||
@lastTarget = {}
|
@lastTarget = {}
|
||||||
@ -78,10 +78,10 @@ module.exports = class APIBinder
|
|||||||
passthrough = _.cloneDeep(requestOpts)
|
passthrough = _.cloneDeep(requestOpts)
|
||||||
passthrough.headers ?= {}
|
passthrough.headers ?= {}
|
||||||
passthrough.headers.Authorization = "Bearer #{currentApiKey}"
|
passthrough.headers.Authorization = "Bearer #{currentApiKey}"
|
||||||
@resinApi = new PinejsClient
|
@balenaApi = new PinejsClient
|
||||||
apiPrefix: baseUrl
|
apiPrefix: baseUrl
|
||||||
passthrough: passthrough
|
passthrough: passthrough
|
||||||
@cachedResinApi = @resinApi.clone({}, cache: {})
|
@cachedBalenaApi = @balenaApi.clone({}, cache: {})
|
||||||
|
|
||||||
start: =>
|
start: =>
|
||||||
@config.getMany([ 'apiEndpoint', 'offlineMode', 'bootstrapRetryDelay' ])
|
@config.getMany([ 'apiEndpoint', 'offlineMode', 'bootstrapRetryDelay' ])
|
||||||
@ -124,7 +124,7 @@ module.exports = class APIBinder
|
|||||||
passthrough:
|
passthrough:
|
||||||
headers: Authorization: "Bearer #{apiKey}"
|
headers: Authorization: "Bearer #{apiKey}"
|
||||||
}
|
}
|
||||||
@resinApi.get(reqOpts)
|
@balenaApi.get(reqOpts)
|
||||||
.get(0)
|
.get(0)
|
||||||
.catchReturn(null)
|
.catchReturn(null)
|
||||||
.timeout(timeout)
|
.timeout(timeout)
|
||||||
@ -191,7 +191,7 @@ module.exports = class APIBinder
|
|||||||
console.log('Device is registered but we still have an apiKey, attempting key exchange')
|
console.log('Device is registered but we still have an apiKey, attempting key exchange')
|
||||||
@_exchangeKeyAndGetDevice(opts)
|
@_exchangeKeyAndGetDevice(opts)
|
||||||
.then ({ id }) =>
|
.then ({ id }) =>
|
||||||
@resinApi.passthrough.headers.Authorization = "Bearer #{opts.deviceApiKey}"
|
@balenaApi.passthrough.headers.Authorization = "Bearer #{opts.deviceApiKey}"
|
||||||
configToUpdate = {
|
configToUpdate = {
|
||||||
registered_at: opts.registered_at
|
registered_at: opts.registered_at
|
||||||
deviceId: id
|
deviceId: id
|
||||||
@ -224,7 +224,7 @@ module.exports = class APIBinder
|
|||||||
@_provisionOrRetry(retryDelay)
|
@_provisionOrRetry(retryDelay)
|
||||||
|
|
||||||
provisionDevice: =>
|
provisionDevice: =>
|
||||||
if !@resinApi?
|
if !@balenaApi?
|
||||||
throw new Error('Trying to provision device without initializing API client')
|
throw new Error('Trying to provision device without initializing API client')
|
||||||
@config.getMany([
|
@config.getMany([
|
||||||
'provisioned'
|
'provisioned'
|
||||||
@ -256,7 +256,7 @@ module.exports = class APIBinder
|
|||||||
uuid: deviceRegister.generateUniqueKey()
|
uuid: deviceRegister.generateUniqueKey()
|
||||||
registered_at: Math.floor(Date.now() / 1000)
|
registered_at: Math.floor(Date.now() / 1000)
|
||||||
})
|
})
|
||||||
@resinApi.post
|
@balenaApi.post
|
||||||
resource: 'device'
|
resource: 'device'
|
||||||
body: device
|
body: device
|
||||||
.timeout(conf.apiTimeout)
|
.timeout(conf.apiTimeout)
|
||||||
@ -272,7 +272,7 @@ module.exports = class APIBinder
|
|||||||
throw new Error('Cannot update dependent device in offline mode')
|
throw new Error('Cannot update dependent device in offline mode')
|
||||||
if !conf.provisioned
|
if !conf.provisioned
|
||||||
throw new Error('Device must be provisioned to update a dependent device')
|
throw new Error('Device must be provisioned to update a dependent device')
|
||||||
@resinApi.patch
|
@balenaApi.patch
|
||||||
resource: 'device'
|
resource: 'device'
|
||||||
id: id
|
id: id
|
||||||
body: updatedFields
|
body: updatedFields
|
||||||
@ -281,7 +281,7 @@ module.exports = class APIBinder
|
|||||||
pinDevice: ({ app, commit }) =>
|
pinDevice: ({ app, commit }) =>
|
||||||
@config.get('deviceId')
|
@config.get('deviceId')
|
||||||
.then (deviceId) =>
|
.then (deviceId) =>
|
||||||
@resinApi.get
|
@balenaApi.get
|
||||||
resource: 'release'
|
resource: 'release'
|
||||||
options:
|
options:
|
||||||
filter:
|
filter:
|
||||||
@ -293,7 +293,7 @@ module.exports = class APIBinder
|
|||||||
releaseId = _.get(release, '[0].id')
|
releaseId = _.get(release, '[0].id')
|
||||||
if !releaseId?
|
if !releaseId?
|
||||||
throw new Error('Cannot continue pinning preloaded device! No release found!')
|
throw new Error('Cannot continue pinning preloaded device! No release found!')
|
||||||
@resinApi.patch
|
@balenaApi.patch
|
||||||
resource: 'device'
|
resource: 'device'
|
||||||
id: deviceId
|
id: deviceId
|
||||||
body:
|
body:
|
||||||
@ -329,7 +329,7 @@ module.exports = class APIBinder
|
|||||||
device: deviceId
|
device: deviceId
|
||||||
name: 'RESIN_' + key
|
name: 'RESIN_' + key
|
||||||
}
|
}
|
||||||
@resinApi.post
|
@balenaApi.post
|
||||||
resource: 'device_config_variable'
|
resource: 'device_config_variable'
|
||||||
body: envVar
|
body: envVar
|
||||||
)
|
)
|
||||||
@ -352,9 +352,9 @@ module.exports = class APIBinder
|
|||||||
requestParams = _.extend
|
requestParams = _.extend
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
url: "#{endpoint}"
|
url: "#{endpoint}"
|
||||||
, @cachedResinApi.passthrough
|
, @cachedBalenaApi.passthrough
|
||||||
|
|
||||||
@cachedResinApi._request(requestParams)
|
@cachedBalenaApi._request(requestParams)
|
||||||
.timeout(apiTimeout)
|
.timeout(apiTimeout)
|
||||||
|
|
||||||
# Get target state from API, set it on @deviceState and trigger a state application
|
# Get target state from API, set it on @deviceState and trigger a state application
|
||||||
@ -388,7 +388,7 @@ module.exports = class APIBinder
|
|||||||
|
|
||||||
startTargetStatePoll: =>
|
startTargetStatePoll: =>
|
||||||
Promise.try =>
|
Promise.try =>
|
||||||
if !@resinApi?
|
if !@balenaApi?
|
||||||
throw new Error('Trying to start poll without initializing API client')
|
throw new Error('Trying to start poll without initializing API client')
|
||||||
@_pollTargetState()
|
@_pollTargetState()
|
||||||
return null
|
return null
|
||||||
@ -412,9 +412,9 @@ module.exports = class APIBinder
|
|||||||
method: 'PATCH'
|
method: 'PATCH'
|
||||||
url: "#{endpoint}"
|
url: "#{endpoint}"
|
||||||
body: stateDiff
|
body: stateDiff
|
||||||
, @cachedResinApi.passthrough
|
, @cachedBalenaApi.passthrough
|
||||||
|
|
||||||
@cachedResinApi._request(requestParams)
|
@cachedBalenaApi._request(requestParams)
|
||||||
|
|
||||||
_report: =>
|
_report: =>
|
||||||
@config.getMany([ 'deviceId', 'apiTimeout', 'apiEndpoint', 'uuid', 'localMode' ])
|
@config.getMany([ 'deviceId', 'apiTimeout', 'apiEndpoint', 'uuid', 'localMode' ])
|
||||||
@ -454,7 +454,7 @@ module.exports = class APIBinder
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
startCurrentStateReport: =>
|
startCurrentStateReport: =>
|
||||||
if !@resinApi?
|
if !@balenaApi?
|
||||||
throw new Error('Trying to start state reporting without initializing API client')
|
throw new Error('Trying to start state reporting without initializing API client')
|
||||||
# patch to the device(id) endpoint
|
# patch to the device(id) endpoint
|
||||||
@deviceState.on 'change', =>
|
@deviceState.on 'change', =>
|
||||||
|
@ -12,7 +12,7 @@ validation = require '../lib/validation'
|
|||||||
# appId
|
# appId
|
||||||
# serviceId
|
# serviceId
|
||||||
# serviceName
|
# serviceName
|
||||||
# imageId (from resin API)
|
# imageId (from balena API)
|
||||||
# releaseId
|
# releaseId
|
||||||
# dependent
|
# dependent
|
||||||
# dockerImageId
|
# dockerImageId
|
||||||
|
@ -245,7 +245,7 @@ export class ExtlinuxConfigBackend extends DeviceConfigBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setBootConfig(opts: ConfigOptions): Promise<void> {
|
public setBootConfig(opts: ConfigOptions): Promise<void> {
|
||||||
// First get a representation of the configuration file, with all resin-supported configuration removed
|
// First get a representation of the configuration file, with all balena-supported configuration removed
|
||||||
return Promise.resolve(fs.readFile(ExtlinuxConfigBackend.bootConfigPath))
|
return Promise.resolve(fs.readFile(ExtlinuxConfigBackend.bootConfigPath))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const extlinuxFile = ExtlinuxConfigBackend.parseExtlinuxFile(data.toString());
|
const extlinuxFile = ExtlinuxConfigBackend.parseExtlinuxFile(data.toString());
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
LocalLogBackend,
|
LocalLogBackend,
|
||||||
LogBackend,
|
LogBackend,
|
||||||
LogMessage,
|
LogMessage,
|
||||||
ResinLogBackend,
|
BalenaLogBackend,
|
||||||
} from './logging-backends';
|
} from './logging-backends';
|
||||||
|
|
||||||
interface LoggerSetupOptions {
|
interface LoggerSetupOptions {
|
||||||
@ -39,7 +39,7 @@ export class Logger {
|
|||||||
);
|
);
|
||||||
|
|
||||||
private backend: LogBackend | null = null;
|
private backend: LogBackend | null = null;
|
||||||
private resinBackend: ResinLogBackend | null = null;
|
private balenaBackend: BalenaLogBackend | null = null;
|
||||||
private localBackend: LocalLogBackend | null = null;
|
private localBackend: LocalLogBackend | null = null;
|
||||||
|
|
||||||
private eventTracker: EventTracker;
|
private eventTracker: EventTracker;
|
||||||
@ -64,10 +64,10 @@ export class Logger {
|
|||||||
localMode,
|
localMode,
|
||||||
}: LoggerSetupOptions,
|
}: LoggerSetupOptions,
|
||||||
) {
|
) {
|
||||||
this.resinBackend = new ResinLogBackend(apiEndpoint, uuid, deviceApiKey);
|
this.balenaBackend = new BalenaLogBackend(apiEndpoint, uuid, deviceApiKey);
|
||||||
this.localBackend = new LocalLogBackend();
|
this.localBackend = new LocalLogBackend();
|
||||||
|
|
||||||
this.backend = localMode ? this.localBackend : this.resinBackend;
|
this.backend = localMode ? this.localBackend : this.balenaBackend;
|
||||||
|
|
||||||
this.backend.offlineMode = offlineMode;
|
this.backend.offlineMode = offlineMode;
|
||||||
this.backend.publishEnabled = enableLogs;
|
this.backend.publishEnabled = enableLogs;
|
||||||
@ -79,9 +79,9 @@ export class Logger {
|
|||||||
this.backend = this.localBackend;
|
this.backend = this.localBackend;
|
||||||
console.log('Switching logging backend to LocalLogBackend');
|
console.log('Switching logging backend to LocalLogBackend');
|
||||||
} else {
|
} else {
|
||||||
// Use the resin backend
|
// Use the balena backend
|
||||||
this.backend = this.resinBackend;
|
this.backend = this.balenaBackend;
|
||||||
console.log('Switching logging backend to ResinLogBackend');
|
console.log('Switching logging backend to BalenaLogBackend');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ interface Options extends url.UrlWithParsedQuery {
|
|||||||
headers: Dictionary<string>;
|
headers: Dictionary<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ResinLogBackend extends LogBackend {
|
export class BalenaLogBackend extends LogBackend {
|
||||||
|
|
||||||
private req: ClientRequest | null = null;
|
private req: ClientRequest | null = null;
|
||||||
private dropCount: number = 0;
|
private dropCount: number = 0;
|
@ -1,10 +1,10 @@
|
|||||||
import { LocalLogBackend } from './local-backend';
|
import { LocalLogBackend } from './local-backend';
|
||||||
import { LogBackend, LogMessage } from './log-backend';
|
import { LogBackend, LogMessage } from './log-backend';
|
||||||
import { ResinLogBackend } from './resin-backend';
|
import { BalenaLogBackend } from './balena-backend';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
LocalLogBackend,
|
LocalLogBackend,
|
||||||
LogBackend,
|
LogBackend,
|
||||||
LogMessage,
|
LogMessage,
|
||||||
ResinLogBackend,
|
BalenaLogBackend,
|
||||||
};
|
};
|
||||||
|
@ -583,11 +583,13 @@ module.exports = class Proxyvisor
|
|||||||
.then (parentApp) =>
|
.then (parentApp) =>
|
||||||
Promise.map parentApp?.services ? [], (service) =>
|
Promise.map parentApp?.services ? [], (service) =>
|
||||||
@docker.getImageEnv(service.image)
|
@docker.getImageEnv(service.image)
|
||||||
.get('RESIN_DEPENDENT_DEVICES_HOOK_ADDRESS')
|
.then (imageEnvs) ->
|
||||||
.then (imageHookAddresses) ->
|
imageHookAddresses = _.map imageEnvs, (env) ->
|
||||||
|
return env.BALENA_DEPENDENT_DEVICES_HOOK_ADDRESS ? env.RESIN_DEPENDENT_DEVICES_HOOK_ADDRESS
|
||||||
for addr in imageHookAddresses
|
for addr in imageHookAddresses
|
||||||
return addr if addr?
|
return addr if addr?
|
||||||
return parentApp?.config?.RESIN_DEPENDENT_DEVICES_HOOK_ADDRESS ?
|
return parentApp?.config?.BALENA_DEPENDENT_DEVICES_HOOK_ADDRESS ?
|
||||||
|
parentApp?.config?.RESIN_DEPENDENT_DEVICES_HOOK_ADDRESS ?
|
||||||
"#{constants.proxyvisorHookReceiver}/v1/devices/"
|
"#{constants.proxyvisorHookReceiver}/v1/devices/"
|
||||||
|
|
||||||
sendUpdate: (device, timeout, endpoint) =>
|
sendUpdate: (device, timeout, endpoint) =>
|
||||||
|
@ -54,7 +54,7 @@ module.exports = class SupervisorAPI
|
|||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
|
|
||||||
# Expires the supervisor's API key and generates a new one.
|
# Expires the supervisor's API key and generates a new one.
|
||||||
# It also communicates the new key to the Resin API.
|
# It also communicates the new key to the balena API.
|
||||||
@_api.post '/v1/regenerate-api-key', (req, res) =>
|
@_api.post '/v1/regenerate-api-key', (req, res) =>
|
||||||
@config.newUniqueKey()
|
@config.newUniqueKey()
|
||||||
.then (secret) =>
|
.then (secret) =>
|
||||||
|
2
sync.js
2
sync.js
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
// The script will first build a non-optimized version of the js code and sync the resulting app.js
|
// The script will first build a non-optimized version of the js code and sync the resulting app.js
|
||||||
// onto the supervisor container at the specified IP. It will also restart the supervisor container.
|
// onto the supervisor container at the specified IP. It will also restart the supervisor container.
|
||||||
// The device must be a development variant of Resin OS and the supervisor must be running.
|
// The device must be a development variant of balenaOS and the supervisor must be running.
|
||||||
|
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
prepare = require './lib/prepare'
|
prepare = require './lib/prepare'
|
||||||
Promise = require 'bluebird'
|
Promise = require 'bluebird'
|
||||||
resinAPI = require './lib/mocked-resin-api'
|
balenaAPI = require './lib/mocked-balena-api'
|
||||||
fs = Promise.promisifyAll(require('fs'))
|
fs = Promise.promisifyAll(require('fs'))
|
||||||
|
|
||||||
m = require 'mochainon'
|
m = require 'mochainon'
|
||||||
@ -39,10 +39,10 @@ mockProvisioningOpts = {
|
|||||||
|
|
||||||
describe 'APIBinder', ->
|
describe 'APIBinder', ->
|
||||||
before ->
|
before ->
|
||||||
spy(resinAPI.resinBackend, 'registerHandler')
|
spy(balenaAPI.balenaBackend, 'registerHandler')
|
||||||
@server = resinAPI.listen(3000)
|
@server = balenaAPI.listen(3000)
|
||||||
after ->
|
after ->
|
||||||
resinAPI.resinBackend.registerHandler.restore()
|
balenaAPI.balenaBackend.registerHandler.restore()
|
||||||
try
|
try
|
||||||
@server.close()
|
@server.close()
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ describe 'APIBinder', ->
|
|||||||
promise = @apiBinder.provisionDevice()
|
promise = @apiBinder.provisionDevice()
|
||||||
expect(promise).to.be.fulfilled
|
expect(promise).to.be.fulfilled
|
||||||
.then =>
|
.then =>
|
||||||
expect(resinAPI.resinBackend.registerHandler).to.be.calledOnce
|
expect(balenaAPI.balenaBackend.registerHandler).to.be.calledOnce
|
||||||
resinAPI.resinBackend.registerHandler.reset()
|
balenaAPI.balenaBackend.registerHandler.reset()
|
||||||
expect(@eventTracker.track).to.be.calledWith('Device bootstrap success')
|
expect(@eventTracker.track).to.be.calledWith('Device bootstrap success')
|
||||||
|
|
||||||
it 'deletes the provisioning key', ->
|
it 'deletes the provisioning key', ->
|
||||||
@ -66,7 +66,7 @@ describe 'APIBinder', ->
|
|||||||
fs.readFileAsync('./test/data/config-apibinder.json')
|
fs.readFileAsync('./test/data/config-apibinder.json')
|
||||||
.then(JSON.parse)
|
.then(JSON.parse)
|
||||||
.then (conf) ->
|
.then (conf) ->
|
||||||
expect(resinAPI.resinBackend.devices).to.deep.equal({
|
expect(balenaAPI.balenaBackend.devices).to.deep.equal({
|
||||||
'1': {
|
'1': {
|
||||||
id: 1
|
id: 1
|
||||||
user: conf.userId
|
user: conf.userId
|
||||||
@ -81,9 +81,9 @@ describe 'APIBinder', ->
|
|||||||
before ->
|
before ->
|
||||||
initModels.call(this, '/config-apibinder.json')
|
initModels.call(this, '/config-apibinder.json')
|
||||||
|
|
||||||
it 'gets a device by its uuid from the Resin API', ->
|
it 'gets a device by its uuid from the balena API', ->
|
||||||
# Manually add a device to the mocked API
|
# Manually add a device to the mocked API
|
||||||
resinAPI.resinBackend.devices[3] = {
|
balenaAPI.balenaBackend.devices[3] = {
|
||||||
id: 3
|
id: 3
|
||||||
user: 'foo'
|
user: 'foo'
|
||||||
application: 1337
|
application: 1337
|
||||||
@ -93,48 +93,48 @@ describe 'APIBinder', ->
|
|||||||
}
|
}
|
||||||
@apiBinder.fetchDevice('abcd', 'someApiKey', 30000)
|
@apiBinder.fetchDevice('abcd', 'someApiKey', 30000)
|
||||||
.then (theDevice) ->
|
.then (theDevice) ->
|
||||||
expect(theDevice).to.deep.equal(resinAPI.resinBackend.devices[3])
|
expect(theDevice).to.deep.equal(balenaAPI.balenaBackend.devices[3])
|
||||||
|
|
||||||
describe '_exchangeKeyAndGetDevice', ->
|
describe '_exchangeKeyAndGetDevice', ->
|
||||||
before ->
|
before ->
|
||||||
initModels.call(this, '/config-apibinder.json')
|
initModels.call(this, '/config-apibinder.json')
|
||||||
|
|
||||||
it 'returns the device if it can fetch it with the deviceApiKey', ->
|
it 'returns the device if it can fetch it with the deviceApiKey', ->
|
||||||
spy(resinAPI.resinBackend, 'deviceKeyHandler')
|
spy(balenaAPI.balenaBackend, 'deviceKeyHandler')
|
||||||
fetchDeviceStub = stub(@apiBinder, 'fetchDevice')
|
fetchDeviceStub = stub(@apiBinder, 'fetchDevice')
|
||||||
fetchDeviceStub.onCall(0).resolves({ id: 1 })
|
fetchDeviceStub.onCall(0).resolves({ id: 1 })
|
||||||
@apiBinder._exchangeKeyAndGetDevice(mockProvisioningOpts)
|
@apiBinder._exchangeKeyAndGetDevice(mockProvisioningOpts)
|
||||||
.then (device) =>
|
.then (device) =>
|
||||||
expect(resinAPI.resinBackend.deviceKeyHandler).to.not.be.called
|
expect(balenaAPI.balenaBackend.deviceKeyHandler).to.not.be.called
|
||||||
expect(device).to.deep.equal({ id: 1 })
|
expect(device).to.deep.equal({ id: 1 })
|
||||||
expect(@apiBinder.fetchDevice).to.be.calledOnce
|
expect(@apiBinder.fetchDevice).to.be.calledOnce
|
||||||
@apiBinder.fetchDevice.restore()
|
@apiBinder.fetchDevice.restore()
|
||||||
resinAPI.resinBackend.deviceKeyHandler.restore()
|
balenaAPI.balenaBackend.deviceKeyHandler.restore()
|
||||||
|
|
||||||
it 'throws if it cannot get the device with any of the keys', ->
|
it 'throws if it cannot get the device with any of the keys', ->
|
||||||
spy(resinAPI.resinBackend, 'deviceKeyHandler')
|
spy(balenaAPI.balenaBackend, 'deviceKeyHandler')
|
||||||
stub(@apiBinder, 'fetchDevice').returns(Promise.resolve(null))
|
stub(@apiBinder, 'fetchDevice').returns(Promise.resolve(null))
|
||||||
promise = @apiBinder._exchangeKeyAndGetDevice(mockProvisioningOpts)
|
promise = @apiBinder._exchangeKeyAndGetDevice(mockProvisioningOpts)
|
||||||
promise.catch(->)
|
promise.catch(->)
|
||||||
expect(promise).to.be.rejected
|
expect(promise).to.be.rejected
|
||||||
.then =>
|
.then =>
|
||||||
expect(resinAPI.resinBackend.deviceKeyHandler).to.not.be.called
|
expect(balenaAPI.balenaBackend.deviceKeyHandler).to.not.be.called
|
||||||
expect(@apiBinder.fetchDevice).to.be.calledTwice
|
expect(@apiBinder.fetchDevice).to.be.calledTwice
|
||||||
@apiBinder.fetchDevice.restore()
|
@apiBinder.fetchDevice.restore()
|
||||||
resinAPI.resinBackend.deviceKeyHandler.restore()
|
balenaAPI.balenaBackend.deviceKeyHandler.restore()
|
||||||
|
|
||||||
it 'exchanges the key and returns the device if the provisioning key is valid', ->
|
it 'exchanges the key and returns the device if the provisioning key is valid', ->
|
||||||
spy(resinAPI.resinBackend, 'deviceKeyHandler')
|
spy(balenaAPI.balenaBackend, 'deviceKeyHandler')
|
||||||
fetchDeviceStub = stub(@apiBinder, 'fetchDevice')
|
fetchDeviceStub = stub(@apiBinder, 'fetchDevice')
|
||||||
fetchDeviceStub.onCall(0).returns(Promise.resolve(null))
|
fetchDeviceStub.onCall(0).returns(Promise.resolve(null))
|
||||||
fetchDeviceStub.onCall(1).returns(Promise.resolve({ id: 1 }))
|
fetchDeviceStub.onCall(1).returns(Promise.resolve({ id: 1 }))
|
||||||
@apiBinder._exchangeKeyAndGetDevice(mockProvisioningOpts)
|
@apiBinder._exchangeKeyAndGetDevice(mockProvisioningOpts)
|
||||||
.then (device) =>
|
.then (device) =>
|
||||||
expect(resinAPI.resinBackend.deviceKeyHandler).to.be.calledOnce
|
expect(balenaAPI.balenaBackend.deviceKeyHandler).to.be.calledOnce
|
||||||
expect(device).to.deep.equal({ id: 1 })
|
expect(device).to.deep.equal({ id: 1 })
|
||||||
expect(@apiBinder.fetchDevice).to.be.calledTwice
|
expect(@apiBinder.fetchDevice).to.be.calledTwice
|
||||||
@apiBinder.fetchDevice.restore()
|
@apiBinder.fetchDevice.restore()
|
||||||
resinAPI.resinBackend.deviceKeyHandler.restore()
|
balenaAPI.balenaBackend.deviceKeyHandler.restore()
|
||||||
|
|
||||||
describe 'offline mode', ->
|
describe 'offline mode', ->
|
||||||
before ->
|
before ->
|
||||||
|
@ -3,19 +3,19 @@ _ = require 'lodash'
|
|||||||
api = express()
|
api = express()
|
||||||
api.use(require('body-parser').json())
|
api.use(require('body-parser').json())
|
||||||
|
|
||||||
api.resinBackend = {
|
api.balenaBackend = {
|
||||||
currentId: 1
|
currentId: 1
|
||||||
devices: {}
|
devices: {}
|
||||||
registerHandler: (req, res) ->
|
registerHandler: (req, res) ->
|
||||||
console.log('/device/register called with ', req.body)
|
console.log('/device/register called with ', req.body)
|
||||||
device = req.body
|
device = req.body
|
||||||
device.id = api.resinBackend.currentId++
|
device.id = api.balenaBackend.currentId++
|
||||||
api.resinBackend.devices[device.id] = device
|
api.balenaBackend.devices[device.id] = device
|
||||||
res.status(201).json(device)
|
res.status(201).json(device)
|
||||||
getDeviceHandler: (req, res) ->
|
getDeviceHandler: (req, res) ->
|
||||||
uuid = req.query['$filter']?.match(/uuid eq '(.*)'/)?[1]
|
uuid = req.query['$filter']?.match(/uuid eq '(.*)'/)?[1]
|
||||||
if uuid?
|
if uuid?
|
||||||
res.json({ d: _.filter(api.resinBackend.devices, (dev) -> dev.uuid is uuid ) })
|
res.json({ d: _.filter(api.balenaBackend.devices, (dev) -> dev.uuid is uuid ) })
|
||||||
else
|
else
|
||||||
res.json({ d: [] })
|
res.json({ d: [] })
|
||||||
deviceKeyHandler: (req, res) ->
|
deviceKeyHandler: (req, res) ->
|
||||||
@ -24,12 +24,12 @@ api.resinBackend = {
|
|||||||
|
|
||||||
|
|
||||||
api.post '/device/register', (req, res) ->
|
api.post '/device/register', (req, res) ->
|
||||||
api.resinBackend.registerHandler(req, res)
|
api.balenaBackend.registerHandler(req, res)
|
||||||
|
|
||||||
api.get '/v5/device', (req, res) ->
|
api.get '/v5/device', (req, res) ->
|
||||||
api.resinBackend.getDeviceHandler(req, res)
|
api.balenaBackend.getDeviceHandler(req, res)
|
||||||
|
|
||||||
api.post '/api-key/device/:deviceId/device-key', (req, res) ->
|
api.post '/api-key/device/:deviceId/device-key', (req, res) ->
|
||||||
api.resinBackend.deviceKeyHandler(req, res)
|
api.balenaBackend.deviceKeyHandler(req, res)
|
||||||
|
|
||||||
module.exports = api
|
module.exports = api
|
Loading…
Reference in New Issue
Block a user