@cloud-carbon-footprint/aws
Version:
The core logic to get cloud usage data and estimate energy and carbon emissions from Amazon Web Services.
1,560 lines (1,509 loc) • 89 kB
text/typescript
/*
* © 2021 Thoughtworks, Inc.
*/
import { mockClient } from 'aws-sdk-client-mock'
import {
AccountDetailsOrIdList,
configLoader,
EstimationResult,
GroupBy,
Logger,
LookupTableInput,
LookupTableOutput,
} from '@cloud-carbon-footprint/common'
import {
ComputeEstimator,
NetworkingEstimator,
MemoryEstimator,
StorageEstimator,
UnknownEstimator,
EmbodiedEmissionsEstimator,
} from '@cloud-carbon-footprint/core'
import CostAndUsageReports, {
tagNameToAthenaColumn,
} from '../lib/CostAndUsageReports'
import { ServiceWrapper } from '../lib'
import {
testAccountId,
athenaMockGetQueryResultsWithEC2EBSLambda,
athenaMockGetQueryResultsWithNetworkingGlueECSDynamoDB,
athenaMockGetQueryResultsWithS3CloudWatchRDS,
athenaMockGetQueryResultsWithKinesisESAndEc2Spot,
athenaMockGetQueryResultsWithECSEksKafkaAndUnknownServices,
athenaMockGetQueryResultsWithDocDBComputeEbsOptimizedSpotUsage,
athenaMockGetQueryResultsWithRedshiftStorageComputeSavingsPlan,
athenaMockGetQueryResultsNetworking,
athenaMockGetQueryResultsMemory,
athenaMockGetQueryResultsS3WithReplicationFactors,
athenaMockGetQueryResultsEC2EFSRDSWithReplicationFactors,
athenaMockGetQueryResultsDatabasesWithReplicationFactors,
athenaMockGetQueryResultsWithReclassifiedUnknowns,
athenaMockGetQueryH1ApiFsxBackupDirectConnectDirectoryService,
athenaMockGetQueryResultsWithEC2ElasticMapWithEmbodiedEmissions,
athenaMockGetQueryResultsWithNoUsageAmount,
athenaMockGetQueryResultsWithUnknownInstanceType,
athenaMockGetQueryResultsWithGPUInstances,
athenaMockGetQueryResultsWithX86AndARMLambdas,
athenaMockGetQueryResultsWithTaggedResources,
} from './fixtures/athena.fixtures'
import { AWS_CLOUD_CONSTANTS } from '../domain'
import {} from '../lib/CostAndUsageTypes'
import { CloudWatchClient } from '@aws-sdk/client-cloudwatch'
import { GlueClient } from '@aws-sdk/client-glue'
import {
AthenaClient,
GetQueryExecutionCommand,
GetQueryExecutionCommandOutput,
GetQueryResultsCommand,
GetQueryResultsCommandOutput,
QueryExecutionState,
StartQueryExecutionCommand,
} from '@aws-sdk/client-athena'
import { S3Client } from '@aws-sdk/client-s3'
import { CostExplorerClient } from '@aws-sdk/client-cost-explorer'
import { CloudWatchLogsClient } from '@aws-sdk/client-cloudwatch-logs'
const testAccountName = 'the-test-account'
const defaultMockConfig = {
AWS: {
ATHENA_DB_NAME: 'test-db',
ATHENA_DB_TABLE: 'test-table',
ATHENA_QUERY_RESULT_LOCATION: 'test-location',
ATHENA_REGION: 'test-region',
RESOURCE_TAG_NAMES: ['user:Environment', 'aws:CreatedBy'],
accounts: [
{
id: testAccountId,
name: testAccountName,
},
],
},
}
jest.mock('@cloud-carbon-footprint/common', () => ({
...(jest.requireActual('@cloud-carbon-footprint/common') as Record<
string,
unknown
>),
configLoader: jest.fn().mockImplementation(() => defaultMockConfig),
}))
describe('CostAndUsageReports Service', () => {
const startDate = new Date('2020-10-01')
const endDate = new Date('2020-11-03')
const grouping = GroupBy.day
const startQueryExecutionResponse = { QueryExecutionId: 'some-execution-id' }
const getQueryExecutionResponse = {
$metadata: {},
QueryExecution: { Status: { State: QueryExecutionState.SUCCEEDED } },
}
const getQueryExecutionFailedResponse = {
$metadata: {},
QueryExecution: {
Status: { State: QueryExecutionState.FAILED, StateChangeReason: 'TEST' },
},
}
const getServiceWrapper = () => {
const serviceWrapper = new ServiceWrapper(
new CloudWatchClient(),
new CloudWatchLogsClient(),
new CostExplorerClient(),
new S3Client(),
new AthenaClient(),
new GlueClient(),
)
// Ensures that tests pass product_vcpu column check by default
serviceWrapper.getAthenaTableDescription = jest.fn().mockResolvedValue({
Table: {
StorageDescriptor: {
Columns: [
{
Name: 'product_vcpu',
Type: 'string',
},
],
},
},
})
return serviceWrapper
}
const athenaClientMock = mockClient(AthenaClient)
beforeEach(() => {
AWS_CLOUD_CONSTANTS.KILOWATT_HOURS_BY_SERVICE_AND_USAGE_UNIT = {
total: {},
}
})
afterEach(() => {
athenaClientMock.reset()
jest.restoreAllMocks()
startQueryExecutionSpy.mockClear()
getQueryExecutionSpy.mockClear()
getQueryResultsSpy.mockClear()
})
it('Gets Estimates for ec2, ebs Snapshot, ebs SDD Storage and lambda across multiple days with accumulation', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(athenaMockGetQueryResultsWithEC2EBSLambda)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
expect(startQueryExecutionSpy).toHaveBeenCalledWith(
{
QueryString: expect.anything(),
QueryExecutionContext: {
Database: 'test-db',
},
ResultConfiguration: {
EncryptionConfiguration: {
EncryptionOption: 'SSE_S3',
},
OutputLocation: 'test-location',
},
},
expect.anything(),
)
expect(getQueryExecutionSpy).toHaveBeenCalledWith(
startQueryExecutionResponse,
expect.anything(),
)
expect(getQueryResultsSpy).toHaveBeenCalledWith(
startQueryExecutionResponse,
expect.anything(),
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
kilowattHours: 0.04514513899824584,
co2e: 0.000016483745125115724,
usesAverageCPUConstant: true,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonEC2',
cost: 3,
region: 'us-east-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
kilowattHours: 0.021943931013313992,
co2e: 0.000008253733875512186,
usesAverageCPUConstant: true,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonEC2',
cost: 3,
region: 'us-east-2',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-02T23:59:59.000Z'),
periodStartDate: new Date('2020-11-02T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-03'),
serviceEstimates: [
{
kilowattHours: 0.021943931013313992,
co2e: 0.000008253733875512186,
usesAverageCPUConstant: true,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonEC2',
cost: 4,
region: 'us-east-2',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-03T23:59:59.000Z'),
periodStartDate: new Date('2020-11-03T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-29'),
serviceEstimates: [
{
kilowattHours: 0.006079968000000001,
co2e: 0.0000022199653186305124,
usesAverageCPUConstant: false,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonEC2',
cost: 5,
region: 'us-east-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-29T23:59:59.000Z'),
periodStartDate: new Date('2020-10-29T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
kilowattHours: 0.00823329,
co2e: 0.000002458873753734411,
usesAverageCPUConstant: false,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonEC2',
cost: 6,
region: 'us-west-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
kilowattHours: 0.00001336777777777778,
co2e: 3.992289585758778e-9,
usesAverageCPUConstant: true,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AWSLambda',
cost: 15,
region: 'us-west-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for CloudWatch, RDS and S3 all on the same day with accumulation', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(athenaMockGetQueryResultsWithS3CloudWatchRDS)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 2.4046587648924287e-18,
cost: 9,
region: 'us-west-1',
serviceName: 'AmazonS3',
usesAverageCPUConstant: false,
kilowattHours: 8.051756594795733e-15,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 1.0094978899256603e-18,
cost: 10,
region: 'us-east-2',
serviceName: 'AmazonCloudWatch',
usesAverageCPUConstant: false,
kilowattHours: 2.6839188649319113e-15,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000001002067678548495,
cost: 11,
region: 'us-east-1',
serviceName: 'AmazonRDS',
usesAverageCPUConstant: false,
kilowattHours: 0.0027444300000000004,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000001513153079221176,
cost: 12,
region: 'us-west-1',
serviceName: 'AmazonRDS',
usesAverageCPUConstant: false,
kilowattHours: 0.005066640000000001,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000008170193100493411,
cost: 13,
region: 'us-west-2',
serviceName: 'AmazonRDS',
usesAverageCPUConstant: true,
kilowattHours: 0.027357065018160803,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for Amazon Glue, ECS and DynamoDB Storage and excluded usage types', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(athenaMockGetQueryResultsWithNetworkingGlueECSDynamoDB)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
kilowattHours: 0.019249600000000002,
co2e: 0.000007028564031506401,
usesAverageCPUConstant: true,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AWSGlue',
cost: 5,
region: 'us-east-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0,
cost: 10,
kilowattHours: 0,
region: 'us-east-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: false,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-31'),
serviceEstimates: [
{
kilowattHours: 0.000013419594324659556,
co2e: 4.0077646081540475e-9,
usesAverageCPUConstant: false,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonDynamoDB',
cost: 13,
region: 'us-west-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-31T23:59:59.000Z'),
periodStartDate: new Date('2020-10-31T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for Kinesis, ES & EC2 Spot Instance', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(athenaMockGetQueryResultsWithKinesisESAndEc2Spot)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
kilowattHours: 0.0078110700000000016,
co2e: 0.0000029379646255669026,
usesAverageCPUConstant: false,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonKinesisAnalytics',
cost: 912,
region: 'us-east-2',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
kilowattHours: 0.037493135999999996,
co2e: 0.000011197332786236701,
usesAverageCPUConstant: false,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonES',
cost: 73,
region: 'us-west-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-31'),
serviceEstimates: [
{
kilowattHours: 0.6833798473215091,
co2e: 0.00024952097782500904,
usesAverageCPUConstant: true,
cloudProvider: 'AWS',
accountId: testAccountId,
accountName: testAccountName,
serviceName: 'AmazonEC2',
cost: 10,
region: 'us-east-1',
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0000021085692094519205,
cost: 14,
kilowattHours: 0.005774880000000001,
region: 'us-east-1',
serviceName: 'AmazonES',
usesAverageCPUConstant: true,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-31T23:59:59.000Z'),
periodStartDate: new Date('2020-10-31T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for ECS Compute + Storage, EKS Compute, Kafka and Unknown Services', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(
athenaMockGetQueryResultsWithECSEksKafkaAndUnknownServices,
)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 5.6351544692651995e-9,
cost: 2,
region: 'us-east-2',
serviceName: 'AmazonECS',
usesAverageCPUConstant: false,
kilowattHours: 0.000014981999999999998,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0000035930606271829,
cost: 2,
region: 'us-west-1',
serviceName: 'AmazonECS',
usesAverageCPUConstant: true,
kilowattHours: 0.012031000000000002,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000019658682722228422,
cost: 4,
region: 'us-west-1',
serviceName: 'AmazonMSK',
usesAverageCPUConstant: true,
kilowattHours: 0.0658251102254754,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00004433395223465754,
cost: 4,
kilowattHours: 0.046578084338065526,
region: 'ap-south-1',
serviceName: 'AmazonMSK',
usesAverageCPUConstant: true,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000007432459565480431,
cost: 2,
kilowattHours: 0.024886838912708185,
region: 'us-west-1',
serviceName: 'AmazonEKS',
usesAverageCPUConstant: false,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0,
cost: 4,
kilowattHours: 0,
region: 'us-west-1',
serviceName: 'AmazonRoute53',
usesAverageCPUConstant: false,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0,
cost: 4,
kilowattHours: 0,
region: 'us-west-1',
serviceName: '8icvdraalzbfrdevgamoddblf', // change because of embodied e
usesAverageCPUConstant: false,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for DocumentDB Compute, ElasticMapReduce, EC2 Credits', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(
athenaMockGetQueryResultsWithDocDBComputeEbsOptimizedSpotUsage,
)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00022566711695111606,
cost: 10,
region: 'us-west-1',
serviceName: 'AmazonDocDB',
usesAverageCPUConstant: true,
kilowattHours: 0.7556235103573926,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00003111035110251798,
cost: 5,
region: 'us-west-2',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 0.10416986323101671,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0005352210795865024,
cost: 25,
region: 'us-west-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 1.7921336366522236,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0000014372242508731602,
cost: 20,
region: 'us-west-1',
serviceName: 'AmazonSimpleDB',
usesAverageCPUConstant: true,
kilowattHours: 0.0048124000000000005,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00003156018799348747,
cost: 20,
region: 'us-west-1',
serviceName: 'ElasticMapReduce',
usesAverageCPUConstant: true,
kilowattHours: 0.10567609655040748,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000006149993527568099,
cost: 20,
region: 'us-east-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 0.016843399999999998,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for Redshift Storage and Compute, and Savings Plan Compute', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(
athenaMockGetQueryResultsWithRedshiftStorageComputeSavingsPlan,
)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0000018499710988587602,
cost: 10,
region: 'us-east-1',
serviceName: 'AmazonRedshift',
usesAverageCPUConstant: false,
kilowattHours: 0.005066640000000001,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 2.7994468136055626e-8,
cost: 10,
region: 'us-west-1',
serviceName: 'AmazonRedshift',
usesAverageCPUConstant: true,
kilowattHours: 0.00009373664435185185,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0008301159037595617,
cost: 15,
region: 'us-west-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 2.7795591208717187,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00003449338202095584,
cost: 15,
region: 'us-west-1',
serviceName: 'AmazonRDS',
usesAverageCPUConstant: true,
kilowattHours: 0.1154976,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for EC2 and ElasticMapReduce with Embodied Emissions', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(
athenaMockGetQueryResultsWithEC2ElasticMapWithEmbodiedEmissions,
)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-30'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000008253733875512186,
cost: 5,
region: 'us-east-2',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 0.021943931013313992,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-30T23:59:59.000Z'),
periodStartDate: new Date('2020-10-30T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00003156018799348747,
cost: 10,
region: 'us-west-1',
serviceName: 'ElasticMapReduce',
usesAverageCPUConstant: true,
kilowattHours: 0.10567609655040748,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-29'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000035573562357077625,
cost: 20,
region: 'ca-central-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 0.30823639508775347,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-29T23:59:59.000Z'),
periodStartDate: new Date('2020-10-29T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-31'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0008301159037595617,
cost: 25,
region: 'us-west-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 2.7795591208717187,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-31T23:59:59.000Z'),
periodStartDate: new Date('2020-10-31T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Gets Estimates for Networking', async () => {
// given
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(athenaMockGetQueryResultsNetworking)
// when
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2021-01-01'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000652297157169285,
cost: 22,
region: 'us-east-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: false,
kilowattHours: 1.7864900000000001,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0002964987078042205,
cost: 10,
kilowattHours: 0.8120409090909092,
region: 'us-east-1',
serviceName: 'AmazonCloudWatch',
usesAverageCPUConstant: false,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-01T23:59:59.000Z'),
periodStartDate: new Date('2021-01-01T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for Memory', async () => {
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(athenaMockGetQueryResultsMemory)
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2021-01-01'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00021009611506982525,
cost: 40,
region: 'us-east-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 0.5754043298914528,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.00008299246283987963,
cost: 7,
region: 'us-west-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: true,
kilowattHours: 0.2778918654677533,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-01T23:59:59.000Z'),
periodStartDate: new Date('2021-01-01T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for S3 with replication factors', async () => {
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(athenaMockGetQueryResultsS3WithReplicationFactors)
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2021-01-02'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 5.082956674181305e-9,
cost: 10,
region: 'us-east-1',
serviceName: 'AmazonS3',
usesAverageCPUConstant: false,
kilowattHours: 0.000013921034560789199,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-02T23:59:59.000Z'),
periodStartDate: new Date('2021-01-02T00:00:00.000Z'),
},
{
timestamp: new Date('2021-01-03'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 4.7021717179043134e-9,
cost: 5,
region: 'us-east-1',
serviceName: 'AmazonS3',
usesAverageCPUConstant: false,
kilowattHours: 0.000012878153246556,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-03T23:59:59.000Z'),
periodStartDate: new Date('2021-01-03T00:00:00.000Z'),
},
{
timestamp: new Date('2021-01-04'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 3.9761760600122215e-12,
cost: 7,
region: 'eu-north-1',
serviceName: 'AmazonS3',
usesAverageCPUConstant: false,
kilowattHours: 4.970220075015277e-7,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-04T23:59:59.000Z'),
periodStartDate: new Date('2021-01-04T00:00:00.000Z'),
},
{
timestamp: new Date('2021-01-05'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 7.73744473690449e-8,
cost: 10,
region: 'us-east-1',
serviceName: 'AmazonS3',
usesAverageCPUConstant: false,
kilowattHours: 0.00021191059160856002,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-05T23:59:59.000Z'),
periodStartDate: new Date('2021-01-05T00:00:00.000Z'),
},
{
timestamp: new Date('2021-01-06'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 1.866654319778587e-10,
cost: 5,
region: 'us-west-1',
serviceName: 'AmazonS3',
usesAverageCPUConstant: false,
kilowattHours: 6.250303140268443e-7,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-06T23:59:59.000Z'),
periodStartDate: new Date('2021-01-06T00:00:00.000Z'),
},
{
timestamp: new Date('2021-01-07'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 3.512980565871229e-10,
cost: 7,
region: 'us-west-1',
serviceName: 'AmazonS3',
usesAverageCPUConstant: false,
kilowattHours: 0.0000011762860016401646,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
],
groupBy: grouping,
periodEndDate: new Date('2021-01-07T23:59:59.000Z'),
periodStartDate: new Date('2021-01-07T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for EC2, RDS and EFS with replication factors', async () => {
mockStartQueryExecution(startQueryExecutionResponse)
mockGetQueryExecution(getQueryExecutionResponse)
mockGetQueryResults(
athenaMockGetQueryResultsEC2EFSRDSWithReplicationFactors,
)
const athenaService = new CostAndUsageReports(
new ComputeEstimator(),
new StorageEstimator(AWS_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(AWS_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(AWS_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(AWS_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(AWS_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
AWS_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
getServiceWrapper(),
)
const result = await athenaService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2021-01-01'),
serviceEstimates: [
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000046163245876723656,
cost: 10,
region: 'ap-south-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: false,
kilowattHours: 0.048499974655632,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.000014388309712007368,
cost: 10,
region: 'us-east-1',
serviceName: 'AmazonEC2',
usesAverageCPUConstant: false,
kilowattHours: 0.039406229407701006,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 5.944189689551422e-21,
cost: 5,
region: 'eu-west-1',
serviceName: 'AmazonEFS',
usesAverageCPUConstant: false,
kilowattHours: 1.9489146523119416e-17,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 2.602146370008931e-8,
cost: 10,
region: 'ap-south-1',
serviceName: 'AmazonRDS',
usesAverageCPUConstant: false,
kilowattHours: 0.000027338639343667196,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 2.258285520784176e-9,
cost: 10,
region: 'eu-west-1',
serviceName: 'AmazonRDS',
usesAverageCPUConstant: false,
kilowattHours: 0.000007404214822243199,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{
accountId: testAccountId,
accountName: testAccountName,
cloudProvider: 'AWS',
co2e: 0.0000186452352,
cost: 7,
region: 'eu-central-1',
serviceName: 'AmazonRDS',
usesAverageCPUConstant: false,
kilowattHours: 0.0506664,
tags: {
'user:Environment': '',
'aws:CreatedBy': '',
},
},
{