Skip to content

Commit 1105514

Browse files
authored
chore: improve error reporting for asset staging (#24596)
Ensure the asset staging error reporting includes the full command being run, which helps with troubleshooting failures. Also, indent the content of `STDOUT` and `STDERR` to facilitate reading when those traces are long. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 30beb10 commit 1105514

File tree

3 files changed

+38
-22
lines changed

3 files changed

+38
-22
lines changed

packages/@aws-cdk/core/lib/private/asset-staging.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export class AssetBundlingVolumeCopy extends AssetBundlingBase {
199199
export function dockerExec(args: string[], options?: SpawnSyncOptions) {
200200
const prog = process.env.CDK_DOCKER ?? 'docker';
201201
const proc = spawnSync(prog, args, options ?? {
202+
encoding: 'utf-8',
202203
stdio: [ // show Docker output
203204
'ignore', // ignore stdio
204205
process.stderr, // redirect stdout to stderr
@@ -211,10 +212,25 @@ export function dockerExec(args: string[], options?: SpawnSyncOptions) {
211212
}
212213

213214
if (proc.status !== 0) {
214-
if (proc.stdout || proc.stderr) {
215-
throw new Error(`[Status ${proc.status}] stdout: ${proc.stdout?.toString().trim()}\n\n\nstderr: ${proc.stderr?.toString().trim()}`);
215+
const reason = proc.signal != null
216+
? `signal ${proc.signal}`
217+
: `status ${proc.status}`;
218+
const command = [prog, ...args.map((arg) => /[^a-z0-9_-]/i.test(arg) ? JSON.stringify(arg) : arg)].join(' ');
219+
220+
function prependLines(firstLine: string, text: Buffer | string | undefined): string[] {
221+
if (!text || text.length === 0) {
222+
return [];
223+
}
224+
const padding = ' '.repeat(firstLine.length);
225+
return text.toString('utf-8').split('\n').map((line, idx) => `${idx === 0 ? firstLine : padding}${line}`);
216226
}
217-
throw new Error(`${prog} exited with status ${proc.status}`);
227+
228+
throw new Error([
229+
`${prog} exited with ${reason}`,
230+
...prependLines('--> STDOUT: ', proc.stdout ) ?? [],
231+
...prependLines('--> STDERR: ', proc.stderr ) ?? [],
232+
`--> Command: ${command}`,
233+
].join('\n'));
218234
}
219235

220236
return proc;

packages/@aws-cdk/core/test/bundling.test.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('bundling', () => {
4444
'-w', '/working-directory',
4545
'alpine',
4646
'cool', 'command',
47-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
47+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
4848
});
4949

5050
test('bundling with image from asset', () => {
@@ -188,7 +188,7 @@ describe('bundling', () => {
188188
});
189189

190190
const image = DockerImage.fromRegistry('alpine');
191-
expect(() => image.run()).toThrow(/\[Status -1\]/);
191+
expect(() => image.run()).toThrow(/exited with status -1/);
192192
});
193193

194194
test('BundlerDockerImage json is the bundler image name by default', () => {
@@ -294,7 +294,7 @@ describe('bundling', () => {
294294
'alpine',
295295
'--cool-entrypoint-arg',
296296
'cool', 'command',
297-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
297+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
298298
});
299299

300300
test('cp utility copies from an image', () => {
@@ -404,7 +404,7 @@ describe('bundling', () => {
404404
'-w', '/working-directory',
405405
'alpine',
406406
'cool', 'command',
407-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
407+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
408408
});
409409

410410
test('adding user provided network options', () => {
@@ -437,7 +437,7 @@ describe('bundling', () => {
437437
'-w', '/working-directory',
438438
'alpine',
439439
'cool', 'command',
440-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
440+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
441441
});
442442

443443
test('adding user provided docker volume options', () => {
@@ -475,7 +475,7 @@ describe('bundling', () => {
475475
'-w', '/working-directory',
476476
'alpine',
477477
'cool', 'command',
478-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
478+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
479479
});
480480

481481
test('ensure selinux docker mount', () => {
@@ -516,7 +516,7 @@ describe('bundling', () => {
516516
'-w', '/working-directory',
517517
'alpine',
518518
'cool', 'command',
519-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
519+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
520520
});
521521

522522
test('ensure selinux docker mount on linux with selinux disabled', () => {
@@ -557,7 +557,7 @@ describe('bundling', () => {
557557
'-w', '/working-directory',
558558
'alpine',
559559
'cool', 'command',
560-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
560+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
561561
});
562562

563563
test('ensure no selinux docker mount if selinuxenabled isn\'t an available command', () => {
@@ -598,7 +598,7 @@ describe('bundling', () => {
598598
'-w', '/working-directory',
599599
'alpine',
600600
'cool', 'command',
601-
], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
601+
], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
602602
});
603603

604604
test('ensure correct Docker CLI arguments are returned', () => {

packages/@aws-cdk/core/test/private/asset-staging.test.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,20 @@ describe('bundling', () => {
3333
// volume Creation
3434
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
3535
'volume', 'create', sinon.match(/assetInput.*/g),
36-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
36+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
3737

3838
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
3939
'volume', 'create', sinon.match(/assetOutput.*/g),
40-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
40+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
4141

4242
// volume removal
4343
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
4444
'volume', 'rm', sinon.match(/assetInput.*/g),
45-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
45+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
4646

4747
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
4848
'volume', 'rm', sinon.match(/assetOutput.*/g),
49-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
49+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
5050

5151
// prepare copy container
5252
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
@@ -58,29 +58,29 @@ describe('bundling', () => {
5858
'sh',
5959
'-c',
6060
`mkdir -p ${AssetStaging.BUNDLING_INPUT_DIR} && chown -R ${options.user} ${AssetStaging.BUNDLING_OUTPUT_DIR} && chown -R ${options.user} ${AssetStaging.BUNDLING_INPUT_DIR}`,
61-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
61+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
6262

6363
// delete copy container
6464
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
6565
'rm', sinon.match(/copyContainer.*/g),
66-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
66+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
6767

6868
// copy files to copy container
6969
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
7070
'cp', `${options.sourcePath}/.`, `${helper.copyContainerName}:${AssetStaging.BUNDLING_INPUT_DIR}`,
71-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
71+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
7272

7373
// copy files from copy container to host
7474
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([
7575
'cp', `${helper.copyContainerName}:${AssetStaging.BUNDLING_OUTPUT_DIR}/.`, options.bundleDir,
76-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
76+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
7777

7878
// actual docker run
7979
expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match.array.contains([
8080
'run', '--rm',
8181
'--volumes-from', helper.copyContainerName,
8282
'alpine',
83-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
83+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
8484

8585
});
8686

@@ -109,6 +109,6 @@ describe('bundling', () => {
109109
'run', '--rm',
110110
'-v',
111111
'alpine',
112-
]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
112+
]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true);
113113
});
114114
});

0 commit comments

Comments
 (0)