Permalink
Browse files

First pass at EC2 wrapper

  • Loading branch information...
0 parents commit 796be8c2a5afffabdb3ab7e3efed3a6df49353bb @halorgium committed Nov 29, 2009
Showing with 1,038 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +4 −0 config-example.js
  3. +77 −0 ec2api.js
  4. +139 −0 ec2client.js
  5. +562 −0 ec2models.js
  6. +203 −0 sha1.js
  7. +9 −0 stunnel.conf
  8. +42 −0 test.js
@@ -0,0 +1,2 @@
+config.js
+tmp
@@ -0,0 +1,4 @@
+exports.load = {
+ accessKeyId: "PUBLIC",
+ secretAccessKey: "PRIVATE"
+};
@@ -0,0 +1,77 @@
+var sys = require("sys");
+var libxml = require("libxmljs");
+var ec2client = require("./ec2client");
+var ec2models = require("./ec2models");
+
+libxml.Document.prototype.at = function(name) {
+ var selected = [];
+ this.find("*").forEach(function(x) { if(x.name() == name) selected.push(x); });
+ return selected[0];
+};
+libxml.Element.prototype.at = libxml.Document.prototype.at;
+
+var ec2api = function (credential) {
+ this.client = ec2client.create(this, credential);
+};
+
+var regions = []
+regions.push(new ec2models.Region("us-east-1", "us-east-1.ec2.amazonaws.com"));
+regions.push(new ec2models.Region("eu-west-1", "eu-west-1.ec2.amazonaws.com"));
+
+var findRegion = function(name) {
+ var aRegion = null;
+ regions.forEach(function(region) {
+ if (region.name == name) aRegion = region;
+ });
+ return aRegion;
+};
+
+ec2api.prototype.setRegion = function(name) {
+ this.region = findRegion(name);
+};
+
+ec2api.prototype.describeAddresses = function (callback) {
+ this.client.queryEC2("DescribeAddresses", [], function (value) {
+ var doc = libxml.parseString(value.body);
+ var items = doc.at("addressesSet").find("*");
+ var addresses = [];
+ for (var i in items) {
+ var item = items[i];
+ var address = new ec2models.AddressMapping(
+ item.at("publicIp").text(),
+ item.at("instanceId").text()
+ );
+ addresses.push(address);
+ }
+ callback({addresses: addresses});
+ });
+};
+
+ec2api.prototype.allocateAddress = function (callback) {
+ this.client.queryEC2("AllocateAddress", [], function (value) {
+ var doc = libxml.parseString(value.body);
+ var address = new ec2models.AddressMapping(doc.at("publicIp").text());
+ callback({address: address});
+ });
+};
+
+ec2api.prototype.releaseAddress = function (address, callback) {
+ this.client.queryEC2("ReleaseAddress", [['PublicIp', address]], function (value) {
+ var doc = libxml.parseString(value.body);
+ var ok = doc.at("return").text();
+ callback({ok: ok == "true"});
+ });
+};
+
+ec2api.prototype.errback = function (value) {
+ var doc = libxml.parseString(value.body);
+ var errorNode = doc.at("Errors").at("Error");
+ sys.puts("error: ");
+ sys.puts(errorNode.at("Code").text());
+ sys.puts(errorNode.at("Message").text());
+};
+
+exports.create = function (accessKeyId, secretAccessKey) {
+ var credential = new ec2models.Credential(accessKeyId, secretAccessKey);
+ return new ec2api(credential);
+};
@@ -0,0 +1,139 @@
+var sys = require("sys");
+var http = require("http");
+var sha1 = require("./sha1");
+
+var log = function(message) {
+ if (process.ENV["LOG"]) sys.puts("log: " + message);
+};
+
+var ec2client = function (api, credential) {
+ this.api = api;
+ this.credential = credential;
+ this.USER_AGENT = "NodeEC2-0.0.1";
+ this.API_VERSION = "2009-07-15";
+ this.VPN_CONFIG_PATH = "http://ec2-downloads.s3.amazonaws.com/";
+};
+
+ec2client.prototype.region = function() {
+ return this.api.region;
+};
+
+ec2client.prototype.queryEC2 = function (action, params, callback) {
+ if (this.credential == null) {
+ throw "Call setCredential to set the EC2 credentials";
+ }
+
+ if (this.region() == null) {
+ throw "Call setRegion to set the EC2 region";
+ }
+
+ var curTime = new Date();
+ var formattedTime = this.formatDate(curTime, "yyyy-MM-ddThh:mm:ssZ");
+
+ var sigValues = new Array();
+ sigValues.push(new Array("Action", action));
+ sigValues.push(new Array("AWSAccessKeyId", this.credential.accessKeyId));
+ sigValues.push(new Array("SignatureVersion","1"));
+ sigValues.push(new Array("Version",this.API_VERSION));
+ sigValues.push(new Array("Timestamp",formattedTime));
+
+ // Mix in the additional parameters. params must be an Array of tuples as for sigValues above
+ for (var i = 0; i < params.length; i++) {
+ sigValues.push(params[i]);
+ }
+
+ // Sort the parameters by their lowercase name
+ sigValues.sort(this.sigParamCmp);
+
+ // Construct the string to sign and query string
+ var strSig = "";
+ var queryParams = "";
+ for (var i = 0; i < sigValues.length; i++) {
+ strSig += sigValues[i][0] + sigValues[i][1];
+ queryParams += sigValues[i][0] + "=" + encodeURIComponent(sigValues[i][1]);
+ if (i < sigValues.length-1)
+ queryParams += "&";
+ }
+
+ log("StrSig ["+strSig+"]");
+ log("Params ["+queryParams+"]");
+
+ var sig = sha1.b64_hmac_sha1(this.credential.secretAccessKey, strSig);
+ log("Sig ["+sig+"]");
+
+ queryParams += "&Signature="+encodeURIComponent(sig);
+ var url = this.serviceURL + "/";
+
+ log("URL ["+url+"]");
+ log("QueryParams ["+queryParams+"]");
+
+ var timerKey = strSig+":"+formattedTime;
+
+ var headers = {
+ "Host": this.region().host,
+ "User-Agent": this.USER_AGENT,
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Content-Length": queryParams.length,
+ "Connection": "close"
+ };
+ var client = this;
+ var request = http.createClient(50000, "localhost").post("/", headers);
+
+ request.sendBody(queryParams);
+ request.finish(function (response) {
+ var body = "";
+ response.addListener("body", function (chunk) {
+ body += chunk;
+ });
+ response.addListener("complete", function () {
+ log("complete: statusCode: " + response.statusCode + ", body: " + body);
+ var ok = response.statusCode >= 200 && response.statusCode < 300;
+ var value = {
+ action: action,
+ response: response,
+ body: body,
+ ok: ok
+ }
+ if (ok) {
+ callback(value);
+ }
+ else {
+ client.api.errback(value);
+ }
+ });
+ });
+
+ return true;
+};
+
+ec2client.prototype.sigParamCmp = function(x, y) {
+ if (x[0].toLowerCase() < y[0].toLowerCase ()) {
+ return -1;
+ }
+ if (x[0].toLowerCase() > y[0].toLowerCase()) {
+ return 1;
+ }
+ return 0;
+};
+
+ec2client.prototype.addZero = function(vNumber) {
+ return ((vNumber < 10) ? "0" : "") + vNumber;
+};
+
+ec2client.prototype.formatDate = function(vDate, vFormat) {
+ var vDay = this.addZero(vDate.getUTCDate());
+ var vMonth = this.addZero(vDate.getUTCMonth()+1);
+ var vYearLong = this.addZero(vDate.getUTCFullYear());
+ var vYearShort = this.addZero(vDate.getUTCFullYear().toString().substring(3,4));
+ var vYear = (vFormat.indexOf("yyyy")>-1?vYearLong:vYearShort);
+ var vHour = this.addZero(vDate.getUTCHours());
+ var vMinute = this.addZero(vDate.getUTCMinutes());
+ var vSecond = this.addZero(vDate.getUTCSeconds());
+ var vDateString= vFormat.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear);
+ vDateString = vDateString.replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond);
+ return vDateString;
+};
+
+exports.create = function(api, credential) {
+ return new ec2client(api, credential);
+};
Oops, something went wrong.

0 comments on commit 796be8c

Please sign in to comment.