UNPKG

@cloud-carbon-footprint/gcp

Version:

The core logic to get cloud usage data and estimate energy and carbon emissions from Google Cloud Platform.

272 lines (252 loc) 6.86 kB
/* * © 2021 Thoughtworks, Inc. */ import { MetricServiceClient } from '@google-cloud/monitoring' import { google } from '@google-cloud/monitoring/build/protos/protos' import Reducer = google.monitoring.v3.Aggregation.Reducer import { ComputeEngine } from '../lib' import { GCP_CLOUD_CONSTANTS } from '../domain' const mockListTimeSeries = jest.fn() jest.mock('@google-cloud/monitoring', () => { return { MetricServiceClient: jest.fn().mockImplementation(() => { return { listTimeSeries: mockListTimeSeries, projectPath: jest .fn() .mockReturnValue('projects/cloud-carbon-footprint'), getProjectId: jest.fn().mockResolvedValue('cloud-carbon-footprint'), } }), } }) describe('ComputeEngine', () => { const startDate = new Date('2020-09-16') const endDate = new Date('2020-09-17') const region = 'us-east1' const cpuMetricType = 'utilization' const vCpuMetricType = 'reserved_cores' const dayOneHourOne = new Date('2020-09-16T22:00:00.000Z') const dayOneHourTwo = new Date('2020-09-16T23:00:00.000Z') const dayOneHourOneInMilliSecs = dayOneHourOne.getTime() / 1000 const dayOneHourTwoInMilliSecs = dayOneHourTwo.getTime() / 1000 const mockCpuUtilizationTimeSeries = [ { points: [ { interval: { startTime: { seconds: dayOneHourOneInMilliSecs, nanos: 0, }, endTime: { seconds: dayOneHourOneInMilliSecs, nanos: 0, }, }, value: { doubleValue: 0.25, value: 'doubleValue', }, }, { interval: { startTime: { seconds: dayOneHourTwoInMilliSecs, nanos: 0, }, endTime: { seconds: dayOneHourTwoInMilliSecs, nanos: 0, }, }, value: { doubleValue: 0.75, value: 'doubleValue', }, }, ], metric: { labels: {}, type: 'compute.googleapis.com/instance/cpu/utilization', }, resource: { labels: { project_id: 'cloud-carbon-footprint', }, type: 'gce_instance', }, metricKind: 'GAUGE', valueType: 'DOUBLE', }, ] const mockVCPUTimeSeries = [ { points: [ { interval: { startTime: { seconds: dayOneHourOneInMilliSecs, nanos: 0, }, endTime: { seconds: dayOneHourOneInMilliSecs, nanos: 0, }, }, value: { doubleValue: 4, value: 'doubleValue', }, }, { interval: { startTime: { seconds: dayOneHourTwoInMilliSecs, nanos: 0, }, endTime: { seconds: dayOneHourTwoInMilliSecs, nanos: 0, }, }, value: { doubleValue: 2, value: 'doubleValue', }, }, ], metric: { labels: {}, type: 'compute.googleapis.com/instance/cpu/reserved_cores', }, }, ] afterEach(() => { mockListTimeSeries.mockReset() }) it('gets compute engine usage for two data points', async () => { mockListTimeSeries.mockResolvedValueOnce([ mockCpuUtilizationTimeSeries, {}, {}, ]) mockListTimeSeries.mockResolvedValueOnce([mockVCPUTimeSeries, {}, {}]) const computeEngineService = new ComputeEngine(new MetricServiceClient()) const result = await computeEngineService.getUsage( startDate, endDate, region, ) expect(mockListTimeSeries).toHaveBeenNthCalledWith( 1, computeEngineService.buildTimeSeriesRequest( startDate, endDate, 'projects/cloud-carbon-footprint', cpuMetricType, Reducer.REDUCE_MEAN, region, ), ) expect(mockListTimeSeries).toHaveBeenNthCalledWith( 2, computeEngineService.buildTimeSeriesRequest( startDate, endDate, 'projects/cloud-carbon-footprint', vCpuMetricType, Reducer.REDUCE_SUM, region, ), ) expect(result).toEqual([ { cpuUtilizationAverage: 0.25, vCpuHours: 4, timestamp: dayOneHourOne, usesAverageCPUConstant: false, }, { cpuUtilizationAverage: 0.75, vCpuHours: 2, timestamp: dayOneHourTwo, usesAverageCPUConstant: false, }, ]) }) it('uses the average cpu utilization constant when there is no measure CPUUtilization returned', async () => { const mockCpuUtilizationTimeSeriesWithMissingPoint = [ { points: [ { interval: { startTime: { seconds: dayOneHourOneInMilliSecs, nanos: 0, }, endTime: { seconds: dayOneHourOneInMilliSecs, nanos: 0, }, }, value: { doubleValue: 0.25, value: 'doubleValue', }, }, ], metric: { labels: {}, type: 'compute.googleapis.com/instance/cpu/utilization', }, resource: { labels: { project_id: 'cloud-carbon-footprint', }, type: 'gce_instance', }, metricKind: 'GAUGE', valueType: 'DOUBLE', }, ] mockListTimeSeries.mockResolvedValueOnce([ mockCpuUtilizationTimeSeriesWithMissingPoint, {}, {}, ]) mockListTimeSeries.mockResolvedValueOnce([mockVCPUTimeSeries, {}, {}]) const computeEngineService = new ComputeEngine(new MetricServiceClient()) const result = await computeEngineService.getUsage( startDate, endDate, region, ) expect(result).toEqual([ { cpuUtilizationAverage: 0.25, vCpuHours: 4, timestamp: dayOneHourOne, usesAverageCPUConstant: false, }, { cpuUtilizationAverage: GCP_CLOUD_CONSTANTS.AVG_CPU_UTILIZATION_2020 / 100, vCpuHours: 2, timestamp: dayOneHourTwo, usesAverageCPUConstant: true, }, ]) }) it('returns an empty array when listTimeSeries returns no data', async () => { mockListTimeSeries.mockResolvedValueOnce([[], {}, {}]) mockListTimeSeries.mockResolvedValueOnce([[], {}, {}]) const computeEngineService = new ComputeEngine(new MetricServiceClient()) const result = await computeEngineService.getUsage( startDate, endDate, region, ) expect(result).toEqual([]) }) })