Skip to content

Commit

Permalink
Merge pull request #4 from logandk/dev
Browse files Browse the repository at this point in the history
Release 1.0.1
  • Loading branch information
logandk authored Dec 19, 2016
2 parents c462bdf + 1350a12 commit f3ddce9
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 22 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# 1.0.1
## Features
* Enable using the requirements packaging functionality alone, without the WSGI handler. This is enabled by omitting the `custom.wsgi.app` setting from `serverless.yml`.
* Load provider and function environment variables when serving WSGI app locally

## Bugs
* If no `requirements.txt` file is present and the WSGI handler is enabled, make sure to package werkzeug.
63 changes: 54 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ Load the plugin and set the `custom.wsgi.app` configuration in `serverless.yml`
module path of your Flask application.

All functions that will use WSGI need to have `wsgi.handler` set as the Lambda handler and
use the `lambda-proxy` integration for API Gateway.
use the default `lambda-proxy` integration for API Gateway. This configuration example treats
API Gateway as a transparent proxy, passing all requests directly to your Flask application,
and letting the application handle errors, 404s etc.

```yaml
service: example
Expand All @@ -80,20 +82,14 @@ functions:
api:
handler: wsgi.handler
events:
- http:
path: cats
method: get
integration: lambda-proxy
- http:
path: dogs/{id}
method: get
integration: lambda-proxy
- http: ANY {proxy+}

custom:
wsgi:
app: api.app
```
### requirements.txt
Add Flask to the application bundle.
Expand Down Expand Up @@ -146,6 +142,21 @@ requests==2.11.1

For more information, see [https://pip.readthedocs.io/en/1.1/requirements.html](https://pip.readthedocs.io/en/1.1/requirements.html).

You can use the requirement packaging functionality of *serverless-wsgi* without the WSGI
handler itself by including the plugin in your `serverless.yml` configuration, without specifying
the `custom.wsgi.app` setting. This will omit the WSGI handler from the package, but include
any requirements specified in `requirements.txt`.

If you do not include the WSGI handler, you'll need to add `.requirements` to the Python search path
manually in your handler, before importing any packages:

```
import os
import sys
root = os.path.abspath(os.path.join(os.path.dirname(__file__)))
sys.path.insert(0, os.path.join(root, '.requirements'))
```

### Local server

For convenience, a `sls wsgi serve` command is provided to run your WSGI application
Expand All @@ -169,3 +180,37 @@ $ sls wsgi serve -p 8000
* Restarting with stat
* Debugger is active!
```


### Explicit routes

If you'd like to be explicit about which routes and HTTP methods should pass through to your
application, see the following example:

```yaml
service: example

provider:
name: aws
runtime: python2.7

plugins:
- serverless-wsgi

functions:
api:
handler: wsgi.handler
events:
- http:
path: cats
method: get
integration: lambda-proxy
- http:
path: dogs/{id}
method: get
integration: lambda-proxy

custom:
wsgi:
app: api.app
```
49 changes: 37 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* jshint ignore:start */
'use strict';

const BbPromise = require('bluebird');
const _ = require('lodash');
const path = require('path');
Expand All @@ -13,13 +12,15 @@ class ServerlessWSGI {
validate() {
if (this.serverless.service.custom && this.serverless.service.custom.wsgi && this.serverless.service.custom.wsgi.app) {
this.wsgiApp = this.serverless.service.custom.wsgi.app;
} else {
throw new this.serverless.classes.Error(
'Missing WSGI app, please specify custom.wsgi.app. For instance, if you have a Flask application "app" in "api.py", set the Serverless custom.wsgi.app configuration option to: api.app');
}
};

packWsgiHandler() {
if (!this.wsgiApp) {
this.serverless.cli.log('Warning: No WSGI app specified, omitting WSGI handler from package');
return BbPromise.resolve();
}

this.serverless.cli.log('Packaging Python WSGI handler...');

return BbPromise.all([
Expand All @@ -34,20 +35,24 @@ class ServerlessWSGI {

packRequirements() {
const requirementsFile = path.join(this.serverless.config.servicePath, 'requirements.txt');
let args = [path.resolve(__dirname, 'requirements.py')];

if (!fse.existsSync(requirementsFile)) {
return BbPromise.resolve();
if (fse.existsSync(requirementsFile)) {
args.push(requirementsFile);
} else {
if (this.wsgiApp) {
args.push(path.resolve(__dirname, 'requirements.txt'));
} else {
return BbPromise.resolve();
}
}

args.push(path.join(this.serverless.config.servicePath, '.requirements'));

this.serverless.cli.log('Packaging required Python packages...');

return new BbPromise((resolve, reject) => {
const res = child_process.spawnSync('python', [
path.resolve(__dirname, 'requirements.py'),
path.resolve(__dirname, 'requirements.txt'),
requirementsFile,
path.join(this.serverless.config.servicePath, '.requirements')
]);
const res = child_process.spawnSync('python', args);
if (res.error) {
return reject(res.error);
}
Expand All @@ -65,7 +70,26 @@ class ServerlessWSGI {
fse.removeAsync(path.join(this.serverless.config.servicePath, artifact))));;
};

loadEnvVars() {
const providerEnvVars = this.serverless.service.provider.environment || {};
_.merge(process.env, providerEnvVars);

_.each(this.serverless.service.functions, function (func) {
if (func.handler == 'wsgi.handler') {
const functionEnvVars = func.environment || {};
_.merge(process.env, functionEnvVars);
}
});

return BbPromise.resolve();
};

serve() {
if (!this.wsgiApp) {
throw new this.serverless.classes.Error(
'Missing WSGI app, please specify custom.wsgi.app. For instance, if you have a Flask application "app" in "api.py", set the Serverless custom.wsgi.app configuration option to: api.app');
}

const port = this.options.port || 5000;

return new BbPromise((resolve, reject) => {
Expand Down Expand Up @@ -113,6 +137,7 @@ class ServerlessWSGI {

'wsgi:serve:serve': () => BbPromise.bind(this)
.then(this.validate)
.then(this.loadEnvVars)
.then(this.serve)
};
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-wsgi",
"version": "1.0.0",
"version": "1.0.1",
"engines": {
"node": ">=4.0"
},
Expand All @@ -27,6 +27,7 @@
"serverless.com"
],
"files": [
"CHANGELOG.md",
"index.js",
"LICENSE",
"package.json",
Expand Down

0 comments on commit f3ddce9

Please sign in to comment.