Added support for Ruby v2 Specialization (#1101)

* Added support for Ruby v2 Specialization
* Added ruby v2 example with builder code
This commit is contained in:
Brendan Stennett
2019-03-13 22:55:26 +08:00
committed by Ta-Ching Chen
parent f104f0b46b
commit c717f182b6
12 changed files with 153 additions and 19 deletions
+5 -10
View File
@@ -1,16 +1,11 @@
FROM ruby:2.4.1 FROM ruby:2.6.1-alpine3.9
RUN apt-get update -qq && apt-get install -y build-essential RUN apk update
RUN apk add --no-cache build-base
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY . /app COPY . /app
WORKDIR /app
EXPOSE 8888 RUN bundle install
ENTRYPOINT ["ruby"] ENTRYPOINT ["ruby"]
CMD ["server.rb"] CMD ["server.rb"]
+1
View File
@@ -2,3 +2,4 @@
source "https://rubygems.org" source "https://rubygems.org"
gem "rack" gem "rack"
gem "thin"
+1 -1
View File
@@ -2,7 +2,7 @@
This is the Ruby environment for Fission. This is the Ruby environment for Fission.
It's a Docker image containing a Ruby 2.4.1 runtime. The image uses It's a Docker image containing a Ruby 2.6.1 runtime. The image uses
Rack with WEBrick to host the internal web server. Rack with WEBrick to host the internal web server.
The environment works via convention where you create a Ruby method The environment works via convention where you create a Ruby method
+12
View File
@@ -0,0 +1,12 @@
ARG BUILDER_IMAGE=fission/builder:latest
FROM ${BUILDER_IMAGE}
FROM ruby:2.6.1-alpine3.9
COPY --from=0 /builder /builder
RUN apk update
RUN apk add --no-cache ruby ruby-dev ruby-bundler build-base
ADD defaultBuildCmd /usr/local/bin/build
EXPOSE 8001
+9
View File
@@ -0,0 +1,9 @@
#!/bin/sh
set -euxo pipefail
if [ -f ${SRC_PKG}/Gemfile.lock ]
then
cd $SRC_PKG
bundle install --deployment
fi
cp -r ${SRC_PKG} ${DEPLOY_PKG}
+3 -3
View File
@@ -7,10 +7,10 @@ module Fission
def self.call(env) def self.call(env)
context = Context.new(env) context = Context.new(env)
response = if method(:handler).arity > 0 response = if $handler.arity > 0
handler(context) $handler.call(context)
else else
handler $handler.call
end end
response.is_a?(Array) ? response : Rack::Response.new([response]).finish response.is_a?(Array) ? response : Rack::Response.new([response]).finish
+65 -3
View File
@@ -1,10 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'benchmark' require 'benchmark'
require 'json'
module Fission module Fission
CODE_PATH = '/userfunc/user'
module Specializer module Specializer
CODE_PATH = '/userfunc/user'
def self.call(env) def self.call(env)
request = Request.new(env) request = Request.new(env)
@@ -12,11 +15,70 @@ module Fission
time = Benchmark.measure { load CODE_PATH } time = Benchmark.measure { load CODE_PATH }
request.logger.info("User code loaded in #{(time.real * 1000).round(3)}ms") request.logger.info("User code loaded in #{(time.real * 1000).round(3)}ms")
Rack::Response.new([], 201).finish # set to "handler" for v1 specialization
$handler = method(:handler)
Rack::Response.new([], 201).finish
rescue => e rescue => e
request.logger.error(%(Specialization failed - #{e}\n#{e.backtrace.join("\n")})) request.logger.error(%(Specialization failed - #{e}\n#{e.backtrace.join("\n")}))
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
end end
end end
module V2
module Specializer
def self.load_vendor(path)
gems = Dir[File.join(path, 'vendor/bundle/ruby/*/gems/*/lib')]
exts = Dir[File.join(path, 'vendor/bundle/ruby/*/extensions/x86_64-linux/*/*')]
$LOAD_PATH.unshift(*gems)
$LOAD_PATH.unshift(*exts)
end
def self.call(env)
request = Request.new(env)
body = JSON.parse(request.body.read)
path = body['filepath']
func = body['functionName']
time = Benchmark.measure do
if File.file?(path)
# If pointing to just a single file, all we need to do is load it
request.logger.debug("Loading file #{path}")
load path
elsif File.directory?(path)
# First we want to load all the vendor files
load_vendor(path)
# We then want to get all the .rb files in this src
rb_files = File.join(path, "**/*.rb")
# But we don't want to include the vendor files that we loaded above
vendor_dir = File.join(path, 'vendor')
src_files = Dir[rb_files].reject {|d| d.start_with?(vendor_dir) }
request.logger.debug("Loading sources #{src_files}")
src_files.each do |file|
load file
end
else
request.logger.error(%(Specialization failed - could not find src at #{path}}))
Rack::Response.new(['500 Internal Server Error'], 500, {}).finish
return
end
end
request.logger.info("User code loaded in #{(time.real * 1000).round(3)}ms")
# set global handler for this specialization
$handler = method(func)
Rack::Response.new([], 201).finish
end
end
end
end end
+14 -2
View File
@@ -1,21 +1,33 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rack' require 'rack'
require 'thin'
require 'logger' require 'logger'
require_relative 'fission/specializer' require_relative 'fission/specializer'
require_relative 'fission/handler' require_relative 'fission/handler'
$handler = nil
app = Rack::Builder.new do app = Rack::Builder.new do
use Rack::Logger, Logger::DEBUG use Rack::Logger, Logger::DEBUG
use Rack::CommonLogger
map "/specialize" do map "/specialize" do
run Fission::Specializer run Fission::Specializer
end end
map "/" do map '/v2/specialize' do
run Fission::V2::Specializer
end
map "/healthz" do
run ->(env) { [ 200, {}, [] ] }
end
map '/' do
run Fission::Handler run Fission::Handler
end end
end end
Rack::Handler::WEBrick.run app, Host: '0.0.0.0', Port: 8888 Rack::Handler::Thin.run app, Host: '0.0.0.0', Port: 8888
+8
View File
@@ -105,3 +105,11 @@ $ curl http://$FISSION_ROUTER/request/123?key=abc
--BODY-- --BODY--
``` ```
## V2 Specification Example (with builder support)
```
$ fission function create --name parse --env ruby --src "parse/*" --entrypoint handler
$ fission fn test --name parse --body '<message>This is my message</message>'
This is my message
```
+7
View File
@@ -0,0 +1,7 @@
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "nokogiri"
+15
View File
@@ -0,0 +1,15 @@
GEM
remote: https://rubygems.org/
specs:
mini_portile2 (2.4.0)
nokogiri (1.10.1)
mini_portile2 (~> 2.4.0)
PLATFORMS
ruby
DEPENDENCIES
nokogiri
BUNDLED WITH
1.16.1
+13
View File
@@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'nokogiri'
def handler(context)
context.logger.info("Received request")
doc = Nokogiri::XML(context.request.body.read)
ele = doc.at_xpath('//message')
msg = ele.nil? ? 'No Message' : ele.content
Rack::Response.new([msg, "\n"]).finish
end