mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-05 01:00:48 +00:00
Merge pull request #932 from balena-io/v2-api-additions
Additions to the V2 supervisor api
This commit is contained in:
commit
40786dda9d
50
docs/API.md
50
docs/API.md
@ -1102,3 +1102,53 @@ Response:
|
|||||||
"serviceName": "main"
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import * as Bluebird from 'bluebird';
|
import * as Bluebird from 'bluebird';
|
||||||
import * as bodyParser from 'body-parser';
|
import * as bodyParser from 'body-parser';
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
|
import { isLeft } from 'fp-ts/lib/Either';
|
||||||
|
import * as t from 'io-ts';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as Path from 'path';
|
import * as Path from 'path';
|
||||||
import { PinejsClientRequest, StatusError } from 'pinejs-client-request';
|
import { PinejsClientRequest, StatusError } from 'pinejs-client-request';
|
||||||
@ -57,6 +59,12 @@ interface DevicePinInfo {
|
|||||||
commit: string;
|
commit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DeviceTag {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
type KeyExchangeOpts = ConfigSchemaType<'provisioningOptions'>;
|
type KeyExchangeOpts = ConfigSchemaType<'provisioningOptions'>;
|
||||||
|
|
||||||
export class APIBinder {
|
export class APIBinder {
|
||||||
@ -376,6 +384,39 @@ export class APIBinder {
|
|||||||
return this.reportCurrentState();
|
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 {
|
private getStateDiff(): DeviceApplicationState {
|
||||||
const lastReportedLocal = this.lastReportedState.local;
|
const lastReportedLocal = this.lastReportedState.local;
|
||||||
const lastReportedDependent = this.lastReportedState.dependent;
|
const lastReportedDependent = this.lastReportedState.dependent;
|
||||||
|
2
src/application-manager.d.ts
vendored
2
src/application-manager.d.ts
vendored
@ -12,6 +12,7 @@ import DB from './db';
|
|||||||
|
|
||||||
import { Service } from './compose/service';
|
import { Service } from './compose/service';
|
||||||
import Config from './config';
|
import Config from './config';
|
||||||
|
import { APIBinder } from './api-binder';
|
||||||
|
|
||||||
declare interface Options {
|
declare interface Options {
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
@ -37,6 +38,7 @@ export class ApplicationManager extends EventEmitter {
|
|||||||
public logger: Logger;
|
public logger: Logger;
|
||||||
public deviceState: any;
|
public deviceState: any;
|
||||||
public eventTracker: EventTracker;
|
public eventTracker: EventTracker;
|
||||||
|
public apiBinder: APIBinder;
|
||||||
|
|
||||||
public services: ServiceManager;
|
public services: ServiceManager;
|
||||||
public config: Config;
|
public config: Config;
|
||||||
|
@ -436,4 +436,34 @@ export function createV2Api(router: Router, applications: ApplicationManager) {
|
|||||||
release: currentRelease,
|
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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ module.exports = class Supervisor extends EventEmitter
|
|||||||
# FIXME: rearchitect proxyvisor to avoid this circular dependency
|
# FIXME: rearchitect proxyvisor to avoid this circular dependency
|
||||||
# by storing current state and having the APIBinder query and report it / provision devices
|
# by storing current state and having the APIBinder query and report it / provision devices
|
||||||
@deviceState.applications.proxyvisor.bindToAPI(@apiBinder)
|
@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({
|
@api = new SupervisorAPI({
|
||||||
@config,
|
@config,
|
||||||
@eventTracker,
|
@eventTracker,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user