Skip to content

Commit a36d24a

Browse files
committed
fix: support pass-through of container runtime opts
1 parent 8568cf4 commit a36d24a

4 files changed

Lines changed: 153 additions & 0 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ PythonFunction(
109109
- Use `bundling` to pass Docker environment variables, asset excludes, build args, command hooks, or a custom builder image.
110110
- Set `bundling.buildArgs.BUNDLING_IMAGE` to swap the Python base image used by the default builder.
111111
- Set `bundling.image` to provide a fully custom builder image.
112+
- `bundling.volumes`, `bundling.volumesFrom`, `bundling.network`, and `bundling.securityOpt` are applied to the reusable builder container.
113+
- `bundling.entrypoint`, `bundling.command`, `bundling.workingDirectory`, and `bundling.platform` are deprecated in this construct and will emit warnings if used.
112114
- See [API.md](API.md) for the full API reference.
113115

114116
## Customizing The Builder Image
@@ -135,6 +137,12 @@ Alpine-based images.
135137

136138
If you need full control over the builder container, pass `bundling.image` instead. Custom images must include Python, `uv`, and the `/opt/uv-python-lambda` scripts expected by this library.
137139

140+
For the default builder container, this construct also supports a safe subset
141+
of Docker run options: `volumes`, `volumesFrom`, `network`, and
142+
`securityOpt`. Other generic Docker run options such as `entrypoint`,
143+
`command`, `workingDirectory`, and `platform` do not fit the reusable
144+
builder-container model and are deprecated here.
145+
138146
```ts
139147
import { DockerImage } from 'aws-cdk-lib';
140148

src/bundling.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export class Bundling {
113113
}
114114

115115
public readonly entrypoint?: string[];
116+
public readonly command?: string[];
116117
public readonly volumes?: DockerVolume[];
117118
public readonly volumesFrom?: string[];
118119
public readonly environment?: { [key: string]: string };
@@ -132,8 +133,10 @@ export class Bundling {
132133
private readonly props: BundlingProps;
133134

134135
constructor(props: BundlingProps) {
136+
warnForUnsupportedOptions(props);
135137
this.props = props;
136138
this.entrypoint = props.entrypoint;
139+
this.command = props.command;
137140
this.volumes = props.volumes;
138141
this.volumesFrom = props.volumesFrom;
139142
this.environment = props.environment;
@@ -196,6 +199,24 @@ export class Bundling {
196199
dockerArgs.push('--user', builderUser);
197200
}
198201

202+
if (this.network) {
203+
dockerArgs.push('--network', this.network);
204+
}
205+
206+
if (this.securityOpt) {
207+
dockerArgs.push('--security-opt', this.securityOpt);
208+
}
209+
210+
for (const volume of this.volumes ?? []) {
211+
const mount = `${volume.hostPath}:${volume.containerPath}`;
212+
const consistency = volume.consistency ? `:${volume.consistency}` : '';
213+
dockerArgs.push('-v', `${mount}${consistency}`);
214+
}
215+
216+
for (const volumeSource of this.volumesFrom ?? []) {
217+
dockerArgs.push('--volumes-from', volumeSource);
218+
}
219+
199220
for (const [name, value] of this.getBuilderEnvironmentEntries()) {
200221
dockerArgs.push('--env', `${name}=${value}`);
201222
}
@@ -358,3 +379,40 @@ function getDockerUserArg() {
358379

359380
return `${process.getuid()}:${process.getgid()}`;
360381
}
382+
383+
function warnForUnsupportedOptions(props: BundlingProps) {
384+
if (props.entrypoint) {
385+
emitDeprecatedBundlingOptionWarning(
386+
'entrypoint',
387+
'This construct manages the reusable builder container entrypoint internally. Use bundling.image for a fully custom builder image.',
388+
);
389+
}
390+
391+
if (props.command) {
392+
emitDeprecatedBundlingOptionWarning(
393+
'command',
394+
'This construct manages the reusable builder container command internally. Use bundling.image for a fully custom builder image.',
395+
);
396+
}
397+
398+
if (props.workingDirectory) {
399+
emitDeprecatedBundlingOptionWarning(
400+
'workingDirectory',
401+
'The reusable builder container manages its own working directory. Use command hooks or bundling.image if you need different behavior.',
402+
);
403+
}
404+
405+
if (props.platform) {
406+
emitDeprecatedBundlingOptionWarning(
407+
'platform',
408+
'The builder image platform is derived from the Lambda architecture option. Set architecture instead.',
409+
);
410+
}
411+
}
412+
413+
function emitDeprecatedBundlingOptionWarning(option: string, details: string) {
414+
process.emitWarning(
415+
`bundling.${option} is deprecated and ignored. ${details}`,
416+
'DeprecationWarning',
417+
);
418+
}

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import type {
77

88
/**
99
* Options for bundling
10+
*
11+
* This construct applies `environment`, `user`, `volumes`, `volumesFrom`,
12+
* `network`, and `securityOpt` to its reusable builder container.
13+
*
14+
* The inherited `entrypoint`, `command`, `workingDirectory`, and `platform`
15+
* options do not fit this builder-container model and are ignored with a
16+
* deprecation warning at runtime.
1017
*/
1118
export interface BundlingOptions extends DockerRunOptions {
1219
/**

test/bundling.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,42 @@ describe('Bundling', () => {
4444
jest.restoreAllMocks();
4545
});
4646

47+
test('warns when deprecated unsupported bundling options are used', () => {
48+
const emitWarningSpy = jest
49+
.spyOn(process, 'emitWarning')
50+
.mockImplementation(() => undefined);
51+
52+
new Bundling({
53+
rootDir: '/tmp/project-deprecated-options',
54+
runtime: Runtime.PYTHON_3_12,
55+
architecture: Architecture.X86_64,
56+
entrypoint: ['/bin/sh', '-c'],
57+
command: ['echo', 'hello'],
58+
workingDirectory: '/tmp',
59+
platform: 'linux/amd64',
60+
});
61+
62+
expect(emitWarningSpy).toHaveBeenCalledTimes(4);
63+
expect(emitWarningSpy).toHaveBeenCalledWith(
64+
expect.stringContaining('bundling.entrypoint is deprecated and ignored'),
65+
'DeprecationWarning',
66+
);
67+
expect(emitWarningSpy).toHaveBeenCalledWith(
68+
expect.stringContaining('bundling.command is deprecated and ignored'),
69+
'DeprecationWarning',
70+
);
71+
expect(emitWarningSpy).toHaveBeenCalledWith(
72+
expect.stringContaining(
73+
'bundling.workingDirectory is deprecated and ignored',
74+
),
75+
'DeprecationWarning',
76+
);
77+
expect(emitWarningSpy).toHaveBeenCalledWith(
78+
expect.stringContaining('bundling.platform is deprecated and ignored'),
79+
'DeprecationWarning',
80+
);
81+
});
82+
4783
test('returns a no-op command when bundling is skipped', () => {
4884
const bundling = new Bundling({
4985
rootDir: '/tmp/project',
@@ -346,4 +382,48 @@ describe('Bundling', () => {
346382
}),
347383
);
348384
});
385+
386+
test('passes supported docker run options to the builder container', () => {
387+
const ensureBuilderContainerMock = jest.fn();
388+
const bundlingModule = loadBundlingModule(ensureBuilderContainerMock);
389+
jest
390+
.spyOn(DockerImage, 'fromBuild')
391+
.mockReturnValue({ image: 'mock-image' } as DockerImage);
392+
393+
const bundling = new bundlingModule.Bundling({
394+
rootDir: '/tmp/project-run-options',
395+
runtime: Runtime.PYTHON_3_12,
396+
architecture: Architecture.X86_64,
397+
network: 'test-network',
398+
securityOpt: 'label=disable',
399+
volumes: [
400+
{
401+
hostPath: '/tmp/cache',
402+
containerPath: '/cache',
403+
},
404+
],
405+
volumesFrom: ['shared-container'],
406+
});
407+
408+
Reflect.get(bundling, 'ensureBuilderReady').call(
409+
bundling,
410+
'/tmp/cdk-run-options',
411+
);
412+
413+
expect(ensureBuilderContainerMock).toHaveBeenCalledWith(
414+
expect.objectContaining({
415+
args: expect.arrayContaining([
416+
'--network',
417+
'test-network',
418+
'--security-opt',
419+
'label=disable',
420+
'-v',
421+
'/tmp/cache:/cache',
422+
'--volumes-from',
423+
'shared-container',
424+
'mock-image',
425+
]),
426+
}),
427+
);
428+
});
349429
});

0 commit comments

Comments
 (0)