@cloud-carbon-footprint/aws
Version:
The core logic to get cloud usage data and estimate energy and carbon emissions from Amazon Web Services.
143 lines • 9.7 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const moment_1 = __importDefault(require("moment"));
const common_1 = require("@cloud-carbon-footprint/common");
const ComputeOptimizer_1 = require("./ComputeOptimizer");
const AWSComputeEstimatesBuilder_1 = __importDefault(require("../AWSComputeEstimatesBuilder"));
const AWSStorageEstimatesBuilder_1 = __importDefault(require("../AWSStorageEstimatesBuilder"));
const AWSMemoryEstimatesBuilder_1 = __importDefault(require("../AWSMemoryEstimatesBuilder"));
const CostAndUsageTypes_1 = require("../CostAndUsageTypes");
const domain_1 = require("../../domain");
const AWSRegions_1 = require("../AWSRegions");
class ComputeOptimizerRecommendations {
constructor(computeEstimator, memoryEstimator, ssdStorageEstimator, hddStorageEstimator, serviceWrapper) {
this.computeEstimator = computeEstimator;
this.memoryEstimator = memoryEstimator;
this.ssdStorageEstimator = ssdStorageEstimator;
this.hddStorageEstimator = hddStorageEstimator;
this.serviceWrapper = serviceWrapper;
this.recommendationsLogger = new common_1.Logger('AWSRecommendations');
}
async getRecommendations(centralComputeOptimizerBucket) {
const params = {
Bucket: centralComputeOptimizerBucket,
};
try {
const bucketObjectsList = await this.serviceWrapper.listBucketObjects(params);
const recommendationsResult = [];
const includedRecommendationTypes = ['OVER_PROVISIONED', 'NOTOPTIMIZED'];
for (const recommendationsData of bucketObjectsList.Contents) {
const recommendationDate = new Date(recommendationsData.Key.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/g)[0].toString());
const isFromPreviousDay = moment_1.default.utc(recommendationDate) >= moment_1.default.utc().subtract(1, 'days');
const isIncludedService = !recommendationsData.Key.includes('asg');
const service = recommendationsData.Key.split('/')[1];
if (isFromPreviousDay && isIncludedService) {
const params = {
Bucket: bucketObjectsList.Name,
Key: recommendationsData.Key,
};
const parsedRecommendations = await this.serviceWrapper.getComputeOptimizerRecommendationsResponse(params);
for (let i = 0; i < parsedRecommendations.length; i++) {
const recommendation = parsedRecommendations[i];
const recommendationType = recommendation.finding;
if (includedRecommendationTypes.includes(recommendationType.toUpperCase())) {
const computeOptimizerRecommendationServices = {
ec2: [
ComputeOptimizer_1.EC2CurrentComputeOptimizerRecommendation,
ComputeOptimizer_1.EC2TargetComputeOptimizerRecommendation,
],
ebs: [
ComputeOptimizer_1.EBSCurrentComputeOptimizerRecommendation,
ComputeOptimizer_1.EBSTargetComputeOptimizerRecommendation,
],
lambda: [
ComputeOptimizer_1.LambdaCurrentComputeOptimizerRecommendation,
ComputeOptimizer_1.LambdaTargetComputeOptimizerRecommendation,
],
};
const currentComputeOptimizerRecommendation = new computeOptimizerRecommendationServices[service][0](recommendation);
const targetComputeOptimizerRecommendation = new computeOptimizerRecommendationServices[service][1](recommendation);
if (targetComputeOptimizerRecommendation.costSavings) {
let kilowattHourSavings = 0;
let co2eSavings = 0;
if (service === 'ebs') {
const currentStorageFootprint = this.getStorageFootprintEstimate(currentComputeOptimizerRecommendation);
const targetStorageFootprint = this.getStorageFootprintEstimate(targetComputeOptimizerRecommendation);
kilowattHourSavings =
currentStorageFootprint.kilowattHours -
targetStorageFootprint.kilowattHours;
co2eSavings =
currentStorageFootprint.co2e - targetStorageFootprint.co2e;
}
else {
const [currentComputeFootprint, currentMemoryFootprint] = await this.getComputeAndMemoryFootprintEstimates(currentComputeOptimizerRecommendation, this.recommendationsLogger);
const [targetComputeFootprint, targetMemoryFootprint] = await this.getComputeAndMemoryFootprintEstimates(targetComputeOptimizerRecommendation, this.recommendationsLogger);
kilowattHourSavings =
currentComputeFootprint.kilowattHours -
targetComputeFootprint.kilowattHours;
co2eSavings =
currentComputeFootprint.co2e - targetComputeFootprint.co2e;
if (currentMemoryFootprint.co2e ||
targetMemoryFootprint.co2e) {
kilowattHourSavings +=
currentMemoryFootprint.kilowattHours -
targetMemoryFootprint.kilowattHours;
co2eSavings +=
currentMemoryFootprint.co2e - targetMemoryFootprint.co2e;
}
}
recommendationsResult.push({
cloudProvider: 'AWS',
accountId: currentComputeOptimizerRecommendation.accountId,
accountName: currentComputeOptimizerRecommendation.accountId,
instanceName: currentComputeOptimizerRecommendation?.instanceName,
region: currentComputeOptimizerRecommendation.region,
recommendationType: currentComputeOptimizerRecommendation.type,
resourceId: currentComputeOptimizerRecommendation.resourceId,
recommendationDetail: this.getRecommendationDetail(service, currentComputeOptimizerRecommendation, targetComputeOptimizerRecommendation),
costSavings: parseFloat(targetComputeOptimizerRecommendation.costSavings) || 0,
co2eSavings,
kilowattHourSavings,
});
}
}
}
}
}
return recommendationsResult;
}
catch (e) {
throw new Error(`Failed to grab AWS Compute Optimizer recommendations. Reason: ${e.message}`);
}
}
getRecommendationDetail(service, currentRecommendation, targetRecommendation) {
const modifyType = {
ec2: 'instance type',
ebs: 'volume type',
lambda: 'configuration memory size',
};
return `Save cost by changing ${modifyType[service]} from ${currentRecommendation.description} to ${targetRecommendation.description}.`;
}
async getComputeAndMemoryFootprintEstimates(computeOptimizerRecommendation, logger) {
const dateTime = new Date().toISOString();
const emissionsFactors = await (0, common_1.getEmissionsFactors)(computeOptimizerRecommendation.region, dateTime, domain_1.AWS_EMISSIONS_FACTORS_METRIC_TON_PER_KWH, AWSRegions_1.AWS_MAPPED_REGIONS_TO_ELECTRICITY_MAPS_ZONES, logger);
const computeFootprint = new AWSComputeEstimatesBuilder_1.default(computeOptimizerRecommendation, this.computeEstimator, emissionsFactors).computeFootprint;
const memoryFootprint = new AWSMemoryEstimatesBuilder_1.default(computeOptimizerRecommendation, this.memoryEstimator, emissionsFactors).memoryFootprint;
return [computeFootprint, memoryFootprint];
}
getStorageFootprintEstimate(computeOptimizerRecommendation) {
let storageEstimator = this.hddStorageEstimator;
if (this.volumeTypeIsSSD(computeOptimizerRecommendation.volumeType)) {
storageEstimator = this.ssdStorageEstimator;
}
return new AWSStorageEstimatesBuilder_1.default(computeOptimizerRecommendation, storageEstimator).storageFootprint;
}
volumeTypeIsSSD(volumeType) {
return CostAndUsageTypes_1.SSD_USAGE_TYPES.some((type) => type.includes(volumeType));
}
}
exports.default = ComputeOptimizerRecommendations;
//# sourceMappingURL=ComputeOptimizerRecommendations.js.map