lamby 1.0.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) 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 +58 -0
  6. data/Dockerfile +12 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +94 -79
  9. data/README.md +9 -3
  10. data/Rakefile +1 -0
  11. data/bin/_setup +6 -0
  12. data/bin/_test +6 -0
  13. data/bin/bootstrap +3 -17
  14. data/bin/setup +3 -3
  15. data/bin/test +6 -3
  16. data/bin/update +2 -2
  17. data/docker-compose.yml +14 -0
  18. data/lamby.gemspec +1 -2
  19. data/lib/lamby.rb +7 -4
  20. data/lib/lamby/debug.rb +8 -1
  21. data/lib/lamby/handler.rb +16 -3
  22. data/lib/lamby/logger.rb +13 -9
  23. data/lib/lamby/rack.rb +21 -9
  24. data/lib/lamby/rack_alb.rb +12 -15
  25. data/lib/lamby/rack_http.rb +84 -0
  26. data/lib/lamby/{rack_api.rb → rack_rest.rb} +7 -3
  27. data/lib/lamby/ssm_parameter_store.rb +9 -8
  28. data/lib/lamby/templates.rake +4 -3
  29. data/lib/lamby/templates/{application_load_balancer.rb → alb.rb} +2 -2
  30. data/lib/lamby/templates/{application_load_balancer → alb}/app.rb +0 -0
  31. data/lib/lamby/templates/{application_load_balancer → alb}/build +1 -1
  32. data/lib/lamby/templates/{application_load_balancer → alb}/deploy +1 -1
  33. data/lib/lamby/templates/{application_load_balancer → alb}/template.yaml +1 -1
  34. data/lib/lamby/templates/{api_gateway.rb → http.rb} +3 -3
  35. data/lib/lamby/templates/{api_gateway → http}/app.rb +1 -1
  36. data/lib/lamby/templates/{api_gateway → http}/build +1 -1
  37. data/lib/lamby/templates/{api_gateway → http}/deploy +1 -1
  38. data/lib/lamby/templates/http/template.yaml +48 -0
  39. data/lib/lamby/templates/rest.rb +25 -0
  40. data/lib/lamby/templates/rest/app.rb +10 -0
  41. data/lib/lamby/templates/rest/build +23 -0
  42. data/lib/lamby/templates/rest/deploy +22 -0
  43. data/lib/lamby/templates/{api_gateway → rest}/template.yaml +2 -2
  44. data/lib/lamby/templates/shared.rb +3 -1
  45. data/lib/lamby/version.rb +1 -1
  46. data/vendor/.keep +0 -0
  47. metadata +29 -37
  48. data/.python-version +0 -1
  49. data/.travis.yml +0 -17
  50. data/Brewfile +0 -5
  51. data/Pipfile +0 -13
  52. data/Pipfile.lock +0 -370
data/bin/setup CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
-
3
2
  set -e
4
3
 
5
- bundle install
6
- pipenv install
4
+ docker-compose run \
5
+ cicd \
6
+ ./bin/_setup
data/bin/test CHANGED
@@ -1,5 +1,8 @@
1
- #!/usr/bin/env bash
2
-
1
+ #!/bin/bash
3
2
  set -e
4
3
 
5
- bundle exec rake test
4
+ export RAILS_ENV="test"
5
+
6
+ docker-compose run \
7
+ cicd \
8
+ ./bin/_test
data/bin/update CHANGED
@@ -1,7 +1,7 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
 
3
3
  set -e
4
4
 
5
- pipenv --rm
5
+ rm -rf ./vendor/bundle
6
6
 
7
7
  ./bin/setup
@@ -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
@@ -19,12 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
  spec.add_dependency 'rack'
22
- spec.add_dependency 'rails'
23
22
  spec.add_development_dependency 'aws-sdk-ssm'
24
23
  spec.add_development_dependency 'bundler'
25
24
  spec.add_development_dependency 'rake'
26
25
  spec.add_development_dependency 'minitest'
26
+ spec.add_development_dependency 'minitest-focus'
27
27
  spec.add_development_dependency 'mocha'
28
28
  spec.add_development_dependency 'pry'
29
- spec.add_development_dependency 'sqlite3'
30
29
  end
@@ -1,16 +1,19 @@
1
1
  require 'lamby/logger'
2
2
  require 'rack'
3
3
  require 'base64'
4
- require 'active_support/all'
5
4
  require 'lamby/version'
6
5
  require 'lamby/sam_helpers'
7
6
  require 'lamby/rack'
8
7
  require 'lamby/rack_alb'
9
- require 'lamby/rack_api'
8
+ require 'lamby/rack_rest'
9
+ require 'lamby/rack_http'
10
10
  require 'lamby/debug'
11
11
  require 'lamby/handler'
12
- require 'rails/railtie'
13
- require 'lamby/railtie'
12
+
13
+ if defined?(Rails)
14
+ require 'rails/railtie'
15
+ require 'lamby/railtie'
16
+ end
14
17
 
15
18
  module Lamby
16
19
 
@@ -6,7 +6,7 @@ module Lamby
6
6
 
7
7
  def on?(event)
8
8
  params = event['multiValueQueryStringParameters'] || event['queryStringParameters']
9
- (Rails.env.development? || ENV['LAMBY_DEBUG']) && params && params['debug'] == '1'
9
+ (development? || ENV['LAMBY_DEBUG']) && params && params['debug'] == '1'
10
10
  end
11
11
 
12
12
  def call(event, context, env)
@@ -42,5 +42,12 @@ module Lamby
42
42
  HTML
43
43
  end
44
44
 
45
+ def development?
46
+ ENV
47
+ .slice('RACK_ENV', 'RAILS_ENV')
48
+ .values
49
+ .any? { |v| v.to_s.casecmp('development').zero? }
50
+ end
51
+
45
52
  end
46
53
  end
@@ -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,26 @@ 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
+ build_query_string
75
+ end
76
+ end
77
+
78
+ def build_query_string
79
+ return if event['queryStringParameters'].nil?
80
+
81
+ Rack::Utils.build_nested_query(
82
+ event.fetch('queryStringParameters')
83
+ )
84
+ .gsub('[', '%5B')
85
+ .gsub(']', '%5D')
74
86
  end
75
87
 
76
88
  def base64_encoded?
@@ -11,7 +11,7 @@ module Lamby
11
11
 
12
12
  def response(handler)
13
13
  hhdrs = handler.headers
14
- multivalue_headers = hhdrs.transform_values { |v| Array.wrap(v) } if multi_value?
14
+ multivalue_headers = hhdrs.transform_values { |v| Array[v].compact.flatten } if multi_value?
15
15
  status_description = "#{handler.status} #{::Rack::Utils::HTTP_STATUS_CODES[handler.status]}"
16
16
  base64_encode = hhdrs['Content-Transfer-Encoding'] == 'binary' || hhdrs['X-Lamby-Base64'] == '1'
17
17
  body = Base64.strict_encode64(handler.body) if base64_encode
@@ -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,84 @@
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 cookies.any?
42
+ hdrs[HTTP_COOKIE] = 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
+ def cookies
52
+ event['cookies'] || []
53
+ end
54
+
55
+ # Using custom domain names with v1.0 yields a good `path` parameter sans
56
+ # stage. However, v2.0 and others do not. So we are just going to remove stage
57
+ # no matter waht from other places for both.
58
+ #
59
+ def path_info
60
+ stage = event.dig('requestContext', 'stage')
61
+ spath = event.dig('requestContext', 'http', 'path') || event.dig('requestContext', 'path')
62
+ spath.sub /\A\/#{stage}/, ''
63
+ end
64
+
65
+ def server_name
66
+ headers['x-forwarded-host'] ||
67
+ headers['X-Forwarded-Host'] ||
68
+ headers['host'] ||
69
+ headers['Host']
70
+ end
71
+
72
+ def server_port
73
+ headers['x-forwarded-port'] || headers['X-Forwarded-Port']
74
+ end
75
+
76
+ def server_protocol
77
+ event.dig('requestContext', 'http', 'protocol') ||
78
+ event.dig('requestContext', 'protocol') ||
79
+ 'HTTP/1.1'
80
+ end
81
+
82
+
83
+ end
84
+ 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
@@ -19,9 +19,10 @@ module Lamby
19
19
  parts = path.from(1).split('/')
20
20
  env = parts.pop
21
21
  path = "/#{parts.join('/')}"
22
- new(path).get!.params.detect do |p|
22
+ param = new(path).get!.params.detect do |p|
23
23
  p.env == env
24
- end.try(:value)
24
+ end
25
+ param&.value
25
26
  end
26
27
 
27
28
  end
@@ -32,9 +33,9 @@ module Lamby
32
33
  @options = options
33
34
  end
34
35
 
35
- def to_env
36
+ def to_env(overwrite: true)
36
37
  params.each do |param|
37
- ENV[param.env] = param.value
38
+ overwrite ? ENV[param.env] = param.value : ENV[param.env] ||= param.value
38
39
  end
39
40
  end
40
41
 
@@ -44,7 +45,7 @@ module Lamby
44
45
 
45
46
  def get!
46
47
  get_all!
47
- get_history! if label.present?
48
+ get_history! unless label.to_s.empty?
48
49
  self
49
50
  end
50
51
 
@@ -68,7 +69,7 @@ module Lamby
68
69
 
69
70
  def dotenv_contents
70
71
  params.each_with_object('') do |param, contents|
71
- line = "export #{param.env}=#{param.value}\n"
72
+ line = "#{param.env}=#{param.value}\n"
72
73
  contents << line
73
74
  end
74
75
  end
@@ -98,7 +99,7 @@ module Lamby
98
99
  with_decryption: true,
99
100
  max_results: MAX_RESULTS
100
101
  }.tap { |options|
101
- token = @all_response.try(:next_token)
102
+ token = @all_response&.next_token
102
103
  options[:next_token] = token if token
103
104
  }
104
105
  end
@@ -137,7 +138,7 @@ module Lamby
137
138
  with_decryption: true,
138
139
  max_results: MAX_RESULTS
139
140
  }.tap { |options|
140
- token = @hist_response.try(:next_token)
141
+ token = @hist_response&.next_token
141
142
  options[:next_token] = token if token
142
143
  }
143
144
  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...'
@@ -10,7 +10,7 @@ gsub_file app_file('template.yaml'), /APPNAMEHERE/, appname
10
10
 
11
11
  say 'Adding to .gitignore...'
12
12
  FileUtils.touch app_file('.gitignore')
13
- append_to_file app_file('.gitignore'), <<-GITIGNORE.strip_heredoc
13
+ append_to_file app_file('.gitignore'), <<~GITIGNORE
14
14
  # Lamby
15
15
  /.aws-sam
16
16
  GITIGNORE