Skip to content

Commit

Permalink
DSO-2697 - Update runtime and dependencies (#37)
Browse files Browse the repository at this point in the history
* update

* test

* fix test

* prettier

* add parser option

* Update deploy.sh

* Update handler.js

* update

* Update serverless.yml

* Update serverless.yml

* update serverless

* Update serverless.yml

* Update serverless.yml

* Update serverless.yml

* Delete .env

* Update serverless.yml

* Update serverless.yml

* update region

* revert

* cleanup

* delete unneeded files
  • Loading branch information
pennyZZZZ committed Oct 17, 2023
1 parent d713378 commit 487432d
Show file tree
Hide file tree
Showing 9 changed files with 6,883 additions and 4,365 deletions.
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
"node": true,
"es6": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2020
}
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
- push

env:
NODE_VERSION: 10.x
NODE_VERSION: 18.x

jobs:
build_and_test:
Expand Down
4 changes: 0 additions & 4 deletions build.sh

This file was deleted.

3 changes: 0 additions & 3 deletions deploy.sh

This file was deleted.

56 changes: 28 additions & 28 deletions handler.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"use strict";

const AWS = require("aws-sdk");
const {
ECRClient,
DescribeImagesCommand,
BatchDeleteImageCommand,
} = require("@aws-sdk/client-ecr");
const moment = require("moment");
const filter = require("lodash.filter");
const rp = require("request-promise");
Expand All @@ -19,17 +22,15 @@ function postToSlack(text) {
return rp(options);
}

function getAllImages(ecr, registryId, repoName) {
async function getAllImages(ecr, registryId, repoName) {
const params = {
registryId,
registryId: registryId,
repositoryName: repoName,
maxResults: 100,
};

return ecr
.describeImages(params)
.promise()
.then((data) => Promise.resolve(data.imageDetails));
const data = await ecr.send(new DescribeImagesCommand(params));
return data.imageDetails;
}

function buildReport(
Expand All @@ -38,13 +39,13 @@ function buildReport(
reposWithUntaggedImages,
reposWithDeletedImagesDryRun,
reposWithDeletedImages,
reposWithImagesThatFailedToDelete
reposWithImagesThatFailedToDelete,
) {
const untaggedRepoKeys = Object.keys(reposWithUntaggedImages);
const deletedDryRunRepoKeys = Object.keys(reposWithDeletedImagesDryRun);
const deletedRepoKeys = Object.keys(reposWithDeletedImages);
const failedToDeletedRepoKeys = Object.keys(
reposWithImagesThatFailedToDelete
reposWithImagesThatFailedToDelete,
);

if (
Expand Down Expand Up @@ -129,21 +130,21 @@ function buildReport(
module.exports.cleanupImages = (event, context, callback) => {
if (typeof process.env.REPO_NAMES === "undefined") {
throw new Error(
"Can't start lambda: missing REPO_NAMES environment variable"
"Can't start lambda: missing REPO_NAMES environment variable",
);
}

if (typeof process.env.AWS_ACCOUNT_ID === "undefined") {
throw new Error(
"Can't start lambda: missing AWS_ACCOUNT_ID environment variable"
"Can't start lambda: missing AWS_ACCOUNT_ID environment variable",
);
}

const repoNames = process.env.REPO_NAMES.split(",");
const registry = process.env.AWS_ACCOUNT_ID;

const ecrRegion = process.env.ECR_REGION || "us-east-1";
const ecr = new AWS.ECR({ apiVersion: "2015-09-21", region: ecrRegion });
const ecrRegion = process.env.ECR_REGION || "ap-southeast-2";
const ecr = new ECRClient({ region: ecrRegion });

const reposNotFound = [];
const reposWithUntaggedImages = {};
Expand Down Expand Up @@ -181,12 +182,12 @@ module.exports.cleanupImages = (event, context, callback) => {
!isUntagged &&
moment(image.imagePushedAt).isBefore(cutOffDate) &&
!image.imageTags.find(
(tag) => tag.indexOf("master") > -1 || tag.indexOf("main") > -1
(tag) => tag.indexOf("master") > -1 || tag.indexOf("main") > -1,
)
);
});
})
.then((toDelete) => {
.then(async (toDelete) => {
if (!toDelete || toDelete.length === 0) {
return Promise.resolve({ imageIds: "none", failures: "none" });
}
Expand Down Expand Up @@ -218,26 +219,25 @@ module.exports.cleanupImages = (event, context, callback) => {
imageIds: convertedToDelete,
};

return ecr
.batchDeleteImage(deleteParams)
.promise()
.then(({ failures, imageIds }) => {
console.log("failures: ", failures);
console.log("imageIds: ", imageIds);
return await ecr
.send(new BatchDeleteImageCommand(deleteParams))
.then((response) => {
console.log("failures: ", response.failures);
console.log("imageIds: ", response.imageIds);

failures.forEach(({ imageId }) => {
response.failures.forEach(({ imageId }) => {
if (
typeof reposWithImagesThatFailedToDelete[repoName] ===
"undefined"
) {
reposWithImagesThatFailedToDelete[repoName] = [];
}
reposWithImagesThatFailedToDelete[repoName].push(
imageId.imageTag
imageId.imageTag,
);
});

imageIds.forEach(({ imageTag }) => {
response.imageIds.forEach(({ imageTag }) => {
if (typeof reposWithDeletedImages[repoName] === "undefined") {
reposWithDeletedImages[repoName] = [];
}
Expand All @@ -254,7 +254,7 @@ module.exports.cleanupImages = (event, context, callback) => {
}

console.log(err);
})
}),
);

return Promise.all(promises)
Expand All @@ -265,7 +265,7 @@ module.exports.cleanupImages = (event, context, callback) => {
reposWithUntaggedImages,
reposWithDeletedImagesDryRun,
reposWithDeletedImages,
reposWithImagesThatFailedToDelete
reposWithImagesThatFailedToDelete,
);

// Log Results
Expand All @@ -274,7 +274,7 @@ module.exports.cleanupImages = (event, context, callback) => {
return Promise.resolve(reportText);
})
.then(
(text) => postToSlack(text) // Post results to Slack
(text) => postToSlack(text), // Post results to Slack
)
.then(() => {
callback(null, { message: "robin executed successfully!", event });
Expand Down
179 changes: 86 additions & 93 deletions handler.test.js
Original file line number Diff line number Diff line change
@@ -1,116 +1,109 @@
const assert = require("assert");
const {
BatchDeleteImageCommand,
DescribeImagesCommand,
ECRClient,
} = require("@aws-sdk/client-ecr");
const moment = require("moment");
const proxyquire = require("proxyquire");
const sinon = require("sinon");
const { mockClient } = require("aws-sdk-client-mock");
const { cleanupImages } = require("./handler");
const { expect } = require("expect");
globalThis.expect = expect;
require("aws-sdk-client-mock-jest");

const deletePromise = {
promise: () =>
Promise.resolve({
imageIds: [
{
imageDigest: "3797470f-bb69-11e6-90e4-19b39eb619f7",
imageTag: "some-tag",
},
],
failures: [],
}),
imageIds: [
{
imageDigest: "3797470f-bb69-11e6-90e4-19b39eb619f7",
imageTag: "some-tag",
},
],
failures: [],
};

const desribeImagesPromise = {
promise: () =>
Promise.resolve({
nextToken: null,
imageDetails: [
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "1",
imageTags: ["1.0.0-master", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment(),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "2",
imageTags: ["1.0.0-master", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment().add(-31, "days"),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "3",
imageTags: ["1.0.0-other", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment(),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "4",
imageTags: ["1.0.0-other", "dont-ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment().add(-31, "days"),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "5",
imageTags: ["1.0.0-main", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment().add(-31, "days"),
},
],
}),
};

const sandbox = sinon.sandbox.create();
const deleteStub = sandbox.stub();
const describeImagesStub = sandbox.stub().returns(desribeImagesPromise);
const mocks = {
"aws-sdk": {
ECR: function () {
// eslint-disable-line
this.batchDeleteImage = deleteStub;
this.describeImages = describeImagesStub;
nextToken: null,
imageDetails: [
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "1",
imageTags: ["1.0.0-master", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment(),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "2",
imageTags: ["1.0.0-master", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment().add(-31, "days"),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "3",
imageTags: ["1.0.0-other", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment(),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "4",
imageTags: ["1.0.0-other", "dont-ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment().add(-31, "days"),
},
{
registryId: "384553929753",
repositoryName: "test-repo",
imageDigest: "5",
imageTags: ["1.0.0-main", "ignore-this"],
imageSizeInBytes: "1024",
imagePushedAt: moment().add(-31, "days"),
},
},
],
};

describe("cleanupImages", () => {
const cleanup = proxyquire("./handler.js", mocks);
const ecrMock = mockClient(ECRClient);

function callback() {}

describe("cleanupImages", () => {
beforeEach(() => {
ecrMock.reset();
process.env.DRY_RUN = "true";
process.env.REPO_NAMES = "test-repo";
process.env.AWS_ACCOUNT_ID = "1234567890";

sandbox.reset();
ecrMock
.on(DescribeImagesCommand)
.resolves(desribeImagesPromise)
.on(BatchDeleteImageCommand)
.resolves(deletePromise);
});

it.only("Should not remove master images", (done) => {
it("Should not remove master images", async () => {
process.env.DRY_RUN = "false";
deleteStub.returns(deletePromise);
cleanup.cleanupImages(null, null, () => {
assert(describeImagesStub.called);
assert(
deleteStub.calledWith({
registryId: "1234567890",
repositoryName: "test-repo",
imageIds: [{ imageDigest: "4" }],
})
);
done();

await cleanupImages(null, null, callback);

expect(ecrMock).toHaveReceivedCommand(DescribeImagesCommand);
expect(ecrMock).toHaveReceivedCommandWith(BatchDeleteImageCommand, {
registryId: "1234567890",
repositoryName: "test-repo",
imageIds: [{ imageDigest: "4" }],
});
});

it("Should not call delete if dry run", (done) => {
deleteStub.returns(deletePromise);
cleanup.cleanupImages(null, null, () => {
assert(describeImagesStub.called);
assert(deleteStub.notCalled);
done();
it("Should not call delete if dry run", async () => {
await cleanupImages(null, null, callback);

expect(ecrMock).toHaveReceivedCommandWith(DescribeImagesCommand, {
maxResults: 100,
registryId: "1234567890",
repositoryName: "test-repo",
});
expect(ecrMock).toHaveReceivedCommandTimes(BatchDeleteImageCommand, 0);
});
});
Loading

0 comments on commit 487432d

Please sign in to comment.