How to deploy a phantomjs node app on AWS Lambda? - node.js

I threw together a small Lambda function together to crawl a website using the SpookyJS, CasperJS, and PhantomJS toolchain for headless browsing. The task is quite simple, and at some point a few months ago it was working in Lambda. I recently had to change a few things around and wanted to work on the project again, but started fresh and had trouble getting Lambda to run without erroring in any capacity. My question is how can I run phantomjs in Lambda?
The example code I am running is:
spooky.start('http://en.wikipedia.org/wiki/Spooky_the_Tuff_Little_Ghost');
spooky.then(function () {
this.emit('hello', 'Hello, from ' + this.evaluate(function () {
return document.title;
}));
});
spooky.run();
The error I am getting in Lambda is:
{ [Error: Child terminated with non-zero exit code 1] details: { code: 1, signal: null } }
I have followed a variety of procedures to ensure everything is able to run on Lambda. Below is a long list of things I've attempted to diagnose:
Run locally using node index.js and confirm it is working
Upload package.json and the js file to an Amazon Linux EC2 instance for compilation as recommended for npm installation calls and described here
Run npm install on the ec2 instance, and again run node index.js to ensure the correct output
zip everything up, and deploy to AWS using the cli
My package.json is:
{
"name": "lambda-spooky-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"casperjs": "^1.1.3",
"phantomjs-prebuilt": "^2.1.10",
"spooky": "^0.2.5"
}
}
I have also attempted the following (most also working locally, and on the AWS EC2 instance, but with the same error on Lambda:
Trying the non -prebuilt version of phantom
Ensuring casperjs and phantomjs are accessible from the path with process.env['PATH'] = process.env['PATH'] + ':' + process.env['LAMBDA_TASK_ROOT'] + ':' + process.env['LAMBDA_TASK_ROOT'] + '/node_modules/.bin';
console.log( 'PATH: ' + process.env.PATH );
Inspecting spawn calls by wrapping child_process's .spawn() call, and got the following:
{ '0': 'casperjs',
'1':
[ '/var/task/node_modules/spooky/lib/bootstrap.js',
'--transport=http',
'--command=casperjs',
'--port=8081',
'--spooky_lib=/var/task/node_modules/spooky/lib/../',
'--spawnOptions=[object Object]' ],
'2': {} }
Calling .exec('casperjs') and .exec('phantomjs --version') directly, confirming it works locally and on EC2, but gets the following error in Lambda. The command:
`require('child_process').exec('casperjs', (error, stdout, stderr) => {
if (error) { console.error('error: ' + error); }
console.log('out: ' + stdout);
console.log('err: ' + stderr);
});
both with the following result:
err: Error: Command failed: /bin/sh -c casperjs
module.js:327
throw err;
^
Error: Cannot find module '/var/task/node_modules/lib/phantomjs'
at Function.Module._resolveFilename (module.js:325:15)
at Function.Module._load (module.js:276:25)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object.<anonymous> (/var/task/node_modules/.bin/phantomjs:16:15)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Function.Module.runMain (module.js:441:10)
2016-08-07T15:36:37.349Z b9a1b509-5cb4-11e6-ae82-256a0a2817b9 sout:
2016-08-07T15:36:37.349Z b9a1b509-5cb4-11e6-ae82-256a0a2817b9 serr: module.js:327
throw err;
^
Error: Cannot find module '/var/task/node_modules/lib/phantomjs'
at Function.Module._resolveFilename (module.js:325:15)
at Function.Module._load (module.js:276:25)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object.<anonymous> (/var/task/node_modules/.bin/phantomjs:16:15)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Function.Module.runMain (module.js:441:10)

I found the issue to be that including the node_modules/.bin in the path works on both local and ec2 machines because those files simply point to the action /bin folders in each respective library. This breaks if calls within those files use relative paths. The issue:
[ec2-user#ip-172-31-32-87 .bin]$ ls -lrt
total 0
lrwxrwxrwx 1 ec2-user ec2-user 35 Aug 7 00:52 phantomjs -> ../phantomjs-prebuilt/bin/phantomjs
lrwxrwxrwx 1 ec2-user ec2-user 24 Aug 7 00:52 casperjs -> ../casperjs/bin/casperjs
I worked around this by adding each library's respective bin to the lambda path in the Lambda handler function:
process.env['PATH'] = process.env['PATH'] + ':' + process.env['LAMBDA_TASK_ROOT']
+ ':' + process.env['LAMBDA_TASK_ROOT'] + '/node_modules/phantomjs-prebuilt/bin'
+ ':' + process.env['LAMBDA_TASK_ROOT'] + '/node_modules/casperjs/bin';
And this will now run phantom, casper, and spooky correctly in Lambda.

Related

How to use a global nodejs module?

I got the following error when I try to use esprima. Does anybody know how to fix the problem? Thanks.
$ npm install -g esprima
/usr/local/bin/esparse -> /usr/local/lib/node_modules/esprima/bin/esparse.js
/usr/local/bin/esvalidate -> /usr/local/lib/node_modules/esprima/bin/esvalidate.js
+ esprima#4.0.1
updated 1 package in 0.837s
$ cat main.js
#!/usr/bin/env node
// vim: set noexpandtab tabstop=2:
var esprima = require('esprima');
var program = 'const answer = 42';
console.log(esprima.tokenize(program));
console.log(esprima.parseScript(program));
$ node main.js
internal/modules/cjs/loader.js:960
throw err;
^
Error: Cannot find module 'esprima'
Require stack:
- /private/tmp/main.js
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:957:15)
at Function.Module._load (internal/modules/cjs/loader.js:840:27)
at Module.require (internal/modules/cjs/loader.js:1019:19)
at require (internal/modules/cjs/helpers.js:77:18)
at Object.<anonymous> (/private/tmp/main.js:4:15)
at Module._compile (internal/modules/cjs/loader.js:1133:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1153:10)
at Module.load (internal/modules/cjs/loader.js:977:32)
at Function.Module._load (internal/modules/cjs/loader.js:877:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12) {
code: 'MODULE_NOT_FOUND',
requireStack: [ '/private/tmp/main.js' ]
}
I think you installed esprima module in global node_modules.
If you want to use esprima in main.js which is located at /private/temp/main.js,
you should run npm install esprima in /private/temp/ without -g
EDIT - How to require global modules
Way 1
I assume you're in mac system.
Before you run your main.js, run export NODE_PATH=/usr/local/lib/node_modules in the shell, not node.js program.
Then you could require the global modules by const esprima = require("esprima").
By the way, global modules position could be different in the different system.
Way 2
After you knew your global modules position,
you could require it by const esprima = require("/usr/local/lib/node_modules/esprima")

SyntaxError when declaring a class in TypeScript

I'm getting an error when declaring a class:
// This is all on my test1.ts file content
class WDesign {
wModel: string;
wQuer: string;
}
let logWDesign = (wd : WDesign) => {
console.log(wd.wModel + " " + wd.wQuer);
}
let wd1 : WDesign;
wd1.wModel = "SDS-9985";
wd1.wQuer = "escarni";
logWDesign(wd1);
Then, to show on Node command prompt:
>node test1.ts
I get the following:
[filePath].test1.ts:3
wModel: string;
^
SyntaxError: Unexpected token :
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
at Module._compile (module.js:617:28)
at Object.Module._extensions..js (module.js:664:10)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)
at Function.Module._load (module.js:498:3)
at Function.Module.runMain (module.js:694:10)
at startup (bootstrap_node.js:204:16)
at bootstrap_node.js:625:3
Typescript ver 3.1.3
Node ver. 8.12.0
Visual Studio Code 1.28.2
You firstly have to transpile the .ts file .js by simply running the command
tsc
node test1.js
This will generate javascript corresponding to your typescript files. Node cannot directly understand typescript. You have to give it javascript code.
You can use typescript-node-starter code provided by Typescript here to save configuration hustle.
Post comments if further help is required.

Found extra flags error in new version of protractor

I am trying to run a js scripts using protractor, but i am getting following error
C:\Users\Hoodi\AppData\Roaming\npm\node_modules\protractor\built\cli.js:172
throw new Error('Found extra flags: ' + unknownKeys.join(', '));
^
Error: Found extra flags: identityManagement
at Object.<anonymous> (C:\Users\Hoodi\AppData\Roaming\npm\node_modules\protractor\built\cli.js:172:15)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.require (module.js:497:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (C:\Users\Hoodi\AppData\Roaming\npm\node_modules\protractor\bin\protractor:5:1)
at Module._compile (module.js:570:32)
My config file
// conf.js
exports.config = {
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
useAllAngular2AppRoots: true,
// Options to be passed to Jasmine-node.
jasmineNodeOpts: {
// Use colors in the command line report.
showColors: true,
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 60000,
},
onPrepare: function() {
require('../../lib/waitReady.js');
},
capabilities :{
browserName : 'chrome',
}
}
console.dir("argv: " + process.argv)
switch(process.argv[3]) {
case '--identityManagement':
exports.config.specs = ['./identityManagement.js'];
break;
default:
exports.config.specs = ['./identityManagement.js'];
}
I tried executing above script using protractor as well as "npm run" command, but in both cases i am getting same error. command that i used
protractor ./conf.js --identityManagement
and
npm run im
This actually works on my other system. npm and node version of system where this works are
node v7.2.1
npm v3.10.10
And where is doesnt work
node v7.4.0
npm v4.0.5
My package.json file looks like this
{
"name": "intelliflash",
"author": "Vishwanath Rawat <rawat#tegile.com>",
"description": "IntelliFlash tests",
"scripts": {
"im": "protractor ./conf.js --identityManagement"
}
}
Please help.
It has nothing to do with your npm or node versions, Latest release of Protractor 5.0 has put a check in cli for unidentified flags as you are putting with the help of process.argv.
You can solve this by disabling the flag checks:
protractor ./conf.js --identityManagement --disableChecks
For more details please refer the Protractor 5.0 changelog
Note: min node version now is v6.9.x which supports this version of protractor

VS 2015 Gulp Failed to load message

I'm just getting started with gulp in a .NET 4.6 project and I'm getting the error "Failed to load" in task runner explorer. My gulpfile is:
var gulp = require('gulp');
var inject = require('gulp-inject');
var concat = require('gulp-concat');
var print = require('gulp-print');
gulp.task('default', function () {
console.log('Hello Gulp!');
});
gulp.task('css-task', function () {
var target = gulp.src('./index.html');
var customCssStream = gulp.src(['./bower_components/bootstrap/dist/css/bootstrap.min.css']);
return target
.pipe(inject(customCssStream.pipe(print())
.pipe(concat('appStyles.css'))
.pipe(gulp.dest('./Content/css')), { name: 'styles' }))
.pipe(gulp.dest('./'));
});
And the error is:
gulp-inject/src/inject/index.js:127 startTag, ^ SyntaxError:
Unexpected token , at exports.runInThisContext (vm.js:73:16) at
Module._compile (module.js:443:25) at Object.Module._extensions..js
(module.js:478:10) at Module.load (module.js:355:32) at
Function.Module._load (module.js:310:12) at Module.require
(module.js:365:17) at require (module.js:384:17) at Object.
(.../node_modules/gulp-inject/index.js:4:28) at Module._compile
(module.js:460:26) at Object.Module._extensions..js (module.js:478:10)
I was able to get the "Hello Gulp!" task to run, but once I add gulp-inject I see the problem.
Turns out the problem was related to the version of node.js that Visual Studio 2015 installed not being compatible with node-inject module. After I installed version 4.4.2 of node.js and pointed visual studio at it as described here it now compiles.

Configure Mocha to continually run tests fails

I'm trying to configure mocha to run the tests instead of running manually using the command:
node_modules\.bin\mocha -w
I get the error as below.
D:\Downloads\Bluemix\dw0015a\dw0015a\node_modules\mocha\lib\utils.js:626
throw new Error("cannot resolve path (or pattern) '" + path + "'");
^
Error: cannot resolve path (or pattern) '-w'
at Object.lookupFiles (D:\Downloads\Bluemix\dw0015a\dw0015a\node_modules\moc
at D:\Downloads\Bluemix\dw0015a\dw0015a\node_modules\mocha\bin\_mocha:316:30
at Array.forEach (native)
at Object.<anonymous> (D:\Downloads\Bluemix\dw0015a\dw0015a\node_modules\moc
at Module._compile (module.js:435:26)
at Object.Module._extensions..js (module.js:442:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:311:12)
at Function.Module.runMain (module.js:467:10)
at startup (node.js:136:18)
at node.js:963:3
I am able to get a behavior similar to what you report is to have the w option preceded by an "en dash" character (Unicode U+2013) rather than a minus character. Like this:
$ mocha –w
/home/x/local/lib/node_modules/mocha/lib/utils.js:626
throw new Error("cannot resolve path (or pattern) '" + path + "'");
^
Error: cannot resolve path (or pattern) '–w'
at Object.lookupFiles (/home/x/local/lib/node_modules/mocha/lib/utils.js:626:15)
[... etc ...]
Make sure you are using the minus character (hex value 0x2d).

Resources