UNPKG

@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
/* * © 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': '', }, }, {