Skip to content

Bring README up to date with master branch #247

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 7 commits into from
Jan 29, 2021
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 19 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,32 @@

This package makes it easy to run AWS Lambda Functions written in Rust. This workspace includes multiple crates:

- [![Docs](https://docs.rs/lambda_runtime_client/badge.svg)](https://docs.rs/lambda_runtime_client) **`lambda-runtime-client`** is a client SDK for the Lambda Runtime APIs. You probably don't need to use this crate directly!
- [![Docs](https://docs.rs/lambda_runtime/badge.svg)](https://docs.rs/lambda_runtime) **`lambda-runtime`** is a library that makes it easy to write Lambda functions in Rust.
- [![Docs](https://docs.rs/lambda/badge.svg)](https://docs.rs/lambda) **`lambda`** is a library that makes it easy to write Lambda functions 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_attributes/badge.svg)](https://docs.rs/lambda_attributes) **`lambda-attributes`** holds an [attribute macro](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros) that runs an `async main` function in the Lamda runtime. You probably don't need to use this crate directly, the macro is exposed by **`lambda`**!

## Example function

The code below creates a simple function that receives an event with a `greeting` and `name` field and returns a `GreetingResponse` message for the given name and greeting. Notice: to run these examples, we require a minimum Rust version of 1.31.
The code below creates a simple function that receives an event and echoes it back as a response. Notice: to run these examples, we require a minimum Rust version of 1.31.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An MSRV of 1.31 is not correct. Right now, it's at least a 1.42_, but I'd for the time being, I'd like to make the policy "latest stable Rust" until this reaches 1.0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 1 on stable rust as a contract

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+ 1 (because github markdown)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does:

Notice: this crate is tested against latest stable Rust.

sound?


```rust,no_run
use std::error::Error;

use lambda_runtime::{error::HandlerError, lambda, Context};
use log::{self, error};
use serde_derive::{Deserialize, Serialize};
use simple_error::bail;
use simple_logger;

#[derive(Deserialize)]
struct CustomEvent {
#[serde(rename = "firstName")]
first_name: String,
}

#[derive(Serialize)]
struct CustomOutput {
message: String,
}

fn main() -> Result<(), Box<dyn Error>> {
simple_logger::init_with_level(log::Level::Debug)?;
lambda!(my_handler);

Ok(())
}
use lambda::{lambda, Context};
use serde_json::Value;

fn my_handler(e: CustomEvent, c: Context) -> Result<CustomOutput, HandlerError> {
if e.first_name == "" {
error!("Empty first name in request {}", c.aws_request_id);
bail!("Empty first name");
}
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;

Ok(CustomOutput {
message: format!("Hello, {}!", e.first_name),
})
#[lambda]
#[tokio::main]
async fn main(event: Value, _: Context) -> Result<Value, Error> {
Ok(event)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking about this, but I think we should cut the #[lambda] macro from the initial release. It's problematic when used with anything that expects to be initialized a single time (e.g., a logger) but a warm invoke occurs. In the case of a warm invoke, this will cause the runtime to panic.

Let's prefer a more full example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I did not know this about the attribute macro. My primary reason for avoiding it has been more do with developer tooling, I found rust-analyser and cargo build output regarding errors to be much less useful when using it.

That type of error (panic on warm invocation) does sound like a reason to hold it back from the release. I can update the README to reflect that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I did not know this about the attribute macro. My primary reason for avoiding it has been more do with developer tooling, I found rust-analyser and cargo build output regarding errors to be much less useful when using it.

That's another compelling reason to not release and I completely forgot about it. I think the #[lambda] macro is one of those things that looks nice on a slide/screenshot, but is something most customers will quickly outgrow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not know this about the attribute macro.

this might just be a matter of documentation. I mentioned this in the lambda http docs as a tradeoff to consider. I still think its useful for specific cases but it's also good to be mindful of the tradeoffs.

```

The code above is the same as the [basic example](https://github.com/awslabs/aws-lambda-rust-runtime/tree/master/lambda-runtime/examples/basic.rs) in the `lambda-runtime` crate.
The code above is the same as the [basic example](https://github.com/awslabs/aws-lambda-rust-runtime/tree/master/lambda-runtime/examples/hello.rs) in the `lambda` crate. An [alternative example](https://github.com/awslabs/aws-lambda-rust-runtime/tree/master/lambda-runtime/examples/hello-without-macros.rs) that uses functions instead of an attribute macro to create the handler is available as well.

### Deployment

There are currently multiple ways of building this package: manually, and with the [Serverless framework](https://serverless.com/framework/).
There are currently multiple ways of building this package: manually with the AWS CLI, and with the [Serverless framework](https://serverless.com/framework/).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's maybe include the SAM CLI with the Makefile approach and (hopefully) aws/aws-lambda-builders#174, once that is merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll see... :) I'll try to keep a some light pressure on that. Ultimately SAM is the most likely AWS-suggested approach to expect with SAM being the tool AWS pushes. Not having a SAM story is worse than not having some SAM story. I agree with the adding SAM + Makefile example being useful

s551228258327922884_p1702_i14_w300

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd much more believe in terraform ;d, could add an example with that

Copy link
Contributor

@rimutaka rimutaka Jul 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd much more believe in terraform ;d, could add an example with that

I prefer TF too, but if AWS prefers SAM then what most people will use anyway.
@Veetaha if you do make an example, can we put it in examples folder with a mention and a link to it from README to keep it concise?

Copy link

@brainstorm brainstorm Sep 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using CDK for deployment of Rust lambdas these days. Switched to SAM now

If people adopt SAM as a Rust-lambda "blueprint project", there I'll go too... My current DX fail with Rust lambdas is that I've been unable to find a lightweight and properly documented way to debug Rust lambdas locally... EDIT: this could work for the time being, I guess: https://github.com/rimutaka/lambda-debug-proxy

My ideal scenario: not having to deal with docker (i.e: lambci) and my VSCode session having all the variable values, watchpoints and backtraces I would normally have for "regular local binaries", ideally with proper observability all the way down to FFI (C bindgen) crates.

Am I dreaming or is this available today and I've missed it? :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially I would like something like this for Rust+SAM+lambdas: https://pawelgrzybek.com/attach-visual-studio-code-debugger-to-sam-serverless-application-model-local-endpoint/ ... does anybody has such a setup? @rimutaka ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brainstorm, That debug-proxy works for me just fine and I use CLI/TF for deployment.


#### AWS CLI

Expand Down Expand Up @@ -91,9 +65,9 @@ You can now test the function using the AWS CLI or the AWS Lambda console

```bash
$ aws lambda invoke --function-name rustTest \
--payload '{"firstName": "world"}' \
--payload '{"Hello,": "world!"}' \
output.json
$ cat output.json # Prints: {"message":"Hello, world!"}
$ cat output.json # Prints: {"Hello,": "world!"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I'm in favor of this chance. Can we prefer the original?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the previous functionality back to the README and the hello-without-macro example that it references. It is working on and generating an untyped json value. Let me know if you'd like to add custom input and output events back to this headline example as well. Leaving the discussion on custom events to later in the documentation allows a gentler introduction in my opinion.

```

**Note:** `--cli-binary-format raw-in-base64-out` is a required
Expand Down Expand Up @@ -167,34 +141,13 @@ $ unzip -o \
# Ctrl-D to yield control back to your function
```

## lambda-runtime-client
## `lambda`

Defines the `RuntimeClient` trait and provides its `HttpRuntimeClient` implementation. The client fetches events and returns output as `Vec<u8>`.
This library makes it easy to create Rust executables for AWS lambda. The library defines an `#[lambda]` attribute macro. Adding the `#[lambda]` attribute to your `main` function allows you to define your event handler logic in one function as shown in the example above.

For error reporting to the runtime APIs the library defines the `RuntimeApiError` trait and the `ErrorResponse` object. Custom errors for the APIs should implement the `to_response() -> ErrorResponse` method of the `RuntimeApiError` trait.
It also exposes the `Handler` trait. A type that conforms to this trait can be passed to the `lambda::run` function, which launches and runs the Lambda runtime.

## lambda-runtime

This library makes it easy to create Rust executables for AWS lambda. The library defines a `lambda!()` macro. Call the `lambda!()` macro from your main method with an implementation the `Handler` type:

```rust
pub trait Handler<E, O> {
/// Run the handler.
fn run(
&mut self,
event: E,
ctx: Context
) -> Result<O, HandlerError>;
}
```

`Handler` provides a default implementation that enables you to provide a Rust closure or function pointer to the `lambda!()` macro.

Optionally, you can pass your own instance of Tokio runtime to the `lambda!()` macro:
```
let rt = tokio::runtime::Runtime::new()?;
lambda!(my_handler, rt);
```
The helper function `handler_fn` provides a default implementation that enables you to provide a Rust closure or function pointer to the `lambda::run` function.

## AWS event objects

Expand All @@ -205,11 +158,7 @@ This project does not currently include Lambda event struct definitions though w
To serialize and deserialize events and responses, we suggest using the use the [`serde`](https://github.com/serde-rs/serde) library. To receive custom events, annotate your structure with Serde's macros:

```rust
extern crate serde;
extern crate serde_derive;
extern crate serde_json;

use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Deserialize};
use serde_json::json;
use std::error::Error;

Expand Down