lamby-no-rails 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.python-version +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +17 -0
- data/Brewfile +5 -0
- data/CHANGELOG.md +109 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +168 -0
- data/LICENSE.txt +21 -0
- data/Pipfile +13 -0
- data/Pipfile.lock +370 -0
- data/README.md +37 -0
- data/Rakefile +12 -0
- data/bin/bootstrap +20 -0
- data/bin/console +14 -0
- data/bin/setup +6 -0
- data/bin/test +5 -0
- data/bin/update +7 -0
- data/lamby.gemspec +29 -0
- data/lib/lamby.rb +25 -0
- data/lib/lamby/debug.rb +46 -0
- data/lib/lamby/handler.rb +71 -0
- data/lib/lamby/logger.rb +23 -0
- data/lib/lamby/rack.rb +85 -0
- data/lib/lamby/rack_alb.rb +73 -0
- data/lib/lamby/rack_api.rb +36 -0
- data/lib/lamby/railtie.rb +12 -0
- data/lib/lamby/sam_helpers.rb +9 -0
- data/lib/lamby/ssm_parameter_store.rb +154 -0
- data/lib/lamby/tasks.rake +15 -0
- data/lib/lamby/templates.rake +36 -0
- data/lib/lamby/templates/api_gateway.rb +25 -0
- data/lib/lamby/templates/api_gateway/app.rb +10 -0
- data/lib/lamby/templates/api_gateway/build +23 -0
- data/lib/lamby/templates/api_gateway/deploy +22 -0
- data/lib/lamby/templates/api_gateway/template.yaml +84 -0
- data/lib/lamby/templates/application_load_balancer.rb +25 -0
- data/lib/lamby/templates/application_load_balancer/app.rb +10 -0
- data/lib/lamby/templates/application_load_balancer/build +23 -0
- data/lib/lamby/templates/application_load_balancer/deploy +39 -0
- data/lib/lamby/templates/application_load_balancer/template.yaml +90 -0
- data/lib/lamby/templates/shared.rb +15 -0
- data/lib/lamby/version.rb +3 -0
- metadata +199 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'aws-sdk-ssm'
|
2
|
+
|
3
|
+
module Lamby
|
4
|
+
class SsmParameterStore
|
5
|
+
|
6
|
+
MAX_RESULTS = 10
|
7
|
+
|
8
|
+
Param = Struct.new :name, :env, :value
|
9
|
+
|
10
|
+
attr_reader :path, :params
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def dotenv(path)
|
15
|
+
new(path).get!.to_dotenv
|
16
|
+
end
|
17
|
+
|
18
|
+
def get!(path)
|
19
|
+
parts = path.from(1).split('/')
|
20
|
+
env = parts.pop
|
21
|
+
path = "/#{parts.join('/')}"
|
22
|
+
new(path).get!.params.detect do |p|
|
23
|
+
p.env == env
|
24
|
+
end.try(:value)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(path, options = {})
|
30
|
+
@path = path
|
31
|
+
@params = []
|
32
|
+
@options = options
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_env(overwrite: true)
|
36
|
+
params.each do |param|
|
37
|
+
overwrite ? ENV[param.env] = param.value : ENV[param.env] ||= param.value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_dotenv
|
42
|
+
File.open(dotenv_file, 'w') { |f| f.write(dotenv_contents) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def get!
|
46
|
+
get_all!
|
47
|
+
get_history! if label.present?
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def label
|
52
|
+
ENV['LAMBY_SSM_PARAMS_LABEL'] || @options[:label]
|
53
|
+
end
|
54
|
+
|
55
|
+
def client
|
56
|
+
@client ||= begin
|
57
|
+
options = @options[:client_options] || {}
|
58
|
+
Aws::SSM::Client.new(options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def dotenv_file
|
66
|
+
@options[:dotenv_file] || ENV['LAMBY_SSM_PARAMS_FILE'] || Rails.root.join(".env.#{Rails.env}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def dotenv_contents
|
70
|
+
params.each_with_object('') do |param, contents|
|
71
|
+
line = "export #{param.env}=#{param.value}\n"
|
72
|
+
contents << line
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Path
|
77
|
+
|
78
|
+
def get_all!
|
79
|
+
return params if @got_all
|
80
|
+
get_parse_all
|
81
|
+
while @all_response.next_token do get_parse_all end
|
82
|
+
@got_all = true
|
83
|
+
params
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_parse_all
|
87
|
+
get_all
|
88
|
+
parse_all
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_all
|
92
|
+
@all_response = client.get_parameters_by_path(get_all_options)
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_all_options
|
96
|
+
{ path: path,
|
97
|
+
recursive: true,
|
98
|
+
with_decryption: true,
|
99
|
+
max_results: MAX_RESULTS
|
100
|
+
}.tap { |options|
|
101
|
+
token = @all_response.try(:next_token)
|
102
|
+
options[:next_token] = token if token
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_all
|
107
|
+
@all_response.parameters.each do |p|
|
108
|
+
env = p.name.split('/').last
|
109
|
+
params << Param.new(p.name, env, p.value)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# History
|
114
|
+
|
115
|
+
def get_history!
|
116
|
+
return params if @got_history
|
117
|
+
params.each do |param|
|
118
|
+
name = param.name
|
119
|
+
get_parse_history(name)
|
120
|
+
while @hist_response.next_token do get_parse_history(name) end
|
121
|
+
end
|
122
|
+
@got_history = true
|
123
|
+
params
|
124
|
+
end
|
125
|
+
|
126
|
+
def get_parse_history(name)
|
127
|
+
get_history(name)
|
128
|
+
parse_history(name)
|
129
|
+
end
|
130
|
+
|
131
|
+
def get_history(name)
|
132
|
+
@hist_response = client.get_parameter_history(get_history_options(name))
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_history_options(name)
|
136
|
+
{ name: name,
|
137
|
+
with_decryption: true,
|
138
|
+
max_results: MAX_RESULTS
|
139
|
+
}.tap { |options|
|
140
|
+
token = @hist_response.try(:next_token)
|
141
|
+
options[:next_token] = token if token
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse_history(name)
|
146
|
+
@hist_response.parameters.each do |p|
|
147
|
+
next unless p.labels.include? label
|
148
|
+
param = params.detect { |param| param.name == name }
|
149
|
+
param.value = p.value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
namespace :lamby do
|
2
|
+
|
3
|
+
namespace :ssm do
|
4
|
+
|
5
|
+
desc 'Create a Dotenv file.'
|
6
|
+
task :dotenv do
|
7
|
+
require 'lamby/ssm_parameter_store'
|
8
|
+
path = ENV['LAMBY_SSM_PARAMS_PATH']
|
9
|
+
raise ArgumentError, 'The LAMBY_SSM_PARAMS_PATH env is required.' unless path
|
10
|
+
Lamby::SsmParameterStore.dotenv(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
installers = {
|
2
|
+
'API Gateway' => :api_gateway,
|
3
|
+
'Application Load Balancer' => :application_load_balancer
|
4
|
+
}.freeze
|
5
|
+
|
6
|
+
namespace :lamby do
|
7
|
+
namespace :install do
|
8
|
+
|
9
|
+
installers.each do |name, task_name|
|
10
|
+
desc "Install Lamby files for #{name}"
|
11
|
+
task task_name do
|
12
|
+
exec "#{base_path} LOCATION=#{template(task_name)}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def template(task_name)
|
17
|
+
File.expand_path "../lamby/templates/#{task_name}.rb", __dir__
|
18
|
+
end
|
19
|
+
|
20
|
+
def bin_path
|
21
|
+
ENV['BUNDLE_BIN'] || './bin'
|
22
|
+
end
|
23
|
+
|
24
|
+
def base_path
|
25
|
+
if Rails::VERSION::MAJOR >= 5
|
26
|
+
"#{RbConfig.ruby} #{bin_path}/rails app:template"
|
27
|
+
else
|
28
|
+
"#{RbConfig.ruby} #{bin_path}/rake rails:template"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Install Lamby files for #{installers.first.first}"
|
35
|
+
task install: 'install:application_load_balancer'
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$LAMBY_INSTALLER_NAME = 'api_gateway'
|
2
|
+
load 'lamby/templates/shared.rb'
|
3
|
+
|
4
|
+
say '==> Running Lamby API Gateway 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: :api
|
10
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
# Clean any previous bundle/builds.
|
5
|
+
rm -rf ./.aws-sam/build
|
6
|
+
|
7
|
+
# Ensure native extensions built for platform.
|
8
|
+
sam build --use-container
|
9
|
+
|
10
|
+
# [HOOK] Environments & Configuration
|
11
|
+
# https://lamby.custominktech.com/docs/environment_and_configuration
|
12
|
+
|
13
|
+
# [HOOK] Asset Hosts & Precompiling
|
14
|
+
# https://lamby.custominktech.com/docs/asset_host_and_precompiling
|
15
|
+
|
16
|
+
# Clean un-needed artifacts.
|
17
|
+
pushd ./.aws-sam/build/RailsFunction/
|
18
|
+
rm -rf .aws-sam \
|
19
|
+
.git \
|
20
|
+
log \
|
21
|
+
test \
|
22
|
+
tmp
|
23
|
+
popd
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
export RAILS_ENV=${RAILS_ENV:="production"}
|
5
|
+
export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:=us-east-1}
|
6
|
+
export CLOUDFORMATION_BUCKET=${CLOUDFORMATION_BUCKET:="lamby.cloudformation.$(whoami)"}
|
7
|
+
|
8
|
+
./bin/build
|
9
|
+
|
10
|
+
sam package \
|
11
|
+
--region ${AWS_DEFAULT_REGION} \
|
12
|
+
--template-file ./.aws-sam/build/template.yaml \
|
13
|
+
--output-template-file ./.aws-sam/build/packaged.yaml \
|
14
|
+
--s3-bucket $CLOUDFORMATION_BUCKET \
|
15
|
+
--s3-prefix "APPNAMEHERE-${RAILS_ENV}"
|
16
|
+
|
17
|
+
sam deploy \
|
18
|
+
--template-file ./.aws-sam/build/packaged.yaml \
|
19
|
+
--stack-name "APPNAMEHERE-${RAILS_ENV}-${AWS_DEFAULT_REGION}" \
|
20
|
+
--capabilities "CAPABILITY_IAM" \
|
21
|
+
--parameter-overrides \
|
22
|
+
RailsEnv=${RAILS_ENV}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
2
|
+
Transform: AWS::Serverless-2016-10-31
|
3
|
+
Description: APPNAMEHERE Lambda (API)
|
4
|
+
|
5
|
+
Parameters:
|
6
|
+
|
7
|
+
RailsEnv:
|
8
|
+
Type: String
|
9
|
+
Default: production
|
10
|
+
AllowedValues:
|
11
|
+
- staging
|
12
|
+
- production
|
13
|
+
|
14
|
+
Resources:
|
15
|
+
|
16
|
+
RailsApi:
|
17
|
+
Type: AWS::Serverless::Api
|
18
|
+
Properties:
|
19
|
+
StageName: !Ref RailsEnv
|
20
|
+
EndpointConfiguration: REGIONAL
|
21
|
+
DefinitionBody:
|
22
|
+
swagger: 2.0
|
23
|
+
info: { title: !Ref 'AWS::StackName' }
|
24
|
+
basePath: !Join [ '', [ '/', !Ref RailsEnv ] ]
|
25
|
+
schemes: [ 'https' ]
|
26
|
+
paths:
|
27
|
+
/:
|
28
|
+
x-amazon-apigateway-any-method:
|
29
|
+
x-amazon-apigateway-integration:
|
30
|
+
uri:
|
31
|
+
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RailsFunction.Arn}:live/invocations
|
32
|
+
httpMethod: POST
|
33
|
+
type: aws_proxy
|
34
|
+
/{resource+}:
|
35
|
+
x-amazon-apigateway-any-method:
|
36
|
+
parameters:
|
37
|
+
- name: resource
|
38
|
+
in: path
|
39
|
+
required: true
|
40
|
+
type: string
|
41
|
+
x-amazon-apigateway-integration:
|
42
|
+
uri:
|
43
|
+
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RailsFunction.Arn}:live/invocations
|
44
|
+
httpMethod: POST
|
45
|
+
type: aws_proxy
|
46
|
+
x-amazon-apigateway-binary-media-types:
|
47
|
+
- '*/*'
|
48
|
+
|
49
|
+
RailsFunction:
|
50
|
+
Type: AWS::Serverless::Function
|
51
|
+
Properties:
|
52
|
+
CodeUri: .
|
53
|
+
Handler: app.handler
|
54
|
+
Runtime: ruby2.5
|
55
|
+
MemorySize: 512
|
56
|
+
Timeout: 30
|
57
|
+
Environment:
|
58
|
+
Variables:
|
59
|
+
RAILS_ENV: !Ref RailsEnv
|
60
|
+
FunctionName: !Join [ '', [ 'APPNAMEHERE-', !Ref RailsEnv, '-', !Ref 'AWS::Region' ] ]
|
61
|
+
Events:
|
62
|
+
Root:
|
63
|
+
Type: Api
|
64
|
+
Properties:
|
65
|
+
Path: /
|
66
|
+
Method: ANY
|
67
|
+
RestApiId: !Ref RailsApi
|
68
|
+
RailsAppAll:
|
69
|
+
Type: Api
|
70
|
+
Properties:
|
71
|
+
Path: /{resource+}
|
72
|
+
Method: ANY
|
73
|
+
RestApiId: !Ref RailsApi
|
74
|
+
AutoPublishAlias: live
|
75
|
+
|
76
|
+
Outputs:
|
77
|
+
|
78
|
+
RailsApiUrl:
|
79
|
+
Description: API Gateway Endpoint
|
80
|
+
Value: !Sub "https://${RailsApi}.execute-api.${AWS::Region}.amazonaws.com/${RailsEnv}/"
|
81
|
+
|
82
|
+
RailsFunctionArn:
|
83
|
+
Description: Lambda ARN
|
84
|
+
Value: !GetAtt RailsFunction.Arn
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$LAMBY_INSTALLER_NAME = 'application_load_balancer'
|
2
|
+
load 'lamby/templates/shared.rb'
|
3
|
+
|
4
|
+
say '==> Running Lamby Application Load Balancer 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: :alb
|
10
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
# Clean any previous bundle/builds.
|
5
|
+
rm -rf ./.aws-sam/build
|
6
|
+
|
7
|
+
# Ensure native extensions built for platform.
|
8
|
+
sam build --use-container
|
9
|
+
|
10
|
+
# [HOOK] Environments & Configuration
|
11
|
+
# https://lamby.custominktech.com/docs/environment_and_configuration
|
12
|
+
|
13
|
+
# [HOOK] Asset Hosts & Precompiling
|
14
|
+
# https://lamby.custominktech.com/docs/asset_host_and_precompiling
|
15
|
+
|
16
|
+
# Clean un-needed artifacts.
|
17
|
+
pushd ./.aws-sam/build/RailsFunction/
|
18
|
+
rm -rf .aws-sam \
|
19
|
+
.git \
|
20
|
+
log \
|
21
|
+
test \
|
22
|
+
tmp
|
23
|
+
popd
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
export RAILS_ENV=${RAILS_ENV:="production"}
|
5
|
+
export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:=us-east-1}
|
6
|
+
export CLOUDFORMATION_BUCKET=${CLOUDFORMATION_BUCKET:="lamby.cloudformation.$(whoami)"}
|
7
|
+
|
8
|
+
./bin/build
|
9
|
+
|
10
|
+
export VPCID=${VPCID:=$(
|
11
|
+
aws ec2 describe-vpcs \
|
12
|
+
--output text \
|
13
|
+
--filters 'Name=state,Values=available' \
|
14
|
+
--query 'Vpcs[0].VpcId'
|
15
|
+
)}
|
16
|
+
|
17
|
+
export SUBNETS=${SUBNETS:=$(
|
18
|
+
aws ec2 describe-subnets \
|
19
|
+
--output text \
|
20
|
+
--filters "Name=state,Values=available,Name=vpc-id,Values=$VPCID" \
|
21
|
+
--query 'Subnets[*].SubnetId' | \
|
22
|
+
tr -s '[:blank:]' ','
|
23
|
+
)}
|
24
|
+
|
25
|
+
sam package \
|
26
|
+
--region ${AWS_DEFAULT_REGION} \
|
27
|
+
--template-file ./.aws-sam/build/template.yaml \
|
28
|
+
--output-template-file ./.aws-sam/build/packaged.yaml \
|
29
|
+
--s3-bucket $CLOUDFORMATION_BUCKET \
|
30
|
+
--s3-prefix "APPNAMEHERE-${RAILS_ENV}"
|
31
|
+
|
32
|
+
sam deploy \
|
33
|
+
--template-file ./.aws-sam/build/packaged.yaml \
|
34
|
+
--stack-name "APPNAMEHERE-${RAILS_ENV}-${AWS_DEFAULT_REGION}" \
|
35
|
+
--capabilities "CAPABILITY_IAM" \
|
36
|
+
--parameter-overrides \
|
37
|
+
RailsEnv=${RAILS_ENV} \
|
38
|
+
VpcId=${VPCID} \
|
39
|
+
Subnets=${SUBNETS}
|