Skip to content

Commit

Permalink
feat(ecr-assets): control docker image asset hash (#16070)
Browse files Browse the repository at this point in the history
Docker build args are meant for build system specific configuration like http proxy or CodeArtifact tokens. Give the user an option to not hash build args so the DockerImageAsset hash can remain consistent even when using build args. An inconsistent hash means the asset is built every synth and that wastes time and space.

This change is backwards compatible as the default hashing behavior remains the same.

closes #15936


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
amirfireeye authored Oct 7, 2021
1 parent d29a20b commit 13f67e7
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 6 deletions.
7 changes: 6 additions & 1 deletion packages/@aws-cdk/aws-ecr-assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
});
```
Expand Down
57 changes: 52 additions & 5 deletions packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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
Expand Down
59 changes: 59 additions & 0 deletions packages/@aws-cdk/aws-ecr-assets/test/image-asset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 13f67e7

Please sign in to comment.