diff --git a/packages/@aws-cdk/aws-ecr-assets/README.md b/packages/@aws-cdk/aws-ecr-assets/README.md index 9be425daadd06..f88cef5b256bb 100644 --- a/packages/@aws-cdk/aws-ecr-assets/README.md +++ b/packages/@aws-cdk/aws-ecr-assets/README.md @@ -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 diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts index 665b67e73105b..b6ddd3f5dafcf 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -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 */ @@ -101,6 +131,13 @@ export interface DockerImageAssetInvalidationOptions { * @default true */ readonly networkMode?: boolean; + + /** + * Use `platform` while calculating the asset hash + * + * @default true + */ + readonly platform?: boolean; } /** @@ -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. * @@ -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 @@ -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); diff --git a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/asset.394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38/Dockerfile b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/asset.394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38/Dockerfile new file mode 100644 index 0000000000000..235b30e9661ed --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/asset.394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38/Dockerfile @@ -0,0 +1,5 @@ +FROM public.ecr.aws/lambda/python:3.6 +EXPOSE 8000 +WORKDIR /src +ADD . /src +CMD python3 index.py diff --git a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/asset.394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38/index.py b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/asset.394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38/index.py new file mode 100644 index 0000000000000..2ccedfce3ab76 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/asset.394b24fcdc153a83b1fc400bf2e812ee67e3a5ffafdf977d531cfe2187d95f38/index.py @@ -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('''\ + +
This container got built and started as part of the integ test.
+
+
+ ''').encode('utf-8'))
+
+
+def main():
+ httpd = http.server.HTTPServer(("", PORT), Handler)
+ print("serving at port", PORT)
+ httpd.serve_forever()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/cdk.out
index 90bef2e09ad39..588d7b269d34f 100644
--- a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/cdk.out
+++ b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/cdk.out
@@ -1 +1 @@
-{"version":"17.0.0"}
\ No newline at end of file
+{"version":"20.0.0"}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ-assets-docker.template.json b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ-assets-docker.template.json
index 1f1fffdf5581f..fcdf714d71415 100644
--- a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ-assets-docker.template.json
+++ b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ-assets-docker.template.json
@@ -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"
+ ]
+ ]
+ }
}
}
}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ.json b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ.json
index d8588aafe50f8..5792f49559a4d 100644
--- a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ.json
+++ b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/integ.json
@@ -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"
],
diff --git a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/manifest.json
index da54ddfe4c530..d3bf0ba4e8491 100644
--- a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/manifest.json
+++ b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/manifest.json
@@ -1,5 +1,5 @@
{
- "version": "17.0.0",
+ "version": "20.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
@@ -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": [
@@ -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"
diff --git a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/tree.json b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/tree.json
index 4764014f0323b..e5caed87f3db3 100644
--- a/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/tree.json
+++ b/packages/@aws-cdk/aws-ecr-assets/test/assets-docker.integ.snapshot/tree.json
@@ -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",
@@ -99,8 +125,8 @@
{
"Action": [
"ecr:BatchCheckLayerAvailability",
- "ecr:BatchGetImage",
- "ecr:GetDownloadUrlForLayer"
+ "ecr:GetDownloadUrlForLayer",
+ "ecr:BatchGetImage"
],
"Effect": "Allow",
"Resource": {
@@ -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": {
diff --git a/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts b/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts
index 19e5b764407ef..4fe6299ab9c87 100644
--- a/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts
+++ b/packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts
@@ -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 */
@@ -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);
diff --git a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts
index d6c4c2aac75b7..6aecb9ac7bda9 100644
--- a/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts
+++ b/packages/@aws-cdk/aws-ecr-assets/test/integ.assets-docker.ts
@@ -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();
diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/assets/docker-image-asset.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/assets/docker-image-asset.ts
index 5b3efeee0b375..ed39ad833b9ce 100644
--- a/packages/@aws-cdk/cloud-assembly-schema/lib/assets/docker-image-asset.ts
+++ b/packages/@aws-cdk/cloud-assembly-schema/lib/assets/docker-image-asset.ts
@@ -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;
}
/**
diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts
index b58d02849bd9c..3ed8bfe42cf50 100644
--- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts
+++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts
@@ -138,6 +138,13 @@ export interface ContainerImageAssetMetadataEntry extends BaseAssetMetadataEntry
* @default - no networking mode specified
*/
readonly networkMode?: string;
+
+ /**
+ * Platform to build for. _Requires Docker Buildx_.
+ *
+ * @default - current machine platform
+ */
+ readonly platform?: string;
}
/**
diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/assets.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/assets.schema.json
index 40134a4e554a5..e2b5aa8780c04 100644
--- a/packages/@aws-cdk/cloud-assembly-schema/schema/assets.schema.json
+++ b/packages/@aws-cdk/cloud-assembly-schema/schema/assets.schema.json
@@ -158,6 +158,10 @@
"networkMode": {
"description": "Networking mode for the RUN commands during build. _Requires Docker Engine API v1.25+_.\n\nSpecify this property to build images on a specific networking mode. (Default - no networking mode specified)",
"type": "string"
+ },
+ "platform": {
+ "description": "Platform to build for. _Requires Docker Buildx_.\n\nSpecify this property to build images on a specific platform/architecture. (Default - current machine platform)",
+ "type": "string"
}
}
},
diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json
index 19ab465985d24..7f877222e4563 100644
--- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json
+++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json
@@ -230,6 +230,10 @@
"description": "Networking mode for the RUN commands during build. (Default - no networking mode specified)",
"type": "string"
},
+ "platform": {
+ "description": "Platform to build for. _Requires Docker Buildx_. (Default - current machine platform)",
+ "type": "string"
+ },
"id": {
"description": "Logical identifier for the asset",
"type": "string"
diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json
index ccdfc1ff96a9d..588d7b269d34f 100644
--- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json
+++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json
@@ -1 +1 @@
-{"version":"19.0.0"}
\ No newline at end of file
+{"version":"20.0.0"}
\ No newline at end of file
diff --git a/packages/@aws-cdk/core/lib/assets.ts b/packages/@aws-cdk/core/lib/assets.ts
index b9a0ebd2b1bc6..906841f21e8eb 100644
--- a/packages/@aws-cdk/core/lib/assets.ts
+++ b/packages/@aws-cdk/core/lib/assets.ts
@@ -212,6 +212,15 @@ export interface DockerImageAssetSource {
* @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.
+ *
+ * @default - no platform specified (the current machine architecture will be used)
+ */
+ readonly platform?: string;
}
/**
diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts
index 204f3b8ba6827..587181de3ce33 100644
--- a/packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts
+++ b/packages/@aws-cdk/core/lib/stack-synthesizers/legacy.ts
@@ -149,6 +149,7 @@ export class LegacyStackSynthesizer extends StackSynthesizer {
target: asset.dockerBuildTarget,
file: asset.dockerFile,
networkMode: asset.networkMode,
+ platform: asset.platform,
};
this.stack.node.addMetadata(cxschema.ArtifactMetadataEntryType.ASSET, metadata);
diff --git a/packages/aws-cdk/lib/assets.ts b/packages/aws-cdk/lib/assets.ts
index 632292853c670..cb343db5282f1 100644
--- a/packages/aws-cdk/lib/assets.ts
+++ b/packages/aws-cdk/lib/assets.ts
@@ -123,6 +123,7 @@ async function prepareDockerImageAsset(
dockerBuildTarget: asset.target,
dockerFile: asset.file,
networkMode: asset.networkMode,
+ platform: asset.platform,
}, {
repositoryName,
imageTag,
diff --git a/packages/cdk-assets/lib/private/docker.ts b/packages/cdk-assets/lib/private/docker.ts
index 1a9b2293230f6..999d44de7fb4c 100644
--- a/packages/cdk-assets/lib/private/docker.ts
+++ b/packages/cdk-assets/lib/private/docker.ts
@@ -16,6 +16,7 @@ interface BuildOptions {
readonly file?: string;
readonly buildArgs?: Record