@cloud-carbon-footprint/gcp
Version:
The core logic to get cloud usage data and estimate energy and carbon emissions from Google Cloud Platform.
1,535 lines (1,468 loc) • 60.6 kB
text/typescript
/*
* © 2021 Thoughtworks, Inc.
*/
import { BigQuery } from '@google-cloud/bigquery'
import {
EstimationResult,
GroupBy,
Logger,
LookupTableOutput,
setConfig,
} from '@cloud-carbon-footprint/common'
import {
ComputeEstimator,
EmbodiedEmissionsEstimator,
MemoryEstimator,
NetworkingEstimator,
StorageEstimator,
UnknownEstimator,
} from '@cloud-carbon-footprint/core'
import { GCP_CLOUD_CONSTANTS } from '../domain'
import BillingExportTable, { buildTagQuery } from '../lib/BillingExportTable'
import {
mockQueryAppEngineComputeUnknownRegion,
mockQueryCloudSpannerKubernetesEngineAndRequestsUsageTypesWithReplicationFactors,
mockQueryCloudStorageWithReplicationFactors,
mockQueryComputeEngineCloudFilestoreCloudSQLWithReplicationFactors,
mockQueryComputeWithDifferentMachineTypes,
mockQueryComputeWithDifferentMachineTypesForEmbodiedEmissions,
mockQueryMemoryStoreWithReplicationFactors,
mockQueryNetworkingWithIngress,
mockQueryReclassifiedUnknowns,
mockQueryResultsAppEngineSSDStorage,
mockQueryResultsCloudSQLSSDComputeEngineDataFlowHDD,
mockQueryResultsComputeEngineRam,
mockQueryResultsForProjectFilter,
mockQueryResultsForProjectFilterArray,
mockQueryResultsForProjectFilterEmpty,
mockQueryResultsForProjectFilterError,
mockQueryResultsGPUMachineTypes,
mockQueryResultsUnknownAndCloudSQLCompute,
mockQueryResultsUnknownUsages,
mockQueryResultsWithNoTags,
mockQueryResultsWithTags,
} from './fixtures/bigQuery.fixtures'
import { lookupTableInputData } from './fixtures/lookupTable.fixtures'
const mockJob = { getQueryResults: jest.fn() }
const mockCreateQueryJob = jest.fn().mockResolvedValue([mockJob, 'test-job-id'])
jest.mock('@google-cloud/bigquery', () => {
return {
BigQuery: jest.fn().mockImplementation(() => {
return {
createQueryJob: mockCreateQueryJob,
}
}),
}
})
describe('GCP BillingExportTable Service', () => {
const startDate = new Date('2020-10-01')
const endDate = new Date('2020-11-03')
const grouping = GroupBy.day
const accountId = 'test-account-id'
const accountName = 'test-account-name'
beforeEach(() => {
GCP_CLOUD_CONSTANTS.KILOWATT_HOURS_BY_SERVICE_AND_USAGE_UNIT = {
total: {},
}
jest.clearAllMocks()
})
it('Returns estimation results for App Engine SSD Storage & GCS Storage accumulated', async () => {
// given
mockJob.getQueryResults.mockResolvedValue(
mockQueryResultsAppEngineSSDStorage,
)
// when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
kilowattHours: 0.005180640794376637,
co2e: 0.000002984049097560943,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'App Engine',
cost: 15,
tags: {},
region: 'us-east1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-02T23:59:59.000Z'),
periodStartDate: new Date('2020-11-02T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Returns estimation results for Cloud SQL SSD Storage, Compute Engine and Cloud Dataflow HDD', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(
mockQueryResultsCloudSQLSSDComputeEngineDataFlowHDD,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
kilowattHours: 1.4206307241693137,
co2e: 0.0008182832971215247,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud SQL',
cost: 7,
tags: {},
region: 'us-east1',
},
{
kilowattHours: 0.058544444444444455,
co2e: 0.000033721600000000006,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 7,
tags: {},
region: 'us-east1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-02T23:59:59.000Z'),
periodStartDate: new Date('2020-11-02T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
kilowattHours: 0.14089588754965615,
co2e: 0.000011130775116422836,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud Dataflow',
cost: 12,
tags: {},
region: 'us-west1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Returns estimation results for Cloud Storage based on replication factors', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(
mockQueryCloudStorageWithReplicationFactors,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
kilowattHours: 1.7757217089335124e-13,
co2e: 1.756188770135244e-16,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud Storage',
cost: 10,
tags: {},
region: 'nam4',
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 1.7665996407027273e-8,
cost: 120,
kilowattHours: 0.000042774809702245215,
tags: {},
region: 'us-central1',
serviceName: 'Cloud Storage',
usesAverageCPUConstant: false,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 7.09386605847006e-13,
cost: 220,
kilowattHours: 1.8619071019606457e-10,
tags: {},
region: 'us',
serviceName: 'Cloud Storage',
usesAverageCPUConstant: false,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-02T23:59:59.000Z'),
periodStartDate: new Date('2020-11-02T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Returns estimation results for ComputeEngine, CloudFileStore and CloudSQL based on replication factors', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(
mockQueryComputeEngineCloudFilestoreCloudSQLWithReplicationFactors,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
kilowattHours: 0.0002839454223632813,
co2e: 1.6355256328125002e-7,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 150,
tags: {},
region: 'us-east1',
},
{
kilowattHours: 8.234108336182544e-11,
co2e: 3.7300510762906926e-14,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 150,
tags: {},
region: 'asia-northeast1',
},
{
kilowattHours: 4.4601420154322116e-11,
co2e: 1.3929023514194795e-13,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 150,
tags: {},
region: 'asia',
},
{
kilowattHours: 4.4601420154322116e-11,
co2e: 3.028436428478472e-14,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 150,
tags: {},
region: 'asia-south1',
},
{
kilowattHours: 0.034632,
co2e: 0.000014303016000000002,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud Filestore',
cost: 70,
tags: {},
region: 'us-central1',
},
{
kilowattHours: 0.0002330859375,
co2e: 7.528675781249999e-8,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud SQL',
cost: 80,
tags: {},
region: 'us-east4',
},
{
kilowattHours: 6.587286728972686e-10,
co2e: 4.4727676889724536e-13,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud SQL',
cost: 80,
tags: {},
region: 'asia-south1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Returns estimation results for Cloud Memory store for Redis based on replication factors', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(
mockQueryMemoryStoreWithReplicationFactors,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
kilowattHours: 0.00001691015625,
co2e: 6.983894531250001e-9,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud Memorystore for Redis',
cost: 170,
tags: {},
region: 'us-central1',
},
{
kilowattHours: 0.0000338203125,
co2e: 1.2581156250000001e-8,
usesAverageCPUConstant: false,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud Memorystore for Redis',
cost: 170,
tags: {},
region: 'us-central2',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Returns estimation results for Compute with different machine types', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(
mockQueryComputeWithDifferentMachineTypes,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
kilowattHours: 13.819853956512677,
co2e: 0.007960235878951302,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 10,
tags: {},
region: 'us-east1',
},
{
kilowattHours: 4.9383708554632895,
co2e: 0.0003901312975815998,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 7,
tags: {},
region: 'us-west1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-02T23:59:59.000Z'),
periodStartDate: new Date('2020-11-02T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
kilowattHours: 8.346969043116246,
co2e: 0.004807854168834958,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 10,
tags: {},
region: 'us-east1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Returns estimation results for Compute with Embodied Emissions', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(
mockQueryComputeWithDifferentMachineTypesForEmbodiedEmissions,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
kilowattHours: 1.0981384033610844,
co2e: 0.0004535311605881279,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 0.758656,
tags: {},
region: 'us-central1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-02T23:59:59.000Z'),
periodStartDate: new Date('2020-11-02T00:00:00.000Z'),
},
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
kilowattHours: 0.2535995174499983,
co2e: 0.00010473660070684932,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 1.570404,
tags: {},
region: 'us-central1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-03'),
serviceEstimates: [
{
kilowattHours: 6.4556663449050875,
co2e: 0.002666190200445801,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 0.959995,
tags: {},
region: 'us-central1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-03T23:59:59.000Z'),
periodStartDate: new Date('2020-11-03T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-04'),
serviceEstimates: [
{
kilowattHours: 0.07312680495575823,
co2e: 0.000049653100564959836,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 0.681886,
tags: {},
region: 'asia-south1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-04T23:59:59.000Z'),
periodStartDate: new Date('2020-11-04T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-05'),
serviceEstimates: [
{
kilowattHours: 0.06856871694444444,
co2e: 0.000046558158805277776,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Compute Engine',
cost: 0.681886,
tags: {},
region: 'asia-south1',
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-05T23:59:59.000Z'),
periodStartDate: new Date('2020-11-05T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('Returns estimation results Unknown Usage Types', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(mockQueryResultsUnknownUsages)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = []
expect(result).toEqual(expectedResult)
})
it('Returns estimates for networking and CLoud SQL Compute usage accumulated', async () => {
//given
mockJob.getQueryResults.mockResolvedValue(
mockQueryResultsUnknownAndCloudSQLCompute,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
// then
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
kilowattHours: 89.16258807652778,
co2e: 0.051357650732080005,
usesAverageCPUConstant: true,
cloudProvider: 'GCP',
accountId: accountId,
accountName: accountName,
serviceName: 'Cloud SQL',
cost: 49,
tags: {},
region: 'us-east1',
},
{
accountId: 'test-account-id',
accountName: 'test-account-name',
cloudProvider: 'GCP',
co2e: 0.0010529468530334721,
cost: 20,
kilowattHours: 13.328441177638888,
tags: {},
region: 'us-west1',
serviceName: 'Cloud SQL',
usesAverageCPUConstant: false,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for unknown App Engine Compute and Cloud DataFlow Compute', async () => {
mockJob.getQueryResults.mockResolvedValue(
mockQueryAppEngineComputeUnknownRegion,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.0004813594007200001,
cost: 190,
tags: {},
region: 'us-east1',
serviceName: 'Cloud Dataflow',
usesAverageCPUConstant: true,
kilowattHours: 0.8356934040277779,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.0000015850660082307972,
cost: 5,
tags: {},
region: 'Unknown',
serviceName: 'App Engine',
usesAverageCPUConstant: false,
kilowattHours: 0.005111991817506756,
},
{
accountId: 'test-account-id',
accountName: 'test-account-name',
cloudProvider: 'GCP',
co2e: 0.000006133537520000001,
cost: 10,
kilowattHours: 0.01064850263888889,
tags: {},
region: 'us-east1',
serviceName: 'App Engine',
usesAverageCPUConstant: false,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('returns estimations for Kubernetes Engine Compute, unknown CloudSpanner and requests usageTypes', async () => {
mockJob.getQueryResults.mockResolvedValue(
mockQueryCloudSpannerKubernetesEngineAndRequestsUsageTypesWithReplicationFactors,
)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 6.983894531250001e-9,
cost: 170,
kilowattHours: 0.00001691015625,
tags: {},
region: 'us-central1',
serviceName: 'Cloud Memorystore for Redis',
usesAverageCPUConstant: false,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 1.9881218437120933,
cost: 150,
kilowattHours: 2928.014497366853,
tags: {},
region: 'asia-south1',
serviceName: 'Kubernetes Engine',
usesAverageCPUConstant: true,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.1047697778207236,
cost: 350,
kilowattHours: 253.67984944485133,
tags: {},
region: 'us-central1',
serviceName: 'Kubernetes Engine',
usesAverageCPUConstant: true,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.19661033311700518,
cost: 50,
tags: {},
region: 'asia-south1',
serviceName: 'Cloud Spanner',
usesAverageCPUConstant: false,
kilowattHours: 289.5586643843964,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 3.4459145126546226e-7,
cost: 150,
tags: {},
region: 'asia-east1',
serviceName: 'Cloud Spanner',
usesAverageCPUConstant: false,
kilowattHours: 0.0007849463582356771,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0,
tags: {},
region: 'europe',
serviceName: 'App Engine',
usesAverageCPUConstant: false,
cost: 10,
kilowattHours: 0,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for Networking', async () => {
mockJob.getQueryResults.mockResolvedValue(mockQueryNetworkingWithIngress)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 1.3354362695571037e-8,
cost: 10,
tags: {},
region: 'us-west1',
serviceName: 'App Engine',
usesAverageCPUConstant: false,
kilowattHours: 0.000169042565766722,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.000001532716343557835,
cost: 10,
tags: {},
region: 'us-east1',
serviceName: 'Cloud Storage',
usesAverageCPUConstant: false,
kilowattHours: 0.0026609658742323523,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.0000017110010571314025,
cost: 10,
kilowattHours: 0.0026609658742323523,
tags: {},
region: 'europe-central2',
serviceName: 'Cloud Pub/Sub',
usesAverageCPUConstant: false,
},
{
accountId: 'test-account-id',
accountName: 'test-account-name',
cloudProvider: 'GCP',
co2e: 1.3354362695571038e-8,
cost: 10,
kilowattHours: 0.00016904256576672202,
tags: {},
region: 'us-west1',
serviceName: 'Compute Engine',
usesAverageCPUConstant: false,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-02T23:59:59.000Z'),
periodStartDate: new Date('2020-11-02T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for Memory', async () => {
mockJob.getQueryResults.mockResolvedValue(mockQueryResultsComputeEngineRam)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 1.0575105746587117e-18,
cost: 10,
tags: {},
region: 'us-west1',
serviceName: 'Compute engine',
usesAverageCPUConstant: false,
kilowattHours: 1.3386209805806478e-14,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 2.9601980414655468e-18,
cost: 8,
tags: {},
region: 'europe-west1',
serviceName: 'Compute engine',
usesAverageCPUConstant: false,
kilowattHours: 2.8739786810345115e-14,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 1.766608531276385e-17,
cost: 5,
tags: {},
region: 'us-central1',
serviceName: 'Compute engine',
usesAverageCPUConstant: false,
kilowattHours: 4.277502497037252e-14,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for GPU Machine Types', async () => {
mockJob.getQueryResults.mockResolvedValue(mockQueryResultsGPUMachineTypes)
//when
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.000012383249999999999,
cost: 10,
tags: {},
region: 'us-west1',
serviceName: 'Compute engine',
usesAverageCPUConstant: true,
kilowattHours: 0.15675,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.00002559756,
cost: 8,
tags: {},
region: 'europe-west1',
serviceName: 'Compute engine',
usesAverageCPUConstant: true,
kilowattHours: 0.24852000000000002,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.000034754020524999994,
cost: 8,
tags: {},
region: 'europe-west1',
serviceName: 'Notebooks',
usesAverageCPUConstant: true,
kilowattHours: 0.337417675,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for reclassified unknowns', async () => {
mockJob.getQueryResults.mockResolvedValue(mockQueryReclassifiedUnknowns)
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
const result = await billingExportTableService.getEstimates(
startDate,
endDate,
grouping,
)
const expectedResult: EstimationResult[] = [
{
timestamp: new Date('2020-10-28'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.0038927308772000006,
cost: 456,
tags: {},
region: 'us-east1',
serviceName: 'Compute Engine',
usesAverageCPUConstant: true,
kilowattHours: 6.758213328472223,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.18390688756792,
cost: 6018.6968,
tags: {},
region: 'us-east1',
serviceName: 'App Engine',
usesAverageCPUConstant: false,
kilowattHours: 319.2827909165278,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-10-28T23:59:59.000Z'),
periodStartDate: new Date('2020-10-28T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-02'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.000002942051349277743,
cost: 789,
tags: {},
region: 'us-east1',
serviceName: 'App Engine',
usesAverageCPUConstant: false,
kilowattHours: 0.005107728036940526,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 9.613035945428663e-18,
cost: 0.012744,
tags: {},
region: 'us-east1',
serviceName: 'Stackdriver Monitoring',
usesAverageCPUConstant: false,
kilowattHours: 1.6689298516369207e-14,
},
],
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: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.000001532716343557835,
cost: 123,
tags: {},
region: 'us-east1',
serviceName: 'Cloud Storage',
usesAverageCPUConstant: false,
kilowattHours: 0.0026609658742323523,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 0.000004317081237745286,
cost: 0.816998,
tags: {},
region: 'us-east1',
serviceName: 'Cloud Run',
usesAverageCPUConstant: false,
kilowattHours: 0.007494932704418899,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-03T23:59:59.000Z'),
periodStartDate: new Date('2020-11-03T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-04'),
serviceEstimates: [
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 6.341850757598878e-17,
cost: 10,
tags: {},
region: 'us-east1',
serviceName: 'Compute Engine',
usesAverageCPUConstant: false,
kilowattHours: 1.1010157565275828e-13,
},
{
accountId: accountId,
accountName: accountName,
cloudProvider: 'GCP',
co2e: 5.012012027370107e-13,
cost: 25,
tags: {},
region: 'us-east1',
serviceName: 'Cloud Run',
usesAverageCPUConstant: false,
kilowattHours: 8.701409769739769e-10,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-04T23:59:59.000Z'),
periodStartDate: new Date('2020-11-04T00:00:00.000Z'),
},
{
timestamp: new Date('2020-11-05'),
serviceEstimates: [
{
accountId: 'test-account-id',
accountName: 'test-account-name',
cloudProvider: 'GCP',
co2e: 0,
cost: 0.000004,
kilowattHours: 0,
tags: {},
region: 'asia-south1',
serviceName: 'Cloud Storage',
usesAverageCPUConstant: false,
},
{
accountId: 'test-account-id',
accountName: 'test-account-name',
cloudProvider: 'GCP',
co2e: 0,
cost: 200,
kilowattHours: 0,
tags: {},
region: 'us-east1',
serviceName: 'Secret Manager',
usesAverageCPUConstant: false,
},
{
accountId: 'test-account-id',
accountName: 'test-account-name',
cloudProvider: 'GCP',
co2e: 0,
cost: 200,
kilowattHours: 0,
tags: {},
region: 'us-east1',
serviceName: 'Cloud Key Management Service (KMS)',
usesAverageCPUConstant: false,
},
],
groupBy: grouping,
periodEndDate: new Date('2020-11-05T23:59:59.000Z'),
periodStartDate: new Date('2020-11-05T00:00:00.000Z'),
},
]
expect(result).toEqual(expectedResult)
})
it('estimation for lookup table input data', async () => {
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
)
const result =
await billingExportTableService.getEstimatesFromInputData(
lookupTableInputData,
)
const expectedResult: LookupTableOutput[] = [
{
co2e: 8.354718e-7,
kilowattHours: 0.0025866,
machineType: '',
region: 'us-east4',
serviceName: 'Compute Engine',
usageType: 'N1 Predefined Instance Core running in Virginia',
},
{
co2e: 0.000022279448991171992,
kilowattHours: 0.06897662226369038,
machineType: 'n1-standard-4',
region: 'us-east4',
serviceName: 'Compute Engine',
usageType: 'N1 Predefined Instance Core running in Virginia',
},
{
co2e: 0.000027316698591171992,
kilowattHours: 0.08457182226369038,
machineType: 'n1-standard-8',
region: 'us-east4',
serviceName: 'Compute Engine',
usageType: 'N1 Predefined Instance Core running in Virginia',
},
{
co2e: 6.637083060923034e-20,
kilowattHours: 6.443769962061198e-16,
machineType: '',
region: 'europe-west1',
serviceName: 'Compute Engine',
usageType: 'Storage PD Capacity',
},
{
co2e: 3.764145076274872e-13,
kilowattHours: 3.654509782791138e-9,
machineType: '',
region: 'europe-west1',
serviceName: 'Compute Engine',
usageType: 'Network Internet Egress from EMEA to Americas',
},
{
co2e: 5.003275873605162e-19,
kilowattHours: 1.2114469427615405e-15,
machineType: '',
region: 'us-central1',
serviceName: 'Compute Engine',
usageType: 'SSD backed PD Capacity',
},
{
co2e: 0.0000053157402,
kilowattHours: 0.0164574,
machineType: '',
region: 'us-east4',
serviceName: 'App Engine',
usageType: 'Backend Instances',
},
{
co2e: 1.1804066598415373e-12,
kilowattHours: 3.6545097827911376e-9,
machineType: '',
region: 'us-east4',
serviceName: 'Compute Engine',
usageType: 'Network Inter Region Ingress from Netherlands to Americas',
},
]
expect(result).toEqual(expectedResult)
})
it('returns estimates for filtered projects that are an array of ids', async () => {
const testAccountId = 'test-account-id'
const testAccountIdTwo = 'test-account-id-two'
setConfig({
GCP: {
projects: [testAccountId, testAccountIdTwo],
},
})
mockJob.getQueryResults.mockResolvedValue(
mockQueryResultsForProjectFilterArray,
)
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator(
GCP_CLOUD_CONSTANTS.SERVER_EXPECTED_LIFESPAN,
),
new BigQuery(),
)
await billingExportTableService.getEstimates(startDate, endDate, grouping)
const expectedWhereFilter = `AND project.id IN ('${testAccountId}', '${testAccountIdTwo}')`
expect(mockCreateQueryJob).toHaveBeenCalledWith(
expect.objectContaining({
query: expect.stringContaining(expectedWhereFilter),
}),
)
})
it('returns estimates for filtered projects when list of projects is provided', async () => {
const testAccountId = 'test-account-id'
const testAccountIdTwo = 'test-account-id-two'
setConfig({
GCP: {
projects: [{ id: testAccountId }, { id: testAccountIdTwo }],
},
})
mockJob.getQueryResults.mockResolvedValue(mockQueryResultsForProjectFilter)
const billingExportTableService = new BillingExportTable(
new ComputeEstimator(),
new StorageEstimator(GCP_CLOUD_CONSTANTS.SSDCOEFFICIENT),
new StorageEstimator(GCP_CLOUD_CONSTANTS.HDDCOEFFICIENT),
new NetworkingEstimator(GCP_CLOUD_CONSTANTS.NETWORKING_COEFFICIENT),
new MemoryEstimator(GCP_CLOUD_CONSTANTS.MEMORY_COEFFICIENT),
new UnknownEstimator(GCP_CLOUD_CONSTANTS.ESTIMATE_UNKNOWN_USAGE_BY),
new EmbodiedEmissionsEstimator