-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcarbonless.js
129 lines (119 loc) · 4.74 KB
/
carbonless.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
'use strict';
const yaml = require('js-yaml');
const fs = require('fs');
const awsToAzureRegionsMap = {
'us-east-2':'eastus2',
'us-east-1':'eastus',
'us-west-1': 'westus',
'us-west-2': 'westus2',
'af-south-1': 'southafricawest',
'ap-east-1': 'eastasia',
'ap-south-2': 'southindia',
'ap-southeast-3': 'southeastasia',
'ap-south-1': 'centralindia',
'ap-northeast-3': 'japanwest',
'ap-northeast-2': 'koreacentral',
'ap-southeast-1': 'southeastasia',
'ap-southeast-2': 'australiaeast',
'ap-northeast-1': 'japaneast',
'ca-central-1': 'canadacentral',
'eu-central-1': 'germanywestcentral',
'eu-west-1': 'northeurope',
'eu-west-2': 'uksouth',
'eu-south-1': 'switzerlandnorth',
'eu-west-3': 'francecentral',
'eu-south-2': 'francesouth',
'eu-north-1': 'swedencentral',
'eu-central-2': 'switzerlandnorth',
'me-south-1': 'uaenorth',
'me-central-1': 'uaecentral',
'sa-east-1': 'brazilsouth',
}
class Carbonless {
constructor(serverless, options, { log }) {
this.serverless = serverless;
this.options = options;
this.log = log;
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'initialize': () => this.init(),
};
}
async init() {
// Check if region has been passed as an option
if (!this.options.region) {
// Check if default region has been overriden in configuration
if (!this.regionInConfiguration()){
// Using default region
this.log.info(`Currently using using default region ${this.serverless.service.provider.region}. Carbonless plugin is checking for a region with lower carbon intensity`);
this.serverless.service.provider.region = await this.getRegionWithLowestCarbonIntensity();
this.log.notice(`Carbonless plugin has changed region to ${this.serverless.service.provider.region}`);
}
}
}
regionInConfiguration = () => {
const configurationFilePath = `${this.serverless.serviceDir}/${this.serverless.configurationFilename}`;
const configurationYaml = yaml.load(fs.readFileSync(configurationFilePath));
if (configurationYaml.provider.region){
return true;
}
return false;
}
getRegionWithLowestCarbonIntensity = async () => {
let bestRegion;
let minCarbonIntensity;
const ec2 = new this.provider.sdk.EC2({region: this.serverless.service.provider.region});
const regions = [];
const regionsJSON = await ec2.describeRegions().promise();
for (let i = 0; i < regionsJSON.Regions.length; i++){
regions.push(regionsJSON.Regions[i].RegionName);
}
for (let i=0; i < regions.length; i++){
try {
const result = await this.getRegionCarbonIntensity(awsToAzureRegionsMap[regions[i]]);
if (result[0]){
const carbonIntensity = result[0]['forecastData'][0]['value'];
this.log.info(`Forecasted Carbon Intensity in ${regions[i]} is ${carbonIntensity}`);
if (minCarbonIntensity){
if (carbonIntensity < minCarbonIntensity){
bestRegion = regions[i];
minCarbonIntensity = carbonIntensity;
}
} else {
bestRegion = regions[i];
minCarbonIntensity = carbonIntensity;
}
}
} catch (error){
console.log(error);
}
}
this.log.notice(`The region with the lowest carbon intensity is ${bestRegion}`);
return bestRegion;
};
getRegionCarbonIntensity = (region) => {
const https = require('https');
const forecastAPI = 'https://carbon-aware-api.azurewebsites.net/emissions/forecasts/current';
const hours = 10;
const dataEndAt = new Date(Date.now() + hours*3600000).toISOString();
return new Promise((resolve, reject) => {
const req = https.get(forecastAPI + `?location=${region}&dataEndAt=${dataEndAt}`, (res) => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (err) {
reject(new Error(err));
}
});
});
req.on('error', err => {
reject(new Error(err));
});
});
};
}
module.exports = Carbonless;