devlopment checkpoint of new features in awsbox 0.2 - to be documented and perhaps rebased shortly
This commit is contained in:
Родитель
c26e67caff
Коммит
405619b015
53
awsbox.js
53
awsbox.js
|
@ -10,7 +10,8 @@ key = require('./lib/key.js'),
|
|||
ssh = require('./lib/ssh.js'),
|
||||
git = require('./lib/git.js'),
|
||||
optimist = require('optimist'),
|
||||
urlparse = require('urlparse');
|
||||
urlparse = require('urlparse'),
|
||||
fs = require('fs');
|
||||
|
||||
var verbs = {};
|
||||
|
||||
|
@ -42,7 +43,7 @@ verbs['destroy'] = function(args) {
|
|||
}
|
||||
var name = args[0];
|
||||
validateName(name);
|
||||
var hostname = name;
|
||||
var hostname = name;
|
||||
|
||||
process.stdout.write("trying to destroy VM for " + hostname + ": ");
|
||||
vm.destroy(name, function(err, deets) {
|
||||
|
@ -88,6 +89,15 @@ verbs['create'] = function(args) {
|
|||
var hostname = name;
|
||||
var longName = process.title + ' deployment (' + name + ')';
|
||||
|
||||
console.log("reading .awsbox.json");
|
||||
|
||||
try {
|
||||
var awsboxJson = JSON.parse(fs.readFileSync("./.awsbox.json"));
|
||||
} catch(e) {
|
||||
console.log("Fatal error! Can't read awsbox.json: " + e);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("attempting to set up VM \"" + name + "\"");
|
||||
|
||||
vm.startImage({
|
||||
|
@ -118,8 +128,15 @@ verbs['create'] = function(args) {
|
|||
checkErr(err);
|
||||
}
|
||||
console.log(" ... and your git remote is all set up");
|
||||
console.log("");
|
||||
printInstructions(name, deets);
|
||||
|
||||
if (awsboxJson.packages) {
|
||||
console.log(" ... finally, installing custom packages: " + awsboxJson.packages.join(', '));
|
||||
console.log("");
|
||||
}
|
||||
ssh.installPackages(deets.ipAddress, awsboxJson.packages, function(err, r) {
|
||||
checkErr(err);
|
||||
printInstructions(name, deets);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -127,6 +144,34 @@ verbs['create'] = function(args) {
|
|||
});
|
||||
};
|
||||
|
||||
verbs['create_ami'] = function(args) {
|
||||
if (!args || args.length != 1) {
|
||||
throw 'missing required argument: name of instance';
|
||||
}
|
||||
|
||||
var name = args[0];
|
||||
validateName(name);
|
||||
var hostname = name;
|
||||
|
||||
console.log("restoring to a pristine state, and creating AMI image from " + name);
|
||||
vm.describe(name, function(err, deets) {
|
||||
console.log("instance found, ip " + deets.ipAddress + ", restoring");
|
||||
checkErr(err);
|
||||
ssh.makePristine(deets.ipAddress, function(err) {
|
||||
console.log("instance is pristine, creating AMI");
|
||||
checkErr(err);
|
||||
vm.createAMI(name, function(err, imageId) {
|
||||
checkErr(err);
|
||||
console.log("Created image:", imageId, "- making it public");
|
||||
vm.makeAMIPublic(imageId, function(err, imageId) {
|
||||
console.log("All done!");
|
||||
checkErr(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
verbs['list'] = function(args) {
|
||||
vm.list(function(err, r) {
|
||||
checkErr(err);
|
||||
|
|
21
lib/ssh.js
21
lib/ssh.js
|
@ -38,7 +38,28 @@ exports.copySSL = function(host, pub, priv, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
exports.installPackages = function(host, packages, cb) {
|
||||
if (!packages || !packages.length) cb();
|
||||
else {
|
||||
var pkg = packages.shift();
|
||||
var cmd = 'ssh -o "StrictHostKeyChecking no" ec2-user@' + host + " sudo yum -y install \'" + pkg + "\'";
|
||||
child_process.exec(cmd, function(err, r) {
|
||||
if (err) return cb(err);
|
||||
var cmd = 'ssh -o "StrictHostKeyChecking no" ec2-user@' + host + " 'echo \"" + pkg + "\" >> packages.txt'";
|
||||
child_process.exec(cmd, function(err, r) {
|
||||
if (err) return cb(err);
|
||||
exports.installPackages(host, packages, cb);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.addSSHPubKey = function(host, pubkey, cb) {
|
||||
var cmd = 'ssh -o "StrictHostKeyChecking no" ec2-user@' + host + " 'echo \'" + pubkey + "\' >> .ssh/authorized_keys'";
|
||||
child_process.exec(cmd, cb);
|
||||
};
|
||||
|
||||
exports.makePristine = function(host, cb) {
|
||||
var cmd = 'ssh -o "StrictHostKeyChecking no" ec2-user@' + host + " './pristinify.sh'";
|
||||
child_process.exec(cmd, cb);
|
||||
};
|
||||
|
|
77
lib/vm.js
77
lib/vm.js
|
@ -4,7 +4,7 @@ jsel = require('JSONSelect'),
|
|||
key = require('./key.js'),
|
||||
sec = require('./sec.js');
|
||||
|
||||
const TEMPLATE_IMAGE_ID = 'ami-6cb56f05';
|
||||
const TEMPLATE_IMAGE_ID = 'ami-bad574d3';
|
||||
|
||||
function extractInstanceDeets(horribleBlob) {
|
||||
var instance = {};
|
||||
|
@ -15,9 +15,9 @@ function extractInstanceDeets(horribleBlob) {
|
|||
var name = jsel.match('.tagSet :has(.key:val("Name")) > .value', horribleBlob);
|
||||
if (name.length) {
|
||||
instance.fullName = name[0];
|
||||
// if this is a 'browserid deployment', we'll only display the hostname chosen by the
|
||||
// if this is a 'awsbox deployment', we'll only display the hostname chosen by the
|
||||
// user
|
||||
var m = /^browserid deployment \((.*)\)$/.exec(instance.fullName);
|
||||
var m = /^awsbox deployment \((.*)\)$/.exec(instance.fullName);
|
||||
instance.name = m ? m[1] : instance.fullName;
|
||||
} else {
|
||||
instance.name = instance.instanceId;
|
||||
|
@ -25,6 +25,16 @@ function extractInstanceDeets(horribleBlob) {
|
|||
return instance;
|
||||
}
|
||||
|
||||
exports.describe = function(name, cb) {
|
||||
exports.list(function(err, r) {
|
||||
if (err) return cb('failed to list vms: ' + err);
|
||||
|
||||
deets = findInstance(r, name);
|
||||
if (!deets) return cb('no such vm');
|
||||
cb(null, deets);
|
||||
});
|
||||
};
|
||||
|
||||
exports.list = function(cb) {
|
||||
aws.call('DescribeInstances', {}, function(result) {
|
||||
var instances = {};
|
||||
|
@ -73,6 +83,67 @@ exports.destroy = function(name, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
function dateBasedVersion() {
|
||||
var d = new Date();
|
||||
function pad(n){return n<10 ? '0'+n : n}
|
||||
return d.getUTCFullYear()+'.'
|
||||
+ pad(d.getUTCMonth()+1)+'.'
|
||||
+ pad(d.getUTCDate())+'-'
|
||||
+ pad(d.getUTCHours())+'.'
|
||||
+ pad(d.getUTCMinutes());
|
||||
}
|
||||
|
||||
exports.createAMI = function(name, cb) {
|
||||
exports.list(function(err, r) {
|
||||
if (err) return cb('failed to list vms: ' + err);
|
||||
|
||||
deets = findInstance(r, name);
|
||||
if (!deets) return cb('no such vm');
|
||||
|
||||
aws.call('CreateImage', {
|
||||
InstanceId: deets.instanceId,
|
||||
Name: "awsbox deployment image v" + dateBasedVersion(),
|
||||
Description: "An image for use with awsbox.org, a DIY PaaS for noders"
|
||||
}, function(result) {
|
||||
try { return cb(result.Errors.Error.Message); } catch(e) {};
|
||||
result = jsel.match('.imageId', result)[0];
|
||||
cb(null, result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.makeAMIPublic = function(imageId, cb) {
|
||||
var startTime = new Date();
|
||||
|
||||
function attempt() {
|
||||
aws.call('ModifyImageAttribute', {
|
||||
ImageId: imageId,
|
||||
'LaunchPermission.Add.1.Group': 'all'
|
||||
}, function(result) {
|
||||
try {
|
||||
if (result.Errors.Error.Message) {
|
||||
|
||||
if (result.Errors.Error.Message.indexOf('currently pending') !== -1) {
|
||||
throw "pending"
|
||||
}
|
||||
|
||||
return cb(result.Errors.Error.Message);
|
||||
}
|
||||
|
||||
cb(null);
|
||||
} catch(e) {};
|
||||
|
||||
if (new Date() - startTime > 120 * 1000) {
|
||||
return cb("timed out waiting for instance to become public");
|
||||
}
|
||||
|
||||
setTimeout(function() { attempt(); }, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
attempt();
|
||||
};
|
||||
|
||||
function returnSingleImageInfo(result, cb) {
|
||||
if (!result) return cb('no results from ec2 api');
|
||||
try { return cb(result.Errors.Error.Message); } catch(e) {};
|
||||
|
|
Загрузка…
Ссылка в новой задаче