-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 8d7f510
Showing
7 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
FROM amazonlinux | ||
|
||
RUN yum install -y tar gunzip wget git java-1.8.0-openjdk-1.8.0.171 | ||
|
||
RUN bash -c "cd /usr/local/bin && curl -fsSLo boot https://github.com/boot-clj/boot-bin/releases/download/latest/boot.sh && chmod 755 boot" | ||
|
||
RUN curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | tee /etc/yum.repos.d/yarn.repo | ||
RUN curl --silent --location https://rpm.nodesource.com/setup_10.x | bash - | ||
|
||
RUN yum install -y yarn gcc-c++ make glibc-static | ||
VOLUME /root/.m2 | ||
WORKDIR /lumo | ||
ENTRYPOINT bash -c "BOOT_AS_ROOT=yes boot release" | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
This repo contains an implementation of a [custom AWS Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) | ||
that enables executions of [ClojureScript](http://clojurescript.org) code in AWS Lambda without any pre-compilation. | ||
|
||
It relies on the awesome [Lumo](https://github.com/anmonteiro/lumo) project, and | ||
was inspired by [this episode of The Repl podcast](https://www.therepl.net/episodes/14/). | ||
|
||
It's based on the [Tutorial from Amazon](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html) | ||
as well as the [Node 10.x/11.x implementations from LambCI](https://github.com/lambci/node-custom-lambda). | ||
|
||
It's still very alpha, but this document contains a step-by-step guide for getting things started. | ||
|
||
|
||
### Clone Lumo fork | ||
The fork of Lumo at https://github.com/grav/lumo is prepared for creating a | ||
static build of Lumo: | ||
|
||
``` | ||
git clone [email protected]:grav/lumo | ||
``` | ||
|
||
### Build Lumo | ||
|
||
Build Docker image: | ||
``` | ||
docker build . -t ami-lumo | ||
``` | ||
|
||
Build Lumo, pointing out the fork of Lumo: | ||
``` | ||
docker run -v --rm ami-lumo \ | ||
-v /path/to/lumo:/lumo | ||
``` | ||
|
||
You'll get an error in the end, but an executable will nevertheless be created in `build/lumo`. | ||
|
||
### Create the runtime archive | ||
``` | ||
zip -j runtime.zip bootstrap /path/to/lumo/build/lumo runtime.cljs | ||
``` | ||
|
||
The flag `-j` just ignores paths and puts everything in the archive root. | ||
|
||
### Publish layer | ||
|
||
A layer can be used by a lambda to pull in additional code. In this context, the layer contains the actual runtime: | ||
|
||
``` | ||
aws lambda publish-layer-version --layer-name lumo-runtime --zip-file fileb://runtime.zip | ||
``` | ||
|
||
You'll get an `arn` with a layer version back, which you'll need when configurating the lambda. | ||
|
||
### Create function archive | ||
``` | ||
zip -r function.zip my_package | ||
``` | ||
|
||
The `my_package` dir in this repo contains a simple handler, but you can provide your own. | ||
|
||
### Create the lambda | ||
|
||
For the `--role` parameter, you must supply a role that can execute lambdas. | ||
See https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html#runtimes-walkthrough-function | ||
|
||
The `--handler` parameter must correspond to the directory structure of the ClojureScript code that you provide: | ||
|
||
``` | ||
aws lambda create-function --function-name test-lumo --zip-file fileb://function.zip --handler my-package.my-ns/my-handler --runtime provided --role arn:aws:iam::xxx:role/lambda-role | ||
``` | ||
|
||
Use the layer `arn` that you received when publishing the layer, including the layer version, to configure the lambda: | ||
|
||
``` | ||
aws lambda update-function-configuration --function-name test-lumo --layers arn:aws:lambda:eu-west-1:xxx:layer:lumo-runtime:1 | ||
``` | ||
|
||
### Invoke the lambda | ||
``` | ||
aws lambda invoke --function-name test-lumo --payload '{"foo":42}' response.txt | ||
``` | ||
|
||
You should receive something like this in `response.txt`: | ||
|
||
``` | ||
{ | ||
"hello": "Hello from my-handler!", | ||
"input": { | ||
"event": { | ||
"foo": 42 | ||
}, | ||
"context": { | ||
"aws-request-id": "b64259ce-03e0-11e9-8db3-1bbff8d08d21", | ||
"lambda-runtime-invoked-function-arn": "arn:aws:lambda:eu-west-1:xxx:function:test-lumo" | ||
} | ||
} | ||
} | ||
``` |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#!/bin/bash | ||
set -euo pipefail | ||
|
||
APP_PARTS=(${_HANDLER//\// }) | ||
tmpfile=`mktemp` | ||
echo "(require '${APP_PARTS[0]})" | cat - /opt/runtime.cljs > $tmpfile | ||
|
||
/opt/lumo $tmpfile |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
(ns my-package.my-ns) | ||
|
||
(defn my-handler [{:keys [_event _context] | ||
:as input}] | ||
{:hello "Hello from my-handler!" | ||
:input input}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "aws-lumo-cljs-runtime", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "runloop.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+ssh://[email protected]/mikkelg/aws-lumo-cljs-runtime.git" | ||
}, | ||
"author": "Mikkel Gravgaard <[email protected]> (https://github.com/grav)", | ||
"license": "ISC", | ||
"bugs": { | ||
"url": "https://gitlab.com/mikkelg/aws-lumo-cljs-runtime/issues" | ||
}, | ||
"homepage": "https://gitlab.com/mikkelg/aws-lumo-cljs-runtime#README", | ||
"dependencies": { | ||
"lumo-cljs": "^1.9.0" | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
function sleep(s){ | ||
return new Promise(function(resolve,reject) { | ||
setTimeout(_ => { | ||
resolve(); | ||
}, 1000 * s); | ||
}); | ||
} | ||
|
||
async function start(){ | ||
while(true){ | ||
console.log("while start") | ||
await sleep(2); | ||
} | ||
|
||
} | ||
|
||
start(); | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
(require 'http) | ||
(require 'clojure.string) | ||
|
||
(def runtime-path (str "http://" (.-AWS_LAMBDA_RUNTIME_API js/process.env) "/2018-06-01/runtime")) | ||
|
||
(defn request [{:keys [url method headers body] | ||
:or {method :get}}] | ||
(js/Promise. | ||
(fn [resolve reject] | ||
(let [headers (merge headers | ||
(when body | ||
{"Content-Length" (js/Buffer.byteLength body)})) | ||
request (http/request | ||
url | ||
(clj->js {:method (clojure.string/upper-case (name method)) | ||
:headers headers}) | ||
(fn [response] | ||
(let [s (atom nil)] | ||
(.on response "data" (fn [chunk] | ||
(swap! s conj (.toString chunk "utf8")))) | ||
(.on response "end" (fn [] | ||
(resolve {:body (apply str @s) | ||
:status (.-statusCode response) | ||
:headers (js->clj (.-headers response))}))) | ||
(.on response "error" reject))))] | ||
(.on request "error" reject) | ||
(when body | ||
(.write request body)) | ||
(.end request))))) | ||
|
||
(def handle | ||
(eval (symbol (.-_HANDLER js/process.env)))) | ||
|
||
(defn post-error [{error :error | ||
{aws-request-id :aws-request-id} :context}] | ||
(let [url (str runtime-path "/invocation/" aws-request-id "/error")] | ||
(-> (request {:url url | ||
:headers {"Content-Type" "application/json" | ||
"Lambda-Runtime-Function-Error-Type" (.-name error)} | ||
:body (js/JSON.stringify #js {:errorType (.-name error) | ||
:errorMessage (.-message error) | ||
:stackTrace (-> (or (.-stack error) "") | ||
(.split "\n") | ||
(.slice 1))})}) | ||
(.then (fn [{:keys [status] | ||
:as res}] | ||
(assert (= status 200) | ||
(str "Unexpected " url "response: " (js/JSON.stringify res))) | ||
res))))) | ||
|
||
(defonce state (atom nil)) | ||
|
||
(defn start [] | ||
(-> (request {:url (str runtime-path "/invocation/next")}) | ||
(.then (fn [{:keys [status body] | ||
{aws-request-id "lambda-runtime-aws-request-id" | ||
lambda-runtime-invoked-function-arn "lambda-runtime-invoked-function-arn"} :headers | ||
:as response}] | ||
(let [context {:aws-request-id aws-request-id | ||
:lambda-runtime-invoked-function-arn lambda-runtime-invoked-function-arn}] | ||
(swap! state assoc :context context) | ||
(assert (= status 200) (str "Unexpected /invocation/next response: " (pr-str response))) | ||
{:event (-> (js/JSON.parse body) | ||
js->clj) | ||
:context context}))) | ||
(.then handle) | ||
(.then (fn [response] | ||
(let [{:keys [aws-request-id]} (:context @state)] | ||
(request {:url (str runtime-path "/invocation/" aws-request-id "/response") | ||
:method :post | ||
:headers {"Content-Type" "application/json"} | ||
:body (-> (clj->js response) | ||
js/JSON.stringify)})))) | ||
(.then (fn [{:keys [status] | ||
:as response}] | ||
(assert (= status 202) (str "Unexpected /invocation/response response:" (pr-str response))))) | ||
(.catch (fn [err] | ||
(post-error {:error err | ||
:context (:context @state)}))) | ||
(.then start))) | ||
|
||
(start) |