Skip to content

Commit

Permalink
fix(cli): unhandled nextToken returned by listImagesCommand in garbag…
Browse files Browse the repository at this point in the history
…e collector
  • Loading branch information
sakurai-ryo committed Dec 28, 2024
1 parent 1c9103e commit 342dff5
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ export class GarbageCollector {
while (batch.length < batchSize) {
const response = await ecr.listImages({
repositoryName: repo,
nextToken: continuationToken,
});

// No images in the repository
Expand Down
85 changes: 85 additions & 0 deletions packages/aws-cdk/test/api/garbage-collection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,91 @@ describe('ECR Garbage Collection', () => {
imageTag: expect.stringContaining(`1-${ECR_ISOLATED_TAG}`),
});
});

test('listImagesCommand returns nextToken', async () => {
// This test is to ensure that the garbage collector can handle paginated responses from the ECR API
// If not handled correctly, the garbage collector will continue to make requests to the ECR API
mockTheToolkitInfo({
Outputs: [
{
OutputKey: 'BootstrapVersion',
OutputValue: '999',
},
],
});

prepareDefaultEcrMock();
ecrClient.on(ListImagesCommand).resolves({ // default response
imageIds: [
{
imageDigest: 'digest1',
imageTag: 'abcde',
},
{
imageDigest: 'digest2',
imageTag: 'fghij',
},
],
nextToken: 'nextToken',
}).on(ListImagesCommand, { // response when nextToken is provided
repositoryName: 'REPO_NAME',
nextToken: 'nextToken',
}).resolves({
imageIds: [
{
imageDigest: 'digest3',
imageTag: 'klmno',
},
],
});
ecrClient.on(BatchGetImageCommand).resolvesOnce({
images: [
{ imageId: { imageDigest: 'digest1' } },
{ imageId: { imageDigest: 'digest2' } },
],
}).resolvesOnce({
images: [
{ imageId: { imageDigest: 'digest3' } },
],
});
ecrClient.on(DescribeImagesCommand).resolvesOnce({
imageDetails: [
{
imageDigest: 'digest1',
imageTags: ['abcde'],
imagePushedAt: daysInThePast(100),
imageSizeInBytes: 1_000_000_000,
},
{ imageDigest: 'digest2', imageTags: ['fghij'], imagePushedAt: daysInThePast(10), imageSizeInBytes: 300_000_000 },
],
}).resolvesOnce({
imageDetails: [
{ imageDigest: 'digest3', imageTags: ['klmno'], imagePushedAt: daysInThePast(2), imageSizeInBytes: 100 },
],
});
prepareDefaultCfnMock();

garbageCollector = gc({
type: 'ecr',
rollbackBufferDays: 0,
action: 'full',
});
await garbageCollector.garbageCollect();

expect(ecrClient).toHaveReceivedCommandTimes(DescribeImagesCommand, 2);
expect(ecrClient).toHaveReceivedCommandTimes(ListImagesCommand, 4);

// no tagging
expect(ecrClient).toHaveReceivedCommandTimes(PutImageCommand, 0);

expect(ecrClient).toHaveReceivedCommandWith(BatchDeleteImageCommand, {
repositoryName: 'REPO_NAME',
imageIds: [
{ imageDigest: 'digest2' },
{ imageDigest: 'digest3' },
],
});
});
});

describe('CloudFormation API calls', () => {
Expand Down

0 comments on commit 342dff5

Please sign in to comment.