Report device state in local mode

In local mode, we now update device status on the backend,
but omit applications info in our updates.

Closes: #959
Change-type: minor
Signed-off-by: Roman Mazur <roman@balena.io>
This commit is contained in:
Roman Mazur 2019-06-19 15:50:26 +03:00
parent f662a6be39
commit 024b9c45f4
No known key found for this signature in database
GPG Key ID: 9459886EFE6EE2F6
2 changed files with 71 additions and 13 deletions

View File

@ -38,7 +38,7 @@ const INTERNAL_STATE_KEYS = [
'update_failed',
];
interface APIBinderConstructOpts {
export interface APIBinderConstructOpts {
config: Config;
// FIXME: Remove this
db: Database;
@ -450,7 +450,7 @@ export class APIBinder {
private async sendReportPatch(
stateDiff: DeviceApplicationState,
conf: { apiEndpoint: string; uuid: string },
conf: { apiEndpoint: string; uuid: string; localMode: boolean },
) {
if (this.cachedBalenaApi == null) {
throw new InternalInconsistencyError(
@ -458,6 +458,16 @@ export class APIBinder {
);
}
let body = stateDiff;
if (conf.localMode) {
body = this.stripDeviceStateInLocalMode(stateDiff);
// In local mode, check if it still makes sense to send any updates after data strip.
if (_.isEmpty(body.local)) {
// Nothing to send.
return;
}
}
const endpoint = url.resolve(
conf.apiEndpoint,
`/device/v2/${conf.uuid}/state`,
@ -467,7 +477,7 @@ export class APIBinder {
{
method: 'PATCH',
url: endpoint,
body: stateDiff,
body,
},
this.cachedBalenaApi.passthrough,
);
@ -475,6 +485,18 @@ export class APIBinder {
await this.cachedBalenaApi._request(requestParams);
}
// Returns an object that contains only status fields relevant for the local mode.
// It basically removes information about applications state.
public stripDeviceStateInLocalMode(
state: DeviceApplicationState,
): DeviceApplicationState {
return {
local: _.cloneDeep(
_.omit(state.local, 'apps', 'is_on__commit', 'logs_channel'),
),
};
}
private async report() {
const conf = await this.config.getMany([
'deviceId',
@ -484,17 +506,12 @@ export class APIBinder {
'localMode',
]);
if (conf.localMode) {
return;
}
const stateDiff = this.getStateDiff();
if (_.size(stateDiff) === 0) {
return 0;
}
const apiEndpoint = conf.apiEndpoint;
const uuid = conf.uuid;
const { apiEndpoint, uuid, localMode } = conf;
if (uuid == null || apiEndpoint == null) {
throw new InternalInconsistencyError(
'No uuid or apiEndpoint provided to ApiBinder.report',
@ -503,7 +520,7 @@ export class APIBinder {
try {
await Bluebird.resolve(
this.sendReportPatch(stateDiff, { apiEndpoint, uuid }),
this.sendReportPatch(stateDiff, { apiEndpoint, uuid, localMode }),
).timeout(conf.apiTimeout);
this.stateReportErrors = 0;
@ -526,9 +543,6 @@ export class APIBinder {
private reportCurrentState(): null {
(async () => {
if ((await this.config.get('localMode')) === true) {
return;
}
this.reportPending = true;
try {
const currentDeviceState = await this.deviceState.getStatus();

44
test/api-binder.ts Normal file
View File

@ -0,0 +1,44 @@
import { expect } from 'chai';
import { APIBinder, APIBinderConstructOpts } from '../src/api-binder';
import { DeviceApplicationState } from '../src/types/state';
describe('APIBinder', () => {
let apiBinder: APIBinder;
before(() => {
apiBinder = new APIBinder({} as APIBinderConstructOpts);
});
describe('stripDeviceStateInLocalMode', () => {
const sampleState = {
local: {
ip_address: '192.168.1.42 192.168.1.99',
api_port: 48484,
api_secret:
'20ffbd6e15aba827dca6381912d6aeb6c3a7a7c7206d4dfadf0d2f0a9e1136',
os_version: 'balenaOS 2.32.0+rev4',
os_variant: 'dev',
supervisor_version: '9.16.3',
provisioning_progress: null,
provisioning_state: '',
status: 'Idle',
logs_channel: null,
apps: {},
is_on__commit: 'whatever',
},
dependent: { apps: {} },
} as DeviceApplicationState;
it('should strip applications data', () => {
const result = apiBinder.stripDeviceStateInLocalMode(
sampleState,
) as Dictionary<any>;
expect(result).to.not.have.property('dependent');
const local = result['local'];
expect(local).to.not.have.property('apps');
expect(local).to.not.have.property('is_on__commit');
expect(local).to.not.have.property('logs_channel');
});
});
});