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(core): Support cache-from and cache-to flags in DockerImage #25925

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 27 additions & 2 deletions packages/aws-cdk-lib/core/lib/bundling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { isAbsolute, join } from 'path';
import { FileSystem } from './fs';
import { dockerExec } from './private/asset-staging';
import { quiet, reset } from './private/jsii-deprecated';
import { DockerCacheOption } from './assets';

/**
* Methods to build Docker CLI arguments for builds using secrets.
Expand Down Expand Up @@ -221,7 +222,7 @@ export class BundlingDockerImage {
}

/** @param image The Docker image */
protected constructor(public readonly image: string, private readonly _imageHash?: string) {}
protected constructor(public readonly image: string, private readonly _imageHash?: string) { }

/**
* Provides a stable representation of this image for JSON serialization.
Expand Down Expand Up @@ -334,6 +335,8 @@ export class DockerImage extends BundlingDockerImage {
...(options.file ? ['-f', join(path, options.file)] : []),
...(options.platform ? ['--platform', options.platform] : []),
...(options.targetStage ? ['--target', options.targetStage] : []),
...(options.cacheFrom ? [...options.cacheFrom.map(cacheFrom => ['--cache-from', this.cacheOptionToFlag(cacheFrom)]).flat()] : []),
...(options.cacheTo ? ['--cache-to', this.cacheOptionToFlag(options.cacheTo)] : []),
...flatten(Object.entries(buildArgs).map(([k, v]) => ['--build-arg', `${k}=${v}`])),
path,
];
Expand Down Expand Up @@ -425,6 +428,14 @@ export class DockerImage extends BundlingDockerImage {
reset(deprecated);
return result;
}

private cacheOptionToFlag(option: DockerCacheOption): string {
let flag = `type=${option.type}`;
if (option.params) {
flag += ',' + Object.entries(option.params).map(([k, v]) => `${k}=${v}`).join(',');
}
return flag;
}
}

/**
Expand Down Expand Up @@ -572,13 +583,27 @@ export interface DockerBuildOptions {
* @default - Build all stages defined in the Dockerfile
*/
readonly targetStage?: string;

/**
* Cache from options to pass to the `docker build` command.
*
* @default - no cache from args are passed
*/
readonly cacheFrom?: DockerCacheOption[];

/**
* Cache to options to pass to the `docker build` command.
*
* @default - no cache to args are passed
*/
readonly cacheTo?: DockerCacheOption;
}

function flatten(x: string[][]) {
return Array.prototype.concat([], ...x);
}

function isSeLinux() : boolean {
function isSeLinux(): boolean {
if (process.platform != 'linux') {
return false;
}
Expand Down
45 changes: 45 additions & 0 deletions packages/aws-cdk-lib/core/test/bundling.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,51 @@ describe('bundling', () => {
])).toEqual(true);
});

test('bundling with image from asset with cache-to & cache-from', () => {
const spawnSyncStub = sinon.stub(child_process, 'spawnSync').returns({
status: 0,
stderr: Buffer.from('stderr'),
stdout: Buffer.from('stdout'),
pid: 123,
output: ['stdout', 'stderr'],
signal: null,
});

const imageHash = '123456abcdef';
const fingerprintStub = sinon.stub(FileSystem, 'fingerprint');
fingerprintStub.callsFake(() => imageHash);
const cacheTo = { type: 'local', params: { dest: "path/to/local/dir" } };
const cacheFrom1 = {
type: 's3', params: { region: 'us-west-2', bucket: 'my-bucket', name: 'foo' }
};
const cacheFrom2 = {
type: 'gha', params: { url: 'https://example.com', token: 'abc123', scope: 'gh-ref-image2' }
};


const image = DockerImage.fromBuild('docker-path', { cacheTo });
image.run();

const tagHash = crypto.createHash('sha256').update(JSON.stringify({
path: 'docker-path',
cacheTo,
})).digest('hex');
const tag = `cdk-${tagHash}`;

expect(spawnSyncStub.firstCall.calledWith(dockerCmd, [
'build', '-t', tag,
'--cache-to', 'type=local,dest=path/to/local/dir',
'--cache-from', 'type=s3,region=us-west-2,bucket=my-bucket,name=foo',
'--cache-from', 'type=gha,url=https://example.com,token=abc123,scope=gh-ref-image2',
'docker-path',
])).toEqual(true);

expect(spawnSyncStub.secondCall.calledWith(dockerCmd, [
'run', '--rm',
tag,
])).toEqual(true);
});

test('bundling with image from asset with target stage', () => {
const spawnSyncStub = sinon.stub(child_process, 'spawnSync').returns({
status: 0,
Expand Down