Skip to content

Commit

Permalink
feat(aws-ecr-assets): support the --platform option when building doc…
Browse files Browse the repository at this point in the history
…ker images (#20439)

This PR adds support for specifying the desired build platform when building docker images (ie: build an arm64 image on an amd64/x86_64 host). Closes #12472 

This PR does NOT touch Lambda builders, only ECR assets. #16770 attempted to implement support for ECR and Lambda but was abandoned. Meanwhile #16858 implemented lambda platform support. This implements the ECR side

I have run `yarn integ`

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
joeflateau committed May 26, 2022
1 parent 4b837df commit adc0368
Show file tree
Hide file tree
Showing 22 changed files with 318 additions and 10 deletions.
12 changes: 12 additions & 0 deletions packages/@aws-cdk/aws-ecr-assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ const asset = new DockerImageAsset(this, 'MyBuildImage', {
})
```

You can optionally pass an alternate platform to the `docker build` command by specifying
the `platform` property:

```ts
import { DockerImageAsset, Platform } from '@aws-cdk/aws-ecr-assets';

const asset = new DockerImageAsset(this, 'MyBuildImage', {
directory: path.join(__dirname, 'my-image'),
platform: Platform.LINUX_ARM64,
})
```

## Images from Tarball

Images are loaded from a local tarball, uploaded to ECR by the CDK toolkit and/or your app's CI-CD pipeline, and can be
Expand Down
46 changes: 46 additions & 0 deletions packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,36 @@ export class NetworkMode {
private constructor(public readonly mode: string) {}
}

/**
* platform supported by docker
*/
export class Platform {
/**
* Build for linux/amd64
*/
public static readonly LINUX_AMD64 = new Platform('linux/amd64');

/**
* Build for linux/arm64
*/
public static readonly LINUX_ARM64 = new Platform('linux/arm64');

/**
* Used to specify a custom platform
* Use this if the platform name is not yet supported by the CDK.
*
* @param platform The platform to use for docker build
*/
public static custom(platform: string) {
return new Platform(platform);
}

/**
* @param platform The platform to use for docker build
*/
private constructor(public readonly platform: string) {}
}

/**
* Options to control invalidation of `DockerImageAsset` asset hashes
*/
Expand Down Expand Up @@ -101,6 +131,13 @@ export interface DockerImageAssetInvalidationOptions {
* @default true
*/
readonly networkMode?: boolean;

/**
* Use `platform` while calculating the asset hash
*
* @default true
*/
readonly platform?: boolean;
}

/**
Expand Down Expand Up @@ -153,6 +190,13 @@ export interface DockerImageAssetOptions extends FingerprintOptions, FileFingerp
*/
readonly networkMode?: NetworkMode;

/**
* Platform to build for. _Requires Docker Buildx_.
*
* @default - no platform specified (the current machine architecture will be used)
*/
readonly platform?: Platform;

/**
* Options to control which parameters are used to invalidate the asset hash.
*
Expand Down Expand Up @@ -286,6 +330,7 @@ export class DockerImageAsset extends CoreConstruct implements IAsset {
if (props.invalidation?.file !== false && props.file) { extraHash.file = props.file; }
if (props.invalidation?.repositoryName !== false && props.repositoryName) { extraHash.repositoryName = props.repositoryName; }
if (props.invalidation?.networkMode !== false && props.networkMode) { extraHash.networkMode = props.networkMode; }
if (props.invalidation?.platform !== false && props.platform) { extraHash.platform = props.platform; }

// add "salt" to the hash in order to invalidate the image in the upgrade to
// 1.21.0 which removes the AdoptedRepository resource (and will cause the
Expand Down Expand Up @@ -318,6 +363,7 @@ export class DockerImageAsset extends CoreConstruct implements IAsset {
dockerFile: props.file,
sourceHash: staging.assetHash,
networkMode: props.networkMode?.mode,
platform: props.platform?.platform,
});

this.repository = ecr.Repository.fromRepositoryName(this, 'Repository', location.repositoryName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM public.ecr.aws/lambda/python:3.6
EXPOSE 8000
WORKDIR /src
ADD . /src
CMD python3 index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/python
import sys
import textwrap
import http.server
import socketserver

PORT = 8000


class Handler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write(textwrap.dedent('''\
<!doctype html>
<html><head><title>It works</title></head>
<body>
<h1>Hello from the integ test container</h1>
<p>This container got built and started as part of the integ test.</p>
<img src="https://media.giphy.com/media/nFjDu1LjEADh6/giphy.gif">
</body>
''').encode('utf-8'))


def main():
httpd = http.server.HTTPServer(("", PORT), Handler)
print("serving at port", PORT)
httpd.serve_forever()


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"17.0.0"}
{"version":"20.0.0"}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,48 @@
]
]
}
},
"ImageUri2": {
"Value": {
"Fn::Join": [
"",
[
{
"Ref": "AWS::AccountId"
},
".dkr.ecr.",
{
"Ref": "AWS::Region"
},
".",
{
"Ref": "AWS::URLSuffix"
},
"/aws-cdk/assets:0a3355be12051c9984bf2b0b2bba4e6ea535968e5b6e7396449701732fe5ed14"
]
]
}
},
"ImageUri3": {
"Value": {
"Fn::Join": [
"",
[
{
"Ref": "AWS::AccountId"
},
".dkr.ecr.",
{
"Ref": "AWS::Region"
},
".",
{
"Ref": "AWS::URLSuffix"
},
"/aws-cdk/assets:394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38"
]
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "18.0.0",
"version": "20.0.0",
"testCases": {
"aws-ecr-assets/test/integ.assets-docker": {
"integ.assets-docker": {
"stacks": [
"integ-assets-docker"
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "17.0.0",
"version": "20.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
Expand All @@ -26,6 +26,18 @@
"path": "asset.0a3355be12051c9984bf2b0b2bba4e6ea535968e5b6e7396449701732fe5ed14",
"sourceHash": "0a3355be12051c9984bf2b0b2bba4e6ea535968e5b6e7396449701732fe5ed14"
}
},
{
"type": "aws:cdk:asset",
"data": {
"repositoryName": "aws-cdk/assets",
"imageTag": "394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38",
"id": "394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38",
"packaging": "container-image",
"path": "asset.394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38",
"sourceHash": "394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38",
"platform": "linux/arm64"
}
}
],
"/integ-assets-docker/MyUser/Resource": [
Expand All @@ -45,6 +57,18 @@
"type": "aws:cdk:logicalId",
"data": "ImageUri"
}
],
"/integ-assets-docker/ImageUri2": [
{
"type": "aws:cdk:logicalId",
"data": "ImageUri2"
}
],
"/integ-assets-docker/ImageUri3": [
{
"type": "aws:cdk:logicalId",
"data": "ImageUri3"
}
]
},
"displayName": "integ-assets-docker"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,32 @@
"version": "0.0.0"
}
},
"DockerImage3": {
"id": "DockerImage3",
"path": "integ-assets-docker/DockerImage3",
"children": {
"Staging": {
"id": "Staging",
"path": "integ-assets-docker/DockerImage3/Staging",
"constructInfo": {
"fqn": "@aws-cdk/core.AssetStaging",
"version": "0.0.0"
}
},
"Repository": {
"id": "Repository",
"path": "integ-assets-docker/DockerImage3/Repository",
"constructInfo": {
"fqn": "@aws-cdk/aws-ecr.RepositoryBase",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-ecr-assets.DockerImageAsset",
"version": "0.0.0"
}
},
"MyUser": {
"id": "MyUser",
"path": "integ-assets-docker/MyUser",
Expand Down Expand Up @@ -99,8 +125,8 @@
{
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Effect": "Allow",
"Resource": {
Expand Down Expand Up @@ -164,6 +190,22 @@
"fqn": "@aws-cdk/core.CfnOutput",
"version": "0.0.0"
}
},
"ImageUri2": {
"id": "ImageUri2",
"path": "integ-assets-docker/ImageUri2",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnOutput",
"version": "0.0.0"
}
},
"ImageUri3": {
"id": "ImageUri3",
"path": "integ-assets-docker/ImageUri3",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnOutput",
"version": "0.0.0"
}
}
},
"constructInfo": {
Expand Down
20 changes: 17 additions & 3 deletions packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as fs from 'fs';
import * as path from 'path';
import { Template } from '@aws-cdk/assertions';
import * as iam from '@aws-cdk/aws-iam';
import { describeDeprecated, testDeprecated, testFutureBehavior } from '@aws-cdk/cdk-build-tools';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import { App, DefaultStackSynthesizer, IgnoreMode, Lazy, LegacyStackSynthesizer, Stack, Stage } from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import { DockerImageAsset, NetworkMode } from '../lib';
import * as fs from 'fs';
import * as path from 'path';
import { DockerImageAsset, NetworkMode, Platform } from '../lib';

/* eslint-disable quote-props */

Expand Down Expand Up @@ -156,6 +156,20 @@ describe('image asset', () => {
expect(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).networkMode).toEqual('default');
});

testFutureBehavior('with platform', flags, App, (app) => {
// GIVEN
const stack = new Stack(app);
// WHEN
new DockerImageAsset(stack, 'Image', {
directory: path.join(__dirname, 'demo-image'),
platform: Platform.LINUX_ARM64,
});

// THEN
const assetMetadata = stack.node.metadataEntry.find(({ type }) => type === cxschema.ArtifactMetadataEntryType.ASSET);
expect(assetMetadata && (assetMetadata.data as cxschema.ContainerImageAssetMetadataEntry).platform).toEqual('linux/arm64');
});

testFutureBehavior('asset.repository.grantPull can be used to grant a principal permissions to use the image', flags, App, (app) => {
// GIVEN
const stack = new Stack(app);
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@ const asset2 = new assets.DockerImageAsset(stack, 'DockerImage2', {
directory: path.join(__dirname, 'demo-image'),
});

const asset3 = new assets.DockerImageAsset(stack, 'DockerImage3', {
directory: path.join(__dirname, 'demo-image'),
platform: assets.Platform.LINUX_ARM64,
});

const user = new iam.User(stack, 'MyUser');
asset.repository.grantPull(user);
asset2.repository.grantPull(user);
asset3.repository.grantPull(user);

new cdk.CfnOutput(stack, 'ImageUri', { value: asset.imageUri });
new cdk.CfnOutput(stack, 'ImageUri2', { value: asset2.imageUri });
new cdk.CfnOutput(stack, 'ImageUri3', { value: asset3.imageUri });

app.synth();
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ export interface DockerImageSource {
* @default - no networking mode specified
*/
readonly networkMode?: string;

/**
* Platform to build for. _Requires Docker Buildx_.
*
* Specify this property to build images on a specific platform/architecture.
*
* @default - current machine platform
*/
readonly platform?: string;
}

/**
Expand Down
Loading

0 comments on commit adc0368

Please sign in to comment.