Crates.io | netlify_lambda_http |
lib.rs | netlify_lambda_http |
version | 0.2.0 |
source | src |
created_at | 2020-12-04 23:57:18.171458 |
updated_at | 2021-01-07 16:26:25.971157 |
description | Application Load Balancer and API Gateway event types for AWS Lambda |
homepage | https://github.com/netlify/aws-lambda-rust-runtime |
repository | |
max_upload_size | |
id | 319771 |
size | 70,694 |
This package makes it easy to run AWS Lambda Functions written in Rust.
:warning: :warning: :warning: :construction_worker: :construction_worker: :construction_worker: :fork_and_knife: :fork_and_knife: :fork_and_knife:
This is a fork for the official AWS Lambda runtime for Rust. We've created this fork to be able to move forward all the contributions from the community that have been stalled in the awslabs repository without a release in sight.
The crates in this repository are published to crates.io with the prefix netlify-
.
Netlify Lambda contains the runtime component. Netlify Lambda Attributes contains definitions to create Lambda functions. Netlify Lambda HTTP contains definitions to create Lambda functions that are accessed via HTTP endpoints.
:warning: :warning: :warning: :construction_worker: :construction_worker: :construction_worker: :fork_and_knife: :fork_and_knife: :fork_and_knife:
The code below creates a simple function that receives an event with a firstName
field and returns a message to the caller. Notice: this crate is tested against latest stable Rust.
use netlify_lambda::{handler_fn, Context};
use serde_json::{json, Value};
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
#[tokio::main]
async fn main() -> Result<(), Error> {
let func = handler_fn(func);
netlify_lambda::run(func).await?;
Ok(())
}
async fn func(event: Value, _: Context) -> Result<Value, Error> {
let first_name = event["firstName"].as_str().unwrap_or("world");
Ok(json!({ "message": format!("Hello, {}!", first_name) }))
}
The code above is the same as the basic example in the lambda
crate.
There are currently multiple ways of building this package: manually with the AWS CLI, and with the Serverless framework.
To deploy the basic sample as a Lambda function using the AWS CLI, we first need to manually build it with cargo
. Since Lambda uses Amazon Linux, you'll need to target your executable for an x86_64-unknown-linux-musl
platform.
Run this script once to add the new target:
$ rustup target add x86_64-unknown-linux-musl
musl-cross
tap from Homebrew which provides a complete cross-compilation toolchain for Mac OS. Once musl-cross
is installed we will also need to inform cargo of the newely installed linker when building for the x86_64-unknown-linux-musl
platform. $ brew install filosottile/musl-cross/musl-cross
$ mkdir .cargo
$ echo $'[target.x86_64-unknown-linux-musl]\nlinker = "x86_64-linux-musl-gcc"' > .cargo/config
Compile one of the examples as a release with a specific target for deployment to AWS:
$ cargo build -p lambda --example hello --release --target x86_64-unknown-linux-musl
For a custom runtime, AWS Lambda looks for an executable called bootstrap
in the deployment package zip. Rename the generated basic
executable to bootstrap
and add it to a zip archive.
$ cp ./target/release/examples/hello ./bootstrap && zip lambda.zip bootstrap && rm bootstrap
Now that we have a deployment package (lambda.zip
), we can use the AWS CLI to create a new Lambda function. Make sure to replace the execution role with an existing role in your account!
$ aws lambda create-function --function-name rustTest \
--handler doesnt.matter \
--zip-file fileb://./lambda.zip \
--runtime provided \
--role arn:aws:iam::XXXXXXXXXXXXX:role/your_lambda_execution_role \
--environment Variables={RUST_BACKTRACE=1} \
--tracing-config Mode=Active
You can now test the function using the AWS CLI or the AWS Lambda console
$ aws lambda invoke --function-name rustTest \
--payload '{"firstName": "world"}' \
output.json
$ cat output.json # Prints: {"message": "Hello, world!"}
Note: --cli-binary-format raw-in-base64-out
is a required
argument when using the AWS CLI version 2. More Information
Alternatively, you can build a Rust-based Lambda function declaratively using the Serverless framework Rust plugin.
A number of getting started Serverless application templates exist to get you up and running quickly
Assuming your host machine has a relatively recent version of node, you won't need to install any host-wide serverless dependencies. To get started, run the following commands to create a new lambda Rust application and install project level dependencies.
$ npx serverless install \
--url https://github.com/softprops/serverless-aws-rust \
--name my-new-app \
&& cd my-new-app \
&& npm install --silent
Deploy it using the standard serverless workflow
# build, package, and deploy service to aws lambda
$ npx serverless deploy
Invoke it using serverless framework or a configured AWS integrated trigger source:
$ npx serverless invoke -f hello -d '{"foo":"bar"}'
Alternatively, you can build a Rust-based Lambda function in a docker mirror of the AWS Lambda provided runtime with the Rust toolchain preinstalled.
Running the following command will start a ephemeral docker container which will build your Rust application and produce a zip file containing its binary auto-renamed to bootstrap
to meet the AWS Lambda's expectations for binaries under target/lambda/release/{your-binary-name}.zip
, typically this is just the name of your crate if you are using the cargo default binary (i.e. main.rs
)
# build and package deploy-ready artifact
$ docker run --rm \
-v ${PWD}:/code \
-v ${HOME}/.cargo/registry:/root/.cargo/registry \
-v ${HOME}/.cargo/git:/root/.cargo/git \
softprops/lambda-rust
With your application build and packaged, it's ready to ship to production. You can also invoke it locally to verify is behavior using the lambdaci :provided docker container which is also a mirror of the AWS Lambda provided runtime with build dependencies omitted.
# start a docker container replicating the "provided" lambda runtime
# awaiting an event to be provided via stdin
$ unzip -o \
target/lambda/release/{your-binary-name}.zip \
-d /tmp/lambda && \
docker run \
-i -e DOCKER_LAMBDA_USE_STDIN=1 \
--rm \
-v /tmp/lambda:/var/task \
lambci/lambda:provided
# provide an event payload via stdin (typically a json blob)
# Ctrl-D to yield control back to your function
lambda
lambda
is a library for authoring reliable and performant Rust-based AWS Lambda functions. At a high level, it provides a few major components:
Handler
, a trait that defines interactions between customer-authored code and this library.netlify_lambda::run
, function that runs an Handler
.The function handler_fn
converts a rust function or closure to Handler
, which can then be run by netlify_lambda::run
.
To serialize and deserialize events and responses, we suggest using the use the serde
library. To receive custom events, annotate your structure with Serde's macros:
use serde::{Serialize, Deserialize};
use serde_json::json;
use std::error::Error;
#[derive(Serialize, Deserialize)]
pub struct NewIceCreamEvent {
pub flavors: Vec<String>,
}
#[derive(Serialize, Deserialize)]
pub struct NewIceCreamResponse {
pub flavors_added_count: usize,
}
fn main() -> Result<(), Box<Error>> {
let flavors = json!({
"flavors": [
"Nocciola",
"抹茶",
"आम"
]
});
let event: NewIceCreamEvent = serde_json::from_value(flavors)?;
let response = NewIceCreamResponse {
flavors_added_count: event.flavors.len(),
};
serde_json::to_string(&response)?;
Ok(())
}