Skip to content

Commit

Permalink
fix(cli): large context causes E2BIG error during synthesis on Linux (a…
Browse files Browse the repository at this point in the history
…ws#21230)

Instead of passing the context in an environment variable, the CLI now writes the context to a temporary file and sets an environment variable only with the location. The app then uses that location to read from the file.

Also tested manually on a Linux machine.

Fixes aws#19261.

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
otaviomacedo authored and mrgrain committed Jul 28, 2022
1 parent 9d12f55 commit 5607286
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 14 deletions.
20 changes: 14 additions & 6 deletions packages/@aws-cdk/core/lib/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as cxapi from '@aws-cdk/cx-api';
import { Construct } from 'constructs';
import * as fs from 'fs-extra';
import { addCustomSynthesis, ICustomSynthesis } from './private/synthesis';
import { TreeMetadata } from './private/tree-metadata';
import { Stage } from './stage';
Expand Down Expand Up @@ -142,16 +143,23 @@ export class App extends Stage {
this.node.setContext(k, v);
}

// read from environment
const contextJson = process.env[cxapi.CONTEXT_ENV];
const contextFromEnvironment = contextJson
? JSON.parse(contextJson)
: { };

for (const [k, v] of Object.entries(contextFromEnvironment)) {
const context = this.readContextFromTempFile() ?? this.readContextFromEnvironment() ?? {};
for (const [k, v] of Object.entries(context)) {
this.node.setContext(k, v);
}
}

private readContextFromTempFile() {
const location = process.env[cxapi.CONTEXT_LOCATION_ENV];
return location != null ? fs.readJSONSync(location) : undefined;
}

// for backward compatibility with old versions of the CLI
private readContextFromEnvironment() {
const contextJson = process.env[cxapi.CONTEXT_ENV];
return contextJson ? JSON.parse(contextJson) : undefined;
}
}

/**
Expand Down
37 changes: 32 additions & 5 deletions packages/@aws-cdk/core/test/app.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as os from 'os';
import * as path from 'path';
import { ContextProvider } from '@aws-cdk/cloud-assembly-schema';
import * as cxapi from '@aws-cdk/cx-api';
import { Construct } from 'constructs';
import * as fs from 'fs-extra';
import { CfnResource, DefaultStackSynthesizer, Stack, StackProps } from '../lib';
import { Annotations } from '../lib/annotations';
import { App, AppProps } from '../lib/app';
Expand Down Expand Up @@ -101,25 +104,49 @@ describe('app', () => {
});
});

test('context can be passed through CDK_CONTEXT', () => {
process.env[cxapi.CONTEXT_ENV] = JSON.stringify({
test('context can be passed through CDK_CONTEXT_LOCATION', async () => {
const contextDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cdk-context'));
const contextLocation = path.join(contextDir, 'context-temp.json');
fs.writeJSONSync(contextLocation, {
key1: 'val1',
key2: 'val2',
});
process.env[cxapi.CONTEXT_LOCATION_ENV] = contextLocation;

const prog = new App();
expect(prog.node.tryGetContext('key1')).toEqual('val1');
expect(prog.node.tryGetContext('key2')).toEqual('val2');
});

test('context passed through CDK_CONTEXT has precedence', () => {
test('context can be passed through CDK_CONTEXT', async () => {
process.env[cxapi.CONTEXT_ENV] = JSON.stringify({
key1: 'val1',
key2: 'val2',
});

const prog = new App();
expect(prog.node.tryGetContext('key1')).toEqual('val1');
expect(prog.node.tryGetContext('key2')).toEqual('val2');
});

test('context passed through CDK_CONTEXT_LOCATION has precedence', async () => {
const contextDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cdk-context'));
const contextLocation = path.join(contextDir, 'context-temp.json');
fs.writeJSONSync(contextLocation, {
key1: 'val1',
key2: 'val2',
});
process.env[cxapi.CONTEXT_LOCATION_ENV] = contextLocation;

process.env[cxapi.CONTEXT_ENV] = JSON.stringify({
key1: 'val3',
key2: 'val4',
});

const prog = new App({
context: {
key1: 'val3',
key2: 'val4',
key1: 'val5',
key2: 'val6',
},
});
expect(prog.node.tryGetContext('key1')).toEqual('val1');
Expand Down
5 changes: 5 additions & 0 deletions packages/@aws-cdk/cx-api/lib/cxapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
export const OUTDIR_ENV = 'CDK_OUTDIR';
export const CONTEXT_ENV = 'CDK_CONTEXT_JSON';

/**
* The name of the temporary file where the context is stored.
*/
export const CONTEXT_LOCATION_ENV = 'CDK_CONTEXT_LOCATION';

/**
* Environment variable set by the CDK CLI with the default AWS account ID.
*/
Expand Down
13 changes: 11 additions & 2 deletions packages/aws-cdk/lib/api/cxapp/exec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as childProcess from 'child_process';
import * as os from 'os';
import * as path from 'path';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import * as cxapi from '@aws-cdk/cx-api';
Expand Down Expand Up @@ -44,7 +45,11 @@ export async function execProgram(aws: SdkProvider, config: Configuration): Prom
context[cxapi.BUNDLING_STACKS] = bundlingStacks;

debug('context:', context);
env[cxapi.CONTEXT_ENV] = JSON.stringify(context);

const contextDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cdk-context'));
const contextLocation = path.join(contextDir, 'context-temp.json');
fs.writeJSONSync(contextLocation, context);
env[cxapi.CONTEXT_LOCATION_ENV] = contextLocation;

const build = config.settings.get(['build']);
if (build) {
Expand Down Expand Up @@ -85,7 +90,11 @@ export async function execProgram(aws: SdkProvider, config: Configuration): Prom

await exec(commandLine.join(' '));

return createAssembly(outdir);
const assembly = createAssembly(outdir);

fs.removeSync(path.dirname(contextLocation));

return assembly;

function createAssembly(appDir: string) {
try {
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-cdk/lib/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function displayCdkEnvironmentVariables() {
print('ℹ️ CDK environment variables:');
let healthy = true;
for (const key of keys.sort()) {
if (key === cxapi.CONTEXT_ENV || key === cxapi.OUTDIR_ENV) {
if (key === cxapi.CONTEXT_ENV || key === cxapi.CONTEXT_LOCATION_ENV || key === cxapi.OUTDIR_ENV) {
print(` - ${chalk.red(key)} = ${chalk.green(process.env[key]!)} (⚠️ reserved for use by the CDK toolkit)`);
healthy = false;
} else {
Expand Down

0 comments on commit 5607286

Please sign in to comment.