Skip to content
This repository was archived by the owner on Dec 1, 2022. It is now read-only.

Commit e0ae9ae

Browse files
committed
Setup webpack for optimised lambda deployment package, use dotenv to specify aws config
1 parent 6437a3e commit e0ae9ae

13 files changed

+308
-33
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ node_modules
3333
.node_repl_history
3434

3535
lib/
36+
dist/
37+
dist.zip
38+
build/
39+
config.env.production

gulpfile.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
var gulp = require("gulp");
2+
var del = require('del');
3+
var rename = require('gulp-rename');
4+
var install = require('gulp-install');
5+
var zip = require('gulp-zip');
6+
var uglify = require('gulp-uglify');
7+
var AWS = require('aws-sdk');
8+
var fs = require('fs');
9+
var runSequence = require('run-sequence');
10+
11+
// First we need to clean out the dist folder and remove the compiled zip file.
12+
gulp.task('clean', function(cb) {
13+
del('./dist');
14+
cb();
15+
});
16+
17+
gulp.task("webpack", function (cb) {
18+
cb();
19+
});
20+
21+
// The js task could be replaced with gulp-coffee as desired.
22+
gulp.task("js", function () {
23+
return gulp
24+
.src("build/lambdaMain.js")
25+
.pipe(gulp.dest("dist/"));
26+
});
27+
28+
// Here we want to install npm packages to dist, ignoring devDependencies.
29+
gulp.task('npm', function() {
30+
return gulp
31+
.src('./package.json')
32+
.pipe(gulp.dest('./dist/'))
33+
.pipe(install({production: true}));
34+
});
35+
36+
// Next copy over environment variables managed outside of source control.
37+
gulp.task('env', function() {
38+
return gulp
39+
.src('./config.env.production')
40+
.pipe(rename('.env'))
41+
.pipe(gulp.dest('./dist'));
42+
});
43+
44+
// Now the dist directory is ready to go. Zip it.
45+
gulp.task('zip', function() {
46+
return gulp
47+
.src(['dist/**/*', '!dist/package.json', 'dist/.*'])
48+
.pipe(zip('dist.zip'))
49+
.pipe(gulp.dest('./'));
50+
});
51+
52+
// Per the gulp guidelines, we do not need a plugin for something that can be
53+
// done easily with an existing node module. #CodeOverConfig
54+
//
55+
// Note: This presumes that AWS.config already has credentials. This will be
56+
// the case if you have installed and configured the AWS CLI.
57+
//
58+
// See http://aws.amazon.com/sdk-for-node-js/
59+
gulp.task('upload', function() {
60+
61+
// TODO: This should probably pull from package.json
62+
AWS.config.region = 'us-east-1';
63+
var lambda = new AWS.Lambda();
64+
var functionName = 'video-events';
65+
66+
lambda.getFunction({FunctionName: functionName}, function(err, data) {
67+
if (err) {
68+
if (err.statusCode === 404) {
69+
var warning = 'Unable to find lambda function ' + deploy_function + '. '
70+
warning += 'Verify the lambda function name and AWS region are correct.'
71+
gutil.log(warning);
72+
} else {
73+
var warning = 'AWS API request failed. '
74+
warning += 'Check your AWS credentials and permissions.'
75+
gutil.log(warning);
76+
}
77+
}
78+
79+
// This is a bit silly, simply because these five parameters are required.
80+
var current = data.Configuration;
81+
var params = {
82+
FunctionName: functionName,
83+
Handler: current.Handler,
84+
Mode: current.Mode,
85+
Role: current.Role,
86+
Runtime: current.Runtime
87+
};
88+
89+
fs.readFile('./dist.zip', function(err, data) {
90+
params['FunctionZip'] = data;
91+
lambda.uploadFunction(params, function(err, data) {
92+
if (err) {
93+
var warning = 'Package upload failed. '
94+
warning += 'Check your iam:PassRole permissions.'
95+
gutil.log(warning);
96+
}
97+
});
98+
});
99+
});
100+
});
101+
102+
// The key to deploying as a single command is to manage the sequence of events.
103+
gulp.task('dist', function(cb) {
104+
return runSequence(
105+
['clean'],
106+
['webpack'],
107+
['js', 'npm', 'env'],
108+
['zip'],
109+
// ['upload'],
110+
function (err) {
111+
//if any error happened in the previous tasks, exit with a code > 0
112+
if (err) {
113+
cb(err);
114+
var exitCode = 2;
115+
console.log('[ERROR] gulp build task failed', err);
116+
console.log('[FAIL] gulp build task failed - exiting with code ' + exitCode);
117+
return process.exit(exitCode);
118+
}
119+
else {
120+
return cb();
121+
}
122+
}
123+
);
124+
});

make-webpack-config.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
var path = require("path");
2+
var webpack = require("webpack");
3+
var StatsPlugin = require("stats-webpack-plugin");
4+
var ContextReplacementPlugin = require("webpack/lib/ContextReplacementPlugin");
5+
6+
module.exports = function(options) {
7+
var entry = { 'index': './src/index.js' };
8+
9+
var loaders = [
10+
{ test: /\.jsx$/, loader: options.hotComponents ? ["react-hot-loader", "babel-loader?stage=0"] : "babel-loader?stage=0"},
11+
{ test: /\.js$/, loader: "babel-loader", include: path.join(__dirname, "src")},
12+
{ test: /\.json$/, loader: 'json-loader' },
13+
{ test: /\.md|markdown$/, loader: 'markdown-loader'}
14+
];
15+
16+
var alias = {};
17+
var aliasLoader = {};
18+
var externals = [
19+
{ 'aws-sdk': 'commonjs aws-sdk' }, // This is already available on the lambda server
20+
{ 'winston': 'commonjs winston' } // Winston looks for its own package.json
21+
];
22+
var modulesDirectories = ['node_modules', 'web_modules'];
23+
var extensions = ["", ".web.js", ".js", ".jsx", ".json"];
24+
var root = __dirname;
25+
26+
var publicPath = options.devServer ?
27+
"http://localhost:2992/assets/" :
28+
"/assets/";
29+
30+
var output = {
31+
path: path.join(__dirname, "build"),
32+
publicPath,
33+
filename: "[name].js",
34+
chunkFilename: options.devServer ? "[id].js" : "[name].js",
35+
sourceMapFilename: "debugging/[file].map",
36+
pathinfo: options.debug,
37+
libraryTarget: 'umd'
38+
};
39+
40+
var excludeFromStats = [
41+
/node_modules[\\\/]react(-router)?[\\\/]/,
42+
/node_modules[\\\/]items-store[\\\/]/
43+
];
44+
45+
var plugins = [
46+
new StatsPlugin(path.join(__dirname, "build", "stats.json"), {
47+
chunkModules: true,
48+
exclude: excludeFromStats
49+
}),
50+
new ContextReplacementPlugin(/moment\.js[\/\\]lang$/, /^\.\/(de|pl)$/)
51+
];
52+
53+
if(options.minimize) {
54+
plugins.push(
55+
new webpack.optimize.UglifyJsPlugin({
56+
compressor: {
57+
warnings: false
58+
}
59+
}),
60+
new webpack.optimize.DedupePlugin()
61+
);
62+
plugins.push(
63+
new webpack.DefinePlugin({
64+
"process.env": {
65+
NODE_ENV: JSON.stringify("production")
66+
}
67+
}),
68+
new webpack.NoErrorsPlugin()
69+
);
70+
}
71+
72+
return {
73+
entry: entry,
74+
output: output,
75+
target: 'node',
76+
module: { loaders: loaders },
77+
devtool: options.devtool,
78+
debug: options.debug,
79+
resolveLoader: {
80+
root: path.join(__dirname, "node_modules"),
81+
alias: aliasLoader
82+
},
83+
externals: externals,
84+
resolve: {
85+
root: root,
86+
modulesDirectories: modulesDirectories,
87+
extensions: extensions,
88+
alias: alias
89+
},
90+
plugins: plugins,
91+
devServer: {
92+
stats: {
93+
cached: false,
94+
exclude: excludeFromStats
95+
}
96+
}
97+
};
98+
};

package.json

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,34 @@
66
"scripts": {
77
"build": "babel src -d lib",
88
"start": "node ./scripts/start.js",
9-
"debug": "node-debug ./scripts/start.js"
9+
"debug": "node-debug ./scripts/start.js",
10+
"build-dev": "webpack --config webpack-dev.config.js --progress --profile --colors",
11+
"build-prod": "webpack --config webpack-prod.config.js --progress --profile --colors",
12+
"dist": "gulp dist"
1013
},
1114
"author": "tmitchel2 <[email protected]>",
1215
"license": "MIT",
1316
"dependencies": {
14-
"aws-sdk": "^2.3.5",
15-
"babel-polyfill": "^6.7.4",
16-
"measured": "^1.1.0",
17+
"dotenv": "^2.0.0",
1718
"winston": "^2.2.0"
1819
},
1920
"devDependencies": {
21+
"aws-sdk": "^2.3.5",
22+
"babel-polyfill": "^6.7.4",
23+
"measured": "^1.1.0",
2024
"babel": "^6.5.2",
2125
"babel-cli": "^6.7.7",
22-
"babel-preset-react-native": "^1.5.7"
26+
"babel-preset-react-native": "^1.5.7",
27+
"webpack": "^1.13.0",
28+
"babel-loader": "^6.2.4",
29+
"del": "^2.2.0",
30+
"gulp": "^3.9.1",
31+
"gulp-install": "^0.6.0",
32+
"gulp-rename": "^1.2.2",
33+
"gulp-uglify": "^1.5.3",
34+
"gulp-zip": "^3.2.0",
35+
"run-sequence": "^1.1.5",
36+
"stats-webpack-plugin": "^0.3.1"
2337
},
2438
"keywords": [
2539
"dynamoDB",

scripts/start.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
try {
2-
var lambda = require('../lib/index.js');
2+
var lambda = require('../build/index.js');
33

44
var context = {
55
succeed: function(data) {

src/ConfigurableProvisioner.js

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,29 @@ class ConfigurableProvisioner {
5252
}
5353

5454
getGlobalSecondaryIndexUpdate(tableDescription, tableConsumedCapacityDescription, gsi){
55-
let gsicc = tableConsumedCapacityDescription.Table.GlobalSecondaryIndexes.find(i => i.IndexName === gsi.IndexName);
56-
let provisionedThroughput = this.getUpdatedProvisionedThroughput({
57-
TableName: tableDescription.Table.TableName,
58-
IndexName: gsicc.IndexName,
59-
ProvisionedThroughput: gsi.ProvisionedThroughput,
60-
ConsumedThroughput: gsicc.ConsumedThroughput
61-
});
62-
63-
if (provisionedThroughput == null) {
64-
return null;
65-
}
55+
try {
56+
let gsicc = tableConsumedCapacityDescription.Table.GlobalSecondaryIndexes.find(i => i.IndexName === gsi.IndexName);
57+
let provisionedThroughput = this.getUpdatedProvisionedThroughput({
58+
TableName: tableDescription.Table.TableName,
59+
IndexName: gsicc.IndexName,
60+
ProvisionedThroughput: gsi.ProvisionedThroughput,
61+
ConsumedThroughput: gsicc.ConsumedThroughput
62+
});
6663

67-
return {
68-
Update: {
69-
IndexName: gsi.IndexName,
70-
ProvisionedThroughput: provisionedThroughput
64+
if (provisionedThroughput == null) {
65+
return null;
7166
}
72-
};
67+
68+
return {
69+
Update: {
70+
IndexName: gsi.IndexName,
71+
ProvisionedThroughput: provisionedThroughput
72+
}
73+
};
74+
} catch (e) {
75+
logger.warn('ConfigurableProvisioner.getGlobalSecondaryIndexUpdate (error)');
76+
throw e;
77+
}
7378
}
7479

7580
getUpdatedProvisionedThroughput(params) {

src/RateLimitedDecrement.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
class RateLimitedDecrement {
22

3+
// TODO dont allow decrement if its going to be really small change
4+
35
static isDecrementAllowed(data) {
46
return this.getNextAllowedDecrementDate(data) <= this.getNowDate();
57
}

src/Throughput.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class Throughput {
33
return (data.ConsumedThroughput.ReadCapacityUnits / data.ProvisionedThroughput.ReadCapacityUnits) * 100;
44
}
55

6-
static getPercentAdjustedReadCapacityUnits(data, max, min) {
6+
static getPercentAdjustedReadCapacityUnits(data, adjustmentPercent, max, min) {
77
let adjustmentUnits = Math.round(data.ProvisionedThroughput.ReadCapacityUnits * (adjustmentPercent / 100));
88
let newValue = data.ProvisionedThroughput.ReadCapacityUnits + adjustmentUnits;
99
return Math.max(Math.min(newValue, max), min);
@@ -13,7 +13,7 @@ class Throughput {
1313
return (data.ConsumedThroughput.WriteCapacityUnits / data.ProvisionedThroughput.WriteCapacityUnits) * 100;
1414
}
1515

16-
static getPercentAdjustedWriteCapacityUnits(data, max, min) {
16+
static getPercentAdjustedWriteCapacityUnits(data, adjustmentPercent, max, min) {
1717
let adjustmentUnits = Math.round(data.ProvisionedThroughput.WriteCapacityUnits * (adjustmentPercent / 100));
1818
let newValue = data.ProvisionedThroughput.WriteCapacityUnits + adjustmentUnits;
1919
return Math.max(Math.min(newValue, max), min);

src/config.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const provisioner = new ConfigurableProvisioner ({
1010
const adjustmentPercent = 100;
1111
const max = 10;
1212
const min = 1;
13-
return Throughput.getPercentAdjustedReadCapacityUnits(adjustmentPercent, max, min);
13+
return Throughput.getPercentAdjustedReadCapacityUnits(data, adjustmentPercent, max, min);
1414
},
1515
},
1616
decrement: {
@@ -23,10 +23,9 @@ const provisioner = new ConfigurableProvisioner ({
2323
isAdjustmentRequired: data => Throughput.getWriteCapacityUtilisationPercent(data) > 90,
2424
calculateValue: data => {
2525
const adjustmentPercent = 100;
26-
const maxValue = 10;
2726
const max = 10;
2827
const min = 1;
29-
return Throughput.getPercentAdjustedWriteCapacityUnits(adjustmentPercent, maxValue);
28+
return Throughput.getPercentAdjustedWriteCapacityUnits(data, adjustmentPercent, max, min);
3029
},
3130
},
3231
decrement: {

0 commit comments

Comments
 (0)