Skip to content

Commit d25e689

Browse files
author
Colby Swandale
committed
re-architect application to use Lambda with Docker, replacing Lambda layers
1 parent f2032b1 commit d25e689

24 files changed

+288
-666
lines changed

CODE_OF_CONDUCT.md

-76
This file was deleted.

Gemfile

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
source "https://rubygems.org"
22

3-
gem "rake"
4-
53
group :test do
64
gem "test-unit"
7-
end
5+
end

Gemfile.lock

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
GEM
22
remote: https://rubygems.org/
33
specs:
4-
power_assert (1.2.0)
5-
rake (13.0.3)
6-
test-unit (3.3.6)
4+
power_assert (2.0.1)
5+
test-unit (3.5.3)
76
power_assert
87

98
PLATFORMS
10-
ruby
9+
arm64-darwin-21
1110

1211
DEPENDENCIES
13-
rake
1412
test-unit
1513

1614
BUNDLED WITH
17-
2.1.4
15+
2.2.32

LICENSE.md

-21
This file was deleted.

README.md

+3-22
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,17 @@ $ sam local start-api
2525
**Note** The local dev server does not automatically build the Ruby Engine layer that each function depends on. You will need to build the layer beforehand for the engine/version being invoked ie:
2626

2727
```
28-
$ sam build MRIRuby27
28+
$ sam build MRIRuby
2929
$ sam local start-api
30-
$ curl http://localhost:3000/exec/mri/27
30+
$ curl http://localhost:3000/exec/mri
3131
```
3232

3333
## Testing repl function
3434

3535
The test suit is located in `tests` and can be executed with:
3636

3737
```bash
38-
$ rake test
39-
```
40-
41-
## Architecture
42-
43-
Each Ruby engine & version is a single lambda function that contains a small ruby application & a [Lambda layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) that contains the given Ruby engine version's program/binary.
44-
45-
The diagram represents the application flow:
46-
47-
```
48-
+-----------------+ +-----------------+ Execute Ruby
49-
| | | | Binary
50-
| | Execute | +---------+
51-
| | Lambda func | | |
52-
| HTTP Endpoint +---------------> Lambda Function | |
53-
| | | +---------v---------+
54-
| | | | |
55-
| | | | Ruby Engine Layer |
56-
| | | | |
57-
+-----------------+ +-----------------+-------------------+
38+
$ ruby tests/unit/*
5839
```
5940

6041
## Deploy to AWS

Rakefile

-9
This file was deleted.

app/Dockerfile

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# syntax=docker/dockerfile:1
2+
FROM public.ecr.aws/lambda/ruby:2.7 as builder
3+
4+
WORKDIR /opt
5+
6+
ARG RUBY_VERSION=3.0.3
7+
ENV RUBY_CFLAGS="-Os"
8+
ENV RUBY_CONFIGURE_OPTS="--disable-install-doc"
9+
10+
RUN yum groupinstall -y 'Development Tools' && \
11+
yum install -y openssl-devel java-1.8.0-openjdk
12+
13+
RUN git clone https://github.com/rbenv/ruby-build /opt/ruby-build --depth 1 && \
14+
/opt/ruby-build/bin/ruby-build ${RUBY_VERSION} /opt/ruby
15+
16+
17+
18+
FROM public.ecr.aws/lambda/ruby:2.7 as app
19+
20+
ENV GEM_HOME=${LAMBDA_TASK_ROOT}
21+
22+
RUN yum install -y java-1.8.0-openjdk
23+
24+
COPY --from=builder /opt/ruby/ /opt/ruby/
25+
COPY . ./
26+
27+
RUN bundle install
28+
29+
# Command can be overwritten by providing a different command in the template directly.
30+
CMD ["app.lambda_handler"]

app/Dockerfile.artichoke

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# syntax=docker/dockerfile:1
2+
FROM rust as builder
3+
4+
WORKDIR /opt
5+
6+
RUN apt update -qq && apt install -y -qq ruby clang && \
7+
cargo install --git https://github.com/artichoke/artichoke \
8+
--branch trunk --locked artichoke --root /opt/artichoke
9+
10+
FROM public.ecr.aws/lambda/ruby:2.7 as app
11+
12+
ENV GEM_HOME=${LAMBDA_TASK_ROOT}
13+
14+
COPY --from=builder /opt/artichoke/ /opt/ruby/
15+
COPY . ./
16+
17+
RUN bundle install
18+
19+
# Command can be overwritten by providing a different command in the template directly.
20+
CMD ["app.lambda_handler"]

app/run.rb renamed to app/app.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
# frozen_string_literal: true
2-
31
require 'json'
42
require 'open3'
53

6-
require_relative './helper'
74
require_relative './code_executor'
85

96
def lambda_handler(event:, context:)
107
event.transform_keys!(&:to_sym)
118

129
body = event.fetch(:body)
1310

14-
return { statusCode: 400, body: {}.to_json } if body.empty?
11+
return empty_response if body.empty?
1512

1613
response = CodeExecutor.run(body)
1714

@@ -21,3 +18,6 @@ def lambda_handler(event:, context:)
2118
}
2219
end
2320

21+
def empty_response
22+
{ statusCode: 200, body: {} }
23+
end

app/code_executor.rb

+47-48
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1-
require_relative './helper'
2-
require_relative './response'
3-
require_relative './payload'
4-
5-
require 'open3'
6-
7-
class CodeExecutor
8-
include Helper
9-
10-
attr_accessor :payload
11-
12-
def self.run(body)
13-
new(body).run
14-
end
15-
16-
def initialize(body)
17-
@payload = Payload.generate_from_body(body)
18-
end
19-
20-
def run
21-
execute_payload
22-
end
23-
24-
private
25-
26-
def execute_payload
27-
stdout, stderr, status = Open3.capture3(ruby_cmd, stdin_data: payload)
28-
Response.new(output: stdout, errors: stderr, status: status&.exitstatus)
29-
end
30-
31-
def ruby_cmd
32-
[ruby_env, ruby_bin_path, ruby_args].join(" ")
33-
end
34-
35-
# Workaround for SAM overwriting the PATH env
36-
def ruby_env
37-
"PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/bin:/opt/bin:/opt/java/bin\""
38-
end
39-
40-
def ruby_bin_path
41-
ENV["RUBY_BIN"] || `which ruby`.strip
42-
end
43-
44-
def ruby_args
45-
return engine_args if engine_args?
46-
47-
"--disable-did_you_mean --disable-gems"
48-
end
1+
require_relative './payload'
2+
3+
require 'open3'
4+
5+
Response = Struct.new(:output, :errors, :status, keyword_init: true) do
6+
def status
7+
self[:status] || 0
8+
end
9+
end
10+
11+
class CodeExecutor
12+
attr_accessor :payload
13+
14+
def self.run(body)
15+
new(body).run
16+
end
17+
18+
def initialize(body)
19+
@payload = Payload.generate_from_body(body)
20+
end
21+
22+
def run
23+
execute_payload
24+
end
25+
26+
private
27+
28+
def execute_payload
29+
stdout, stderr, status = Open3.capture3(ruby_cmd, stdin_data: payload)
30+
Response.new(output: stdout, errors: stderr, status: status&.exitstatus)
31+
end
32+
33+
def ruby_cmd
34+
[ruby_bin_path, ruby_args].join(" ")
35+
end
36+
37+
def ruby_bin_path
38+
if ENV["RUBY_BIN_PATH"] && !ENV["RUBY_BIN_PATH"].empty?
39+
ENV["RUBY_BIN_PATH"]
40+
else
41+
"/opt/ruby/bin/ruby"
42+
end
43+
end
44+
45+
def ruby_args
46+
ENV["ENGINE_ARGS"]
47+
end
4948
end

app/helper.rb

-17
This file was deleted.

0 commit comments

Comments
 (0)