Skip to content

Lambda Extensions #376

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 11, 2021
Merged
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[workspace]
members = [
"lambda-http",
"lambda-runtime"
"lambda-runtime-api-client",
"lambda-runtime",
"lambda-extension"
]
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ This package makes it easy to run AWS Lambda Functions written in Rust. This wor

- [![Docs](https://docs.rs/lambda_runtime/badge.svg)](https://docs.rs/lambda_runtime) **`lambda-runtime`** is a library that provides a Lambda runtime for applications written in Rust.
- [![Docs](https://docs.rs/lambda_http/badge.svg)](https://docs.rs/lambda_http) **`lambda-http`** is a library that makes it easy to write API Gateway proxy event focused Lambda functions in Rust.
- [![Docs](https://docs.rs/lambda_extension/badge.svg)](https://docs.rs/lambda_extension) **`lambda-extension`** is a library that makes it easy to write Lambda Runtime Extensions in Rust.
- [![Docs](https://docs.rs/lambda_runtime_api_client/badge.svg)](https://docs.rs/lambda_runtime_api_client) **`lambda-runtime-api-client`** is a shared library between the lambda runtime and lambda extension libraries that includes a common API client to talk with the AWS Lambda Runtime API.


## Example function

Expand Down
31 changes: 31 additions & 0 deletions lambda-extension/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "lambda_extension"
version = "0.1.0"
edition = "2021"
authors = ["David Calavera <[email protected]>"]
description = "AWS Lambda Extension API"
license = "Apache-2.0"
repository = "https://github.com/awslabs/aws-lambda-rust-runtime"
categories = ["web-programming::http-server"]
keywords = ["AWS", "Lambda", "API"]
readme = "README.md"

[dependencies]
tokio = { version = "1.0", features = ["macros", "io-util", "sync", "rt-multi-thread"] }
hyper = { version = "0.14", features = ["http1", "client", "server", "stream", "runtime"] }
serde = { version = "1", features = ["derive"] }
serde_json = "^1"
bytes = "1.0"
http = "0.2"
async-stream = "0.3"
tracing = { version = "0.1", features = ["log"] }
tower-service = "0.3"
tokio-stream = "0.1.2"
lambda_runtime_api_client = { version = "0.4", path = "../lambda-runtime-api-client" }

[dev-dependencies]
tracing-subscriber = "0.3"
once_cell = "1.4.0"
simple_logger = "1.6.0"
log = "^0.4"
simple-error = "0.2"
58 changes: 58 additions & 0 deletions lambda-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Runtime Extensions for AWS Lambda in Rust

[![Docs](https://docs.rs/lambda_extension/badge.svg)](https://docs.rs/lambda_extension)

**`lambda-extension`** is a library that makes it easy to write [AWS Lambda Runtime Extensions](https://docs.aws.amazon.com/lambda/latest/dg/using-extensions.html) in Rust.

## Example extension

The code below creates a simple extension that's registered to every `INVOKE` and `SHUTDOWN` events, and logs them in CloudWatch.

```rust,no_run
use lambda_extension::{extension_fn, Error, NextEvent};
use log::LevelFilter;
use simple_logger::SimpleLogger;
use tracing::info;

async fn log_extension(event: NextEvent) -> Result<(), Error> {
match event {
NextEvent::Shutdown(event) => {
info!("{}", event);
}
NextEvent::Invoke(event) => {
info!("{}", event);
}
}
Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();

let func = extension_fn(log_extension);
lambda_extension::run(func).await
}
```

## Deployment

Lambda extensions can be added to your functions either using [Lambda layers](https://docs.aws.amazon.com/lambda/latest/dg/using-extensions.html#using-extensions-config), or adding them to [containers images](https://docs.aws.amazon.com/lambda/latest/dg/using-extensions.html#invocation-extensions-images).

Regardless of how you deploy them, the extensions MUST be compiled against the same architecture that your lambda functions runs on.

### Building extensions

Once you've decided which target you'll use, you can install it by running the next `rustup` command:

```bash
$ rustup target add x86_64-unknown-linux-musl
```

Then, you can compile the extension against that target:

```bash
$ cargo build -p lambda_extension --example basic --release --target x86_64-unknown-linux-musl
```

This previous command will generate a binary file in `target/x86_64-unknown-linux-musl/release/examples` called `basic`. When the extension is registered with the [Runtime Extensions API](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html#runtimes-extensions-api-reg), that's the name that the extension will be registered with. If you want to register the extension with a different name, you only have to rename this binary file and deploy it with the new name.
25 changes: 25 additions & 0 deletions lambda-extension/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use lambda_extension::{extension_fn, Error, NextEvent};
use log::LevelFilter;
use simple_logger::SimpleLogger;

async fn my_extension(event: NextEvent) -> Result<(), Error> {
match event {
NextEvent::Shutdown(_e) => {
// do something with the shutdown event
}
NextEvent::Invoke(_e) => {
// do something with the invoke event
}
}
Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
// can be replaced with any other method of initializing `log`
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();

let func = extension_fn(my_extension);
lambda_extension::run(func).await
}
30 changes: 30 additions & 0 deletions lambda-extension/examples/custom_events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use lambda_extension::{extension_fn, Error, NextEvent, Runtime};
use log::LevelFilter;
use simple_logger::SimpleLogger;

async fn my_extension(event: NextEvent) -> Result<(), Error> {
match event {
NextEvent::Shutdown(_e) => {
// do something with the shutdown event
}
_ => {
// ignore any other event
// because we've registered the extension
// only to receive SHUTDOWN events
}
}
Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
// can be replaced with any other method of initializing `log`
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();

let func = extension_fn(my_extension);

let runtime = Runtime::builder().with_events(&["SHUTDOWN"]).register().await?;

runtime.run(func).await
}
36 changes: 36 additions & 0 deletions lambda-extension/examples/custom_trait_implementation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use lambda_extension::{run, Error, Extension, InvokeEvent, NextEvent};
use log::LevelFilter;
use simple_logger::SimpleLogger;
use std::{
future::{ready, Future},
pin::Pin,
};

#[derive(Default)]
struct MyExtension {
data: Vec<InvokeEvent>,
}

impl Extension for MyExtension {
type Fut = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
fn call(&mut self, event: NextEvent) -> Self::Fut {
match event {
NextEvent::Shutdown(_e) => {
self.data.clear();
}
NextEvent::Invoke(e) => {
self.data.push(e);
}
}
Box::pin(ready(Ok(())))
}
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// required to enable CloudWatch error logging by the runtime
// can be replaced with any other method of initializing `log`
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();

run(MyExtension::default()).await
}
Loading