Skip to content

Commit 1b25a4b

Browse files
iliapolomergify[bot]
authored andcommitted
feat(ecr-assets): custom docker files (#5652)
* support custom docker files * revert tsconfig changes * doc strings modification according to conventions
1 parent 3b5959f commit 1b25a4b

File tree

10 files changed

+156
-4
lines changed

10 files changed

+156
-4
lines changed

packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export interface DockerImageAssetProps extends assets.CopyOptions {
3939
* @default - no target
4040
*/
4141
readonly target?: string;
42+
43+
/**
44+
* Path to the Dockerfile (relative to the directory).
45+
*
46+
* @default 'Dockerfile'
47+
*/
48+
readonly file?: string;
4249
}
4350

4451
/**
@@ -71,8 +78,11 @@ export class DockerImageAsset extends Construct implements assets.IAsset {
7178
if (!fs.existsSync(dir)) {
7279
throw new Error(`Cannot find image directory at ${dir}`);
7380
}
74-
if (!fs.existsSync(path.join(dir, 'Dockerfile'))) {
75-
throw new Error(`No 'Dockerfile' found in ${dir}`);
81+
82+
// validate the docker file exists
83+
const file = path.join(dir, props.file || 'Dockerfile');
84+
if (!fs.existsSync(file)) {
85+
throw new Error(`Cannot find file at ${file}`);
7686
}
7787

7888
let exclude: string[] = props.exclude || [];
@@ -96,6 +106,7 @@ export class DockerImageAsset extends Construct implements assets.IAsset {
96106
directoryName: staging.stagedPath,
97107
dockerBuildArgs: props.buildArgs,
98108
dockerBuildTarget: props.target,
109+
dockerFile: file,
99110
repositoryName: props.repositoryName || `cdk/${this.node.uniqueId.replace(/[:/]/g, '-').toLowerCase()}`,
100111
sourceHash: staging.sourceHash
101112
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM python:3.6
2+
EXPOSE 8000
3+
WORKDIR /src
4+
ADD . /src
5+
CMD python3 index.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/python
2+
import sys
3+
import textwrap
4+
import http.server
5+
import socketserver
6+
7+
PORT = 8000
8+
9+
10+
class Handler(http.server.SimpleHTTPRequestHandler):
11+
def do_GET(self):
12+
self.send_response(200)
13+
self.send_header('Content-Type', 'text/html')
14+
self.end_headers()
15+
self.wfile.write(textwrap.dedent('''\
16+
<!doctype html>
17+
<html><head><title>It works</title></head>
18+
<body>
19+
<h1>Hello from the integ test container</h1>
20+
<p>This container got built and started as part of the integ test.</p>
21+
<img src="https://media.giphy.com/media/nFjDu1LjEADh6/giphy.gif">
22+
</body>
23+
''').encode('utf-8'))
24+
25+
26+
def main():
27+
httpd = http.server.HTTPServer(("", PORT), Handler)
28+
print("serving at port", PORT)
29+
httpd.serve_forever()
30+
31+
32+
if __name__ == '__main__':
33+
main()

packages/@aws-cdk/aws-ecr-assets/test/test.image-asset.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ export = {
8787
test.done();
8888
},
8989

90+
'with file'(test: Test) {
91+
// GIVEN
92+
const stack = new Stack();
93+
94+
const directoryPath = path.join(__dirname, 'demo-image-custom-docker-file');
95+
// WHEN
96+
new DockerImageAsset(stack, 'Image', {
97+
directory: directoryPath,
98+
file: 'Dockerfile.Custom'
99+
});
100+
101+
// THEN
102+
const assetMetadata = stack.node.metadata.find(({ type }) => type === ASSET_METADATA);
103+
test.deepEqual(assetMetadata && assetMetadata.data.file, path.join(directoryPath, 'Dockerfile.Custom'));
104+
test.done();
105+
},
106+
90107
'asset.repository.grantPull can be used to grant a principal permissions to use the image'(test: Test) {
91108
// GIVEN
92109
const stack = new Stack();
@@ -211,7 +228,21 @@ export = {
211228
new DockerImageAsset(stack, 'Asset', {
212229
directory: __dirname
213230
});
214-
}, /No 'Dockerfile' found in/);
231+
}, /Cannot find file at/);
232+
test.done();
233+
},
234+
235+
'fails if the file does not exist'(test: Test) {
236+
// GIVEN
237+
const stack = new Stack();
238+
239+
// THEN
240+
test.throws(() => {
241+
new DockerImageAsset(stack, 'Asset', {
242+
directory: __dirname,
243+
file: 'doesnt-exist'
244+
});
245+
}, /Cannot find file at/);
215246
test.done();
216247
},
217248

packages/@aws-cdk/aws-ecs/lib/images/asset-image.ts

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ export interface AssetImageProps {
2020
* @default none
2121
*/
2222
readonly target?: string;
23+
24+
/**
25+
* Path to the Dockerfile (relative to the directory).
26+
*
27+
* @default 'Dockerfile'
28+
*/
29+
readonly file?: string;
30+
2331
}
2432

2533
/**
@@ -40,6 +48,7 @@ export class AssetImage extends ContainerImage {
4048
directory: this.directory,
4149
buildArgs: this.props.buildArgs,
4250
target: this.props.target,
51+
file: this.props.file,
4352
});
4453
asset.repository.grantPull(containerDefinition.taskDefinition.obtainExecutionRole());
4554

packages/@aws-cdk/core/lib/assets.ts

+7
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ export interface DockerImageAssetSource {
5757
*/
5858
readonly dockerBuildTarget?: string;
5959

60+
/**
61+
* Path to the Dockerfile (relative to the directory).
62+
*
63+
* @default - no file
64+
*/
65+
readonly dockerFile?: string;
66+
6067
/**
6168
* ECR repository name
6269
*

packages/@aws-cdk/core/lib/stack.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,8 @@ export class Stack extends Construct implements ITaggable {
563563
imageNameParameter: params.imageNameParameter.logicalId,
564564
repositoryName: asset.repositoryName,
565565
buildArgs: asset.dockerBuildArgs,
566-
target: asset.dockerBuildTarget
566+
target: asset.dockerBuildTarget,
567+
file: asset.dockerFile,
567568
};
568569

569570
this.node.addMetadata(cxapi.ASSET_METADATA, metadata);

packages/@aws-cdk/cx-api/lib/assets.ts

+8
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ export interface ContainerImageAssetMetadataEntry extends BaseAssetMetadataEntry
106106
* @default no build target
107107
*/
108108
readonly target?: string;
109+
110+
/**
111+
* Path to the Dockerfile (relative to the directory).
112+
*
113+
* @default - no file is passed
114+
*/
115+
readonly file?: string;
116+
109117
}
110118

111119
export type AssetMetadataEntry = FileAssetMetadataEntry | ContainerImageAssetMetadataEntry;

packages/aws-cdk/lib/docker.ts

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ export async function prepareContainerAsset(assemblyDir: string,
7474
baseCommand.push('--target', asset.target);
7575
}
7676

77+
if (asset.file) {
78+
baseCommand.push('--file', asset.file);
79+
}
80+
7781
const command = ci
7882
? [...baseCommand, '--cache-from', latest] // This does not fail if latest is not available
7983
: baseCommand;

packages/aws-cdk/test/test.docker.ts

+43
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,49 @@ export = {
264264
test.done();
265265
},
266266

267+
async 'passes the correct file to docker build'(test: Test) {
268+
// GIVEN
269+
const toolkit = new ToolkitInfo({
270+
sdk: new MockSDK(),
271+
bucketName: 'BUCKET_NAME',
272+
bucketEndpoint: 'BUCKET_ENDPOINT',
273+
environment: { name: 'env', account: '1234', region: 'abc' }
274+
});
275+
276+
const prepareEcrRepositoryStub = sinon.stub(toolkit, 'prepareEcrRepository').resolves({
277+
repositoryUri: 'uri',
278+
repositoryName: 'name'
279+
});
280+
281+
const shellStub = sinon.stub(os, 'shell').rejects('STOPTEST');
282+
283+
// WHEN
284+
const asset: cxapi.ContainerImageAssetMetadataEntry = {
285+
id: 'assetId',
286+
imageNameParameter: 'MyParameter',
287+
packaging: 'container-image',
288+
path: '/foo',
289+
sourceHash: '1234567890abcdef',
290+
repositoryName: 'some-name',
291+
file: 'some-file'
292+
};
293+
294+
try {
295+
await prepareContainerAsset('.', asset, toolkit, false);
296+
} catch (e) {
297+
if (!/STOPTEST/.test(e.toString())) { throw e; }
298+
}
299+
300+
// THEN
301+
const command = ['docker', 'build', '--tag', `uri:latest`, '/foo', '--file', 'some-file'];
302+
303+
test.ok(shellStub.calledWith(command));
304+
305+
prepareEcrRepositoryStub.restore();
306+
shellStub.restore();
307+
test.done();
308+
},
309+
267310
async 'relative path'(test: Test) {
268311
// GIVEN
269312
const toolkit = new ToolkitInfo({

0 commit comments

Comments
 (0)