Eine AWS Lambda mit Rust
Musl
Die für AWS Lambda verwendeten Micro-VM unterstützen kein Standard glibc sondern nur musl. Die grundlegenden Funktionen sind zwar mit glibc deckungsgleich, aber es gibt Anwendungsfälle, die schlechter unterstützt werden. Daher muss Cargo wissen, dass die Lambdas auch nach Musl kompiliert werden müssen. Um das lokale Build-Target mit Cargo, den Rust Package Manager, erstellen zu können, müssen einige Vorbereitungen getroffen werden.
Target hinzufuegen
rustup target add x86_64-unknown-linux-musl
Kompilieren
cargo build --release --target x86_64-unknown-linux-musl
Paketieren
zip -j rust.zip ./target/x86_64-unknown-linux-musl/release/bootstrap
Docker
Auf einigen Systemen kann es zu Probleme bei dem lokalen bauen von musl paketen kommen, glücklicherweise gibt es ein Docker Image das bei dieser Aufgabe unterstuetzt: https://github.com/emk/rust-musl-builder
Da beim entwickeln der Lambda, der Compiler oefter mal aufgerufen wird, hilft ein kleines Alias: alias rust-musl-builder=‘docker run –rm -it -v “$(pwd)”:/home/rust/src ekidd/rust-musl-builder’ rust-musl-builder cargo build –release
Lambdas Funktion
Die AWS-Labs stellen eine Rust Runtime für die Lambda bereit, die bereits eine handler_fn, Context und Error handling mitbringt. https://github.com/awslabs/aws-lambda-rust-runtime
Die Beispiel Lambda reagiert auf Nachrichten in AWS SNS (Simple Notification Service), die beispielsweise von einer CodePipeline im Fehlerfall erstellt werden. Dieses SNS Event kann dann an einen Slack Webhook weitergeleitet werden.
use lambda_runtime::{handler_fn, Context, Error};
use serde_json::{json, Value};
use aws_lambda_events::event::sns::{SnsEvent};
use slack_hook3::{Slack, PayloadBuilder};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Error> {
let func = handler_fn(func);
lambda_runtime::run(func).await?;
Ok(())
}
async fn call_slack(message: &str) {
let slack_channel =env::var("SLACK_CHANNEL").unwrap();
let slack_username =env::var("SLACK_USER").unwrap();
let slack_webhook_url = env::var("SLACK_WEBHOOK_URL").unwrap();
let slack = Slack::new(slack_webhook_url).unwrap();
let p = PayloadBuilder::new()
.text(message)
.channel(slack_channel)
.username(slack_username)
.build()
.unwrap();
let res = slack.send(&p);
print!("{:?}",res.await)
}
async fn func(input: Value, _: Context) -> Result<Value, Error> {
let event: SnsEvent = serde_json::from_value(input)?;
let tmp_message: &Option<String> = &event.records[0].sns.message.to_owned();
let message = tmp_message.as_deref().unwrap_or("no message");
call_slack(message).await;
Ok(json!({ "message": format!("{:?}", event.records) }))
}
Die passende Cargo Einstellungen sehen in etwa so aus:
[package]
name = "my_lambda_function"
version = "0.1.0"
authors = ["Jon Doe <your-email-id>"]
edition = "2018"
autobins = false
[dependencies]
lambda_runtime = "0.3.0"
serde = "^1"
serde_json = "^1"
serde_derive = "^1"
log = "^0.4"
simple_logger = "^1"
tokio = "1.9.0"
aws_lambda_events = "0.4.0"
slack-hook3 = "0.11"
[[bin]]
name = "bootstrap"
path = "src/main.rs"
Fazit
Nachdem klar ist, wie das Musl Target erstellt werden muss, geht das Schreiben der Lambda an sich recht fliesen von der Hand.