From 6597a09310fbc13d43389eca91b0e4b26f8ca680 Mon Sep 17 00:00:00 2001 From: kirintw Date: Tue, 9 Feb 2021 20:27:02 +0800 Subject: [PATCH] feat(core): configure bundling docker entrypoint (#12660) Allow customizing the docker entrypoint for the bundling image. Note that the option `entrypoint` is a `string[]` which match up with nearly every container runtime. The problem is that `docker run` 's `--entrypoint` only accepts a string. If the entrypoint we specify is `["/bin/sh", "-c"]`, the final command should be like: `docker run --entrypoint /bin/sh some-img -c some commands`. I have do some experiments to prove this: ```sh $ docker run -it --rm --name test --entrypoint /bin/sh -c alpine ls invalid argument "alpine" for "-c, --cpu-shares" flag: strconv.ParseInt: parsing "alpine": invalid syntax See 'docker run --help'. $docker run -it --rm --name test --entrypoint "/bin/sh -c" alpine ls docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/bin/sh -c\": stat /bin/sh -c: no such file or directory": unknown. $docker run -it --rm --name test --entrypoint /bin/sh alpine -c ls bin etc lib mnt proc run srv tmp var dev home media opt root sbin sys usr ``` One more thing: if one specify custom entrypoint but no custom commands, the default cmd for the image will not be presented. I do think it's a expected behavior for container runtime through. References: * [kubernetes - Define a Command and Arguments for a Container](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes) close #11984 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-s3-assets/README.md | 1 + packages/@aws-cdk/core/lib/bundling.ts | 31 +++++++++++++++- packages/@aws-cdk/core/test/bundling.test.ts | 38 ++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-s3-assets/README.md b/packages/@aws-cdk/aws-s3-assets/README.md index 3d508070d6a50..aab4c46d9c44d 100644 --- a/packages/@aws-cdk/aws-s3-assets/README.md +++ b/packages/@aws-cdk/aws-s3-assets/README.md @@ -115,6 +115,7 @@ new assets.Asset(this, 'BundledAsset', { }, // Docker bundling fallback image: BundlingDockerImage.fromRegistry('alpine'), + entrypoint: ['/bin/sh', '-c'], command: ['bundle'], }, }); diff --git a/packages/@aws-cdk/core/lib/bundling.ts b/packages/@aws-cdk/core/lib/bundling.ts index c9a9c07e77f34..b1247fd913ea0 100644 --- a/packages/@aws-cdk/core/lib/bundling.ts +++ b/packages/@aws-cdk/core/lib/bundling.ts @@ -13,6 +13,17 @@ export interface BundlingOptions { */ readonly image: BundlingDockerImage; + /** + * The entrypoint to run in the Docker container. + * + * @example ['/bin/sh', '-c'] + * + * @see https://docs.docker.com/engine/reference/builder/#entrypoint + * + * @default - run the entrypoint defined in the image + */ + readonly entrypoint?: string[]; + /** * The command to run in the Docker container. * @@ -152,7 +163,15 @@ export class BundlingDockerImage { public run(options: DockerRunOptions = {}) { const volumes = options.volumes || []; const environment = options.environment || {}; - const command = options.command || []; + const entrypoint = options.entrypoint?.[0] || null; + const command = [ + ...options.entrypoint?.[1] + ? [...options.entrypoint.slice(1)] + : [], + ...options.command + ? [...options.command] + : [], + ]; const dockerArgs: string[] = [ 'run', '--rm', @@ -164,6 +183,9 @@ export class BundlingDockerImage { ...options.workingDirectory ? ['-w', options.workingDirectory] : [], + ...entrypoint + ? ['--entrypoint', entrypoint] + : [], this.image, ...command, ]; @@ -238,6 +260,13 @@ export enum DockerVolumeConsistency { * Docker run options */ export interface DockerRunOptions { + /** + * The entrypoint to run in the container. + * + * @default - run the entrypoint defined in the image + */ + readonly entrypoint?: string[]; + /** * The command to run in the container. * diff --git a/packages/@aws-cdk/core/test/bundling.test.ts b/packages/@aws-cdk/core/test/bundling.test.ts index e2b0d6b43b98b..258860d65585c 100644 --- a/packages/@aws-cdk/core/test/bundling.test.ts +++ b/packages/@aws-cdk/core/test/bundling.test.ts @@ -171,6 +171,44 @@ nodeunitShim({ test.done(); }, + 'custom entrypoint is passed through to docker exec'(test: Test) { + const spawnSyncStub = sinon.stub(child_process, 'spawnSync').returns({ + status: 0, + stderr: Buffer.from('stderr'), + stdout: Buffer.from('stdout'), + pid: 123, + output: ['stdout', 'stderr'], + signal: null, + }); + + const image = BundlingDockerImage.fromRegistry('alpine'); + image.run({ + entrypoint: ['/cool/entrypoint', '--cool-entrypoint-arg'], + command: ['cool', 'command'], + environment: { + VAR1: 'value1', + VAR2: 'value2', + }, + volumes: [{ hostPath: '/host-path', containerPath: '/container-path' }], + workingDirectory: '/working-directory', + user: 'user:group', + }); + + test.ok(spawnSyncStub.calledWith('docker', [ + 'run', '--rm', + '-u', 'user:group', + '-v', '/host-path:/container-path:delegated', + '--env', 'VAR1=value1', + '--env', 'VAR2=value2', + '-w', '/working-directory', + '--entrypoint', '/cool/entrypoint', + 'alpine', + '--cool-entrypoint-arg', + 'cool', 'command', + ], { stdio: ['ignore', process.stderr, 'inherit'] })); + test.done(); + }, + 'cp utility copies from an image'(test: Test) { // GIVEN const containerId = '1234567890abcdef1234567890abcdef';