Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ecr-assets): control docker image asset hash #16070

Merged
merged 8 commits into from
Oct 7, 2021
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