lambda_builder

Version, currently main branch1 version
  • main branchlatestJan 8, 2023

github.com/spinscale/crystal-aws-lambda

Create and deploy AWS lambdas written in Crystal

41 stars
0 dependents
License: MIT

Installation

# Add this to your shard.yml
dependencies:
  lambda_builder:
    github: spinscale/crystal-aws-lambda
    branch: main

main is a branch, not a release, so this tracks it rather than pinning a version.

Then run:

shards install

shard.yml

Crystal
1.6.2
License
MIT
Authors
Alexander Reelsen <alexander@reelsen.net>, Josephine Pfeiffer <hi@josie.lol>

Dependencies

Development Dependencies

  • ameba1.3.1github: crystal-ameba/amebadev
  • webmock0.14.0github: manastech/webmock.crdev

README

# crystal-aws-lambda

A small library to simplify running lambdas written in [crystallang](https://crystal-lang.org/). Not production ready, just an evening hack!

## Installation

You can include this as a dependency in your project in `shards.yml` file

```
dependencies:
  lambda_builder:
    github: spinscale/crystal-aws-lambda
    branch: main
```

Now run the the `shards` command to download the dependency. You can now create your own lambda handlers like this


```crystal
require "lambda_builder"

runtime = Lambda::Builder::Runtime.new

runtime.register_handler("httpevent") do |input|
  req = Lambda::Builder::HTTPRequest.new(input)
  user = req.query_params.fetch("hello", "World")
  response = Lambda::Builder::HTTPResponse.new(200, "Hello #{user} from Crystal")
  # not super efficient, serializing to JSON string and then parsing, simplify this
  JSON.parse response.to_json
end

runtime.register_handler("scheduledevent") do |input|
  runtime.logger.debug("Hello from scheduled event, input: #{input}")
  JSON.parse "{}"
end

runtime.register_handler("snsevent") do |input|
  runtime.logger.info("SNSEvent input: #{input}")
  JSON.parse "{}"
end

runtime.run
```

The `input` variable is of type `JSON::Any` and represents the JSON handed over by the lambda event.

There is a helper class to create a HTTP request from the input, no need to do that manually.

## Deployment

Make sure the [serverless framework](https://serverless.com/) is set up properly. The next step is to create a proper serverless configuration file like this

```yml
service: crystal-hello-world

provider:
  name: aws
  runtime: provided

package:
  artifact: ./bootstrap.zip

functions:
  httpevent:
    handler: httpevent
    events:
      - http:
          memorySize: 128
          path: hello
          method: get

  snsevent:
    handler: snsevent
    memorySize: 128
    events:
      - sns: my-sns-topic

  scheduledevent:
    handler: scheduledevent
    memorySize: 128
    events:
      - schedule:
          rate: rate(10 minutes)
          input:
            hello: world
```

If you are using osx, make sure you are building your app using docker, as an AWS lambda runtime environment is based on Linux. You can create a linux binary using docker like this

```
docker run --rm -it -v $PWD:/app -w /app crystallang/crystal:latest crystal build src/bootstrap.cr -o bin/bootstrap --release --static --no-debug
```

Now package the zip file required for deployment and deploy

```
zip -j bootstrap.zip bin/bootstrap
sls deploy
```

In order to monitor executions you can check the corresponding function logs like this

```
sls logs -f httpevent -t
sls logs -f snsevent -t
sls logs -f scheduledevent -t
```

you can also get some very simple metrics per functions (this might require additional permissions)

```
sls metrics -f httpevent
sls metrics -f snsevent
sls metrics -f scheduledevent
```

## Compiling with shared libraries

While often it's easy to compile with just static linking on a host system like Alpine, sometimes it's more convenient to deploy a dynamically linked binary. To do this, all the required linked dependencies have to be copied into the zip file. The binary itself needs to be modified using [patchelf](https://github.com/NixOS/patchelf) to find them too.

The required libraries can be obtained automatically using `ldd bin/bootstrap`. An example build and packaging process looks like this:
```
shards build --release bootstrap
mkdir -p build/lib

# copy the main binary
cp bin/bootstrap build

# copy the libraries
cp $(ldd bin/bootstrap | awk '/ => / {print $3}') build/lib
# copy the loader
cp /lib64/ld-linux-x86-64.so.2 build/lib
# update the library lookup paths to use ./lib rather than the system copies
patchelf --set-interpreter ./lib/ld-linux-x86-64.so.2 --set-rpath ./lib --force-rpath build/bootstrap

# zip it all up for deployment
(cd build && zip -r ../bootstrap.zip *)
```

## Example

If you want to get up and running with an example, run the following commands

```
git clone https://github.com/spinscale/crystal-aws-lambda
cd crystal-aws-lambda/example
# download dependencies
shards
# built binary (using docker under osx) and creates the zip file
make
# deploy to AWS, requires the serverless tool to be properly set up
sls deploy
```

This will start a sample runtime, that includes a HTTP endpoint, a scheduled event and an SQS listening event.


## Contributing

1. Fork it (<https://github.com/spinscale/crystal-aws-lambda/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`), also run `bin/ameba`
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
6. Don't forget to add proper tests, if possible

## Contributors

- [Alexander Reelsen](https://github.com/spinscale) - creator and maintainer