-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 28ddfdf
Showing
4 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/node_modules | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |