Created
September 14, 2021 05:09
-
-
Save cqsd/95737123b8b326d64aa0e9d1485d7f9f to your computer and use it in GitHub Desktop.
An AWS Lambda runtime in bash
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
Invoke with SQS. | |
{ command: "ls $1", "argv": "/tmp" } |
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
#!/bin/bash | |
# TODO: add a trap in the process_payload function to fail outright and prevent | |
# unnecessary retries | |
set -eu | |
BUCKET=${XARGS_LAMBDA_BUCKET_NAME:-""} | |
PREFIX=${XARGS_LAMBDA_BUCKET_PREFIX:-"b"} | |
if [ -z "$BUCKET" ]; then | |
echo 'missing required configuration XARGS_LAMBDA_BUCKET_NAME in env' | |
exit 2 | |
fi | |
# Parse the payload out of an SQS event. | |
# Looks like { command: string, output_queue: string, argv: string } | |
get_event_payloads() { | |
echo $1 | jq '.Records[].body | fromjson' | |
} | |
# Run the script and send the output to the runtime API | |
process_payload() { | |
REQUEST_ID=$1 | |
DATA_FILE=$2 | |
CMD_FILE=$(mktemp) | |
jq -cr .command < "$DATA_FILE" > "$CMD_FILE" | |
QUEUE=$(jq -cr .output_queue < "$DATA_FILE") | |
ARGV=$(jq -cr .argv < "$DATA_FILE") | |
echo "running $(<$CMD_FILE) $ARGV" | |
# run the command and collect the output | |
STDOUT=$(mktemp) | |
STDERR=$(mktemp) | |
# tee to files while keeping stdin and stderr as separate streams | |
# bash $CMD_FILE $ARGV > >(tee -a $STDOUT) 2> >(tee -a $STDERR >&2) | |
# never mind, /dev/fd doesn't exist in lambda, and you're not root so no ln hax | |
# whatever it doesn't really matter | |
bash $CMD_FILE $ARGV 2>"$STDERR" >"$STDOUT" | |
cat $STDOUT $STDERR | |
DATA="{\"stdout\": \"$(base64 -w 0 < $STDOUT)\", \"stderr\": \"$(base64 -w 0 < $STDERR)\"}" | |
# send the response to the runtime api | |
curl -s -X POST \ | |
"http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" \ | |
-d "$DATA" | |
} | |
# Report an error to the runtime API | |
process_invalid_payload() { | |
REQUEST_ID=$1 | |
DATA_FILE=$2 | |
echo Unknown event data format $(cat "$DATA_FILE") | |
DATA='{ "errorMessage": "Invalid event data", "errorType": "InvalidEventDataException" }' | |
curl -s -X POST \ | |
"http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/error" \ | |
-d "$DATA" >/dev/null | |
} | |
# Get events from the runtime API and process them in a loop | |
start_runtime() { | |
while true; do | |
# get an event from the runtime API | |
HEADERS="$(mktemp)" | |
DATA_FILE="$(mktemp)" | |
curl -sSL \ | |
"http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next" \ | |
-D "$HEADERS" \ | |
-o "$DATA_FILE" | |
# get request ID from headers | |
REQUEST_ID=$(grep -i Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) | |
if [ ! "$(jq -cr .Records < $DATA_FILE)" = "null" ]; then | |
# asynchronous invoke from SQS | |
for payload in $(get_event_payloads "$EVENT_DATA"); do | |
process_payload "$REQUEST_ID" "$payload" | |
done | |
elif [ ! "$(jq -cr .command < $DATA_FILE)" = "null" ]; then | |
# synchronous invoke | |
process_payload "$REQUEST_ID" "$DATA_FILE" | |
else | |
process_invalid_payload "$REQUEST_ID" "$DATA_FILE" | |
fi | |
done | |
} | |
start_runtime |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment