Convert 'logs' command to async/await and add tests

Change-type: patch
This commit is contained in:
Paulo Castro 2020-06-15 23:53:05 +01:00
parent 7e1d58546c
commit 19c3069b22
2 changed files with 82 additions and 19 deletions

View File

@ -83,16 +83,16 @@ export const logs: CommandDefinition<
}, },
], ],
primary: true, primary: true,
async action(params, options, done) { async action(params, options) {
normalizeUuidProp(params); normalizeUuidProp(params);
const balena = getBalenaSdk(); const balena = getBalenaSdk();
const { ExpectedError } = await import('../errors');
const { serviceIdToName } = await import('../utils/cloud'); const { serviceIdToName } = await import('../utils/cloud');
const { displayDeviceLogs, displayLogObject } = await import( const { displayDeviceLogs, displayLogObject } = await import(
'../utils/device/logs' '../utils/device/logs'
); );
const { validateIPAddress } = await import('../utils/validation'); const { validateIPAddress } = await import('../utils/validation');
const { checkLoggedIn } = await import('../utils/patterns'); const { checkLoggedIn } = await import('../utils/patterns');
const { exitWithExpectedError } = await import('../errors');
const Logger = await import('../utils/logger'); const Logger = await import('../utils/logger');
const logger = Logger.getLogger(); const logger = Logger.getLogger();
@ -136,15 +136,12 @@ export const logs: CommandDefinition<
try { try {
await deviceApi.ping(); await deviceApi.ping();
} catch (e) { } catch (e) {
exitWithExpectedError( throw new ExpectedError(
new Error( `Cannot access local mode device at address ${params.uuidOrDevice}`,
`Cannot access local mode device at address ${params.uuidOrDevice}`,
),
); );
} }
const logStream = await deviceApi.getLogStream(); const logStream = await deviceApi.getLogStream();
displayDeviceLogs( await displayDeviceLogs(
logStream, logStream,
logger, logger,
options.system || false, options.system || false,
@ -153,18 +150,19 @@ export const logs: CommandDefinition<
} else { } else {
await checkLoggedIn(); await checkLoggedIn();
if (options.tail) { if (options.tail) {
return balena.logs const logStream = await balena.logs.subscribe(params.uuidOrDevice, {
.subscribe(params.uuidOrDevice, { count: 100 }) count: 100,
.then(function(logStream) { });
logStream.on('line', displayCloudLog); // Never resolve (quit with CTRL-C), but reject on a broken connection
logStream.on('error', done); await new Promise((_resolve, reject) => {
}) logStream.on('line', displayCloudLog);
.catch(done); logStream.on('error', reject);
});
} else { } else {
return balena.logs const logMessages = await balena.logs.history(params.uuidOrDevice);
.history(params.uuidOrDevice) for (const logMessage of logMessages) {
.each(displayCloudLog) await displayCloudLog(logMessage);
.catch(done); }
} }
} }
}, },

View File

@ -0,0 +1,65 @@
/**
* @license
* Copyright 2020 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect } from 'chai';
import { BalenaAPIMock } from '../balena-api-mock';
import { cleanOutput, runCommand } from '../helpers';
import { SupervisorMock } from '../supervisor-mock';
const itS = process.env.BALENA_CLI_TEST_TYPE === 'standalone' ? it : it.skip;
describe('balena logs', function() {
let api: BalenaAPIMock;
let supervisor: SupervisorMock;
this.beforeEach(() => {
api = new BalenaAPIMock();
supervisor = new SupervisorMock();
api.expectGetWhoAmI();
api.expectGetMixpanel({ optional: true });
});
this.afterEach(() => {
// Check all expected api calls have been made and clean up.
api.done();
supervisor.done();
});
// skip non-standalone tests because nock's mock socket causes the error:
// "setKeepAliveInterval expects an instance of socket as its first argument"
// in utils/device/api.ts: NetKeepalive.setKeepAliveInterval(sock, 5000);
itS('should reach the expected endpoints on a local device', async () => {
supervisor.expectGetPing();
supervisor.expectGetLogs();
const { err, out } = await runCommand('logs 1.2.3.4');
expect(err).to.be.empty;
const removeTimestamps = (logLine: string) =>
logLine.replace(/(?<=\[Logs\]) \[.+?\]/, '');
const cleanedOut = cleanOutput(out, true).map(l => removeTimestamps(l));
expect(cleanedOut).to.deep.equal([
'[Logs] Streaming logs',
'[Logs] [bar] bar 8 (332) Linux 4e3f81149d71 4.19.75 #1 SMP PREEMPT Mon Mar 23 11:50:49 UTC 2020 aarch64 GNU/Linux',
'[Logs] [foo] foo 8 (200) Linux cc5df60d89ee 4.19.75 #1 SMP PREEMPT Mon Mar 23 11:50:49 UTC 2020 aarch64 GNU/Linux',
'[Error] Connection to device lost',
]);
});
});