mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-04-16 15:28:52 +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"
|
||||
}
|
||||
```
|
||||
|
||||
### 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 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;
|
||||
|
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 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;
|
||||
|
@ -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),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user