-
Notifications
You must be signed in to change notification settings - Fork 1
/
runtime.cljs
114 lines (102 loc) · 5.62 KB
/
runtime.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
(ns runtime
(:require clojure.string
http))
(def runtime-path (str "http://" (.-AWS_LAMBDA_RUNTIME_API js/process.env) "/2018-06-01/runtime"))
(defn successful?
[status]
(<= 200 status 299))
(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
(do (assert (.-_HANDLER js/process.env) "The _HANDLER env vars must contain the handler location.\n\nSee https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html\n")
(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")
runtime-error #js {:errorType (.-name error)
:errorMessage (.-message error)
:stackTrace (-> (or (.-stack error) "")
(.split "\n")
(.slice 1))}]
(-> (request {:url url
:method :post
:headers {"Content-Type" "application/json"
"Lambda-Runtime-Function-Error-Type" (.-name error)}
:body (js/JSON.stringify runtime-error)})
(.then (fn [{:keys [status]
:as res}]
;; We treat all errors from the API as an unrecoverable error. This is
;; because the API returns 4xx errors for responses that are too long. In
;; that case, we simply log the output and fail.
(assert (successful? status) (str "Error from Runtime API\n"
(-> {:url url
:response res
:error runtime-error}
(clj->js)
(js/JSON.stringify nil 2))))
res)))))
(defonce state (atom nil))
(defn -main
[& args]
(let [url (str runtime-path "/invocation/next")]
(-> (request {:url url})
(.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}]
;; We treat all errors from the API as an unrecoverable error. This is
;; because the API returns 4xx errors for responses that are too long. In
;; that case, we simply log the output and fail.
(assert (successful? status) (str "Error from Runtime API\n"
(-> {:url url
:response response
:context @state}
(clj->js)
(js/JSON.stringify nil 2))))
(let [context {:aws-request-id aws-request-id
:lambda-runtime-invoked-function-arn lambda-runtime-invoked-function-arn}]
(swap! state assoc :context context)
{: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 (successful? status) (str "Error from Runtime API\n"
(-> {:context @state
:response response}
(clj->js)
(js/JSON.stringify nil 2))))))
(.catch (fn [err]
(post-error {:error err
:context (:context @state)})))
(.then -main))))