Skip to content

Commit

Permalink
Add GET /v2/device/metrics endpoint
Browse files Browse the repository at this point in the history
Closes: #1710
Change-type: minor
Signed-off-by: Christina Ying Wang <[email protected]>
  • Loading branch information
cywang117 committed Jan 9, 2025
1 parent 89ddfa6 commit 4a3c0f0
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 0 deletions.
30 changes: 30 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -1298,3 +1298,33 @@ Show journal logs until the given `until` timestamp, formats are described here:

An example project using this endpoint can be found
[in this repository](https://github.com/balena-io-playground/device-cloud-logging).

#### Device metrics

> **Introduced in supervisor v16.11.0**
Get current device metrics.

From an app container:

```bash
$ curl "$BALENA_SUPERVISOR_ADDRESS/v2/device/metrics?apikey=$BALENA_SUPERVISOR_API_KEY"
```

Response:
```json
{
"status": "success",
"metrics": {
"cpu_usage": 0.5,
"memory_usage": 1024,
"memory_total": 2048,
"storage_usage": 1024,
"storage_total": 2048,
"storage_block_device": "sda",
"cpu_temp": 50,
"cpu_id": "1234567890",
"is_undervolted": false
}
}
```
13 changes: 13 additions & 0 deletions src/device-api/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
BadRequestError,
} from '../lib/errors';
import { withLock } from '../lib/update-lock';
import * as systemInfo from '../lib/system-info';

/**
* Run an array of healthchecks, outputting whether all passed or not
Expand Down Expand Up @@ -445,3 +446,15 @@ export const patchHostConfig = async (conf: unknown, force: boolean) => {
}
await hostConfig.patch(parsedConf, force);
};

/**
* Get device metrics and checks
* Used by:
* - GET /v2/device/metrics
*/
export const getMetrics = async () => {
return {
...(await systemInfo.getSystemMetrics()),
...(await systemInfo.getSystemChecks()),
};
};
12 changes: 12 additions & 0 deletions src/device-api/v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,15 @@ router.post('/v2/journal-logs', (req, res) => {
res.end();
});
});

router.get('/v2/device/metrics', async (_req, res, next) => {
try {
const metrics = await actions.getMetrics();
return res.json({
status: 'success',
metrics,
});
} catch (e) {
next(e);
}
});
15 changes: 15 additions & 0 deletions test/integration/device-api/actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1338,3 +1338,18 @@ describe('patches host config', () => {
expect(hostConfigPatch).to.have.been.calledWith(conf, true);
});
});

describe('gets metrics', () => {
it('gets system metrics and checks', async () => {
const metrics = await actions.getMetrics();
expect(metrics).to.have.property('cpu_usage');
expect(metrics).to.have.property('memory_usage');
expect(metrics).to.have.property('memory_total');
expect(metrics).to.have.property('storage_usage');
expect(metrics).to.have.property('storage_total');
expect(metrics).to.have.property('storage_block_device');
expect(metrics).to.have.property('cpu_temp');
expect(metrics).to.have.property('cpu_id');
expect(metrics).to.have.property('is_undervolted');
});
});
43 changes: 43 additions & 0 deletions test/integration/device-api/v2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,4 +639,47 @@ describe('device-api/v2', () => {
.expect(503);
});
});

describe('GET /v2/device/metrics', () => {
// Actions are tested elsewhere so we can stub the dependency here
let getMetricsStub: SinonStub;
beforeEach(() => {
getMetricsStub = stub(actions, 'getMetrics').resolves();
});
afterEach(async () => {
getMetricsStub.restore();
// Remove all scoped API keys between tests
await db.models('apiSecret').whereNot({ appId: 0 }).del();
});

it('responds with 200 if metrics are returned', async () => {
const metrics = {
cpu_usage: 0.5,
memory_usage: 1024,
memory_total: 2048,
storage_usage: 1024,
storage_total: 2048,
storage_block_device: 'sda',
cpu_temp: 50,
cpu_id: '1234567890',
is_undervolted: false,
};
getMetricsStub.resolves(metrics);
await request(api)
.get('/v2/device/metrics')
.set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(200, {
status: 'success',
metrics,
});
});

it('responds with 503 if there is an error getting metrics', async () => {
getMetricsStub.throws(new Error());
await request(api)
.get('/v2/device/metrics')
.set('Authorization', `Bearer ${await apiKeys.getGlobalApiKey()}`)
.expect(503);
});
});
});

0 comments on commit 4a3c0f0

Please sign in to comment.