Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
johntitus committed Sep 30, 2015
0 parents commit a5ce96d
Show file tree
Hide file tree
Showing 1,469 changed files with 139,142 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
15 changes: 15 additions & 0 deletions cli/thaumaturgy
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env node

var program = require('commander');

program
.version('0.0.1')
.description('Build a NPM package compatible with AWS Lambda using AWS Lambda.')
.command('configure', 'Configure Thaumaturgy with your AWS Credentials.')
.command('deploy', 'Deploy the Thaumaturgy builder lambda function to AWS Lambda.')
.command('build <packages>', "Build NPM packages using Thaumaturge in AWS Lambda. See 'thaumaturgy help build'.")
.parse(process.argv);

if (!process.argv.slice(2).length) {
program.outputHelp();
}
162 changes: 162 additions & 0 deletions cli/thaumaturgy-build
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/env node

var program = require('commander');
var osenv = require('osenv');
var fs = require('fs');
var AWS = require('aws-sdk');
var Promise = require('bluebird');

var description = "Build NPM packages using Thaumaturgy in AWS Lambda." +
"\n Package list should be in a space-separated list of packages in the same format as a package.json file." +
"\n\n Examples:" + "\n\n thaumaturgy build mkdirp:^0.5.1" +
"\n thaumaturgy build mysql2:0.15.8 mongodb:~2.0.45" +
"\n thaumaturgy build redis:latest --download /tmp/redis.zip" +
"\n thaumaturgy build --file package.json";

program
.option('-f, --file [path]', 'build all dependencies in a package.json file')
.option('-d, --download [path]', 'download build package from S3 and store at [path]')
.description(description)
.parse(process.argv);

// Make sure we can read their thaumaturgy config file.
var workDir = osenv.home() + '/.thaumaturgy';
var config;
try {
config = JSON.parse(fs.readFileSync(workDir + '/config.json'));
} catch (err) {
console.error("Could not read configuration. Run 'thaumaturgy configure' first.");
process.exit(1);
}

// Make sure they've actually deployed thaumaturgy to AWS Lambda.
if (typeof config.lambdaArn === 'undefined') {
console.error("Thaumaturgy does not seem to have been deployed to AWS Lambda yet. Run 'thaumaturgy deploy'.");
process.exit(1);
}


var packages = {};


// Get the list of packages, either from the command line or the specified file.
if (!program.file){ // Command line
var packageList = program.args;

// Check their package list to make sure they're valid.
if (!validPackages(packageList)) {
process.exit(1);
}

// Turn the list of packages into an object
packageList.forEach(function(package){
var pieces = package.split(':');

//Could be a url, which has a : we don't want to split, so rejoin
pieces = [pieces.shift(), pieces.join(':')];
packages[pieces[0]] = pieces[1];
});
console.log("Invoking Thaumaturgy Lambda in AWS with packages %s.", packageList.join(' '));

} else { // Read dependencies from package.json-type file

var dependencies;
try {
var packagejson = JSON.parse(fs.readFileSync(program.file));
dependencies = packagejson.dependencies;
} catch (err ){
console.log('Could not read file at',program.file);
process.exit(1);
}
packages = dependencies;

console.log("Invoking Thaumaturgy Lambda in AWS for dependencies in", program.file);
}

AWS.config.update({
accessKeyId: config.awsAccessKey,
secretAccessKey: config.awsSecretAccessKey,
region: 'us-east-1'
});

var lambda = new AWS.Lambda();

var params = {
FunctionName : 'Thaumaturgy',
Payload : JSON.stringify({
bucket : config.s3Bucket,
packages : packages
})
}

lambda.invoke(params, function(err, data ){
if (err) {
console.error("Thaumaturgy reported an error:");
console.error(err);
process.exit(1);
}
else {
console.log('Build completed successfully.');

if ( program.download ){
console.log('Downloading.')
var s3 = new AWS.S3();

var params = {
Bucket : config.s3Bucket,
Key : 'thaumaturgy.zip'
}

var file = fs.createWriteStream(program.download);
var pipe = s3.getObject(params).createReadStream().pipe(file);
pipe.on('error', function(err){
console.error('An error occurred downloading the file.');
console.error(err);
process.exit(1);
})
pipe.on('finish', function(){
console.log('Download complete. File at', program.download);
});
} else {
console.log('Results at: s3://' + config.s3Bucket + '/thaumaturgy.zip');
process.exit(0);
}
}
});



// Make sure list is in a valid package.json dependencies format.
function validPackages(packageList) {
var valid = true;

packageList.forEach(function(package) {
if (package.indexOf(':') == -1) {
console.error(package + ' is not valid. Missing a :');
valid = false;
}
var pieces = package.split(':');

//Could be a url, which has a : we don't want to split, so rejoin
pieces = [pieces.shift(), pieces.join(':')];

var version = pieces[1];

//console.log(version);
if (!isValidVersionRange(version)) {
console.error(package + ' does not have a valid version.');
valid = false;
}
});
return valid;
}

function isValidVersionRange(v) {
return (/^[\^<>=~]{0,2}[0-9.x]+/).test(v) ||
/^https*:\/\/[a-z.\-0-9]+/.test(v) ||
v == "*" ||
v === "" ||
v.indexOf("git") === 0 ||
v === "latest" ||
false;
}
59 changes: 59 additions & 0 deletions cli/thaumaturgy-configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env node

var prompt = require('prompt');
var fs = require('fs');
var osenv = require('osenv');
var path = require('path');
var mkdirp = require('mkdirp');

var credentials = {};
var schema = {
properties: {
'awsAccessKey': {
required: true,
pattern : /^[a-zA-Z0-9]{20}$/, // 20 alphanumeric characters
description: 'AWS Access Key ID:'.cyan,
message: 'That does not seem to be a valid AWS Access Key.'
},
'awsSecretAccessKey': {
required: true,
pattern : /^[a-zA-Z0-9/+=]{40}$/, // 40 base64 characters
description: 'AWS Secret Access Key:'.cyan,
message: 'That does not seem to be a valid AWS Secret Access Key.'
},
'roleArn' :{
required : true,
description: 'AWS Role ARN:'.cyan,
pattern : /^arn:aws:iam.*/,
message : 'That does not look like a valid AWS ARN. Should start with arn:aws:iam'
},
's3Bucket' :{
required : true,
description: 'S3 Bucket:'.cyan
},
'npmVersion' :{
required : true,
description: 'NPM Version (2 or 3):'.cyan,
enum: ['2', '3'],
message : 'Must be version 2 or 3'
}
}
}

prompt.message = '';
prompt.delimiter = '';
prompt.start();
prompt.get(schema, function(err, options) {

if (err) {
console.error('\nConfiguration stopped.');
}
else {
var workDir = osenv.home() + '/.thaumaturgy';
mkdirp.sync(workDir);
console.log('Created dirctory: ' + workDir);
fs.writeFileSync(workDir + '/config.json',JSON.stringify(options,null,4));
console.log('Wrote file: ' + workDir + '/config.json')
console.log('Thaumaturgy configure complete.')
}
});
106 changes: 106 additions & 0 deletions cli/thaumaturgy-deploy
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env node

var prompt = require('prompt');
var fs = require('fs');
var osenv = require('osenv');
var path = require('path');
var archiver = require('archiver');
var mkdirp = require('mkdirp');
var AWS = require('aws-sdk');
var Promise = require('bluebird');

var workDir = osenv.home() + '/.thaumaturgy';
var buildDir = workDir + '/build';

var config;
try {
config = JSON.parse(fs.readFileSync(workDir + '/config.json'));
} catch (err){
console.error("Could not read configuration. Run 'thaumaturgy configure' first.");
process.exit(1);
}

build()
.then(deploy)
.then(function(deployResults){
config.lambdaArn = deployResults.FunctionArn;
fs.writeFileSync(workDir + '/config.json',JSON.stringify(config,null,4));
console.log('Thaumaturgy Lambda deployed to AWS Lambda. Ready to build packages.')
})
.catch(function(e){
console.error(e);
});

function build(){
return new Promise( function( resolve, reject ){
console.log('Creating Thaumaturgy Lambda zip file.');
console.log('Using NPM %s.', config.npmVersion);

mkdirp.sync(buildDir)

var output = fs.createWriteStream(buildDir + '/thaumaturgy.zip');
var archive = archiver.create('zip', {}); // or archiver('zip', {});
archive.pipe(output);

output.on('close', function() {
console.log('Thaumaturgy Lambda zip file built.');
resolve();
});

archive.on('error', function(err) {
reject(err);
});

var basePath = path.join(__dirname, '..');

archive.directory(basePath + '/lib/npm' + config.npmVersion,'node_modules/npm');
archive.directory(basePath + '/node_modules/nopt','node_modules/nopt');
archive.directory(basePath + '/node_modules/archiver','node_modules/archiver');
archive.file(basePath + '/package.json', { name : 'package.json'});
archive.file(basePath + '/index.js', { name : 'index.js'});
archive.finalize();
});
}

function deploy(){
console.log('Deploying Thaumaturgy Lambda to AWS Lambda.');

AWS.config.update({
accessKeyId: config.awsAccessKey,
secretAccessKey: config.awsSecretAccessKey,
region: 'us-east-1'
});

var lambda = new AWS.Lambda();

if (config.lambdaArn) { // update lambda
var params = {
FunctionName: 'Thaumaturgy',
ZipFile: fs.readFileSync(buildDir + '/thaumaturgy.zip')
}
return new Promise(function(resolve, reject) {
lambda.updateFunctionCode(params, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
} else { //new lambda
var params = {
FunctionName: 'Thaumaturgy',
Handler: 'index.handler',
Role: config.roleArn,
Runtime: 'nodejs',
MemorySize : 512,
Timeout : 60,
Code: {
ZipFile: fs.readFileSync(buildDir + '/thaumaturgy.zip')
}
}
return new Promise(function(resolve, reject) {
lambda.createFunction(params, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}
}
Loading

0 comments on commit a5ce96d

Please sign in to comment.