Skip to content

Commit

Permalink
Merge branch 'master' into feature/sqs_to_another_account
Browse files Browse the repository at this point in the history
  • Loading branch information
theburningmonk committed Mar 28, 2020
2 parents 57cbdfb + 8b4378d commit bde1897
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 1 deletion.
19 changes: 18 additions & 1 deletion src/commands/clear-account.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ const { getAllRolesCount, deleteAllRoles } = require("../lib/iam");
const { getAllApiGwCount, deleteAllApiGw } = require("../lib/apigw");
const { getBucketCount, deleteAllBuckets } = require("../lib/s3");
const { deleteAllStacks, getAllStacksCount } = require("../lib/cloudformation");
const { getAllLogGroupsCount, deleteAllLogGroups } = require("../lib/cloudwatch-logs");
const { getAllNatGatewaysCount, deleteAllNatGateways } = require("../lib/nat");
const { track } = require("../lib/analytics");
require("colors");
const { getAllLogGroupsCount, deleteAllLogGroups } = require("../lib/cloudwatch-logs");

class ClearAccountCommand extends Command {
async run() {
Expand Down Expand Up @@ -176,6 +177,20 @@ class ClearAccountCommand extends Command {
);
}

async clearNatGateways(AWS) {
this.log("NAT Gateways".bold);
await this.resourceDeletion(
async () => {
return await getAllNatGatewaysCount(AWS);
},
async () => {
return await deleteAllNatGateways(AWS);
},
"NAT Gateway",
true
);
}

async clearEnvironment(AWS) {
await this.clearS3(AWS);
console.info("");
Expand All @@ -189,6 +204,8 @@ class ClearAccountCommand extends Command {
console.info("");
await this.clearLogGroups(AWS);
console.info("");
await this.clearNatGateways(AWS);
console.info("");
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/lib/apigw.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const regions = [
"ap-south-1",
"sa-east-1"
];

const getAllApiGwInRegion = async (region, AWS) => {
const HttpApiGw = new AWS.ApiGatewayV2({ region });

Expand Down Expand Up @@ -62,6 +63,7 @@ const getAllApiGwInRegion = async (region, AWS) => {

return foundApiGw;
};

const getAllApiGwCount = async AWS => {
const allApiPromises = regions.map(region => getAllApiGwInRegion(region, AWS));
const allApis = await Promise.all(allApiPromises);
Expand Down Expand Up @@ -112,6 +114,7 @@ const deleteAllApiGw = async AWS => {

return await Promise.all(apiToDeletePromises);
};

module.exports = {
deleteApiGw,
deleteAllApiGw,
Expand Down
1 change: 1 addition & 0 deletions src/lib/cloudformation.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ const getAllStacksCount = async AWS => {

return _.flatten(allStacks).length;
};

module.exports = {
getAllStacksInRegion,
deleteAllStacks,
Expand Down
1 change: 1 addition & 0 deletions src/lib/cloudwatch-logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const getAllLogGroupsCount = async AWS => {

return _.flatten(allLogGroups).length;
};

module.exports = {
getAllLogGroupsInRegion,
deleteAllLogGroups,
Expand Down
1 change: 1 addition & 0 deletions src/lib/iam.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const deleteAllRoles = async AWS => {

return await Promise.all(apiToDeletePromises);
};

module.exports = {
deleteAllRoles,
getAllRoles,
Expand Down
107 changes: 107 additions & 0 deletions src/lib/nat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
const _ = require("lodash");
const { ClearResult } = require("./utils");
const retry = require("async-retry");
const async = require("async");

const regions = [
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"ap-south-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"eu-north-1",
"sa-east-1"
];

const deleteNatGateway = async (natGatewayId, region, retryOpts, AWS) => {
const EC2 = new AWS.EC2({ region });
await retry(async bail => {
try {
await EC2.deleteNatGateway({
NatGatewayId: natGatewayId
}).promise();
} catch (e) {
if (e.code !== "Throttling") {
bail(e);
} else {
throw e;
}
}
}, retryOpts);
};

const deleteAllNatGateways = async (
AWS,
retryOpts = {
retries: 3,
minTimeout: 1000
}
) => {
const allNatGatewaysPromises = regions.map(region =>
getAllNatGatewaysInRegion(region, AWS)
);
const allNatGateways = await Promise.all(allNatGatewaysPromises);
const results = [];
const asyncQueue = async.queue(async x => {
try {
await deleteNatGateway(x.natGatewayId, x.region, retryOpts, AWS);
process.stdout.write(".".green);
results.push(ClearResult.getSuccess(x.natGatewayId, x.region));
} catch (e) {
process.stdout.write("F".red);
results.push(ClearResult.getFailed(x.natGatewayId, x.region, e));
}
}, 10);

_.flatten(allNatGateways).forEach(natGateway => {
asyncQueue.push(natGateway);
});

await asyncQueue.drain();
return results;
};

const getAllNatGatewaysInRegion = async (region, AWS) => {
const EC2 = new AWS.EC2({ region });

let natGateways = [];
let response = {};
do {
const params = response.NextToken ? { NextToken: response.NextToken } : {};
response = await EC2.describeNatGateways(params).promise();
natGateways = natGateways.concat(
response.NatGateways.map(val => {
return {
natGatewayId: val.NatGatewayId,
vpcId: val.VpcId,
region
};
})
);
} while (response.NextToken);

return natGateways;
};

const getAllNatGatewaysCount = async AWS => {
const allNatGatewaysPromises = regions.map(region =>
getAllNatGatewaysInRegion(region, AWS)
);
const allNatGateways = await Promise.all(allNatGatewaysPromises);

return _.flatten(allNatGateways).length;
};

module.exports = {
deleteAllNatGateways,
getAllNatGatewaysCount
};
1 change: 1 addition & 0 deletions src/lib/s3.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const getBucketCount = async AWS => {
const response = await S3.listBuckets().promise();
return response.Buckets.length;
};

module.exports = {
deleteAllBuckets,
getBucketCount
Expand Down
6 changes: 6 additions & 0 deletions test/commands/clear-account.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const s3 = require("../../src/lib/s3");
const cf = require("../../src/lib/cloudformation");
const versionCheck = require("../../src/lib/version-check");
const cwLogs = require("../../src/lib/cloudwatch-logs");
const natGateways = require("../../src/lib/nat");

jest.mock("../../src/lib/version-check");
jest.mock("../../src/lib/cloudformation");
Expand All @@ -14,6 +15,7 @@ jest.mock("../../src/lib/s3");
jest.mock("../../src/lib/apigw");
jest.mock("../../src/lib/iam");
jest.mock("../../src/lib/lambda");
jest.mock("../../src/lib/nat");
describe("User forces clear account", () => {
beforeEach(() => {
versionCheck.checkVersion.mockResolvedValue(null);
Expand All @@ -29,6 +31,8 @@ describe("User forces clear account", () => {
cwLogs.deleteAllLogGroups.mockResolvedValue([{ status: "success" }]);
lambda.getAllFunctionsCount.mockResolvedValue(0);
lambda.deleteAllFunctions.mockResolvedValue([{ status: "success" }]);
natGateways.getAllNatGatewaysCount.mockResolvedValue(1);
natGateways.deleteAllNatGateways.mockResolvedValue([{ status: "success" }]);
});
afterEach(() => {
jest.restoreAllMocks();
Expand All @@ -42,11 +46,13 @@ describe("User forces clear account", () => {
expect(ctx.stdout).to.contain("Deleting 1 CF stack(s)");
expect(ctx.stdout).to.contain("Deleting 1 role(s)");
expect(ctx.stdout).to.contain("Deleting 1 API Gateway(s)");
expect(ctx.stdout).to.contain("Deleting 1 NAT Gateway(s)");

expect(cf.deleteAllStacks.mock.calls.length).to.equal(1);
expect(lambda.deleteAllFunctions.mock.calls.length).to.equal(0);
expect(iam.deleteAllRoles.mock.calls.length).to.equal(1);
expect(apigw.deleteAllApiGw.mock.calls.length).to.equal(1);
expect(s3.deleteAllBuckets.mock.calls.length).to.equal(3); // retry
expect(natGateways.deleteAllNatGateways.mock.calls.length).to.equal(1);
});
});
60 changes: 60 additions & 0 deletions test/lib/nat.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const { expect } = require("@oclif/test");
const { getAWSSDK } = require("../../src/lib/aws");
const { deleteAllNatGateways, getAllNatGatewaysCount } = require("../../src/lib/nat");
const chaiAsPromised = require("chai-as-promised");
const chai = require("chai");
require("colors");
const { success, fail, getPromiseResponse } = require("../test-utils/jest-mocks"); // Required for avoid fail on console printing

jest.spyOn(global.console, "log");
global.console.log.mockImplementation(() => {});
chai.use(chaiAsPromised);

describe("delete all NAT Gateways", () => {
let AWS = null;

beforeEach(() => {
AWS = getAWSSDK();

AWS.EC2.prototype.describeNatGateways = getPromiseResponse({
NatGateways: [
{
NatGatewayId: "natgwid",
VpcId: "vpcid"
}
]
});
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Delete all NAT Gateways successfully", async function() {
AWS.EC2.prototype.deleteNatGateway = success;

const result = await deleteAllNatGateways(AWS);

expect(result.length).to.equal(16);
result.forEach(val => {
expect(val.status).to.equal("success");
});
});

it("Fail Deleting all NAT Gateways", async function() {
AWS.EC2.prototype.deleteNatGateway = fail;

const result = await deleteAllNatGateways(AWS, { retries: 1, minTimeout: 2 });

expect(result.length).to.equal(16);
result.forEach(val => {
expect(val.status).to.equal("fail");
});
});

it("Count number of NAT Gateways", async function() {
const count = await getAllNatGatewaysCount(AWS);

expect(count).to.equal(16);
});
});

0 comments on commit bde1897

Please sign in to comment.