diff --git a/packages/@aws-cdk/core/lib/private/asset-staging.ts b/packages/@aws-cdk/core/lib/private/asset-staging.ts index 27cfbfc604152..9f6e6b89e2452 100644 --- a/packages/@aws-cdk/core/lib/private/asset-staging.ts +++ b/packages/@aws-cdk/core/lib/private/asset-staging.ts @@ -199,6 +199,7 @@ export class AssetBundlingVolumeCopy extends AssetBundlingBase { export function dockerExec(args: string[], options?: SpawnSyncOptions) { const prog = process.env.CDK_DOCKER ?? 'docker'; const proc = spawnSync(prog, args, options ?? { + encoding: 'utf-8', stdio: [ // show Docker output 'ignore', // ignore stdio process.stderr, // redirect stdout to stderr @@ -211,10 +212,25 @@ export function dockerExec(args: string[], options?: SpawnSyncOptions) { } if (proc.status !== 0) { - if (proc.stdout || proc.stderr) { - throw new Error(`[Status ${proc.status}] stdout: ${proc.stdout?.toString().trim()}\n\n\nstderr: ${proc.stderr?.toString().trim()}`); + const reason = proc.signal != null + ? `signal ${proc.signal}` + : `status ${proc.status}`; + const command = [prog, ...args.map((arg) => /[^a-z0-9_-]/i.test(arg) ? JSON.stringify(arg) : arg)].join(' '); + + function prependLines(firstLine: string, text: Buffer | string | undefined): string[] { + if (!text || text.length === 0) { + return []; + } + const padding = ' '.repeat(firstLine.length); + return text.toString('utf-8').split('\n').map((line, idx) => `${idx === 0 ? firstLine : padding}${line}`); } - throw new Error(`${prog} exited with status ${proc.status}`); + + throw new Error([ + `${prog} exited with ${reason}`, + ...prependLines('--> STDOUT: ', proc.stdout ) ?? [], + ...prependLines('--> STDERR: ', proc.stderr ) ?? [], + `--> Command: ${command}`, + ].join('\n')); } return proc; diff --git a/packages/@aws-cdk/core/test/bundling.test.ts b/packages/@aws-cdk/core/test/bundling.test.ts index fe2e0cea608f7..4fa1a29c49d89 100644 --- a/packages/@aws-cdk/core/test/bundling.test.ts +++ b/packages/@aws-cdk/core/test/bundling.test.ts @@ -44,7 +44,7 @@ describe('bundling', () => { '-w', '/working-directory', 'alpine', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('bundling with image from asset', () => { @@ -188,7 +188,7 @@ describe('bundling', () => { }); const image = DockerImage.fromRegistry('alpine'); - expect(() => image.run()).toThrow(/\[Status -1\]/); + expect(() => image.run()).toThrow(/exited with status -1/); }); test('BundlerDockerImage json is the bundler image name by default', () => { @@ -294,7 +294,7 @@ describe('bundling', () => { 'alpine', '--cool-entrypoint-arg', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('cp utility copies from an image', () => { @@ -404,7 +404,7 @@ describe('bundling', () => { '-w', '/working-directory', 'alpine', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('adding user provided network options', () => { @@ -437,7 +437,7 @@ describe('bundling', () => { '-w', '/working-directory', 'alpine', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('adding user provided docker volume options', () => { @@ -475,7 +475,7 @@ describe('bundling', () => { '-w', '/working-directory', 'alpine', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('ensure selinux docker mount', () => { @@ -516,7 +516,7 @@ describe('bundling', () => { '-w', '/working-directory', 'alpine', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('ensure selinux docker mount on linux with selinux disabled', () => { @@ -557,7 +557,7 @@ describe('bundling', () => { '-w', '/working-directory', 'alpine', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('ensure no selinux docker mount if selinuxenabled isn\'t an available command', () => { @@ -598,7 +598,7 @@ describe('bundling', () => { '-w', '/working-directory', 'alpine', 'cool', 'command', - ], { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ], { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); test('ensure correct Docker CLI arguments are returned', () => { diff --git a/packages/@aws-cdk/core/test/private/asset-staging.test.ts b/packages/@aws-cdk/core/test/private/asset-staging.test.ts index adcbf71ffe5d9..4d29406552dfc 100644 --- a/packages/@aws-cdk/core/test/private/asset-staging.test.ts +++ b/packages/@aws-cdk/core/test/private/asset-staging.test.ts @@ -33,20 +33,20 @@ describe('bundling', () => { // volume Creation expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ 'volume', 'create', sinon.match(/assetInput.*/g), - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ 'volume', 'create', sinon.match(/assetOutput.*/g), - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); // volume removal expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ 'volume', 'rm', sinon.match(/assetInput.*/g), - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ 'volume', 'rm', sinon.match(/assetOutput.*/g), - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); // prepare copy container expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ @@ -58,29 +58,29 @@ describe('bundling', () => { 'sh', '-c', `mkdir -p ${AssetStaging.BUNDLING_INPUT_DIR} && chown -R ${options.user} ${AssetStaging.BUNDLING_OUTPUT_DIR} && chown -R ${options.user} ${AssetStaging.BUNDLING_INPUT_DIR}`, - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); // delete copy container expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ 'rm', sinon.match(/copyContainer.*/g), - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); // copy files to copy container expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ 'cp', `${options.sourcePath}/.`, `${helper.copyContainerName}:${AssetStaging.BUNDLING_INPUT_DIR}`, - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); // copy files from copy container to host expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match([ 'cp', `${helper.copyContainerName}:${AssetStaging.BUNDLING_OUTPUT_DIR}/.`, options.bundleDir, - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); // actual docker run expect(spawnSyncStub.calledWith(DOCKER_CMD, sinon.match.array.contains([ 'run', '--rm', '--volumes-from', helper.copyContainerName, 'alpine', - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); @@ -109,6 +109,6 @@ describe('bundling', () => { 'run', '--rm', '-v', 'alpine', - ]), { stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); + ]), { encoding: 'utf-8', stdio: ['ignore', process.stderr, 'inherit'] })).toEqual(true); }); });