mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-20 17:52:51 +00:00
Use actions & write tests for GET /v1/device
Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
parent
72c683d5ff
commit
250684d651
@ -453,3 +453,44 @@ export const getSingleContainerApp = async (appId: number) => {
|
||||
releaseId: service.releaseId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns legacy device info, update status, and service status for a single-container application.
|
||||
* Used by:
|
||||
* - GET /v1/device
|
||||
*/
|
||||
export const getLegacyDeviceState = async () => {
|
||||
const state = await deviceState.getLegacyState();
|
||||
const stateToSend = _.pick(state.local, [
|
||||
'api_port',
|
||||
'ip_address',
|
||||
'os_version',
|
||||
'mac_address',
|
||||
'supervisor_version',
|
||||
'update_pending',
|
||||
'update_failed',
|
||||
'update_downloaded',
|
||||
]) as Dictionary<any>;
|
||||
|
||||
if (state.local?.is_on__commit != null) {
|
||||
stateToSend.commit = state.local.is_on__commit;
|
||||
}
|
||||
|
||||
// NOTE: This only returns the status of the first service,
|
||||
// even in a multi-container app. We should deprecate this endpoint
|
||||
// in favor of a multi-container friendly device endpoint (which doesn't
|
||||
// exist yet), and use that for cloud dashboard diagnostic queries.
|
||||
const service = _.toPairs(
|
||||
_.toPairs(state.local?.apps)[0]?.[1]?.services,
|
||||
)[0]?.[1];
|
||||
|
||||
if (service != null) {
|
||||
stateToSend.status = service.status;
|
||||
if (stateToSend.status === 'Running') {
|
||||
stateToSend.status = 'Idle';
|
||||
}
|
||||
stateToSend.download_progress = service.download_progress;
|
||||
}
|
||||
|
||||
return stateToSend;
|
||||
};
|
||||
|
@ -235,38 +235,11 @@ router.patch('/v1/device/host-config', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/v1/device', async (_req, res) => {
|
||||
router.get('/v1/device', async (_req, res, next) => {
|
||||
try {
|
||||
const state = await deviceState.getLegacyState();
|
||||
const stateToSend = _.pick(state.local, [
|
||||
'api_port',
|
||||
'ip_address',
|
||||
'os_version',
|
||||
'mac_address',
|
||||
'supervisor_version',
|
||||
'update_pending',
|
||||
'update_failed',
|
||||
'update_downloaded',
|
||||
]) as Dictionary<unknown>;
|
||||
if (state.local?.is_on__commit != null) {
|
||||
stateToSend.commit = state.local.is_on__commit;
|
||||
}
|
||||
const service = _.toPairs(
|
||||
_.toPairs(state.local?.apps)[0]?.[1]?.services,
|
||||
)[0]?.[1];
|
||||
|
||||
if (service != null) {
|
||||
stateToSend.status = service.status;
|
||||
if (stateToSend.status === 'Running') {
|
||||
stateToSend.status = 'Idle';
|
||||
}
|
||||
stateToSend.download_progress = service.download_progress;
|
||||
}
|
||||
res.json(stateToSend);
|
||||
} catch (e: any) {
|
||||
res.status(500).json({
|
||||
Data: '',
|
||||
Error: (e != null ? e.message : undefined) || e || 'Unknown error',
|
||||
});
|
||||
const state = await actions.getLegacyDeviceState();
|
||||
return res.json(state);
|
||||
} catch (e: unknown) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
@ -378,6 +378,39 @@ describe('manages application lifecycle', () => {
|
||||
expect(body.env).to.have.property('BALENA_SERVICE_NAME', serviceNames[0]);
|
||||
});
|
||||
|
||||
it('should return legacy information about device state', async () => {
|
||||
containers = await waitForSetup(targetState);
|
||||
|
||||
const { body } = await request(BALENA_SUPERVISOR_ADDRESS).get(
|
||||
'/v1/device',
|
||||
);
|
||||
|
||||
expect(body).to.have.property('api_port', 48484);
|
||||
// Versions match semver versioning scheme: major.minor.patch(+rev)?
|
||||
expect(body)
|
||||
.to.have.property('os_version')
|
||||
.that.matches(/balenaOS\s[1-2]\.[0-9]{1,3}\.[0-9]{1,3}(?:\+rev[0-9])?/);
|
||||
expect(body)
|
||||
.to.have.property('supervisor_version')
|
||||
.that.matches(/(?:[0-9]+\.?){3}/);
|
||||
// Matches a space-separated string of IPv4 and/or IPv6 addresses
|
||||
expect(body)
|
||||
.to.have.property('ip_address')
|
||||
.that.matches(
|
||||
/(?:(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}))\s?/,
|
||||
);
|
||||
// Matches a space-separated string of MAC addresses
|
||||
expect(body)
|
||||
.to.have.property('mac_address')
|
||||
.that.matches(/(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}\s?/);
|
||||
expect(body).to.have.property('update_pending').that.is.a('boolean');
|
||||
expect(body).to.have.property('update_failed').that.is.a('boolean');
|
||||
expect(body).to.have.property('update_downloaded').that.is.a('boolean');
|
||||
// Container should be running so the overall status is Idle
|
||||
expect(body).to.have.property('status', 'Idle');
|
||||
expect(body).to.have.property('download_progress', null);
|
||||
});
|
||||
|
||||
// This test should be ordered last in this `describe` block, because the test compares
|
||||
// the `CreatedAt` timestamps of volumes to determine whether purge was successful. Thus,
|
||||
// ordering the assertion last will ensure some time has passed between the first `CreatedAt`
|
||||
|
@ -744,4 +744,28 @@ describe('device-api/v1', () => {
|
||||
.expect(503);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /v1/device', () => {
|
||||
let getLegacyDeviceStateStub: SinonStub;
|
||||
beforeEach(() => {
|
||||
getLegacyDeviceStateStub = stub(actions, 'getLegacyDeviceState');
|
||||
});
|
||||
afterEach(() => getLegacyDeviceStateStub.restore());
|
||||
|
||||
it('responds with 200 and legacy device state', async () => {
|
||||
getLegacyDeviceStateStub.resolves({ test_state: 'Success' });
|
||||
await request(api)
|
||||
.get('/v1/device')
|
||||
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`)
|
||||
.expect(200, { test_state: 'Success' });
|
||||
});
|
||||
|
||||
it('responds with 503 for other errors that occur during request', async () => {
|
||||
getLegacyDeviceStateStub.throws(new Error());
|
||||
await request(api)
|
||||
.get('/v1/device')
|
||||
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`)
|
||||
.expect(503);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -107,18 +107,6 @@ describe('SupervisorAPI [V1 Endpoints]', () => {
|
||||
loggerStub.restore();
|
||||
});
|
||||
|
||||
describe('GET /v1/device', () => {
|
||||
it('returns MAC address', async () => {
|
||||
const response = await request
|
||||
.get('/v1/device')
|
||||
.set('Accept', 'application/json')
|
||||
.set('Authorization', `Bearer ${await deviceApi.getGlobalApiKey()}`)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('mac_address').that.is.not.empty;
|
||||
});
|
||||
});
|
||||
|
||||
describe('/v1/device/host-config', () => {
|
||||
// Wrap GET and PATCH /v1/device/host-config tests in the same block to share
|
||||
// common scoped variables, namely file paths and file content
|
||||
|
Loading…
x
Reference in New Issue
Block a user