Skip to content

Commit 10f106d

Browse files
authored
feat(serverless-offline): add support for serverless-offline plugin (#71)
* feat(serverless-offline): add support for serverless-offline plugin Use serverless-offline hooks for integration. Add new `iopipe clean` command as it is useful with offline. Update docs. fix #70
1 parent e6ce34a commit 10f106d

File tree

7 files changed

+142
-49
lines changed

7 files changed

+142
-49
lines changed

readme.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ You're set! The plugin will run during an `sls deploy` or during `sls invoke loc
4848
Check out an [example here](https://github.com/iopipe/serverless-plugin-iopipe/blob/master/example/serverless.yml).
4949

5050
# How Does it Work?
51-
`serverless-plugin-iopipe` outputs a file that imports and wraps the function handlers defined in `serverless.yml` with IOpipe so you don't have to. It allows you to deploy and upgrade multiple functions simultaneously.
51+
`serverless-plugin-iopipe` outputs files that import and wrap the function handlers defined in `serverless.yml` with IOpipe so you don't have to. It allows you to deploy and upgrade multiple functions simultaneously.
52+
53+
# Commands
54+
- `sls iopipe clean` This command cleans up your project folder of extraneous `*-iopipe.js` files if needed. This can be useful when using the [serverless-offline](https://github.com/dherault/serverless-offline) plugin.
5255

5356
# Options
5457
Beyond the required $IOPIPE_TOKEN environment variable, some options can be set [in the "custom" config](https://serverless.com/framework/docs/providers/aws/guide/plugins#installing-plugins) in `serverless.yml`. [See Example](https://github.com/iopipe/serverless-plugin-iopipe/blob/master/example/serverless.yml)
@@ -85,8 +88,10 @@ By default, the plugin sends _anonymized_, non-identifying usage statistics to G
8588
- serverless-plugin-iopipe
8689
- serverless-webpack
8790
```
91+
- Does this work with [serverless-offline](https://github.com/dherault/serverless-offline)?
92+
- Yes, list `serverless-plugin-iopipe` first before any other plugins in `serverless.yml`.
8893
- Can I use IOpipe plugins?
89-
- Yes, you can specify iopipe plugins through your [package.json file, or an iopipe.rc file](https://github.com/iopipe/iopipe-js-core#packagejson-configuration). You will have to make sure those plugins are installed into node_modules.
94+
- Yes, you can specify iopipe plugins through your [package.json file, or an iopipe.rc file](https://github.com/iopipe/iopipe-js-core#packagejson-configuration). Ensure those plugins are installed into your node_modules folder (yarn or npm).
9095

9196
## Known Issues
9297
- If you have lambda functions that are already wrapped by iopipe via code, you may experience unexpected results. Remove the iopipe wrapping code from those handlers.

src/index.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,24 @@ class ServerlessIOpipePlugin {
110110
iopipe: {
111111
usage:
112112
"Automatically wraps your function handlers in IOpipe, so you don't have to.",
113-
lifecycleEvents: ['run']
113+
lifecycleEvents: ['run', 'clean'],
114+
commands: {
115+
clean: {
116+
usage: 'Cleans up extra IOpipe files if necessary',
117+
lifecycleEvents: ['init']
118+
}
119+
}
114120
}
115121
};
116122
this.hooks = {
123+
'iopipe:run': this.greeting.bind(this),
117124
'before:package:createDeploymentArtifacts': this.run.bind(this),
118125
'before:invoke:local:invoke': this.run.bind(this),
126+
'before:offline:start:init': this.run.bind(this),
127+
'before:step-functions-offline:start': this.run.bind(this),
119128
'after:package:createDeploymentArtifacts': this.finish.bind(this),
120129
'after:invoke:local:invoke': this.finish.bind(this),
121-
'iopipe:run': this.greeting.bind(this)
130+
'iopipe:clean:init': this.finish.bind(this)
122131
};
123132
}
124133
getOptions(obj = this.options) {
@@ -389,9 +398,7 @@ class ServerlessIOpipePlugin {
389398
}
390399
async finish() {
391400
const debug = createDebugger('finish');
392-
this.log(
393-
'Successfully wrapped Node.js functions with IOpipe, cleaning up.'
394-
);
401+
this.log('Cleaning up extraneous IOpipe files');
395402
debug(`Removing ${this.handlerFileName}.js`);
396403
await del(join(this.originalServicePath, '*-iopipe.js'), { force: true });
397404
this.track({
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
exports.handler = (event, context) => {
2+
let body = { success: true };
3+
const params = event.queryStringParameters || {};
4+
const { fail } = params;
5+
if (!context.iopipe || fail) {
6+
body = { err: 'No iopipe object found on context' };
7+
}
8+
return context.succeed({
9+
statusCode: 200,
10+
body: JSON.stringify(body)
11+
});
12+
};

testProjects/offline/package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "sls-unit-test-local-invoke",
3+
"version": "0.0.0-test",
4+
"description": "",
5+
"main": "handler.js",
6+
"dependencies": {
7+
"@iopipe/iopipe": "^1.5.0",
8+
"cross-spawn": "^6.0.5",
9+
"got": "^8.3.0",
10+
"serverless-offline": "^3.20.2"
11+
},
12+
"scripts": {
13+
"build": "echo 'No need to build'",
14+
"start": "SLS_DEBUG=* IOPIPE_TOKEN=test_token node ../../node_modules/serverless/bin/serverless offline start",
15+
"test": "IOPIPE_TOKEN=test_token node test"
16+
},
17+
"author": "",
18+
"license": "ISC",
19+
"devDependencies": {}
20+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
service: sls-iopipe-offline-test
2+
provider:
3+
name: aws
4+
runtime: nodejs6.10
5+
stage: prod
6+
region: us-west-2
7+
environment:
8+
IOPIPE_TOKEN: ${env:IOPIPE_TOKEN}
9+
plugins:
10+
- serverless-plugin-iopipe/index.js
11+
- serverless-offline
12+
functions:
13+
index:
14+
handler: handlers/index.handler
15+
events:
16+
- http:
17+
path: /
18+
method: get
19+
custom:
20+
serverless-offline:
21+
port: 4982

testProjects/offline/test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const gotLib = require('got');
2+
const spawn = require('cross-spawn');
3+
4+
const got = str => gotLib(str, { json: true });
5+
const BASE = `http://localhost:4982`;
6+
7+
async function run() {
8+
// make sure that the error state works too
9+
const { body: { err } } = await got(`${BASE}?fail=true`);
10+
if (err !== 'No iopipe object found on context') {
11+
throw new Error(err || 'Wrong');
12+
}
13+
const { body: { success } } = await got(BASE);
14+
if (success !== true) {
15+
throw new Error('No success msg');
16+
}
17+
return true;
18+
}
19+
20+
const offlineChild = spawn('npm', ['start'], {
21+
stdio: 'inherit',
22+
// http://azimi.me/2014/12/31/kill-child_process-node-js.html
23+
detached: true
24+
});
25+
26+
setTimeout(async () => {
27+
let err;
28+
try {
29+
await run();
30+
} catch (e) {
31+
err = e;
32+
}
33+
process.kill(-offlineChild.pid, 'SIGINT');
34+
if (err) {
35+
throw err;
36+
}
37+
}, process.env.OFFLINE_STARTUP_MILLIS || 5000);

util/testProjects.js

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,49 +18,40 @@ const folders = _.chain(testDirFiles)
1818
})
1919
.value();
2020

21-
const results = [];
22-
function resultPush({ status } = {}) {
23-
results.push(status);
24-
}
21+
const results = _.flatten(
22+
folders.map(folder => {
23+
console.log(`Running tests for ${folder}...`);
24+
return _.compact([
25+
!argv.noInstall &&
26+
spawn.sync('yarn', ['install', '--cwd', `testProjects/${folder}`], {
27+
stdio: 'inherit'
28+
}),
2529

26-
folders.forEach(folder => {
27-
console.log(`Running tests for ${folder}...`);
28-
!argv.noInstall &&
29-
resultPush(
30-
spawn.sync('yarn', ['install', '--cwd', `testProjects/${folder}`], {
31-
stdio: 'inherit'
32-
})
33-
);
30+
fs.copySync(
31+
'dist/index.js',
32+
`testProjects/${folder}/.serverless_plugins/serverless-plugin-iopipe/index.js`
33+
),
3434

35-
resultPush(
36-
fs.copySync(
37-
'dist/index.js',
38-
`testProjects/${folder}/.serverless_plugins/serverless-plugin-iopipe/index.js`
39-
)
40-
);
35+
!argv.noBuild &&
36+
spawn.sync('yarn', ['--cwd', `testProjects/${folder}`, 'build'], {
37+
stdio: 'inherit'
38+
}),
4139

42-
!argv.noBuild &&
43-
resultPush(
44-
spawn.sync('yarn', ['--cwd', `testProjects/${folder}`, 'build'], {
45-
stdio: 'inherit'
46-
})
47-
);
48-
49-
resultPush(
50-
spawn.sync(
51-
'yarn',
52-
_.compact([
53-
'--cwd',
54-
`testProjects/${folder}`,
55-
'test',
56-
(argv.u || argv.updateSnapshot) && '--updateSnapshot'
57-
]),
58-
{
59-
stdio: 'inherit'
60-
}
61-
)
62-
);
63-
console.log(`Finished tests for ${folder}.`);
64-
});
40+
spawn.sync(
41+
'yarn',
42+
_.compact([
43+
'--cwd',
44+
`testProjects/${folder}`,
45+
'test',
46+
(argv.u || argv.updateSnapshot) && '--updateSnapshot'
47+
]),
48+
{
49+
stdio: 'inherit'
50+
}
51+
),
52+
console.log(`Finished tests for ${folder}.`)
53+
]);
54+
})
55+
);
6556

66-
process.exit(_.max(results) || 0);
57+
process.exit(_.max(_.map(results, 'status')) || 0);

0 commit comments

Comments
 (0)