I'm trying to write a lambda function for AWS in Rust using cargo-lambda.
The example function generated by cargo lambda new
builds fine when I call cargo lambda build --release --arm64
.
However, when I try to build code I write by following the instructions and examples on https://github.com/awslabs/aws-lambda-rust-runtime & https://www.cargo-lambda.info/guide/getting-started.html, the build fails to find the headers for OpenSSL.
I already installed OpenSSL with pacman -S openssl
and ran pacman -S linux-headers
for good measure, and tried running export OPENSSL_LIB_DIR=/usr/lib/
and export OPENSSL_INCLUDE_DIR=/usr/include/openssl/
as well as setting them in my shell config file, but neither has worked.
I can't get past:
cargo:warning=build/expando.c:1:10: fatal error: 'openssl/opensslv.h' file not found
cargo:warning=#include <openssl/opensslv.h>
I have visually verified the file exists in the directory but for some reason openssl-sys-0.9.87
fails to find it and panics. Has anyone else encountered this, or does anyone have any ideas I can try? I'm not sure if the problem is with my cargo-lambda setup, my OpenSSL setup, my code, or something else entirely.
This code was generated with cargo lambda new project_name
and builds fine:
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
/// Main function generated by cargo-lambda
#[tokio::main]
async fn main() -> Result<(), Error> {
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();
lambda_runtime::run(service_fn(function_handler)).await
}
/// This is a made-up example.
#[derive(Deserialize)]
struct Request {
command: String,
}
/// This is a made-up example of what a response structure may look like.
#[derive(Serialize)]
struct Response {
req_id: String,
msg: String,
}
/// This is the main body for the function.
/// Write your code inside it.
async fn function_handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
// Extract some useful info from the request
let command = event.payload.command;
// Prepare the response
let resp = Response {
req_id: event.context.request_id,
msg: format!("Command {}.", command),
};
// Return `Response` (it will be serialized to JSON automatically by the runtime)
Ok(resp)
}
This is the code I'm trying to build but getting an error from openssl-sys-0.9.87
even though it was written in the same project and follows the examples from the online documentation:
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
#[tokio::main]
async fn main() -> Result<(), lambda_runtime::Error> {
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 query = service_fn(query_handler);
run(query).await
}
async fn query_handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
let (event, _context) = event.into_parts();
let symbol = event["symbol"].to_string();
let message = query_price(symbol)?;
Ok(json!({ "message": format!("{}", message) }))
}
#[derive(Deserialize, Debug)]
#[allow(non_snake_case)]
struct PriceQuote {
pub s: String,
pub symbol: Vec<String>,
pub ask: Vec<f32>,
pub askSize: Vec<u32>,
pub bid: Vec<f32>,
pub bidSize: Vec<u32>,
pub mid: Vec<f32>,
pub last: Vec<f32>,
pub volume: Vec<u32>,
pub updated: Vec<u32>,
}
fn query_price(symbol: String) -> Result<String, reqwest::Error> {
//let symbol = "AAPL";
let url = format!("https://api.marketdata.app/v1/stocks/quotes/{}", symbol);
let client = Client::new();
let response = client.get(url).send()?;
let price_quote: PriceQuote = response.json()?;
let symbol: &String = &price_quote.symbol[0];
let last_price: &f32 = &price_quote.last[0];
Ok(format!("Last price for {} is {}", symbol, last_price).to_string())
}
Why would one build but not the other? Are the dependencies I added messing with it? Do I have the wrong OpenSSL version? Did I make a mistake? What am I missing? Sorry if these are dumb questions, I'm new to AWS and my head is spinning.
Unsure of exactly what went wrong, but it seems the reqwest crate was the root of the trouble. I've never had this problem except when building via cargo-lambda so I imagine it has something to do with Zig?
Either way, it can be solved by adding openssl directly to Cargo.toml:
openssl = { version = "0.10.35", features = ["vendored"] }
This seems to provide the correct linkage for reqwest and allows cargo lambda to build. Haven't tested if reqwest still works but I see no reason it shouldn't.
Update: The function deploys and reqwest works fine