diff --git a/packages/@aws-cdk/aws-ecr-assets/README.md b/packages/@aws-cdk/aws-ecr-assets/README.md index 8221fe8091d14..b106c952007b8 100644 --- a/packages/@aws-cdk/aws-ecr-assets/README.md +++ b/packages/@aws-cdk/aws-ecr-assets/README.md @@ -50,13 +50,18 @@ Use `asset.imageUri` to reference the image. It includes both the ECR image URL and tag. You can optionally pass build args to the `docker build` command by specifying -the `buildArgs` property: +the `buildArgs` property. It is recommended to skip hashing of `buildArgs` for +values that can change between different machines to maintain a consistent +asset hash. ```ts const asset = new DockerImageAsset(this, 'MyBuildImage', { directory: path.join(__dirname, 'my-image'), buildArgs: { HTTP_PROXY: 'http://10.20.30.2:1234' + }, + invalidation: { + buildArgs: false } }); ``` 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 7003fc6931826..e579baf55ac41 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -12,6 +12,46 @@ import { FingerprintOptions, FollowMode, IAsset } from '@aws-cdk/assets'; // eslint-disable-next-line no-duplicate-imports, import/order import { Construct as CoreConstruct } from '@aws-cdk/core'; +/** + * Options to control invalidation of `DockerImageAsset` asset hashes + */ +export interface DockerImageAssetInvalidationOptions { + /** + * Use `extraHash` while calculating the asset hash + * + * @default true + */ + readonly extraHash?: boolean; + + /** + * Use `buildArgs` while calculating the asset hash + * + * @default true + */ + readonly buildArgs?: boolean; + + /** + * Use `target` while calculating the asset hash + * + * @default true + */ + readonly target?: boolean; + + /** + * Use `file` while calculating the asset hash + * + * @default true + */ + readonly file?: boolean; + + /** + * Use `repositoryName` while calculating the asset hash + * + * @default true + */ + readonly repositoryName?: boolean; +} + /** * Options for DockerImageAsset */ @@ -54,6 +94,13 @@ export interface DockerImageAssetOptions extends FingerprintOptions, FileFingerp * @default 'Dockerfile' */ readonly file?: string; + + /** + * Options to control which parameters are used to invalidate the asset hash. + * + * @default - hash all parameters + */ + readonly invalidation?: DockerImageAssetInvalidationOptions; } /** @@ -150,11 +197,11 @@ export class DockerImageAsset extends CoreConstruct implements IAsset { // include build context in "extra" so it will impact the hash const extraHash: { [field: string]: any } = {}; - if (props.extraHash) { extraHash.user = props.extraHash; } - if (props.buildArgs) { extraHash.buildArgs = props.buildArgs; } - if (props.target) { extraHash.target = props.target; } - if (props.file) { extraHash.file = props.file; } - if (props.repositoryName) { extraHash.repositoryName = props.repositoryName; } + if (props.invalidation?.extraHash !== false && props.extraHash) { extraHash.user = props.extraHash; } + if (props.invalidation?.buildArgs !== false && props.buildArgs) { extraHash.buildArgs = props.buildArgs; } + if (props.invalidation?.target !== false && props.target) { extraHash.target = props.target; } + if (props.invalidation?.file !== false && props.file) { extraHash.file = props.file; } + if (props.invalidation?.repositoryName !== false && props.repositoryName) { extraHash.repositoryName = props.repositoryName; } // 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 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 667a2ba9d0145..e08ef41d2d868 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 @@ -55,6 +55,65 @@ describe('image asset', () => { }); + testFutureBehavior('with hash options', flags, App, (app) => { + // WHEN + const stack = new Stack(app); + new DockerImageAsset(stack, 'Image1', { + directory: path.join(__dirname, 'demo-image'), + buildArgs: { + a: 'b', + }, + invalidation: { + buildArgs: false, + }, + }); + new DockerImageAsset(stack, 'Image2', { + directory: path.join(__dirname, 'demo-image'), + buildArgs: { + a: 'c', + }, + invalidation: { + buildArgs: false, + }, + }); + new DockerImageAsset(stack, 'Image3', { + directory: path.join(__dirname, 'demo-image'), + buildArgs: { + a: 'b', + }, + }); + + // THEN + const asm = app.synth(); + const artifact = asm.getStackArtifact(stack.artifactId); + expect(artifact.template).toEqual({}); + expect(artifact.assets).toEqual([ + { + 'buildArgs': { + 'a': 'b', + }, + repositoryName: 'aws-cdk/assets', + imageTag: '8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', + id: '8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', + packaging: 'container-image', + path: 'asset.8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', + sourceHash: '8c1d9ca9f5d37b1c4870c13a9f855301bb42c1848dbcdd5edc8fe2c6c7261d48', + }, + { + 'buildArgs': { + 'a': 'b', + }, + 'id': 'd4bbfde4749763cef9707486f81ce1e95d25cedaf4cc34cfcdab7232ec1948ff', + 'imageTag': 'd4bbfde4749763cef9707486f81ce1e95d25cedaf4cc34cfcdab7232ec1948ff', + 'packaging': 'container-image', + 'path': 'asset.d4bbfde4749763cef9707486f81ce1e95d25cedaf4cc34cfcdab7232ec1948ff', + 'repositoryName': 'aws-cdk/assets', + 'sourceHash': 'd4bbfde4749763cef9707486f81ce1e95d25cedaf4cc34cfcdab7232ec1948ff', + }, + ]); + + }); + testFutureBehavior('with target', flags, App, (app) => { // WHEN const stack = new Stack(app);