@cloud-carbon-footprint/aws
Version:
The core logic to get cloud usage data and estimate energy and carbon emissions from Amazon Web Services.
107 lines (96 loc) • 3.46 kB
text/typescript
/*
* © 2021 Thoughtworks, Inc.
*/
import moment from 'moment'
import { CostExplorer } from 'aws-sdk'
import {
StorageUsage,
FootprintEstimate,
StorageEstimator,
CloudConstants,
CloudConstantsEmissionsFactors,
} from '@cloud-carbon-footprint/core'
import { AWS_CLOUD_CONSTANTS } from '../domain'
import { ServiceWrapper } from './ServiceWrapper'
export class VolumeUsage implements StorageUsage {
readonly terabyteHours: number
readonly timestamp: Date
readonly diskType: DiskType
}
export interface MutableFootprintEstimate {
timestamp: Date
co2e: number
kilowattHours: number
}
export enum DiskType {
SSD = 'SSD',
HDD = 'HDD',
UNKNOWN = 'UNKNOWN',
}
export async function getUsageFromCostExplorer(
params: CostExplorer.GetCostAndUsageRequest,
diskTypeCallBack: (awsGroupKey: string) => DiskType,
serviceWrapper: ServiceWrapper,
): Promise<VolumeUsage[]> {
const responses: CostExplorer.GetCostAndUsageResponse[] =
await serviceWrapper.getCostAndUsageResponses(params)
return responses
.map((response) => {
return response.ResultsByTime.map((result) => {
const timestampString = result.TimePeriod.Start
return result.Groups.map((group) => {
const gbMonth = Number.parseFloat(group.Metrics.UsageQuantity.Amount)
const terabyteHours = estimateTerabyteHours(gbMonth, timestampString)
const diskType = diskTypeCallBack(group.Keys[0]) // Should be improved
return {
terabyteHours: terabyteHours,
timestamp: new Date(timestampString),
diskType: diskType,
}
})
})
})
.flat()
.flat()
.filter((storageUsage: StorageUsage) => storageUsage.terabyteHours)
}
function estimateTerabyteHours(sizeGbMonth: number, timestamp: string) {
// This function converts an AWS EBS Gigabyte-Month pricing metric into a TerabyteHours value that is needed for Storage Estimation.
// We do this by converting Gigabytes into Terabytes, then multiplying this by the number of hours in a month.
// Source: https://aws.amazon.com/premiumsupport/knowledge-center/ebs-volume-charges/
return (sizeGbMonth / 1000) * moment(timestamp).daysInMonth() * 24
}
export function getEstimatesFromCostExplorer(
region: string,
volumeUsages: VolumeUsage[],
emissionsFactors: CloudConstantsEmissionsFactors,
constants: CloudConstants,
): FootprintEstimate[] {
const ssdEstimator = new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT)
const hddEstimator = new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT)
const ssdUsage = volumeUsages.filter(
({ diskType: diskType }) => DiskType.SSD === diskType,
)
const hddUsage = volumeUsages.filter(
({ diskType: diskType }) => DiskType.HDD === diskType,
)
const footprintEstimates = [
...ssdEstimator.estimate(ssdUsage, region, emissionsFactors, constants),
...hddEstimator.estimate(hddUsage, region, emissionsFactors, constants),
]
return Object.values(
footprintEstimates.reduce(
(acc: { [key: string]: MutableFootprintEstimate }, estimate) => {
if (!acc[estimate.timestamp.toISOString()]) {
acc[estimate.timestamp.toISOString()] = estimate
return acc
}
acc[estimate.timestamp.toISOString()].co2e += estimate.co2e
acc[estimate.timestamp.toISOString()].kilowattHours +=
estimate.kilowattHours
return acc
},
{},
),
)
}