Skip to content

Commit 191ed69

Browse files
fix(cli): drop invalid commands from telemetry (#997)
We should only capture CLI sessions that were invoked with a valid subcommand. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 720cf68 commit 191ed69

File tree

7 files changed

+55
-16
lines changed

7 files changed

+55
-16
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as path from 'path';
2+
import * as fs from 'fs-extra';
3+
import { integTest, withDefaultFixture } from '../../lib';
4+
5+
jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
6+
7+
integTest('no telemetry is collected if command is invalid', withDefaultFixture(async (fixture) => {
8+
const telemetryFile = path.join(fixture.integTestDir, `telemetry-${Date.now()}.json`);
9+
10+
const commandOutput = await fixture.cdk(['invalid-command', `--telemetry-file=${telemetryFile}`], { verboseLevel: 3, allowErrExit: true }); // trace mode
11+
12+
expect(commandOutput).toContain('Session instantiated with an invalid command');
13+
expect(fs.existsSync(telemetryFile)).toBeFalsy();
14+
}),
15+
);

packages/@aws-cdk-testing/cli-integ/tests/telemetry-integ-tests/cdk-synth-telemetry-with-errors.integtest.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as path from 'path';
22
import * as fs from 'fs-extra';
3+
import { CURRENT_TELEMETRY_VERSION } from './constants';
34
import { integTest, withDefaultFixture } from '../../lib';
45

56
jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
@@ -51,7 +52,7 @@ integTest(
5152
identifiers: expect.objectContaining({
5253
installationId: expect.anything(),
5354
sessionId: expect.anything(),
54-
telemetryVersion: '1.0',
55+
telemetryVersion: CURRENT_TELEMETRY_VERSION,
5556
cdkCliVersion: expect.anything(),
5657
cdkLibraryVersion: fixture.library.requestedVersion(),
5758
region: expect.anything(),
@@ -99,7 +100,7 @@ integTest(
99100
identifiers: expect.objectContaining({
100101
installationId: expect.anything(),
101102
sessionId: expect.anything(),
102-
telemetryVersion: '1.0',
103+
telemetryVersion: CURRENT_TELEMETRY_VERSION,
103104
cdkCliVersion: expect.anything(),
104105
cdkLibraryVersion: fixture.library.requestedVersion(),
105106
region: expect.anything(),

packages/@aws-cdk-testing/cli-integ/tests/telemetry-integ-tests/cdk-synth-telemetry.integtest.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as path from 'path';
22
import * as fs from 'fs-extra';
3+
import { CURRENT_TELEMETRY_VERSION } from './constants';
34
import { integTest, withDefaultFixture } from '../../lib';
45

56
jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
@@ -48,7 +49,7 @@ integTest(
4849
identifiers: expect.objectContaining({
4950
installationId: expect.anything(),
5051
sessionId: expect.anything(),
51-
telemetryVersion: '1.0',
52+
telemetryVersion: CURRENT_TELEMETRY_VERSION,
5253
cdkCliVersion: expect.anything(),
5354
cdkLibraryVersion: fixture.library.requestedVersion(),
5455
region: expect.anything(),
@@ -96,7 +97,7 @@ integTest(
9697
identifiers: expect.objectContaining({
9798
installationId: expect.anything(),
9899
sessionId: expect.anything(),
99-
telemetryVersion: '1.0',
100+
telemetryVersion: CURRENT_TELEMETRY_VERSION,
100101
cdkCliVersion: expect.anything(),
101102
cdkLibraryVersion: fixture.library.requestedVersion(),
102103
region: expect.anything(),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const CURRENT_TELEMETRY_VERSION = '2.0';

packages/aws-cdk/lib/cli/io-host/cli-io-host.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ export type { IIoHost, IoMessage, IoMessageCode, IoMessageLevel, IoRequest };
2626
* The current action being performed by the CLI. 'none' represents the absence of an action.
2727
*/
2828
type CliAction =
29-
| ToolkitAction
30-
| 'context'
31-
| 'docs'
32-
| 'flags'
33-
| 'notices'
34-
| 'version'
35-
| 'cli-telemetry'
36-
| 'none';
29+
| ToolkitAction
30+
| 'context'
31+
| 'docs'
32+
| 'flags'
33+
| 'notices'
34+
| 'version'
35+
| 'cli-telemetry'
36+
| 'none';
3737

3838
export interface CliIoHostProps {
3939
/**
@@ -189,6 +189,17 @@ export class CliIoHost implements IIoHost {
189189
}
190190

191191
public async startTelemetry(args: any, context: Context, proxyAgent?: Agent) {
192+
// eslint-disable-next-line @typescript-eslint/no-require-imports
193+
const config = require('../cli-type-registry.json');
194+
const validCommands = Object.keys(config.commands);
195+
const cmd = args._[0];
196+
if (!validCommands.includes(cmd)) {
197+
// the user typed in an invalid command - no need for telemetry since the invocation is going to fail
198+
// imminently anyway.
199+
await this.asIoHelper().defaults.trace(`Session instantiated with an invalid command (${cmd}). Not starting telemetry.`);
200+
return;
201+
}
202+
192203
let sinks: ITelemetrySink[] = [];
193204
const telemetryFilePath = args['telemetry-file'];
194205
if (telemetryFilePath) {

packages/aws-cdk/lib/cli/telemetry/session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class TelemetrySession {
4848
identifiers: {
4949
installationId: await getOrCreateInstallationId(this.ioHost.asIoHelper()),
5050
sessionId: randomUUID(),
51-
telemetryVersion: '1.0',
51+
telemetryVersion: '2.0',
5252
cdkCliVersion: versionNumber(),
5353
cdkLibraryVersion: await getLibraryVersion(this.ioHost.asIoHelper()),
5454
},
@@ -126,7 +126,7 @@ export class TelemetrySession {
126126
duration: {
127127
total: event.duration,
128128
},
129-
...( event.error ? {
129+
...(event.error ? {
130130
error: {
131131
name: event.error.name,
132132
},

packages/aws-cdk/test/cli/io-host/cli-io-host.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ let passThrough: PassThrough;
1414
const originalProcessOn = process.on;
1515

1616
// Mock process.on to be a no-op function that returns process for chaining
17-
process.on = jest.fn().mockImplementation(function() {
17+
process.on = jest.fn().mockImplementation(function () {
1818
return process;
1919
}) as any;
2020

@@ -279,6 +279,16 @@ describe('CliIoHost', () => {
279279
});
280280
});
281281

282+
test('telemetry should not be instantiated with an invalid command', async () => {
283+
const telemetryIoHost = CliIoHost.instance({
284+
logLevel: 'trace',
285+
}, true);
286+
287+
await telemetryIoHost.startTelemetry({ _: ['invalid'] }, new Context());
288+
289+
expect(telemetryIoHost.telemetry).toBeUndefined();
290+
});
291+
282292
describe('telemetry', () => {
283293
let telemetryIoHost: CliIoHost;
284294
let telemetryEmitSpy: jest.SpyInstance;
@@ -293,7 +303,7 @@ describe('CliIoHost', () => {
293303
telemetryIoHost = CliIoHost.instance({
294304
logLevel: 'trace',
295305
}, true);
296-
await telemetryIoHost.startTelemetry({ '_': 'init', 'telemetry-file': telemetryFilePath }, new Context());
306+
await telemetryIoHost.startTelemetry({ '_': ['init'], 'telemetry-file': telemetryFilePath }, new Context());
297307

298308
expect(telemetryIoHost.telemetry).toBeDefined();
299309

0 commit comments

Comments
 (0)