This is pretty alpha level software, but it seems to work fairly reliably and I’d welcome users to try it out. You may also be interested in using it in convert with the serverless framework, by means of my serverless-lisp plugin, which you can pull from npm.
If that didn’t deter you, read on!
Hopefully the easy route, by defining your system, you can just define the code that needs to be run without caring much about the runtime implementation.
Define an asd file like this one, which marks your system as defining lambda functions. It will automatically cause your system to generate a binary named bootstrap
, which is what the aws runtime expects. It will also automatically depend on the cl-aws-lambda
system.
(defsystem example-lambda
:defsystem-depends-on ("cl-aws-lambda/asdf")
:class :lambda-system
:components ((:file "hello")))
Now, you can write regular common lisp however you’d like to, for example, this function that says hello when given a name:
(defpackage example-lambda
(:use :cl)
(:export #:hello))
(in-package :example-lambda)
(defun hello (event)
(let ((name (cdr (assoc "name" event :test #'string=))))
(if name
(format nil "Hello, ~a!" name)
(error "No name supplied!"))))
You should now be able to compile your binary by calling (asdf:make :example-lambda)
. The bootstrap binary produced is suitable for uploading to a lambda as a ‘runtime provided’ zip file. You can see an example of this in the example/
directory.
https://quay.io/repository/fisxoj/cl-lambda-builder
You should be able to build your bootstrap / function.zip using the prebuilt docker image (the Dockerfile in this repo). By pointing your code at the /work
directory and passing in your system name as LAMBDA_SYSTEM_NAME
, it will automagically build your lambdas for you.
docker run --rm -it -v $PWD:/work:Z -e LAMBDA_SYSTEM_NAME="my-cool-function" quay.io/fisxoj/cl-aws-builder:latest
podman run --rm -it -v $PWD:/work:Z -e LAMBDA_SYSTEM_NAME="my-cool-function" quay.io/fisxoj/cl-aws-builder:latest
If you want to cache build artifacts between builds, you can check out the example in example/Makefile
, which creates docker volumes for the build artifacts and quicklisp so they don’t need to be fetched every build.
As always, it’s recommended you pin the image to a version so you get consistent behavior in your builds. I don’t have a strong enough idea of what stable means for this project to have versions beyond short hashes, but those are available as tags.
You can achieve the equivalent of the asdf helper class by providing these values to your system definition:
:build-operation "program-op"
:build-pathname "bootstrap"
:entry-point "cl-aws-lambda/runtime:main"
Any function can handle lambda requests! Just define the handler as the lowercase name of the function so the runtime can find it (eg. example-lambda:hello
in the example above).
Handler functions are currently functions of one argument that receive the json passed to the lambda decoded as an alist (to avoid keyword explosion of parsing it as a plist). The context is available as cl-aws-lambda/runtime-interface:*context*
and you can investigate that package to find the class definition for that object.
A helpful hint from stassats on reddit sorted this one out:
Can you try SBCL built after https://github.com/sbcl/sbcl/commit/6a78c5d72f14e65e0dd5be48efc00375e1df966e ?
It seems like the minimum version of sbcl required is 1.4.14. Prior to that, it couldn’t handle older versions of libc, like the one in the aws lambda runtime.
There is a dockerfile in the root directory that downloads a sufficiently recent sbcl to build its lambda that you can use as an example.