Skip to content

Commit e145223

Browse files
committed
add options to build requirements as a lambda layer
1 parent 190df96 commit e145223

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,38 @@ custom:
146146
```
147147
This will remove all folders within the installed requirements that match
148148
the names in `slimPatterns`
149+
150+
### Lamba Layer
151+
Another method for dealing with large dependencies is to put them into a
152+
[Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html).
153+
Simply add the `layer` option to the configuration.
154+
```yaml
155+
custom:
156+
pythonRequirements:
157+
layer: true
158+
```
159+
The requirements will be zipped up and a layer will be created automatically.
160+
Now just add the reference to the functions that will use the layer.
161+
```yaml
162+
functions:
163+
hello:
164+
handler: handler.hello
165+
layers:
166+
- {Ref: PythonRequirementsLambdaLayer}
167+
```
168+
If the layer requires additional or custom configuration, add them onto the `layer` option.
169+
```yaml
170+
custom:
171+
pythonRequirements:
172+
layer:
173+
name: ${self:provider.stage}-layerName
174+
description: Python requirements lamba layer
175+
compatibleRuntimes:
176+
- python3.7
177+
licenseInfo: GPLv3
178+
allowedAccounts:
179+
- '*'
180+
```
149181
## Omitting Packages
150182
You can omit a package from deployment with the `noDeploy` option. Note that
151183
dependencies of omitted packages must explicitly be omitted too. By default,

index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
packRequirements
1111
} = require('./lib/zip');
1212
const { injectAllRequirements } = require('./lib/inject');
13+
const { layerRequirements } = require('./lib/layer');
1314
const { installAllRequirements } = require('./lib/pip');
1415
const { pipfileToRequirements } = require('./lib/pipenv');
1516
const { cleanup, cleanupCache } = require('./lib/clean');
@@ -31,6 +32,8 @@ class ServerlessPythonRequirements {
3132
slimPatterns: false,
3233
slimPatternsAppendDefaults: true,
3334
zip: false,
35+
inject: true,
36+
layer: false,
3437
cleanupZipHelper: true,
3538
invalidateCaches: false,
3639
fileName: 'requirements.txt',
@@ -94,6 +97,14 @@ class ServerlessPythonRequirements {
9497
}`;
9598
options.dockerImage = options.dockerImage || defaultImage;
9699
}
100+
if (options.layer) {
101+
// If layers are being used, dependencies should not be injected.
102+
options.inject = false;
103+
// If layer was set as a boolean, set it to an empty object to use the layer defaults.
104+
if (options.layer === true) {
105+
options.layer = {};
106+
}
107+
}
97108
return options;
98109
}
99110

@@ -167,6 +178,7 @@ class ServerlessPythonRequirements {
167178
}
168179
return BbPromise.bind(this)
169180
.then(removeVendorHelper)
181+
.then(layerRequirements)
170182
.then(() =>
171183
injectAllRequirements.bind(this)(
172184
arguments[1].functionObj &&

lib/inject.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ function moveModuleUp(source, target, module) {
7272
* @return {Promise} the combined promise for requirements injection.
7373
*/
7474
function injectAllRequirements(funcArtifact) {
75+
if (!this.options.inject) {
76+
return BbPromise.resolve();
77+
}
78+
7579
this.serverless.cli.log('Injecting required Python packages to package...');
7680

7781
if (this.serverless.service.package.individually) {

lib/layer.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const BbPromise = require('bluebird');
2+
const fse = require('fs-extra');
3+
const path = require('path');
4+
const JSZip = require('jszip');
5+
const { writeZip, addTree } = require('./zipTree');
6+
7+
BbPromise.promisifyAll(fse);
8+
9+
/**
10+
* Zip up requirements to be used as layer package.
11+
* @return {Promise} the JSZip object constructed.
12+
*/
13+
function zipRequirements() {
14+
return addTree(new JSZip(), path.join('.serverless', 'requirements')).then(zip =>
15+
writeZip(zip, path.join('.serverless', 'pythonRequirements.zip'))
16+
);
17+
}
18+
19+
/**
20+
* Creates a layer on the serverless service for the requirements zip.
21+
* @return {Promise}
22+
*/
23+
function createLayers() {
24+
this.serverless.service.layers['pythonRequirements'] = Object.assign({
25+
artifact: path.join('.serverless', 'pythonRequirements.zip'),
26+
name: `${this.serverless.service.stage}-python-requirements`,
27+
description: 'Python requirements generated by serverless-python-requirements.',
28+
}, this.options.layer);
29+
30+
return BbPromise.resolve();
31+
}
32+
33+
/**
34+
* Creates a layer from the installed requirements.
35+
* @return {Promise} the combined promise for requirements layer.
36+
*/
37+
function layerRequirements() {
38+
if (!this.options.layer) {
39+
return BbPromise.resolve()
40+
}
41+
42+
this.serverless.cli.log('Packaging Python Requirements Lambda Layer...');
43+
44+
return BbPromise.bind(this)
45+
.then(zipRequirements)
46+
.then(createLayers)
47+
}
48+
49+
module.exports = { layerRequirements };

0 commit comments

Comments
 (0)