Skip to content

Commit aa9d873

Browse files
authored
Merge pull request #14 from softprops/exp-provided-runtime
move to provided runtime
2 parents 4e922c6 + 642dcc1 commit aa9d873

File tree

5 files changed

+875
-869
lines changed

5 files changed

+875
-869
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# ⚡ 0.2.0
2+
3+
* Switch from supporting the Lambda `python3.6` runtime to a new ✨ `rust` runtime ✨ ( which runs on the `provided` runtime under the covers )
4+
* you can now deploy independent functions with `npx serverless deploy function -f func-name`
5+
* you no longer have to be explicit about function binary to include, this plugin generates and configures the artifact (zip) file for you
6+
* you no longer have to set default exclusion rules
7+
* you can deploy a `rust` runtime function side by side with other serverless runtime functions
8+
within the same service, to facilitate experimentation and learning.
9+
110
# ⚡ 0.1.7
211

312
* bump [lambda-rust](https://hub.docker.com/r/softprops/lambda-rust/) docker version to 0.1.0-rust-1.30.1, to make the new default Rust 1.30.1 (the latest release of Rust at this time)

README.md

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,32 @@
77

88
Install the plugin with npm
99

10-
```bash
11-
$ npm install serverless-rust@0.1.7
10+
```sh
11+
$ npm install serverless-rust
1212
```
1313

14-
💡 This serverless plugin assumes you are building Rustlang lambdas using the [lando](https://github.com/softprops/lando) or [crowbar](https://github.com/ilianaw/rust-crowbar) rustlang crates.
14+
💡 This serverless plugin assumes you are building Rustlang lambdas targetting the AWS Lambda "provided" runtime. The [AWS Lambda Rust Runtime](https://github.com/awslabs/aws-lambda-rust-runtime) makes this easy.
1515

1616
Add the following to your serverless project's `serverless.yml` file
1717

1818
```yaml
1919
service: demo
2020
provider:
2121
name: aws
22-
# crowbar and lando integrate with aws lambda's python3.6 runtime
23-
runtime: python3.6
22+
runtime: rust
2423
plugins:
2524
# this adds informs servleress to use
2625
# the serverless-rust plugin
2726
- serverless-rust
28-
# the follow is recommended for small deployment sizes
29-
# (faster uploads)
27+
# creates one artifact for each function
3028
package:
3129
individually: true
32-
exclude:
33-
- ./**
3430
functions:
3531
test:
36-
# liblambda.handler is the default function name when
37-
# you follow lando/crowbar conventions
38-
handler: liblambda.handler
39-
# the following limits the function packaging
40-
# to just the resulting binary
41-
package:
42-
include:
43-
- liblambda.so
32+
# handler value syntax is `{cargo-package}.{bin-name}`
33+
# or `{bin-name}` for short when you are building a
34+
# default bin for a given package.
35+
handler: your-crate-name
4436
events:
4537
- http:
4638
path: /test
@@ -53,14 +45,13 @@ functions:
5345
You can optionally adjust the default settings of the dockerized build env using
5446
a custom section of your serverless.yaml configuration
5547
56-
5748
```yaml
5849
custom:
59-
# this section customizes the default
50+
# this section allows for customization of the default
6051
# serverless-rust plugin settings
6152
rust:
6253
# flags passed to cargo
63-
cargoFlags: '--features lando/python3-sys'
54+
cargoFlags: '--features enable-awesome'
6455
# custom docker tag
6556
dockerTag: 'some-custom-tag'
6657
```
@@ -77,15 +68,11 @@ functions:
7768
test:
7869
rust:
7970
# function specific flags passed to cargo
80-
cargoFlags: '--features ...'
81-
# liblambda.handler is the default function name when
82-
# you follow lando/crowbar conventions
83-
handler: liblambda.handler
84-
# the following limits the function packaging
85-
# to just the resulting binary
86-
package:
87-
include:
88-
- liblambda.so
71+
cargoFlags: '--features enable-awesome'
72+
# handler value syntax is `{cargo-package}.{bin-name}`
73+
# or `{bin-name}` for short when you are building a
74+
# default bin for a given package.
75+
handler: your-crate-name
8976
events:
9077
- http:
9178
path: /test
@@ -95,6 +82,16 @@ functions:
9582
9683
## 🏗️ serverless templates
9784
85+
### 0.2.*
86+
87+
* a minimal echo application - https://github.com/softprops/serverless-aws-rust
88+
89+
### 0.1.*
90+
91+
Older versions targeted the python 3.6 AWS Lambda runtime and [rust crowbar](https://github.com/ilianaw/rust-crowbar) and [lando](https://github.com/softprops/lando) applications
92+
9893
* lando api gateway application - https://github.com/softprops/serverless-lando
9994
* multi function lando api gateway application - https://github.com/softprops/serverless-multi-lando
10095
* crowbar cloudwatch scheduled lambda application - https://github.com/softprops/serverless-crowbar
96+
97+
Doug Tangren (softprops) 2018

index.js

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,31 @@
22

33
// https://serverless.com/blog/writing-serverless-plugins/
44
// https://serverless.com/framework/docs/providers/aws/guide/plugins/
5+
// https://github.com/softprops/lambda-rust/
56

67
const { spawnSync } = require('child_process');
7-
const { removeSync } = require('fs-extra');
8-
const copyFileSync = require('fs-copy-file-sync')
98
const path = require('path');
109

10+
const DEFAULT_DOCKER_TAG = '0.2.0-rust-1.31.0';
11+
const RUST_RUNTIME = 'rust';
12+
const BASE_RUNTIME = 'provided';
1113
const NO_OUTPUT_CAPTURE = { stdio: ['ignore', process.stdout, process.stderr] };
1214

1315
/** assumes docker is on the host's execution path */
14-
class ServerlessPlugin {
16+
class RustPlugin {
1517
constructor(serverless, options) {
18+
1619
this.serverless = serverless;
1720
this.options = options;
1821
this.servicePath = this.serverless.config.servicePath || '';
1922
this.hooks = {
2023
'before:package:createDeploymentArtifacts': this.build.bind(this),
21-
'after:package:createDeploymentArtifacts': this.clean.bind(this),
24+
'before:deploy:function:packageFunction': this.build.bind(this),
2225
};
2326
this.custom = Object.assign(
2427
{
2528
cargoFlags: "",
26-
dockerTag: "0.1.0-rust-1.30.1"
29+
dockerTag: DEFAULT_DOCKER_TAG
2730
},
2831
this.serverless.service.custom && this.serverless.service.custom.rust || {}
2932
);
@@ -34,12 +37,9 @@ class ServerlessPlugin {
3437
// and this plugin being installed, it will be excluded anyway.
3538
// Therefore, the filtering can be disabled to speed up (~3.2s) the process.
3639
this.serverless.service.package.excludeDevDependencies = false;
37-
38-
// represented as an object for unique keys
39-
this.artifacts = {};
4040
}
4141

42-
runDocker(funcArgs, captureOutput) {
42+
runDocker(funcArgs, cargoPackage) {
4343
const defaultArgs = [
4444
'run',
4545
'--rm',
@@ -49,9 +49,16 @@ class ServerlessPlugin {
4949
`-v`, `${process.env['HOME']}/.cargo/git:/root/.cargo/git`,
5050
];
5151
const customArgs = [];
52-
const cargoFlags = (funcArgs || {}).cargoFlags || this.custom.cargoFlags;
52+
let cargoFlags = (funcArgs || {}).cargoFlags || this.custom.cargoFlags;
53+
if (cargoPackage != undefined) {
54+
if (cargoFlags) {
55+
cargoFlags = `${cargoFlags} -p ${cargoPackage}`
56+
} else {
57+
cargoFlags = ` -p ${cargoPackage}`;
58+
}
59+
}
5360
if (cargoFlags) {
54-
// --features python3-sys, ect
61+
// --features awesome-feature, ect
5562
customArgs.push('-e', `CARGO_FLAGS=${cargoFlags}`);
5663
};
5764
const dockerTag = (funcArgs || {}).dockerTag || this.custom.dockerTag;
@@ -62,32 +69,71 @@ class ServerlessPlugin {
6269
...customArgs,
6370
`softprops/lambda-rust:${dockerTag}`
6471
],
65-
captureOutput ? {} : NO_OUTPUT_CAPTURE
72+
NO_OUTPUT_CAPTURE
6673
);
6774
}
6875

76+
functions() {
77+
if (this.options.function) {
78+
return [this.options.function];
79+
} else {
80+
return this.serverless.service.getAllFunctions();
81+
}
82+
}
83+
6984
build() {
7085
const service = this.serverless.service;
71-
service.getAllFunctions().forEach(funcName => {
86+
if (service.provider.name != 'aws') {
87+
return;
88+
}
89+
let rustFunctionsFound = false
90+
this.functions().forEach(funcName => {
7291
const func = service.getFunction(funcName);
73-
const [crate, fn] = func.handler.split('.');
92+
const runtime = func.runtime || service.provider.runtime;
93+
if (runtime != RUST_RUNTIME) {
94+
// skip functions which don't apply
95+
return;
96+
}
97+
rustFunctionsFound = true;
98+
let [cargoPackage, binary] = func.handler.split('.');
99+
if (binary == undefined) {
100+
binary = cargoPackage;
101+
}
74102
this.serverless.cli.log(`Building native Rust ${func.handler} func...`);
75-
const res = this.runDocker(func.rust);
103+
const res = this.runDocker(func.rust, cargoPackage);
76104
if (res.error || res.status > 0) {
77-
this.serverless.cli.log(`Dockerized Rust build encountered an error.`);
105+
this.serverless.cli.log(`Dockerized Rust build encountered an error: ${res.error} ${res.status}.`);
78106
throw new Error(res.error);
79107
}
80-
const executablePath = path.resolve('target/lambda/release', crate + '.so');
81-
const targetPath = path.resolve(this.servicePath, crate + '.so');
82-
copyFileSync(executablePath, targetPath);
83-
// only the keys matters
84-
this.artifacts[targetPath] = null;
85-
})
86-
}
108+
// If all went well, we should now have find a packaged compiled binary under `target/lambda/release`.
109+
//
110+
// The AWS "provided" lambda runtime requires executables to be named
111+
// "bootstrap" -- https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html
112+
//
113+
// To avoid artifact nameing conflicts when we potentially have more than one function
114+
// we leverage the ability to declare a package artifact directly
115+
// see https://serverless.com/framework/docs/providers/aws/guide/packaging/
116+
// for more information
117+
const artifactPath = path.join('target/lambda/release', binary + ".zip")
118+
func.package = func.package || {};
119+
func.package.artifact = artifactPath;
87120

88-
clean() {
89-
Object.keys(this.artifacts).forEach(removeSync);
121+
// Ensure the runtime is set to a sane value for other plugins
122+
if (func.runtime == RUST_RUNTIME) {
123+
func.runtime = BASE_RUNTIME
124+
}
125+
})
126+
if (service.provider.runtime === RUST_RUNTIME) {
127+
service.provider.runtime = BASE_RUNTIME;
128+
}
129+
if (!rustFunctionsFound) {
130+
throw new Error(
131+
`Error: no Rust functions found. ` +
132+
`Use 'runtime: ${RUST_RUNTIME}' in global or ` +
133+
`function configuration to use this plugin.`
134+
);
135+
}
90136
}
91137
}
92138

93-
module.exports = ServerlessPlugin;
139+
module.exports = RustPlugin;

0 commit comments

Comments
 (0)