Skip to content

Commit

Permalink
Initial commit for awespottr.
Browse files Browse the repository at this point in the history
  • Loading branch information
arithmetric committed Aug 8, 2016
0 parents commit 28ddfdf
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules

66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# awespottr
## Find the cheapest EC2 spot prices across all regions

Usage:

- `node cli.js <ec2 type>`

- `AWS_PROFILE=awespottr node cli.js g2.8xlarge`

Minimal IAM policy:

```
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1470583998000",
"Effect": "Allow",
"Action": [
"ec2:DescribeAvailabilityZones",
"ec2:DescribeRegions",
"ec2:DescribeSpotPriceHistory"
],
"Resource": [
"*"
]
}
]
}
```

Example output:

```
Checking spot prices for m4.2xlarge instance type.
AWS Zone Hourly Rate
------------------------ ------------
eu-west-1b $0.063000
eu-central-1a $0.067000
eu-central-1b $0.072700
ap-northeast-2c $0.073300
ap-southeast-2b $0.078100
us-west-2c $0.078100
ap-southeast-1a $0.079800
ap-southeast-1b $0.081200
eu-west-1c $0.086200
ap-northeast-1a $0.086600
us-east-1e $0.088900
ap-northeast-1c $0.090200
us-west-1c $0.091600
us-west-1b $0.096700
ap-northeast-2a $0.096800
eu-west-1a $0.099200
ap-southeast-2a $0.099600
ap-southeast-2c $0.103100
us-east-1a $0.178500
ap-south-1a $0.292000
us-west-2b $0.390700
ap-south-1b $0.430000
us-east-1c $0.479000
us-west-2a $1.000000
us-east-1d $1.500000
Cheapest hourly rate for m4.2xlarge is $0.063000 in zone eu-west-1b
```
102 changes: 102 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
var _ = require('lodash');
var AWS = require('aws-sdk');
var chalk = require('chalk');
var moment = require('moment');
var program = require('commander');

var ec2Type = false;
var awsRegions = {};
var zones = {};
var lowPrice = false;
var lowZone = false;

program
.version('0.1.0')
.usage('[options] <EC2 instance types ...>')
.action(function(type) {
ec2Type = type;
})
.parse(process.argv);

console.log('Checking spot prices for ' + ec2Type + ' instance type.\n');

function getRegions() {
return new Promise(function(resolve, reject) {
var ec2 = new AWS.EC2({apiVersion: '2016-04-01', region: 'us-west-2'});
ec2.describeRegions({}, function(err, data) {
if (err) {
console.log(err, err.stack);
return reject(err);
}

var promises = [];
if (data && data.Regions) {
_.each(data.Regions, function(region) {
if (region.RegionName && !awsRegions.hasOwnProperty(region.RegionName)) {
awsRegions[region.RegionName] = {};
promises.push(getRegionSpots(region.RegionName));
}
})
}
return Promise.all(promises).then(resolve);
});
});
}

function getRegionSpots(regionName) {
return new Promise(function(resolve, reject) {
var ec2 = new AWS.EC2({apiVersion: '2016-04-01', region: regionName});
var params = {
InstanceTypes: [ec2Type],
ProductDescriptions: ['Linux/UNIX', 'Linux/UNIX (Amazon VPC)'],
StartTime: moment().subtract(4, 'hours').utc().toDate()
};
ec2.describeSpotPriceHistory(params, function(err, data) {
if (err) {
console.log(err, err.stack);
return reject(err);
}

if (data && data.SpotPriceHistory) {
_.each(data.SpotPriceHistory, function(sdata) {
if (sdata.SpotPrice && sdata.Timestamp && sdata.AvailabilityZone) {
var sdate = moment(sdata.Timestamp);
if (!zones.hasOwnProperty(sdata.AvailabilityZone) || zones[sdata.AvailabilityZone].lastDate < sdate) {
zones[sdata.AvailabilityZone] = {
lastDate: sdate,
lastPrice: sdata.SpotPrice,
zone: sdata.AvailabilityZone
};
if (!lowPrice || sdata.SpotPrice < lowPrice) {
lowPrice = sdata.SpotPrice;
lowZone = sdata.AvailabilityZone;
}
}
}
});
}
resolve();
});
});
}

getRegions()
.then(function() {
console.log(_.padEnd('AWS Zone', 24) + ' ' + _.padEnd('Hourly Rate', 12));
console.log(_.pad('', 24, '-') + ' ' + _.pad('', 12, '-'));
var sortedZones = _.values(zones);
sortedZones = _.sortBy(sortedZones, function(val) { return Number(val.lastPrice); }, 'zone');
_.each(sortedZones, function(data) {
var msg = _.padEnd(data.zone, 24) + ' ' + _.padEnd('$' + data.lastPrice, 12);
if (data.zone === lowZone || data.lastPrice === lowPrice) {
msg = chalk.green(msg);
} else if (lowPrice * 1.1 >= data.lastPrice) {
msg = chalk.yellow(msg);
}
console.log(msg);
});
console.log(chalk.green('\nCheapest hourly rate for ' + ec2Type + ' is $' + lowPrice + ' in zone ' + lowZone));
})
.catch(function(err) {
console.log('error', err);
});
18 changes: 18 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "awespottr",
"version": "0.1.0",
"description": "Find the cheapest EC2 spot prices in any region.",
"main": "cli.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Joe Turgeon <[email protected]>",
"license": "MIT",
"dependencies": {
"aws-sdk": "^2.4.13",
"chalk": "^1.1.3",
"commander": "^2.9.0",
"lodash": "^4.14.1",
"moment": "^2.14.1"
}
}

0 comments on commit 28ddfdf

Please sign in to comment.