Merge pull request #932 from balena-io/v2-api-additions

Additions to the V2 supervisor api
This commit is contained in:
CameronDiver 2019-03-13 16:10:20 +00:00 committed by GitHub
commit 40786dda9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 0 deletions

View File

@ -1102,3 +1102,53 @@ Response:
"serviceName": "main"
}
```
### V2 Device Information
#### Device name
Added in supervisor version v9.11.0
Get the last returned device name from the balena API. Note that this differs from the
`BALENA_DEVICE_NAME_AT_INIT` environment variable provided to containers, as this will
not change throughout the runtime of the container, but the endpoint will always return
the latest known device name.
From an application container:
```
$ curl "$BALENA_SUPERVISOR_ADDRESS/v2/device/name?apikey=$BALENA_SUPERVISOR_API_KEY"
```
Response:
```json
{
"status": "success",
"deviceName": "holy-wildflower"
}
```
#### Device tags
Added in supervisor version v9.11.0
Retrieve any device tags from the balena API. Note that this endpoint will not work when
the device does not have an available connection to the balena API.
From an application container:
```
$ curl "$BALENA_SUPERVISOR_ADDRESS/v2/device/tags?apikey=$BALENA_SUPERVISOR_API_KEY"
```
Response:
```json
{
"status": "success",
"tags": [
{
"id": 188303,
"name": "DeviceLocation",
"value": "warehouse #3"
}
]
}
```

View File

@ -1,6 +1,8 @@
import * as Bluebird from 'bluebird';
import * as bodyParser from 'body-parser';
import * as express from 'express';
import { isLeft } from 'fp-ts/lib/Either';
import * as t from 'io-ts';
import * as _ from 'lodash';
import * as Path from 'path';
import { PinejsClientRequest, StatusError } from 'pinejs-client-request';
@ -57,6 +59,12 @@ interface DevicePinInfo {
commit: string;
}
interface DeviceTag {
id: number;
name: string;
value: string;
}
type KeyExchangeOpts = ConfigSchemaType<'provisioningOptions'>;
export class APIBinder {
@ -376,6 +384,39 @@ export class APIBinder {
return this.reportCurrentState();
}
public async fetchDeviceTags(): Promise<DeviceTag[]> {
if (this.balenaApi == null) {
throw new Error(
'Attempt to communicate with API, without initialized client',
);
}
const tags = (await this.balenaApi.get({
resource: 'device_tag',
options: {
$select: ['id', 'tag_key', 'value'],
},
})) as Array<Dictionary<unknown>>;
return tags.map(tag => {
// Do some type safe decoding and throw if we get an unexpected value
const id = t.number.decode(tag.id);
const name = t.string.decode(tag.tag_key);
const value = t.string.decode(tag.value);
if (isLeft(id) || isLeft(name) || isLeft(value)) {
throw new Error(
`There was an error parsing device tags from the api. Device tag: ${JSON.stringify(
tag,
)}`,
);
}
return {
id: id.value,
name: name.value,
value: value.value,
};
});
}
private getStateDiff(): DeviceApplicationState {
const lastReportedLocal = this.lastReportedState.local;
const lastReportedDependent = this.lastReportedState.dependent;

View File

@ -12,6 +12,7 @@ import DB from './db';
import { Service } from './compose/service';
import Config from './config';
import { APIBinder } from './api-binder';
declare interface Options {
force?: boolean;
@ -37,6 +38,7 @@ export class ApplicationManager extends EventEmitter {
public logger: Logger;
public deviceState: any;
public eventTracker: EventTracker;
public apiBinder: APIBinder;
public services: ServiceManager;
public config: Config;

View File

@ -436,4 +436,34 @@ export function createV2Api(router: Router, applications: ApplicationManager) {
release: currentRelease,
});
});
router.get('/v2/device/name', async (_req, res) => {
try {
const deviceName = await applications.config.get('name');
res.json({
status: 'success',
deviceName,
});
} catch (e) {
res.status(503).json({
status: 'failed',
message: messageFromError(e),
});
}
});
router.get('/v2/device/tags', async (_req, res) => {
try {
const tags = await applications.apiBinder.fetchDeviceTags();
return res.json({
status: 'success',
tags,
});
} catch (e) {
res.status(503).json({
status: 'failed',
message: messageFromError(e),
});
}
});
}

View File

@ -37,6 +37,9 @@ module.exports = class Supervisor extends EventEmitter
# FIXME: rearchitect proxyvisor to avoid this circular dependency
# by storing current state and having the APIBinder query and report it / provision devices
@deviceState.applications.proxyvisor.bindToAPI(@apiBinder)
# We could also do without the below dependency, but it's part of a much larger refactor
@deviceState.applications.apiBinder = @apiBinder
@api = new SupervisorAPI({
@config,
@eventTracker,