lamby 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/cicd.yml +17 -0
  3. data/.gitignore +1 -1
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +25 -0
  6. data/Dockerfile +12 -0
  7. data/Gemfile.lock +82 -83
  8. data/README.md +9 -3
  9. data/Rakefile +1 -0
  10. data/bin/_setup +6 -0
  11. data/bin/_test +6 -0
  12. data/bin/bootstrap +3 -17
  13. data/bin/setup +3 -3
  14. data/bin/test +6 -3
  15. data/bin/update +2 -2
  16. data/docker-compose.yml +14 -0
  17. data/lamby.gemspec +1 -1
  18. data/lib/lamby.rb +2 -1
  19. data/lib/lamby/handler.rb +16 -3
  20. data/lib/lamby/logger.rb +13 -9
  21. data/lib/lamby/rack.rb +11 -9
  22. data/lib/lamby/rack_alb.rb +11 -14
  23. data/lib/lamby/rack_http.rb +80 -0
  24. data/lib/lamby/{rack_api.rb → rack_rest.rb} +7 -3
  25. data/lib/lamby/ssm_parameter_store.rb +1 -1
  26. data/lib/lamby/templates.rake +4 -3
  27. data/lib/lamby/templates/{application_load_balancer.rb → alb.rb} +1 -1
  28. data/lib/lamby/templates/{application_load_balancer → alb}/app.rb +0 -0
  29. data/lib/lamby/templates/{application_load_balancer → alb}/build +1 -1
  30. data/lib/lamby/templates/{application_load_balancer → alb}/deploy +1 -1
  31. data/lib/lamby/templates/{application_load_balancer → alb}/template.yaml +1 -1
  32. data/lib/lamby/templates/{api_gateway.rb → http.rb} +2 -2
  33. data/lib/lamby/templates/{api_gateway → http}/app.rb +1 -1
  34. data/lib/lamby/templates/{api_gateway → http}/build +1 -1
  35. data/lib/lamby/templates/{api_gateway → http}/deploy +1 -1
  36. data/lib/lamby/templates/http/template.yaml +48 -0
  37. data/lib/lamby/templates/rest.rb +25 -0
  38. data/lib/lamby/templates/rest/app.rb +10 -0
  39. data/lib/lamby/templates/rest/build +23 -0
  40. data/lib/lamby/templates/rest/deploy +22 -0
  41. data/lib/lamby/templates/{api_gateway → rest}/template.yaml +2 -2
  42. data/lib/lamby/templates/shared.rb +3 -1
  43. data/lib/lamby/version.rb +1 -1
  44. data/vendor/.keep +0 -0
  45. metadata +29 -23
  46. data/.python-version +0 -1
  47. data/.travis.yml +0 -17
  48. data/Brewfile +0 -5
  49. data/Pipfile +0 -13
  50. data/Pipfile.lock +0 -370
@@ -0,0 +1,14 @@
1
+ version: '3.7'
2
+ services:
3
+ cicd:
4
+ build: .
5
+ environment:
6
+ - AWS_PROFILE=${AWS_PROFILE-default}
7
+ - RAILS_ENV=${RAILS_ENV-test}
8
+ - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION-us-east-1}
9
+ - SAM_CLI_TELEMETRY=0
10
+ - DIDPWD=${PWD}
11
+ volumes:
12
+ - ~/.aws:/root/.aws:delegated
13
+ - .:/var/task:delegated
14
+ - /var/run/docker.sock:/var/run/docker.sock
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'bundler'
25
25
  spec.add_development_dependency 'rake'
26
26
  spec.add_development_dependency 'minitest'
27
+ spec.add_development_dependency 'minitest-focus'
27
28
  spec.add_development_dependency 'mocha'
28
29
  spec.add_development_dependency 'pry'
29
- spec.add_development_dependency 'sqlite3'
30
30
  end
@@ -6,7 +6,8 @@ require 'lamby/version'
6
6
  require 'lamby/sam_helpers'
7
7
  require 'lamby/rack'
8
8
  require 'lamby/rack_alb'
9
- require 'lamby/rack_api'
9
+ require 'lamby/rack_rest'
10
+ require 'lamby/rack_http'
10
11
  require 'lamby/debug'
11
12
  require 'lamby/handler'
12
13
  require 'rails/railtie'
@@ -44,14 +44,27 @@ module Lamby
44
44
  self
45
45
  end
46
46
 
47
+ def base64_encodeable?
48
+ @headers && (
49
+ @headers['Content-Transfer-Encoding'] == 'binary' ||
50
+ @headers['X-Lamby-Base64'] == '1'
51
+ )
52
+ end
53
+
54
+ def body64
55
+ Base64.strict_encode64(body)
56
+ end
57
+
47
58
  private
48
59
 
49
60
  def rack
50
61
  @rack ||= case @options[:rack]
51
- when :api
52
- Lamby::RackApi.new @event, @context
53
- else
62
+ when :rest, :api
63
+ Lamby::RackRest.new @event, @context
64
+ when :alb
54
65
  Lamby::RackAlb.new @event, @context
66
+ else
67
+ Lamby::RackHttp.new @event, @context
55
68
  end
56
69
  end
57
70
 
@@ -1,19 +1,23 @@
1
1
  require 'logger'
2
2
 
3
- ENV['RAILS_LOG_TO_STDOUT'] = '1'
3
+ unless ENV['LAMBY_TEST']
4
4
 
5
- module Lamby
6
- module Logger
5
+ ENV['RAILS_LOG_TO_STDOUT'] = '1'
7
6
 
8
- def initialize(*args)
9
- args[0] = STDOUT
10
- super(*args)
11
- end
7
+ module Lamby
8
+ module Logger
9
+
10
+ def initialize(*args)
11
+ args[0] = STDOUT
12
+ super(*args)
13
+ end
12
14
 
15
+ end
13
16
  end
14
- end
15
17
 
16
- Logger.prepend Lamby::Logger
18
+ Logger.prepend Lamby::Logger
19
+
20
+ end
17
21
 
18
22
  # TODO: Railtie initializer
19
23
  # Rails.application.config.logger = ActiveSupport::TaggedLogging.new(
@@ -6,6 +6,7 @@ module Lamby
6
6
  LAMBDA_EVENT = 'lambda.event'.freeze
7
7
  LAMBDA_CONTEXT = 'lambda.context'.freeze
8
8
  HTTP_X_REQUESTID = 'HTTP_X_REQUEST_ID'.freeze
9
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
9
10
 
10
11
  attr_reader :event, :context
11
12
 
@@ -22,14 +23,6 @@ module Lamby
22
23
  {}
23
24
  end
24
25
 
25
- def api?
26
- false
27
- end
28
-
29
- def alb?
30
- false
31
- end
32
-
33
26
  def multi_value?
34
27
  false
35
28
  end
@@ -70,7 +63,16 @@ module Lamby
70
63
  end
71
64
 
72
65
  def query_string
73
- @query_string ||= event['queryStringParameters'].try(:to_query)
66
+ @query_string ||= if event.key?('rawQueryString')
67
+ event['rawQueryString']
68
+ elsif event.key?('multiValueQueryStringParameters')
69
+ query = event['multiValueQueryStringParameters'] || {}
70
+ query.map do |key, value|
71
+ value.map{ |v| "#{key}=#{v}" }.join('&')
72
+ end.flatten.join('&')
73
+ else
74
+ event['queryStringParameters'].try(:to_query)
75
+ end
74
76
  end
75
77
 
76
78
  def base64_encoded?
@@ -53,20 +53,17 @@ module Lamby
53
53
  end
54
54
 
55
55
  def headers_multi
56
- (event['multiValueHeaders'] || {}).transform_values do |v|
57
- v.is_a?(Array) ? v.first : v
58
- end
59
- end
60
-
61
- def query_string
62
- @query_string ||= multi_value? ? query_string_multi : super
63
- end
64
-
65
- def query_string_multi
66
- query = event['multiValueQueryStringParameters'] || {}
67
- string = query.map do |key, value|
68
- value.map{ |v| "#{key}=#{v}" }.join('&')
69
- end.flatten.join('&')
56
+ Hash[(event['multiValueHeaders'] || {}).map do |k,v|
57
+ if v.is_a?(Array)
58
+ if k == 'x-forwarded-for'
59
+ [k, v.join(', ')]
60
+ else
61
+ [k, v.first]
62
+ end
63
+ else
64
+ [k,v]
65
+ end
66
+ end]
70
67
  end
71
68
 
72
69
  end
@@ -0,0 +1,80 @@
1
+ module Lamby
2
+ class RackHttp < Lamby::Rack
3
+
4
+ def response(handler)
5
+ if handler.base64_encodeable?
6
+ { isBase64Encoded: true, body: handler.body64 }
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def env_base
15
+ { ::Rack::REQUEST_METHOD => request_method,
16
+ ::Rack::SCRIPT_NAME => '',
17
+ ::Rack::PATH_INFO => path_info,
18
+ ::Rack::QUERY_STRING => query_string,
19
+ ::Rack::SERVER_NAME => server_name,
20
+ ::Rack::SERVER_PORT => server_port,
21
+ ::Rack::SERVER_PROTOCOL => server_protocol,
22
+ ::Rack::RACK_VERSION => ::Rack::VERSION,
23
+ ::Rack::RACK_URL_SCHEME => 'https',
24
+ ::Rack::RACK_INPUT => StringIO.new(body || ''),
25
+ ::Rack::RACK_ERRORS => $stderr,
26
+ ::Rack::RACK_MULTITHREAD => false,
27
+ ::Rack::RACK_MULTIPROCESS => false,
28
+ ::Rack::RACK_RUNONCE => false,
29
+ LAMBDA_EVENT => event,
30
+ LAMBDA_CONTEXT => context
31
+ }.tap do |env|
32
+ ct = content_type
33
+ cl = content_length
34
+ env['CONTENT_TYPE'] = ct if ct
35
+ env['CONTENT_LENGTH'] = cl if cl
36
+ end
37
+ end
38
+
39
+ def env_headers
40
+ super.tap do |hdrs|
41
+ if event['cookies'].present?
42
+ hdrs[HTTP_COOKIE] = event['cookies'].join('; ')
43
+ end
44
+ end
45
+ end
46
+
47
+ def request_method
48
+ event.dig('requestContext', 'http', 'method') || event['httpMethod']
49
+ end
50
+
51
+ # Using custom domain names with v1.0 yields a good `path` parameter sans
52
+ # stage. However, v2.0 and others do not. So we are just going to remove stage
53
+ # no matter waht from other places for both.
54
+ #
55
+ def path_info
56
+ stage = event.dig('requestContext', 'stage')
57
+ spath = event.dig('requestContext', 'http', 'path') || event.dig('requestContext', 'path')
58
+ spath.sub /\A\/#{stage}/, ''
59
+ end
60
+
61
+ def server_name
62
+ headers['x-forwarded-host'] ||
63
+ headers['X-Forwarded-Host'] ||
64
+ headers['host'] ||
65
+ headers['Host']
66
+ end
67
+
68
+ def server_port
69
+ headers['x-forwarded-port'] || headers['X-Forwarded-Port']
70
+ end
71
+
72
+ def server_protocol
73
+ event.dig('requestContext', 'http', 'protocol') ||
74
+ event.dig('requestContext', 'protocol') ||
75
+ 'HTTP/1.1'
76
+ end
77
+
78
+
79
+ end
80
+ end
@@ -1,8 +1,12 @@
1
1
  module Lamby
2
- class RackApi < Lamby::Rack
2
+ class RackRest < Lamby::Rack
3
3
 
4
- def api?
5
- true
4
+ def response(handler)
5
+ if handler.base64_encodeable?
6
+ { isBase64Encoded: true, body: handler.body64 }
7
+ else
8
+ super
9
+ end
6
10
  end
7
11
 
8
12
  private
@@ -68,7 +68,7 @@ module Lamby
68
68
 
69
69
  def dotenv_contents
70
70
  params.each_with_object('') do |param, contents|
71
- line = "export #{param.env}=#{param.value}\n"
71
+ line = "#{param.env}=#{param.value}\n"
72
72
  contents << line
73
73
  end
74
74
  end
@@ -1,6 +1,7 @@
1
1
  installers = {
2
- 'API Gateway' => :api_gateway,
3
- 'Application Load Balancer' => :application_load_balancer
2
+ 'API Gateway (HTTP API)' => :http,
3
+ 'API Gateway (REST API)' => :rest,
4
+ 'Application Load Balancer' => :alb
4
5
  }.freeze
5
6
 
6
7
  namespace :lamby do
@@ -32,5 +33,5 @@ namespace :lamby do
32
33
  end
33
34
 
34
35
  desc "Install Lamby files for #{installers.first.first}"
35
- task install: 'install:application_load_balancer'
36
+ task install: 'install:http'
36
37
  end
@@ -1,4 +1,4 @@
1
- $LAMBY_INSTALLER_NAME = 'application_load_balancer'
1
+ $LAMBY_INSTALLER_NAME = 'alb'
2
2
  load 'lamby/templates/shared.rb'
3
3
 
4
4
  say '==> Running Lamby Application Load Balancer installer...'
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  # Clean any previous bundle/builds.
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  export RAILS_ENV=${RAILS_ENV:="production"}
@@ -70,7 +70,7 @@ Resources:
70
70
  Properties:
71
71
  CodeUri: .
72
72
  Handler: app.handler
73
- Runtime: ruby2.5
73
+ Runtime: ruby2.7
74
74
  MemorySize: 512
75
75
  Timeout: 30
76
76
  Environment:
@@ -1,7 +1,7 @@
1
- $LAMBY_INSTALLER_NAME = 'api_gateway'
1
+ $LAMBY_INSTALLER_NAME = 'http'
2
2
  load 'lamby/templates/shared.rb'
3
3
 
4
- say '==> Running Lamby API Gateway installer...'
4
+ say '==> Running Lamby API Gateway (HTTP API) installer...'
5
5
 
6
6
  say 'Copying files...'
7
7
  copy_file tpl_file('app.rb'), app_file('app.rb')
@@ -6,5 +6,5 @@ require_relative 'config/environment'
6
6
  $app = Rack::Builder.new { run Rails.application }.to_app
7
7
 
8
8
  def handler(event:, context:)
9
- Lamby.handler $app, event, context, rack: :api
9
+ Lamby.handler $app, event, context, rack: :http
10
10
  end
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  # Clean any previous bundle/builds.
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  export RAILS_ENV=${RAILS_ENV:="production"}
@@ -0,0 +1,48 @@
1
+ AWSTemplateFormatVersion: '2010-09-09'
2
+ Transform: AWS::Serverless-2016-10-31
3
+ Description: APPNAMEHERE Lambda (HTTP API)
4
+
5
+ Parameters:
6
+
7
+ RailsEnv:
8
+ Type: String
9
+ Default: production
10
+ AllowedValues:
11
+ - staging
12
+ - production
13
+
14
+ Resources:
15
+
16
+ RailsFunction:
17
+ Type: AWS::Serverless::Function
18
+ Properties:
19
+ CodeUri: .
20
+ Handler: app.handler
21
+ Runtime: ruby2.7
22
+ MemorySize: 512
23
+ Timeout: 30
24
+ Environment:
25
+ Variables:
26
+ RAILS_ENV: !Ref RailsEnv
27
+ FunctionName: !Join [ '', [ 'APPNAMEHERE-', !Ref RailsEnv, '-', !Ref 'AWS::Region' ] ]
28
+ Events:
29
+ RailsApiProxy:
30
+ Type: HttpApi
31
+ Properties:
32
+ ApiId: !Ref RailsApi
33
+ AutoPublishAlias: live
34
+
35
+ RailsApi:
36
+ Type: AWS::Serverless::HttpApi
37
+ Properties:
38
+ StageName: !Ref RailsEnv
39
+
40
+ Outputs:
41
+
42
+ RailsApiUrl:
43
+ Description: API Gateway Endpoint
44
+ Value: !Sub "https://${RailsApi}.execute-api.${AWS::Region}.amazonaws.com/${RailsEnv}/"
45
+
46
+ RailsFunctionArn:
47
+ Description: Lambda ARN
48
+ Value: !GetAtt RailsFunction.Arn
@@ -0,0 +1,25 @@
1
+ $LAMBY_INSTALLER_NAME = 'rest'
2
+ load 'lamby/templates/shared.rb'
3
+
4
+ say '==> Running Lamby API Gateway (REST API) installer...'
5
+
6
+ say 'Copying files...'
7
+ copy_file tpl_file('app.rb'), app_file('app.rb')
8
+ copy_file tpl_file('template.yaml'), app_file('template.yaml')
9
+ gsub_file app_file('template.yaml'), /APPNAMEHERE/, appname
10
+
11
+ say 'Adding to .gitignore...'
12
+ FileUtils.touch app_file('.gitignore')
13
+ append_to_file app_file('.gitignore'), <<-GITIGNORE.strip_heredoc
14
+ # Lamby
15
+ /.aws-sam
16
+ GITIGNORE
17
+
18
+ say 'Creating ./bin files for build and deploy...'
19
+ copy_file tpl_file('build'), app_file('bin/build')
20
+ chmod app_file('bin/build'), 0755
21
+ copy_file tpl_file('deploy'), app_file('bin/deploy')
22
+ chmod app_file('bin/deploy'), 0755
23
+ gsub_file app_file('bin/deploy'), /APPNAMEHERE/, appname.downcase
24
+
25
+ say 'Welcome to AWS Lambda and Rails 🎉', :green
@@ -0,0 +1,10 @@
1
+ require_relative 'config/boot'
2
+ require 'lamby'
3
+ require_relative 'config/application'
4
+ require_relative 'config/environment'
5
+
6
+ $app = Rack::Builder.new { run Rails.application }.to_app
7
+
8
+ def handler(event:, context:)
9
+ Lamby.handler $app, event, context, rack: :rest
10
+ end