lamby 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gemfile.lock +15 -1
- data/README.md +347 -16
- data/bin/setup +1 -2
- data/bin/test +5 -0
- data/doc/template.yml +85 -0
- data/lamby.gemspec +7 -5
- data/lib/lamby.rb +16 -3
- data/lib/lamby/debug.rb +46 -0
- data/lib/lamby/handler.rb +58 -0
- data/lib/lamby/logger.rb +23 -0
- data/lib/lamby/rack.rb +88 -0
- data/lib/lamby/sam_helpers.rb +9 -0
- data/lib/lamby/version.rb +1 -1
- metadata +37 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2da2381e04438228fa63901ffd863bc2d1f97bf84e834cb57d40fc8d2353fc4e
|
4
|
+
data.tar.gz: 8bd9e4a546114e937fc3da4efa0fddd1e5d0800e5b757145662ac0d7e3c603e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff7c934023c0237523b96cf5ebb374d3866c4eecadc3790615fc783e8f93db32ebf99b7c3c96b309debab0eaff54f8e5e1680d8e58bdbc67c3a96d954fe9fdb5
|
7
|
+
data.tar.gz: c9ea5922cd3e329b0b4665d0a7a45c2d0f8155a7cb25928ba35ea719d81511465263b94ee195b8b378761460c2fdba84d03fc42444f2ed92a573b04b8387a81f
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,27 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lamby (0.
|
4
|
+
lamby (0.2.0)
|
5
|
+
activesupport
|
6
|
+
rack
|
5
7
|
|
6
8
|
GEM
|
7
9
|
remote: https://rubygems.org/
|
8
10
|
specs:
|
11
|
+
activesupport (5.2.2)
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
|
+
i18n (>= 0.7, < 2)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
tzinfo (~> 1.1)
|
16
|
+
concurrent-ruby (1.1.4)
|
17
|
+
i18n (1.6.0)
|
18
|
+
concurrent-ruby (~> 1.0)
|
9
19
|
minitest (5.11.3)
|
20
|
+
rack (2.0.6)
|
10
21
|
rake (12.3.2)
|
22
|
+
thread_safe (0.3.6)
|
23
|
+
tzinfo (1.2.5)
|
24
|
+
thread_safe (~> 0.1)
|
11
25
|
|
12
26
|
PLATFORMS
|
13
27
|
ruby
|
data/README.md
CHANGED
@@ -1,42 +1,373 @@
|
|
1
|
+
|
1
2
|
# Lamby
|
2
3
|
|
3
|
-
|
4
|
+
<img src="https://git.io/fhjSv" alt="Lamby: Simple Rails & AWS Lambda Integration using Rack." align="right" /><h3>Simple Rails & AWS Lambda Integration using Rack</h3>
|
5
|
+
|
6
|
+
The goal of this project is to provide minimal code along with comprehensive documentation to get your Rails application running under AWS Lambda.
|
7
|
+
|
8
|
+
This gem's code will focus mainly on converting API Gateway `event` and `context` objects into a Rack `env` to send to your Rails application. Most everything else is documentation. Please use the table of contents below to quickly navigate to any guides that interests you the most.
|
9
|
+
|
10
|
+
### Table of Contents
|
11
|
+
|
12
|
+
* [How Does Lamby Work?](#how-does-lamby-work)
|
13
|
+
* [Getting Started](#getting-started)
|
14
|
+
* [Installing AWS CLI and AWS SAM](#installing-aws-cli-and-aws-sam)
|
15
|
+
* [Bin Script Conventions](#bin-script-conventions)
|
16
|
+
* [SAM Bugs and Patches](#sam-bugs-and-patches)
|
17
|
+
* [About AWS SAM and CloudFormation](#about-aws-sam-and-cloudformation)
|
18
|
+
* [Performance](#performance)
|
19
|
+
* [Database Connections](#database-connections)
|
20
|
+
* [Basic Ruby and Lambda](#basic-ruby-and-lambda)
|
21
|
+
* [Contributing](#contributing)
|
22
|
+
* [Code of Conduct](#code-of-conduct)
|
23
|
+
|
24
|
+
The following are code and/or documentation items we are currently working on. Please open an issue and suggest a new one if you do not see it here. Thanks!
|
25
|
+
|
26
|
+
* [ ] Ensure Rack integration is solid and high quality.
|
27
|
+
- Cookies, Sessions, Query Params, Forms, etc.
|
28
|
+
* [ ] Encrypted Session Secret via AWS System Manager Parameter Store.
|
29
|
+
* [ ] Better Gemfile usage for development and/or test groups.
|
30
|
+
* [ ] Hooking up Rails asset precompile to a S3 bucket and using an asset host.
|
31
|
+
* [ ] Ensure Rails tagged UUID logging matches APIGateway/Lambda IDs in CloudWatch.
|
32
|
+
* [ ] Documentation around using ImageMagick and Lambda Layers.
|
33
|
+
- https://twitter.com/clare_liguori/status/1087861037712400385
|
34
|
+
- https://github.com/mthenw/awesome-layers
|
35
|
+
* [ ] Explore how New Relic could be used if at all.
|
36
|
+
* [ ] Add `isBase64Encoded` to response as needed.
|
37
|
+
* [ ] Better Railtie hooks, and gem structure. Tests.
|
38
|
+
|
39
|
+
|
40
|
+
## How Does Lamby Work?
|
41
|
+
|
42
|
+
Since [Rails is on Rack](https://guides.rubyonrails.org/rails_on_rack.html), the Lamby gem relies on converting API Gateway events sent to your `handler` and converting them to a [Rack](https://rack.github.io) `env` object. We then send that object to your application and pass the result back to the Lambda handler which expects a simple object/hash containing the `statusCode`, `body`, and `headers`. It is that simple.
|
43
|
+
|
44
|
+
Thanks to the projects and people below which inspired our code and implementation strategies.
|
45
|
+
|
46
|
+
* [AWS Sinatra Example](https://github.com/aws-samples/serverless-sinatra-sample)
|
47
|
+
* [Rack Lambda Handler Pull Request](https://github.com/rack/rack/pull/1337)
|
48
|
+
* [Serverless Rack Plugin](https://github.com/logandk/serverless-rack)
|
49
|
+
* [Jets' Rack Implementation](https://github.com/tongueroo/jets/blob/master/lib/jets/controller/rack/env.rb)
|
50
|
+
|
51
|
+
Other small details which Lamby helps with.
|
52
|
+
|
53
|
+
* Ensure all `Logger` objects use `STDOUT`.
|
54
|
+
* Sets the `RAILS_LOG_TO_STDOUT` environment variable.
|
55
|
+
* Provides a debug response in development (or when `LAMBY_DEBUG` env set) using `?debug=1` query param.
|
56
|
+
|
4
57
|
|
5
|
-
|
58
|
+
## Getting Started
|
6
59
|
|
7
|
-
|
60
|
+
ℹ️ Some of these steps may be automated once we learn they are worth automating.
|
8
61
|
|
9
|
-
|
62
|
+
#### New Basic Rails Application Example
|
63
|
+
|
64
|
+
Assuming you have a basic Rails application, or maybe you want to make a new one from scratch.
|
65
|
+
|
66
|
+
```shell
|
67
|
+
$ gem install rails
|
68
|
+
$ rails new my_app \
|
69
|
+
--skip-active-record \
|
70
|
+
--skip-action-mailer \
|
71
|
+
--skip-active-storage \
|
72
|
+
--skip-action-cable \
|
73
|
+
--skip-spring \
|
74
|
+
--skip-coffee \
|
75
|
+
--skip-turbolinks \
|
76
|
+
--skip-bootsnap
|
77
|
+
$ cd my_app
|
78
|
+
$ rbenv local 2.5.3
|
79
|
+
```
|
80
|
+
|
81
|
+
#### Add The Gem
|
82
|
+
|
83
|
+
Add the Lamby gem to your Rails project's `Gemfile`.
|
10
84
|
|
11
85
|
```ruby
|
12
86
|
gem 'lamby'
|
13
87
|
```
|
14
88
|
|
15
|
-
|
89
|
+
#### Create Handler
|
90
|
+
|
91
|
+
Create an `app.rb` file that will be the source for the Lambda's handler. Example:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
ENV['SECRET_KEY_BASE'] = '...' # Temporary hack, see TODO list above.
|
95
|
+
require_relative 'config/boot'
|
96
|
+
require 'lamby'
|
97
|
+
require_relative 'config/application'
|
98
|
+
require_relative 'config/environment'
|
99
|
+
|
100
|
+
$app = Rack::Builder.new { run Rails.application }.to_app
|
101
|
+
|
102
|
+
def handler(event:, context:)
|
103
|
+
Lamby.handler $app, event, context
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
#### Create SAM Template
|
108
|
+
|
109
|
+
Create an [AWS SAM](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md) `template.yaml` file that expresses your function's resources and how it receives events from API Gateway. Please use this full [doc/template.yaml](doc/template.yaml) file hosted here as a starting point. The basic template accomplishes the following.
|
16
110
|
|
17
|
-
|
111
|
+
* Defines a `RailsEnv` input parameter.
|
112
|
+
- Applies this to the `RAILS_ENV` environment variable.
|
113
|
+
* Creates a API Gateway resource named `RailsApi`.
|
114
|
+
- Ensures that the stage name is set to the Rails env.
|
115
|
+
- Using inline Swagger, define a root `/` via GET and greedy proxy `/{resource+}` via ANY.
|
116
|
+
- Allow any binary media type to be a valid response.
|
117
|
+
* Creates a Lambda function resource named `RailsFunction`.
|
118
|
+
- Sets the handler to `app.handler`. File above.
|
119
|
+
- Defines events from API above for both root and greedy proxy.
|
120
|
+
* Simple `Outputs` that echos the resources you created.
|
18
121
|
|
19
|
-
|
122
|
+
#### Git Ignores
|
20
123
|
|
21
|
-
|
124
|
+
Ensure both `/.aws-sam` and `/vendor/bundle` are added to `.gitignore`. AWS SAM uses the `.aws-sam` directory to build your project. No file there should be committed to source control.
|
22
125
|
|
23
|
-
|
126
|
+
```shell
|
127
|
+
$ echo '/.aws-sam' >> .gitignore
|
128
|
+
$ echo '/vendor/bundle' >> .gitignore
|
129
|
+
```
|
24
130
|
|
25
|
-
|
131
|
+
#### Get Running
|
26
132
|
|
27
|
-
|
133
|
+
To run your Lambda locally or deploy it, please read the following sections:
|
134
|
+
|
135
|
+
* [Installing AWS CLI and AWS SAM](#installing-aws-cli-and-aws-sam)
|
136
|
+
* [Bin Script Conventions](#bin-script-conventions)
|
137
|
+
|
138
|
+
|
139
|
+
## Installing AWS CLI and AWS SAM
|
140
|
+
|
141
|
+
You will need both of these tools to run SAM locally and/or deploy your Lambda application to AWS. SAM also requires the usage of Docker.
|
142
|
+
|
143
|
+
* [Install SAM CLI](https://aws.amazon.com/serverless/sam/)
|
144
|
+
* [Installing the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)
|
145
|
+
* [Install Docker](https://docs.docker.com/install/)
|
146
|
+
|
147
|
+
|
148
|
+
## Bin Script Conventions
|
149
|
+
|
150
|
+
Below is a list of bin scripts we recommend creating for your AWS SAM & Rails projects. Each section contains a short description on what the script does. Remember, think of these scripts as starting points! You should add to or adjust each to meet your needs while following [Strap](https://github.com/MikeMcQuaid/strap) and [Scripts to Rule Them All](https://githubengineering.com/scripts-to-rule-them-all/) conventions.
|
151
|
+
|
152
|
+
#### bin/bootstrap
|
153
|
+
|
154
|
+
The current Docker image for Lambda's Ruby runtime is only compatible with Bundler prior to version 2.0.0. So this bootstrap makes sure the latest 1.x bundler is installed and used.
|
155
|
+
|
156
|
+
```shell
|
157
|
+
#!/bin/bash
|
158
|
+
set -e
|
159
|
+
|
160
|
+
gem uninstall -aIx bundler
|
161
|
+
gem install bundler -v 1.17.3
|
162
|
+
```
|
163
|
+
|
164
|
+
#### bin/setup
|
165
|
+
|
166
|
+
Nothing fancy here, just calling build where most of the work is done.
|
167
|
+
|
168
|
+
```shell
|
169
|
+
#!/bin/bash
|
170
|
+
set -e
|
171
|
+
|
172
|
+
./bin/build
|
173
|
+
```
|
174
|
+
|
175
|
+
#### bin/build
|
176
|
+
|
177
|
+
This makes use of the [sam build](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-build.html) command along with the `--use-container` option to ensure gems with native extensions are built for both the AWS' Linux platform and your native host platform. It also:
|
178
|
+
|
179
|
+
* Sets up a pristine build environment.
|
180
|
+
* Removes unnecessary project files copied over during the build.
|
181
|
+
* Cleans build directory's vendor gems of unwanted cache files. Saves ~40% [package size](https://docs.aws.amazon.com/lambda/latest/dg/limits.html).
|
182
|
+
* Builds local platform gems on top of copied Linux platform gems.
|
183
|
+
|
184
|
+
```shell
|
185
|
+
#!/bin/bash
|
186
|
+
set -e
|
187
|
+
|
188
|
+
# Clean any previous bundle dir.
|
189
|
+
rm -rf ./.bundle \
|
190
|
+
./vendor/bundle \
|
191
|
+
./.aws-sam/build \
|
192
|
+
node_modules
|
193
|
+
|
194
|
+
# Clean up Rails
|
195
|
+
./bin/rails log:clear tmp:clear
|
196
|
+
|
197
|
+
# Ensure native extensions built for platform.
|
198
|
+
sam build --use-container
|
199
|
+
|
200
|
+
# Clean build dir of unneeded artifacts.
|
201
|
+
pushd ./.aws-sam/build/RailsFunction/
|
202
|
+
rm -rf .aws-sam \
|
203
|
+
.git \
|
204
|
+
node_modules \
|
205
|
+
test \
|
206
|
+
template.yaml \
|
207
|
+
package.json \
|
208
|
+
yarn.lock \
|
209
|
+
tmp
|
210
|
+
rm -rf vendor/bundle/ruby/2.5.0/cache
|
211
|
+
popd
|
212
|
+
|
213
|
+
# Avoid doing local bundle work if building for deploy.
|
214
|
+
if [ -z ${SKIP_LOCAL_BUNDLE+x} ]; then
|
215
|
+
# Copy the build bundle to allow server native extensions to work.
|
216
|
+
cp -R ./.aws-sam/build/RailsFunction/vendor/bundle ./vendor/bundle
|
217
|
+
# Make a comingled bundle dir for build and this platform.
|
218
|
+
rm -rf ./.bundle
|
219
|
+
bundle install --path vendor/bundle
|
220
|
+
fi
|
221
|
+
```
|
28
222
|
|
29
|
-
|
223
|
+
#### bin/deploy
|
224
|
+
|
225
|
+
Uses both the [sam package](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-package.html) and [sam deploy](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html) commands to ship your Lambda stack to AWS. It also:
|
226
|
+
|
227
|
+
* Uses or sets the `RAILS_ENV` variable. Defaults to `development`.
|
228
|
+
* Uses or sets the `CF_BUCKET_NAME` variable. This is the name of the S3 bucket that holds your deploy artifacts.
|
229
|
+
* Sets the `SKIP_LOCAL_BUNDLE` environment variable used by build.
|
230
|
+
|
231
|
+
```shell
|
232
|
+
#!/bin/bash
|
233
|
+
set -e
|
234
|
+
|
235
|
+
export RAILS_ENV=${RAILS_ENV:="development"}
|
236
|
+
export CF_BUCKET_NAME=${CF_BUCKET_NAME:="mycloudformationbucket.example.org"}
|
237
|
+
export SKIP_LOCAL_BUNDLE="1"
|
238
|
+
|
239
|
+
./bin/build
|
240
|
+
|
241
|
+
sam package \
|
242
|
+
--template-file ./.aws-sam/build/template.yaml \
|
243
|
+
--output-template-file ./.aws-sam/build/packaged.yaml \
|
244
|
+
--s3-bucket $CF_BUCKET_NAME \
|
245
|
+
--s3-prefix "my-app-${RAILS_ENV}"
|
246
|
+
|
247
|
+
sam deploy \
|
248
|
+
--template-file ./.aws-sam/build/packaged.yaml \
|
249
|
+
--stack-name "my-app-${RAILS_ENV}" \
|
250
|
+
--capabilities "CAPABILITY_IAM" \
|
251
|
+
--parameter-overrides \
|
252
|
+
RailsEnv=${RAILS_ENV}
|
253
|
+
```
|
254
|
+
|
255
|
+
So an example `bin/deploy-production` script would look like this.
|
256
|
+
|
257
|
+
```shell
|
258
|
+
#!/bin/bash
|
259
|
+
set -e
|
260
|
+
|
261
|
+
export RAILS_ENV="production"
|
262
|
+
|
263
|
+
./bin/deploy
|
264
|
+
```
|
265
|
+
|
266
|
+
#### bin/server
|
267
|
+
|
268
|
+
Start the normal Rails development server. Make sure to run setup/build prior.
|
269
|
+
|
270
|
+
```shell
|
271
|
+
#!/bin/bash
|
272
|
+
set -e
|
273
|
+
|
274
|
+
./bin/rails server
|
275
|
+
```
|
276
|
+
|
277
|
+
#### bin/server-sam
|
278
|
+
|
279
|
+
Uses the [sam local start-api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-start-api.html) command to
|
280
|
+
|
281
|
+
```shell
|
282
|
+
#!/bin/bash
|
283
|
+
set -e
|
284
|
+
|
285
|
+
# Delete the static build to avoid local server using it.
|
286
|
+
rm -rf ./.aws-sam/build
|
287
|
+
sam local start-api
|
288
|
+
```
|
289
|
+
|
290
|
+
|
291
|
+
## SAM Bugs and Patches
|
292
|
+
|
293
|
+
Unfortunately, the SAM CLI project has a few needed enhancements and if you decide to do any local development via the [sam local start-api](#binserver-sam) command, it will need to be patched on your local machine. I used the commands below to find the two files needing patching.
|
294
|
+
|
295
|
+
```shell
|
296
|
+
$ which sam # Use directory info below in find command.
|
297
|
+
$ find /usr/local -name container.py | grep samcli
|
298
|
+
$ find /usr/local -name local_apigw_service.py | grep samcli
|
299
|
+
```
|
300
|
+
|
301
|
+
Here are the issues we have created on the SAM CLI project. Details on the patches below.
|
302
|
+
|
303
|
+
* [Add Docker Delegated Consistency to Volume](https://github.com/awslabs/aws-sam-cli/pull/1046)
|
304
|
+
* [Missing Authentication Token for root path '/'](https://github.com/awslabs/aws-sam-cli/issues/437)
|
305
|
+
|
306
|
+
#### Docker Volume Mount Performance on Mac
|
307
|
+
|
308
|
+
If you are on a Mac, Docker has a [known performance issue](https://forums.docker.com/t/file-access-in-mounted-volumes-extremely-slow-cpu-bound/8076) when sharing volumes due to the overhead of keeping files in sync. This performance issue means your Rails application could take ~60 seconds to load in development. Thankfully, Docker has [tooling in place](https://docs.docker.com/docker-for-mac/osxfs-caching/) to help.
|
309
|
+
|
310
|
+
Once you found the correct `container.py` file, make this change below to tell the Docker SDK to mount the volume with both the `ro` (read-only) option and `delegated` consistency mode.
|
311
|
+
|
312
|
+
```diff
|
313
|
+
@@ -95,7 +95,7 @@
|
314
|
+
# https://docs.docker.com/storage/bind-mounts
|
315
|
+
# Mount the host directory as "read only" inside container
|
316
|
+
"bind": self._working_dir,
|
317
|
+
- "mode": "ro"
|
318
|
+
+ "mode": "ro,delegated"
|
319
|
+
}
|
320
|
+
},
|
321
|
+
# We are not running an interactive shell here.
|
322
|
+
```
|
323
|
+
|
324
|
+
#### Fixing Root SAM Local Proxy Paths
|
325
|
+
|
326
|
+
We need our API Gateway to use both a root path and a greedy proxy path to forward to Rails in development. SAM has a feature compatibility bug where it forces static file hosting on the root path. Once you found the correct `local_apigw_service.py` file, make the change below to disable static `public` directory assets on the root path. Don't worry, Rails & Rack will serve your static files automatically instead.
|
327
|
+
|
328
|
+
```diff
|
329
|
+
@@ -71,7 +71,7 @@
|
330
|
+
"""
|
331
|
+
|
332
|
+
self._app = Flask(__name__,
|
333
|
+
- static_url_path="", # Mount static files at root '/'
|
334
|
+
+ static_url_path=None, # Mount static files at root '/'
|
335
|
+
static_folder=self.static_dir # Serve static files from this directory
|
336
|
+
)
|
337
|
+
```
|
338
|
+
|
339
|
+
|
340
|
+
## About AWS SAM and CloudFormation
|
341
|
+
|
342
|
+
AWS SAM is shorthand for the [Serverless Application Model](https://github.com/awslabs/serverless-application-model) and it is a superset of CloudFormation - a language that describes and provisions your infrastructure using code. As your application grows and requires additional AWS resources, learning how to express this in your `template.yml` is critical. We recommend the following links when needing to learn both SAM and CloudFormation.
|
343
|
+
|
344
|
+
* [AWS Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md)
|
345
|
+
* [AWS CloudFormation User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html)
|
346
|
+
|
347
|
+
|
348
|
+
## Performance
|
349
|
+
|
350
|
+
Because your Rails app is initialized outside the `handler` it should be loaded into memory and respond to requests in a very timely manner. Make sure to set the `MemorySize` as needed in your `template.yml` and you should get comparable performance to EC2. Initial tests show that basic view rendering only takes a few milliseconds. Please do share your performance results if you have any!
|
351
|
+
|
352
|
+
|
353
|
+
## Database Connections
|
354
|
+
|
355
|
+
If you want to use a database with Rails and Lambda, I would highly suggest using DynamoDB with the excellent [AWS Record](https://github.com/aws/aws-sdk-ruby-record) gem. However, if you want to explore and share how you might use Lamby with PG or MySQL, I'd love to hear about it.
|
356
|
+
|
357
|
+
|
358
|
+
## Basic Ruby and Lambda
|
359
|
+
|
360
|
+
Curious about using AWS SAM with Ruby and no Rails? Please see this 757rb (Norfolk Ruby User's Group) repository for an overview on how to kick-start your next Ruby project.
|
361
|
+
|
362
|
+
* [Using Ruby with AWS Lambda & SAM](https://github.com/757rb/hello-757rb-lambda)
|
30
363
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
364
|
|
33
365
|
## Contributing
|
34
366
|
|
35
|
-
|
367
|
+
After checking out the repo, run `./bin/setup` to install dependencies. Then, run `./bin/test` to run the tests. **NOTE: There are no tests now but adding them is on our TODO list.**
|
36
368
|
|
37
|
-
|
369
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/metaskills/lamby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
38
370
|
|
39
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
40
371
|
|
41
372
|
## Code of Conduct
|
42
373
|
|
data/bin/setup
CHANGED
data/bin/test
ADDED
data/doc/template.yml
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
2
|
+
Transform: AWS::Serverless-2016-10-31
|
3
|
+
Description: Hello Rails Lambda
|
4
|
+
|
5
|
+
Parameters:
|
6
|
+
|
7
|
+
RailsEnv:
|
8
|
+
Type: String
|
9
|
+
Default: development
|
10
|
+
AllowedValues:
|
11
|
+
- development
|
12
|
+
- test
|
13
|
+
- staging
|
14
|
+
- production
|
15
|
+
|
16
|
+
Resources:
|
17
|
+
|
18
|
+
RailsApi:
|
19
|
+
Type: AWS::Serverless::Api
|
20
|
+
Properties:
|
21
|
+
StageName: !Ref RailsEnv
|
22
|
+
EndpointConfiguration: EDGE
|
23
|
+
DefinitionBody:
|
24
|
+
swagger: 2.0
|
25
|
+
info: { title: !Ref 'AWS::StackName' }
|
26
|
+
basePath: !Join [ '', [ '/', !Ref RailsEnv ] ]
|
27
|
+
schemes: [ 'https' ]
|
28
|
+
paths:
|
29
|
+
/:
|
30
|
+
get:
|
31
|
+
x-amazon-apigateway-integration:
|
32
|
+
uri:
|
33
|
+
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RailsFunction.Arn}:live/invocations
|
34
|
+
httpMethod: POST
|
35
|
+
type: aws_proxy
|
36
|
+
/{resource+}:
|
37
|
+
x-amazon-apigateway-any-method:
|
38
|
+
parameters:
|
39
|
+
- name: resource
|
40
|
+
in: path
|
41
|
+
required: true
|
42
|
+
type: string
|
43
|
+
x-amazon-apigateway-integration:
|
44
|
+
uri:
|
45
|
+
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RailsFunction.Arn}:live/invocations
|
46
|
+
httpMethod: POST
|
47
|
+
type: aws_proxy
|
48
|
+
x-amazon-apigateway-binary-media-types:
|
49
|
+
- '*/*'
|
50
|
+
|
51
|
+
RailsFunction:
|
52
|
+
Type: AWS::Serverless::Function
|
53
|
+
Properties:
|
54
|
+
CodeUri: .
|
55
|
+
Handler: app.handler
|
56
|
+
Runtime: ruby2.5
|
57
|
+
MemorySize: 512
|
58
|
+
Timeout: 30
|
59
|
+
Environment:
|
60
|
+
Variables:
|
61
|
+
RAILS_ENV: !Ref RailsEnv
|
62
|
+
Events:
|
63
|
+
Root:
|
64
|
+
Type: Api
|
65
|
+
Properties:
|
66
|
+
Path: /
|
67
|
+
Method: GET
|
68
|
+
RestApiId: !Ref RailsApi
|
69
|
+
RailsAppAll:
|
70
|
+
Type: Api
|
71
|
+
Properties:
|
72
|
+
Path: /{resource+}
|
73
|
+
Method: ANY
|
74
|
+
RestApiId: !Ref RailsApi
|
75
|
+
AutoPublishAlias: live
|
76
|
+
|
77
|
+
Outputs:
|
78
|
+
|
79
|
+
RailsApiUrl:
|
80
|
+
Description: API Gateway Endpoint
|
81
|
+
Value: !Sub "https://${RailsApi}.execute-api.${AWS::Region}.amazonaws.com/${RailsEnv}/"
|
82
|
+
|
83
|
+
RailsFunctionArn:
|
84
|
+
Description: Lambda Function ARN
|
85
|
+
Value: !GetAtt RailsFunction.Arn
|
data/lamby.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
|
-
lib = File.expand_path(
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require 'lamby/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "lamby"
|
@@ -18,7 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.bindir = "exe"
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
|
-
spec.
|
22
|
-
spec.
|
23
|
-
spec.add_development_dependency
|
21
|
+
spec.add_dependency 'rack'
|
22
|
+
spec.add_dependency 'activesupport'
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
spec.add_development_dependency 'minitest'
|
24
26
|
end
|
data/lib/lamby.rb
CHANGED
@@ -1,6 +1,19 @@
|
|
1
|
-
require
|
1
|
+
require 'lamby/logger'
|
2
|
+
require 'rack'
|
3
|
+
require 'base64'
|
4
|
+
require 'active_support/all'
|
5
|
+
require 'lamby/version'
|
6
|
+
require 'lamby/sam_helpers'
|
7
|
+
require 'lamby/rack'
|
8
|
+
require 'lamby/debug'
|
9
|
+
require 'lamby/handler'
|
2
10
|
|
3
11
|
module Lamby
|
4
|
-
|
5
|
-
|
12
|
+
|
13
|
+
extend self
|
14
|
+
|
15
|
+
def handler(app, event, context)
|
16
|
+
Handler.call(app, event, context)
|
17
|
+
end
|
18
|
+
|
6
19
|
end
|
data/lib/lamby/debug.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Lamby
|
2
|
+
module Debug
|
3
|
+
include Lamby::SamHelpers
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def on?(event)
|
8
|
+
params = event['queryStringParameters']
|
9
|
+
(Rails.env.development? || ENV['LAMBY_DEBUG']) && params && params['debug'] == '1'
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(event, context, env)
|
13
|
+
[ 200, { 'Content-Type' => 'text/html' }, [body(event, context, env)] ]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def body(event, context, env)
|
19
|
+
<<-HTML
|
20
|
+
<!DOCTYPE html>
|
21
|
+
<html>
|
22
|
+
<body>
|
23
|
+
<h1>Lamby Debug Response</h1>
|
24
|
+
<h2>Event</h2>
|
25
|
+
<pre>
|
26
|
+
#{JSON.pretty_generate(event)}
|
27
|
+
</pre>
|
28
|
+
<h2>Rack Env</h2>
|
29
|
+
<pre>
|
30
|
+
#{JSON.pretty_generate(env)}
|
31
|
+
</pre>
|
32
|
+
<h2>#{context.class.name}</h2>
|
33
|
+
<code>
|
34
|
+
#{CGI::escapeHTML(context.inspect)}
|
35
|
+
</code>
|
36
|
+
<h2>Environment</h2>
|
37
|
+
<pre>
|
38
|
+
#{sam_local? ? JSON.pretty_generate(ENV.to_h) : 'N/A'}
|
39
|
+
</pre>
|
40
|
+
</body>
|
41
|
+
</html>
|
42
|
+
HTML
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Lamby
|
2
|
+
class Handler
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def call(app, event, context)
|
7
|
+
new(app, event, context).call.response
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(app, event, context)
|
13
|
+
@app = app
|
14
|
+
@event = event
|
15
|
+
@context = context
|
16
|
+
@rack = Lamby::Rack.new event, context
|
17
|
+
@called = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def response
|
21
|
+
{ statusCode: status,
|
22
|
+
headers: headers,
|
23
|
+
body: body }
|
24
|
+
end
|
25
|
+
|
26
|
+
def status
|
27
|
+
@status
|
28
|
+
end
|
29
|
+
|
30
|
+
def headers
|
31
|
+
@headers
|
32
|
+
end
|
33
|
+
|
34
|
+
def body
|
35
|
+
@rbody ||= ''.tap do |rbody|
|
36
|
+
@body.each { |part| rbody << part }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def call
|
41
|
+
return self if @called
|
42
|
+
@status, @headers, @body = call_app
|
43
|
+
@called = true
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def call_app
|
50
|
+
if Debug.on?(@event)
|
51
|
+
Debug.call @event, @context, @rack.env
|
52
|
+
else
|
53
|
+
@app.call @rack.env
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/lamby/logger.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
ENV['RAILS_LOG_TO_STDOUT'] = '1'
|
4
|
+
|
5
|
+
module Lamby
|
6
|
+
module Logger
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
args[0] = STDOUT
|
10
|
+
super(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Logger.prepend Lamby::Logger
|
17
|
+
|
18
|
+
# TODO: Railtie initializer
|
19
|
+
# Rails.application.config.logger = ActiveSupport::TaggedLogging.new(
|
20
|
+
# ActiveSupport::Logger.new(STDOUT).tap { |logger|
|
21
|
+
# logger.formatter = Rails.application.config.log_formatter
|
22
|
+
# }
|
23
|
+
# )
|
data/lib/lamby/rack.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Lamby
|
2
|
+
class Rack
|
3
|
+
|
4
|
+
include SamHelpers
|
5
|
+
|
6
|
+
LAMBDA_EVENT = 'lambda.event'.freeze
|
7
|
+
LAMBDA_CONTEXT = 'lambda.context'.freeze
|
8
|
+
HTTP_X_APIGWSTAGE = 'HTTP_X_APIGWSTAGE'.freeze
|
9
|
+
|
10
|
+
attr_reader :event, :context
|
11
|
+
|
12
|
+
def initialize(event, context)
|
13
|
+
@event = event
|
14
|
+
@context = context
|
15
|
+
end
|
16
|
+
|
17
|
+
def env
|
18
|
+
@env ||= env_base.merge!(env_headers)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def env_base
|
24
|
+
{ ::Rack::REQUEST_METHOD => event['httpMethod'],
|
25
|
+
::Rack::SCRIPT_NAME => '',
|
26
|
+
::Rack::PATH_INFO => event['path'] || '',
|
27
|
+
::Rack::QUERY_STRING => query_string,
|
28
|
+
::Rack::SERVER_NAME => headers['Host'],
|
29
|
+
::Rack::SERVER_PORT => headers['X-Forwarded-Port'],
|
30
|
+
::Rack::SERVER_PROTOCOL => server_protocol,
|
31
|
+
::Rack::RACK_VERSION => ::Rack::VERSION,
|
32
|
+
::Rack::RACK_URL_SCHEME => 'https',
|
33
|
+
::Rack::RACK_INPUT => StringIO.new(body || ''),
|
34
|
+
::Rack::RACK_ERRORS => $stderr,
|
35
|
+
::Rack::RACK_MULTITHREAD => false,
|
36
|
+
::Rack::RACK_MULTIPROCESS => false,
|
37
|
+
::Rack::RACK_RUNONCE => false,
|
38
|
+
LAMBDA_EVENT => event,
|
39
|
+
LAMBDA_CONTEXT => context
|
40
|
+
}.tap do |env|
|
41
|
+
ct = content_type
|
42
|
+
cl = content_length
|
43
|
+
env[::Rack::CONTENT_TYPE] = ct if ct
|
44
|
+
env[::Rack::CONTENT_LENGTH] = cl if cl
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def env_headers
|
49
|
+
headers.transform_keys do |key|
|
50
|
+
"HTTP_#{key.to_s.upcase.tr '-', '_'}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def content_type
|
55
|
+
headers.delete('Content-Type') || headers.delete('content-type')
|
56
|
+
end
|
57
|
+
|
58
|
+
def content_length
|
59
|
+
bytesize = body.bytesize.to_s if body
|
60
|
+
headers.delete('Content-Length') || headers.delete('content-length') || bytesize
|
61
|
+
end
|
62
|
+
|
63
|
+
def body
|
64
|
+
@body ||= if event['body'] && base64_encoded?
|
65
|
+
Base64.decode64 event['body']
|
66
|
+
else
|
67
|
+
event['body']
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def headers
|
72
|
+
event['headers'] || {}
|
73
|
+
end
|
74
|
+
|
75
|
+
def query_string
|
76
|
+
@query_string ||= event['queryStringParameters'].try(:to_query)
|
77
|
+
end
|
78
|
+
|
79
|
+
def base64_encoded?
|
80
|
+
event['isBase64Encoded']
|
81
|
+
end
|
82
|
+
|
83
|
+
def server_protocol
|
84
|
+
event.dig('requestContext', 'protocol') || 'HTTP/1.1'
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
data/lib/lamby/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lamby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ken Collins
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,8 +98,15 @@ files:
|
|
70
98
|
- Rakefile
|
71
99
|
- bin/console
|
72
100
|
- bin/setup
|
101
|
+
- bin/test
|
102
|
+
- doc/template.yml
|
73
103
|
- lamby.gemspec
|
74
104
|
- lib/lamby.rb
|
105
|
+
- lib/lamby/debug.rb
|
106
|
+
- lib/lamby/handler.rb
|
107
|
+
- lib/lamby/logger.rb
|
108
|
+
- lib/lamby/rack.rb
|
109
|
+
- lib/lamby/sam_helpers.rb
|
75
110
|
- lib/lamby/version.rb
|
76
111
|
homepage: https://github.com/customink/lamby
|
77
112
|
licenses:
|