Skip to content

feat(cli): cli-telemetry status command #697

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ integTest(
['cli-telemetry']: false,
});
} finally {
await fs.unlink(path.join(fixture.integTestDir, 'cdk.context.json'));
await fs.unlink(contextFile);
}
}),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { promises as fs } from 'fs';
import * as path from 'path';
import { integTest, withDefaultFixture } from '../../../lib';

jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime

integTest(
'CLI Telemetry reports status',
withDefaultFixture(async (fixture) => {
const contextFile = path.join(fixture.integTestDir, 'cdk.context.json');
const userContextFile = path.join(fixture.integTestDir, 'cdk.json');
const context = {
existedBefore: 'this was here',
};
await fs.writeFile(
contextFile,
JSON.stringify(context),
);
try {
// default status is enabled
const output1 = await fixture.cdk(['cli-telemetry', '--status']);
expect(output1).toContain('Telemetry is enabled. Run \'cdk cli-telemetry --disable\' to disable.');

// disable status
await fs.writeFile(userContextFile, JSON.stringify({ context: { 'cli-telemetry': false } }));
const output2 = await fixture.cdk(['cli-telemetry', '--status']);
expect(output2).toContain('Telemetry is disabled. Run \'cdk cli-telemetry --enable\' to enable.');
} finally {
await fs.unlink(contextFile);
await fs.unlink(userContextFile);
}
}),
);
63 changes: 43 additions & 20 deletions packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,27 @@

The AWS CDK Toolkit provides the `cdk` command-line interface that can be used to work with AWS CDK applications. This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.

| Command | Description |
| ------------------------------------- | --------------------------------------------------------------------------------- |
| [`cdk docs`](#cdk-docs) | Access the online documentation |
| [`cdk init`](#cdk-init) | Start a new CDK project (app or library) |
| [`cdk list`](#cdk-list) | List stacks and their dependencies in an application |
| [`cdk synth`](#cdk-synth) | Synthesize a CDK app to CloudFormation template(s) |
| [`cdk diff`](#cdk-diff) | Diff stacks against current state |
| [`cdk deploy`](#cdk-deploy) | Deploy a stack into an AWS account |
| [`cdk rollback`](#cdk-rollback) | Roll back a failed deployment |
| [`cdk import`](#cdk-import) | Import existing AWS resources into a CDK stack |
| [`cdk migrate`](#cdk-migrate) | Migrate AWS resources, CloudFormation stacks, and CloudFormation templates to CDK |
| [`cdk watch`](#cdk-watch) | Watches a CDK app for deployable and hotswappable changes |
| [`cdk destroy`](#cdk-destroy) | Deletes a stack from an AWS account |
| [`cdk bootstrap`](#cdk-bootstrap) | Deploy a toolkit stack to support deploying large stacks & artifacts |
| [`cdk gc`](#cdk-gc) | Garbage collect assets associated with the bootstrapped stack |
| [`cdk doctor`](#cdk-doctor) | Inspect the environment and produce information useful for troubleshooting |
| [`cdk acknowledge`](#cdk-acknowledge) | Acknowledge (and hide) a notice by issue number |
| [`cdk notices`](#cdk-notices) | List all relevant notices for the application |
| [`cdk refactor`](#cdk-refactor) | Moves resources between stacks or within the same stack |
| [`cdk drift`](#cdk-drift) | Detect drifts in the given CloudFormation stack(s) |
| Command | Description |
| ---------------------------------------- | --------------------------------------------------------------------------------- |
| [`cdk docs`](#cdk-docs) | Access the online documentation |
| [`cdk init`](#cdk-init) | Start a new CDK project (app or library) |
| [`cdk list`](#cdk-list) | List stacks and their dependencies in an application |
| [`cdk synth`](#cdk-synth) | Synthesize a CDK app to CloudFormation template(s) |
| [`cdk diff`](#cdk-diff) | Diff stacks against current state |
| [`cdk deploy`](#cdk-deploy) | Deploy a stack into an AWS account |
| [`cdk rollback`](#cdk-rollback) | Roll back a failed deployment |
| [`cdk import`](#cdk-import) | Import existing AWS resources into a CDK stack |
| [`cdk migrate`](#cdk-migrate) | Migrate AWS resources, CloudFormation stacks, and CloudFormation templates to CDK |
| [`cdk watch`](#cdk-watch) | Watches a CDK app for deployable and hotswappable changes |
| [`cdk destroy`](#cdk-destroy) | Deletes a stack from an AWS account |
| [`cdk bootstrap`](#cdk-bootstrap) | Deploy a toolkit stack to support deploying large stacks & artifacts |
| [`cdk gc`](#cdk-gc) | Garbage collect assets associated with the bootstrapped stack |
| [`cdk doctor`](#cdk-doctor) | Inspect the environment and produce information useful for troubleshooting |
| [`cdk acknowledge`](#cdk-acknowledge) | Acknowledge (and hide) a notice by issue number |
| [`cdk notices`](#cdk-notices) | List all relevant notices for the application |
| [`cdk refactor`](#cdk-refactor) | Moves resources between stacks or within the same stack |
| [`cdk drift`](#cdk-drift) | Detect drifts in the given CloudFormation stack(s) |
| [`cdk cli-telemetry](#cdk-cli-telemetry) | Enable or disable cli telemetry collection |

## Common topics

Expand Down Expand Up @@ -1208,6 +1209,28 @@ $ # Detect drift against the currently-deployed stack with the verbose flag enab
$ cdk drift --verbose
```

### `cdk cli-telemetry`

Enables or disables cli telemetry collection for your local CDK App. Records your
choice in `cdk.context.json`.

```bash
$ # Disable telemetry
$ cdk cli-telemetry --disable

$ # Enable telemetry
$ cdk cli-telemetry --enable
```

You can also check the current status on whether your CDK App is opted in or out of
cli telemetry collection. Note that this takes into account all methods of disabling
cli telemetry, including environment variables and preferences set globally in `~/.cdk.json`.

```bash
$ # Check the current status of telemetry
$ cdk cli-telemetry --status
```

## Global Options

### `unstable`
Expand Down
10 changes: 10 additions & 0 deletions packages/aws-cdk/lib/cli/cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
serializeStructure,
validateSnsTopicArn,
} from '../util';
import { canCollectTelemetry } from './telemetry/collect-telemetry';

// Must use a require() otherwise esbuild complains about calling a namespace
// eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/consistent-type-imports
Expand Down Expand Up @@ -195,6 +196,15 @@ export class CdkToolkit {
await this.props.configuration.saveContext();
}

public async cliTelemetryStatus() {
const currentStatus = canCollectTelemetry(this.props.configuration.context);
if (currentStatus) {
info('CLI Telemetry is enabled. Run \'cdk cli-telemetry --disable\' to disable for this CDK App.');
} else {
info('CLI Telemetry is disabled. Run \'cdk cli-telemetry --enable\' to enable for this CDK App.');
}
}

public async cliTelemetry(enable: boolean) {
this.props.configuration.context.set('cli-telemetry', enable);
await this.props.configuration.saveContext();
Expand Down
5 changes: 5 additions & 0 deletions packages/aws-cdk/lib/cli/cli-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,11 @@ export async function makeConfig(): Promise<CliConfig> {
desc: 'Disable anonymous telemetry',
conflicts: 'enable',
},
status: {
type: 'boolean',
desc: 'Report telemetry opt-in/out status',
conflicts: ['enable', 'disable'],
},
},
},
},
Expand Down
8 changes: 8 additions & 0 deletions packages/aws-cdk/lib/cli/cli-type-registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,14 @@
"type": "boolean",
"desc": "Disable anonymous telemetry",
"conflicts": "enable"
},
"status": {
"type": "boolean",
"desc": "Report telemetry opt-in/out status",
"conflicts": [
"enable",
"disable"
]
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions packages/aws-cdk/lib/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,16 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise<n

case 'cli-telemetry':
ioHost.currentAction = 'cli-telemetry';
if (args.enable === undefined && args.disable === undefined) {
throw new ToolkitError('Must specify either \'--enable\' or \'--disable\'');
if (args.enable === undefined && args.disable === undefined && args.status === undefined) {
throw new ToolkitError('Must specify \'--enable\', \'--disable\', or \'--status\'');
}

const enable = args.enable ?? !args.disable;
return cli.cliTelemetry(enable);

if (args.status) {
return cli.cliTelemetryStatus();
} else {
const enable = args.enable ?? !args.disable;
return cli.cliTelemetry(enable);
}
case 'init':
ioHost.currentAction = 'init';
const language = configuration.settings.get(['language']);
Expand Down
2 changes: 2 additions & 0 deletions packages/aws-cdk/lib/cli/convert-to-user-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ export function convertYargsToUserInput(args: any): UserInput {
commandOptions = {
enable: args.enable,
disable: args.disable,
status: args.status,
};
break;
}
Expand Down Expand Up @@ -482,6 +483,7 @@ export function convertConfigToUserInput(config: any): UserInput {
const cliTelemetryOptions = {
enable: config.cliTelemetry?.enable,
disable: config.cliTelemetry?.disable,
status: config.cliTelemetry?.status,
};
const userInput: UserInput = {
globalOptions,
Expand Down
6 changes: 6 additions & 0 deletions packages/aws-cdk/lib/cli/parse-command-line-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,12 @@ export function parseCommandLineArguments(args: Array<string>): any {
type: 'boolean',
desc: 'Disable anonymous telemetry',
conflicts: 'enable',
})
.option('status', {
default: undefined,
type: 'boolean',
desc: 'Report telemetry opt-in/out status',
conflicts: ['enable', 'disable'],
}),
)
.version(helpers.cliVersion())
Expand Down
12 changes: 12 additions & 0 deletions packages/aws-cdk/lib/cli/telemetry/collect-telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Context } from '../../api/context';

/**
* Whether or not we collect telemetry
*/
export function canCollectTelemetry(context: Context): boolean {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iliapolo I pulled out this function from #631 and removed our own feature flag CLI_TELEMETRY=true because it misrepresents whether our users have enabled/disabled telemetry.

if ((['true', '1'].includes(process.env.CDK_CLI_DISABLE_TELEMETRY ?? '')) || ['false', false].includes(context.get('cli-telemetry'))) {
return false;
}

return true;
}
7 changes: 7 additions & 0 deletions packages/aws-cdk/lib/cli/user-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1477,4 +1477,11 @@ export interface CliTelemetryOptions {
* @default - undefined
*/
readonly disable?: boolean;

/**
* Report telemetry opt-in/out status
*
* @default - undefined
*/
readonly status?: boolean;
}
4 changes: 2 additions & 2 deletions packages/aws-cdk/test/cli/cli-commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ describe('context', () => {
});

describe('cli-telemetry', () => {
test('requires either --enable or --disable flag', async () => {
test('requires a flag to be set', async () => {
await expect(exec(['cli-telemetry']))
.rejects
.toThrow("Must specify either '--enable' or '--disable'");
.toThrow("Must specify '--enable', '--disable', or '--status'");
});
});
35 changes: 35 additions & 0 deletions packages/aws-cdk/test/cli/telemetry/collect-telemetry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Context } from '../../../lib/api/context';
import { canCollectTelemetry } from '../../../lib/cli/telemetry/collect-telemetry';

describe(canCollectTelemetry, () => {
let context: Context;

beforeEach(() => {
context = new Context();
});

test('returns true by default', async () => {
expect(canCollectTelemetry(context)).toBeTruthy();
});

test('returns false if env variable is set', async () => {
process.env.DISABLE_CLI_TELEMETRY = 'true';
expect(canCollectTelemetry(context)).toBeTruthy();
});

test('returns false if context is set to false', async () => {
context.set('cli-telemetry', false);
expect(canCollectTelemetry(context)).toBeFalsy();

context.set('cli-telemetry', 'false');
expect(canCollectTelemetry(context)).toBeFalsy();
});

test('returns true if context is set to true', async () => {
context.set('cli-telemetry', true);
expect(canCollectTelemetry(context)).toBeTruthy();

context.set('cli-telemetry', 'true');
expect(canCollectTelemetry(context)).toBeTruthy();
});
});
37 changes: 37 additions & 0 deletions packages/aws-cdk/test/commands/telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,41 @@ describe('telemetry command', () => {
expect(configuration.context.get('cli-telemetry')).toBe(false);
expect(info).toHaveBeenCalledWith('Telemetry disabled');
});

test('status reports current telemetry status -- enabled by default', async () => {
// WHEN
await toolkit.cliTelemetryStatus();

// THEN
expect(info).toHaveBeenCalledWith('CLI Telemetry is enabled. Run \'cdk cli-telemetry --disable\' to disable for this CDK App.');
});

test('status reports current telemetry status -- enabled intentionally', async () => {
// WHEN
configuration.context.set('cli-telemetry', true);
await toolkit.cliTelemetryStatus();

// THEN
expect(info).toHaveBeenCalledWith('CLI Telemetry is enabled. Run \'cdk cli-telemetry --disable\' to disable for this CDK App.');
});

test('status reports current telemetry status -- disabled via context', async () => {
// WHEN
configuration.context.set('cli-telemetry', false);
await toolkit.cliTelemetryStatus();

// THEN
expect(info).toHaveBeenCalledWith('CLI Telemetry is disabled. Run \'cdk cli-telemetry --enable\' to enable for this CDK App.');
});

test('status reports current telemetry status -- disabled via env var', async () => {
// WHEN
const CDK_CLI_DISABLE_TELEMETRY = process.env.CDK_CLI_DISABLE_TELEMETRY;
process.env.CDK_CLI_DISABLE_TELEMETRY = 'true';
await toolkit.cliTelemetryStatus();

// THEN
expect(info).toHaveBeenCalledWith('CLI Telemetry is disabled. Run \'cdk cli-telemetry --enable\' to enable for this CDK App.');
process.env.CDK_CLI_DISABLE_TELEMETRY = CDK_CLI_DISABLE_TELEMETRY;
});
});