Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ $ unzip -o \
# Ctrl-D to yield control back to your function
```

### Debugging

Lambdas can be run and debugged locally using a special [Lambda debug proxy](https://github.com/rimutaka/lambda-debug-proxy) (a non-AWS repo maintained by @rimutaka), which is a Lambda function that forwards incoming requests to one AWS SQS queue and reads responses from another queue. A local proxy running on your development computer reads the queue, calls your lambda locally and sends back the response. This approach allows debugging of Lambda functions locally while being part of your AWS workflow. The lambda handler code does not need to be modified between the local and AWS versions.

## `lambda`

`lambda_runtime` is a library for authoring reliable and performant Rust-based AWS Lambda functions. At a high level, it provides a few major components:
Expand Down
25 changes: 23 additions & 2 deletions lambda-runtime/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ mod endpoint_tests {
use hyper::{server::conn::Http, service::service_fn, Body};
use serde_json::json;
use simulated::DuplexStreamWrapper;
use std::convert::TryFrom;
use std::{convert::TryFrom, env};
use tokio::{
io::{self, AsyncRead, AsyncWrite},
select,
Expand Down Expand Up @@ -274,9 +274,30 @@ mod endpoint_tests {
}
let f = crate::handler_fn(func);

// set env vars needed to init Config if they are not already set in the environment
if env::var("AWS_LAMBDA_RUNTIME_API").is_err() {
env::set_var("AWS_LAMBDA_RUNTIME_API", "http://localhost:9001");
}
if env::var("AWS_LAMBDA_FUNCTION_NAME").is_err() {
env::set_var("AWS_LAMBDA_FUNCTION_NAME", "test_fn");
}
if env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE").is_err() {
env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128");
}
if env::var("AWS_LAMBDA_FUNCTION_VERSION").is_err() {
env::set_var("AWS_LAMBDA_FUNCTION_VERSION", "1");
}
if env::var("AWS_LAMBDA_LOG_STREAM_NAME").is_err() {
env::set_var("AWS_LAMBDA_LOG_STREAM_NAME", "test_stream");
}
if env::var("AWS_LAMBDA_LOG_GROUP_NAME").is_err() {
env::set_var("AWS_LAMBDA_LOG_GROUP_NAME", "test_log");
}
let config = crate::Config::from_env().expect("Failed to read env vars");

let client = &runtime.client;
let incoming = incoming(client).take(1);
runtime.run(incoming, f).await?;
runtime.run(incoming, f, &config).await?;

// shutdown server
tx.send(()).expect("Receiver has been dropped");
Expand Down
25 changes: 15 additions & 10 deletions lambda-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use types::Diagnostic;
pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;

/// Configuration derived from environment variables.
#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct Config {
/// The host and port of the [runtime API](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html).
pub endpoint: String,
Expand All @@ -54,12 +54,15 @@ impl Config {
/// Attempts to read configuration from environment variables.
pub fn from_env() -> Result<Self, Error> {
let conf = Config {
endpoint: env::var("AWS_LAMBDA_RUNTIME_API")?,
function_name: env::var("AWS_LAMBDA_FUNCTION_NAME")?,
memory: env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")?.parse::<i32>()?,
version: env::var("AWS_LAMBDA_FUNCTION_VERSION")?,
log_stream: env::var("AWS_LAMBDA_LOG_STREAM_NAME")?,
log_group: env::var("AWS_LAMBDA_LOG_GROUP_NAME")?,
endpoint: env::var("AWS_LAMBDA_RUNTIME_API").expect("Missing AWS_LAMBDA_RUNTIME_API env var"),
function_name: env::var("AWS_LAMBDA_FUNCTION_NAME").expect("Missing AWS_LAMBDA_FUNCTION_NAME env var"),
memory: env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")
.expect("Missing AWS_LAMBDA_FUNCTION_MEMORY_SIZE env var")
.parse::<i32>()
.expect("AWS_LAMBDA_FUNCTION_MEMORY_SIZE env var is not <i32>"),
version: env::var("AWS_LAMBDA_FUNCTION_VERSION").expect("Missing AWS_LAMBDA_FUNCTION_VERSION env var"),
log_stream: env::var("AWS_LAMBDA_LOG_STREAM_NAME").expect("Missing AWS_LAMBDA_LOG_STREAM_NAME env var"),
log_group: env::var("AWS_LAMBDA_LOG_GROUP_NAME").expect("Missing AWS_LAMBDA_LOG_GROUP_NAME env var"),
};
Ok(conf)
}
Expand Down Expand Up @@ -133,6 +136,7 @@ where
&self,
incoming: impl Stream<Item = Result<http::Response<hyper::Body>, Error>> + Send,
handler: F,
config: &Config,
) -> Result<(), Error>
where
F: Handler<A, B> + Send + Sync + 'static,
Expand All @@ -150,6 +154,7 @@ where
let (parts, body) = event.into_parts();

let ctx: Context = Context::try_from(parts.headers)?;
let ctx: Context = ctx.with_config(config);
let body = hyper::body::to_bytes(body).await?;
trace!("{}", std::str::from_utf8(&body)?); // this may be very verbose
let body = serde_json::from_slice(&body)?;
Expand Down Expand Up @@ -299,16 +304,16 @@ where
{
trace!("Loading config from env");
let config = Config::from_env()?;
let uri = config.endpoint.try_into().expect("Unable to convert to URL");
let uri = config.endpoint.clone().try_into().expect("Unable to convert to URL");
let runtime = Runtime::builder()
.with_connector(HttpConnector::new())
.with_endpoint(uri)
.build()
.expect("Unable create runtime");
.expect("Unable to create a runtime");

let client = &runtime.client;
let incoming = incoming(client);
runtime.run(incoming, handler).await
runtime.run(incoming, handler, &config).await
}

fn type_name_of_val<T>(_: T) -> &'static str {
Expand Down
12 changes: 11 additions & 1 deletion lambda-runtime/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub struct CognitoIdentity {
/// are populated using the [Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html)
/// and the headers returned by the poll request to the Runtime APIs.
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Default)]
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct Context {
/// The AWS request ID generated by the Lambda service.
pub request_id: String,
Expand Down Expand Up @@ -141,3 +141,13 @@ impl TryFrom<HeaderMap> for Context {
Ok(ctx)
}
}

impl Context {
/// Add environment details to the context by setting `env_config`.
pub fn with_config(self, config: &Config) -> Self {
Self {
env_config: config.clone(),
..self
}
}
}