-
Notifications
You must be signed in to change notification settings - Fork 348
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
785 additions
and
453 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
[workspace] | ||
resolver = "2" | ||
members = [ | ||
"lambda-http", | ||
"lambda-integration-tests", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "basic-runtime-hooks" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
|
||
# Use cargo-edit(https://github.com/killercup/cargo-edit#installation) | ||
# to manage dependencies. | ||
# Running `cargo add DEPENDENCY_NAME` will | ||
# add the latest version of a dependency to the list, | ||
# and it will keep the alphabetic ordering for you. | ||
|
||
[dependencies] | ||
lambda_runtime = { path = "../../lambda-runtime" } | ||
serde = "1.0.136" | ||
tokio = { version = "1", features = ["macros"] } | ||
tracing = { version = "0.1", features = ["log"] } | ||
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } | ||
uuid = { version = "1.4.1", features = ["v4"]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# AWS Lambda Function example | ||
|
||
## Build & Deploy | ||
|
||
1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation) | ||
2. Build the function with `cargo lambda build --release` | ||
3. Deploy the function to AWS Lambda with `cargo lambda deploy --iam-role YOUR_ROLE` | ||
|
||
## Build for ARM 64 | ||
|
||
Build the function with `cargo lambda build --release --arm64` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// This example demonstrates use of shared resources such as DB connections | ||
// or local caches that can be initialized at the start of the runtime and | ||
// reused by subsequent lambda handler calls. | ||
// Run it with the following input: | ||
// { "command": "do something" } | ||
|
||
use lambda_runtime::{crac::Resource, service_fn, Error, LambdaEvent, Runtime}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::cell::RefCell; | ||
use uuid::Uuid; | ||
|
||
/// This is also a made-up example. Requests come into the runtime as unicode | ||
/// strings in json format, which can map to any structure that implements `serde::Deserialize` | ||
/// The runtime pays no attention to the contents of the request payload. | ||
#[derive(Deserialize)] | ||
struct Request { | ||
command: String, | ||
} | ||
|
||
/// This is a made-up example of what a response structure may look like. | ||
/// There is no restriction on what it can be. The runtime requires responses | ||
/// to be serialized into json. The runtime pays no attention | ||
/// to the contents of the response payload. | ||
#[derive(Serialize)] | ||
struct Response { | ||
req_id: String, | ||
msg: String, | ||
secret: String, | ||
} | ||
|
||
struct SharedClient { | ||
name: &'static str, | ||
secret: RefCell<String>, | ||
} | ||
|
||
impl SharedClient { | ||
fn new(name: &'static str, secret: String) -> Self { | ||
Self { | ||
name, | ||
secret: RefCell::new(secret), | ||
} | ||
} | ||
|
||
fn response(&self, req_id: String, command: String) -> Response { | ||
Response { | ||
req_id, | ||
msg: format!("Command {} executed by {}.", command, self.name), | ||
secret: self.secret.borrow().clone(), | ||
} | ||
} | ||
} | ||
|
||
impl Resource for SharedClient { | ||
fn before_checkpoint(&self) -> Result<(), Error> { | ||
// clear the secret before checkpointing | ||
*self.secret.borrow_mut() = String::new(); | ||
tracing::info!("in before_checkpoint: secret={:?}", self.secret.borrow()); | ||
Ok(()) | ||
} | ||
fn after_restore(&self) -> Result<(), Error> { | ||
// regenerate the secret after restoring | ||
let secret = Uuid::new_v4().to_string(); | ||
*self.secret.borrow_mut() = secret; | ||
tracing::info!("in after_restore: secret={:?}", self.secret.borrow()); | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Error> { | ||
// required to enable CloudWatch error logging by the runtime | ||
tracing_subscriber::fmt() | ||
.with_max_level(tracing::Level::INFO) | ||
// disable printing the name of the module in every log line. | ||
.with_target(false) | ||
// disabling time is handy because CloudWatch will add the ingestion time. | ||
.without_time() | ||
.init(); | ||
|
||
let secret = Uuid::new_v4().to_string(); | ||
let client = SharedClient::new("Shared Client 1 (perhaps a database)", secret); | ||
let client_ref = &client; | ||
tracing::info!("In main function: secret={:?}", client_ref.secret.borrow()); | ||
|
||
Runtime::new() | ||
.register(client_ref) | ||
.run(service_fn(move |event: LambdaEvent<Request>| async move { | ||
tracing::info!("In handler function: secret={:?}", client_ref.secret.borrow()); | ||
let command = event.payload.command; | ||
Ok::<Response, Error>(client_ref.response(event.context.request_id, command)) | ||
})) | ||
.await?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "http-runtime-hooks" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# Starting in Rust 1.62 you can use `cargo add` to add dependencies | ||
# to your project. | ||
# | ||
# If you're using an older Rust version, | ||
# download cargo-edit(https://github.com/killercup/cargo-edit#installation) | ||
# to install the `add` subcommand. | ||
# | ||
# Running `cargo add DEPENDENCY_NAME` will | ||
# add the latest version of a dependency to the list, | ||
# and it will keep the alphabetic ordering for you. | ||
|
||
[dependencies] | ||
lambda_http = { path = "../../lambda-http" } | ||
tokio = { version = "1", features = ["macros"] } | ||
tracing = { version = "0.1", features = ["log"] } | ||
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } | ||
uuid = { version = "1.4.1", features = ["v4"]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# AWS Lambda Function example | ||
|
||
## Build & Deploy | ||
|
||
1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation) | ||
2. Build the function with `cargo lambda build --release` | ||
3. Deploy the function to AWS Lambda with `cargo lambda deploy --iam-role YOUR_ROLE` | ||
|
||
## Build for ARM 64 | ||
|
||
Build the function with `cargo lambda build --release --arm64` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
use lambda_http::{crac, service_fn, Body, Error, IntoResponse, Request, RequestExt, Response, Runtime}; | ||
use std::sync::Arc; | ||
use std::sync::RwLock; | ||
use uuid::Uuid; | ||
|
||
struct SharedClient { | ||
name: &'static str, | ||
secret: Arc<RwLock<String>>, | ||
} | ||
|
||
impl SharedClient { | ||
fn new(name: &'static str, secret: String) -> Self { | ||
Self { | ||
name, | ||
secret: Arc::new(RwLock::new(secret)), | ||
} | ||
} | ||
|
||
fn response(&self, req_id: String, first_name: &str) -> String { | ||
format!("{}: Client ({}) invoked by {}.", req_id, self.name, first_name) | ||
} | ||
} | ||
|
||
impl crac::Resource for SharedClient { | ||
fn before_checkpoint(&self) -> Result<(), Error> { | ||
// clear the secret before checkpointing | ||
{ | ||
let mut write_lock = self.secret.write().unwrap(); | ||
*write_lock = String::new(); | ||
} // release the write lock | ||
|
||
{ | ||
tracing::info!("in before_checkpoint: secret={:?}", self.secret.read().unwrap()); | ||
} // release the read lock | ||
|
||
Ok(()) | ||
} | ||
fn after_restore(&self) -> Result<(), Error> { | ||
// regenerate the secret after restoring | ||
{ | ||
let mut write_lock = self.secret.write().unwrap(); | ||
*write_lock = Uuid::new_v4().to_string(); | ||
} // release the write lock | ||
|
||
{ | ||
tracing::info!("in after_restore: secret={:?}", self.secret.read().unwrap()); | ||
} // release the read lock | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Error> { | ||
// required to enable CloudWatch error logging by the runtime | ||
tracing_subscriber::fmt() | ||
.with_max_level(tracing::Level::INFO) | ||
// disable printing the name of the module in every log line. | ||
.with_target(false) | ||
// disabling time is handy because CloudWatch will add the ingestion time. | ||
.without_time() | ||
.init(); | ||
|
||
// Create the "client" and a reference to it, so that we can pass this into the handler closure below. | ||
let secret = Uuid::new_v4().to_string(); | ||
let shared_client = SharedClient::new("random_client_name_1", secret); | ||
let shared_client_ref = &shared_client; | ||
{ | ||
tracing::info!( | ||
"In main function: secret={:?}", | ||
shared_client_ref.secret.read().unwrap() | ||
); | ||
} // release the read lock | ||
|
||
// Define a closure here that makes use of the shared client. | ||
let handler_func_closure = move |event: Request| async move { | ||
{ | ||
tracing::info!( | ||
"In handler function: secret={:?}", | ||
shared_client_ref.secret.read().unwrap() | ||
); | ||
} // release the read lock | ||
|
||
Result::<Response<Body>, Error>::Ok( | ||
match event | ||
.query_string_parameters_ref() | ||
.and_then(|params| params.first("first_name")) | ||
{ | ||
Some(first_name) => { | ||
shared_client_ref | ||
.response( | ||
event | ||
.lambda_context_ref() | ||
.map(|ctx| ctx.request_id.clone()) | ||
.unwrap_or_default(), | ||
first_name, | ||
) | ||
.into_response() | ||
.await | ||
} | ||
None => Response::builder() | ||
.status(400) | ||
.body("Empty first name".into()) | ||
.expect("failed to render response"), | ||
}, | ||
) | ||
}; | ||
|
||
// Pass the closure to the runtime here. | ||
Runtime::new() | ||
.register(shared_client_ref) | ||
.run(service_fn(handler_func_closure)) | ||
.await?; | ||
Ok(()) | ||
} |
Oops, something went wrong.