jets 2.3.19 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +0 -3
- data/.python-version +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +28 -2
- data/README.md +2 -2
- data/backers.md +1 -0
- data/jets.gemspec +11 -10
- data/lib/jets.rb +9 -13
- data/lib/jets/application.rb +9 -2
- data/lib/jets/application/defaults.rb +17 -15
- data/lib/jets/autoloaders.rb +15 -1
- data/lib/jets/booter.rb +3 -3
- data/lib/jets/builders/code_builder.rb +16 -15
- data/lib/jets/builders/gem_replacer.rb +3 -16
- data/lib/jets/builders/lambda_layer.rb +4 -5
- data/lib/jets/builders/ruby_packager.rb +18 -42
- data/lib/jets/builders/tidy.rb +1 -2
- data/lib/jets/bundle.rb +6 -0
- data/lib/jets/cfn/builders/api_gateway_builder.rb +61 -7
- data/lib/jets/cfn/ship.rb +2 -1
- data/lib/jets/cli.rb +6 -1
- data/lib/jets/commands/base.rb +1 -1
- data/lib/jets/commands/call.rb +14 -1
- data/lib/jets/commands/clean.rb +1 -1
- data/lib/jets/commands/clean/base.rb +1 -1
- data/lib/jets/commands/configure.rb +51 -0
- data/lib/jets/commands/delete.rb +2 -2
- data/lib/jets/commands/gems.rb +2 -10
- data/lib/jets/commands/help/call.md +8 -0
- data/lib/jets/commands/help/gems/check.md +3 -5
- data/lib/jets/commands/main.rb +9 -1
- data/lib/jets/commands/new.rb +9 -1
- data/lib/jets/commands/sequence.rb +6 -0
- data/lib/jets/commands/templates/skeleton/Gemfile.tt +1 -1
- data/lib/jets/commands/templates/skeleton/config/application.rb.tt +1 -1
- data/lib/jets/commands/templates/skeleton/public/index.html.tt +1 -1
- data/lib/jets/commands/url.rb +1 -0
- data/lib/jets/controller/base.rb +14 -4
- data/lib/jets/controller/middleware/main.rb +2 -1
- data/lib/jets/controller/params.rb +26 -4
- data/lib/jets/controller/rack/env.rb +18 -1
- data/lib/jets/controller/rendering.rb +5 -2
- data/lib/jets/controller/rendering/rack_renderer.rb +7 -1
- data/lib/jets/core.rb +12 -4
- data/lib/jets/dotenv/ssm.rb +18 -4
- data/lib/jets/generator.rb +2 -3
- data/lib/jets/internal/app/functions/jets/base_path.rb +10 -149
- data/lib/jets/internal/app/functions/jets/base_path_mapping.rb +81 -0
- data/lib/jets/internal/app/shared/functions/jets/s3_bucket_config.rb +14 -24
- data/lib/jets/resource/api_gateway/base_path/function.rb +6 -1
- data/lib/jets/resource/api_gateway/deployment.rb +2 -0
- data/lib/jets/resource/api_gateway/rest_api/logical_id.rb +34 -0
- data/lib/jets/resource/api_gateway/rest_api/logical_id/message.rb +49 -0
- data/lib/jets/resource/child_stack/api_deployment.rb +2 -0
- data/lib/jets/resource/lambda/function.rb +1 -1
- data/lib/jets/router/dsl.rb +7 -1
- data/lib/jets/router/method_creator/code.rb +1 -1
- data/lib/jets/router/scope.rb +7 -3
- data/lib/jets/spec_helpers/controllers.rb +10 -3
- data/lib/jets/spec_helpers/controllers/request.rb +12 -5
- data/lib/jets/stack/main/dsl/lambda.rb +1 -1
- data/lib/jets/turbo.rb +1 -0
- data/lib/jets/version.rb +1 -1
- metadata +51 -58
- data/vendor/cfn-status/CHANGELOG.md +0 -14
- data/vendor/cfn-status/Gemfile +0 -4
- data/vendor/cfn-status/LICENSE.txt +0 -21
- data/vendor/cfn-status/README.md +0 -56
- data/vendor/cfn-status/Rakefile +0 -6
- data/vendor/cfn-status/bin/console +0 -14
- data/vendor/cfn-status/bin/setup +0 -8
- data/vendor/cfn-status/cfn-status.gemspec +0 -30
- data/vendor/cfn-status/lib/cfn-status.rb +0 -1
- data/vendor/cfn-status/lib/cfn_status.rb +0 -245
- data/vendor/cfn-status/lib/cfn_status/aws_service.rb +0 -51
- data/vendor/cfn-status/lib/cfn_status/version.rb +0 -3
- data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-1.json +0 -1103
- data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-2.json +0 -1104
- data/vendor/cfn-status/spec/fixtures/cfn/pages/fresh/describe_stack_events-3.json +0 -1103
- data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-1.json +0 -1103
- data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-2.json +0 -1104
- data/vendor/cfn-status/spec/fixtures/cfn/pages/updating/describe_stack_events-3.json +0 -1103
- data/vendor/cfn-status/spec/fixtures/cfn/stack-events-complete.json +0 -1080
- data/vendor/cfn-status/spec/fixtures/cfn/stack-events-in-progress.json +0 -1080
- data/vendor/cfn-status/spec/fixtures/cfn/stack-events-update-rollback-complete.json +0 -1086
- data/vendor/cfn-status/spec/lib/cfn_status_spec.rb +0 -153
- data/vendor/cfn-status/spec/spec_helper.rb +0 -14
@@ -53,6 +53,14 @@ Jets figures out what functions to call by evaluating the app code and finds if
|
|
53
53
|
|
54
54
|
jets call admin-related_pages_controller-index --no-guess
|
55
55
|
|
56
|
+
If you want to call a function which runs too long time, you can set `read_timeout`.
|
57
|
+
|
58
|
+
jets call some_long_job-index --read_timeout 900
|
59
|
+
|
60
|
+
And you can set `retry_limit`. If you don't want to retry you can set 0.
|
61
|
+
|
62
|
+
jets call some_long_job-index --retry_limit 0
|
63
|
+
|
56
64
|
## Local mode
|
57
65
|
|
58
66
|
Instead of calling AWS lambda remote, you can also have `jets call` use the code directly on your machine. To enable this, use the `--local` flag. Example:
|
@@ -1,8 +1,6 @@
|
|
1
|
-
You can configure
|
1
|
+
Check if pre-built Lambda gems are available from the gems source. You can configure the gem in config/application.rb:
|
2
2
|
|
3
3
|
# Sources for check for pre-compiled Lambda gems. Checks the list in order.
|
4
4
|
Jets.application.configure do
|
5
|
-
config.gems.
|
6
|
-
|
7
|
-
]
|
8
|
-
end
|
5
|
+
config.gems.source = "https://api.serverlessgems.com/api/v1"
|
6
|
+
end
|
data/lib/jets/commands/main.rb
CHANGED
@@ -12,6 +12,12 @@ module Jets::Commands
|
|
12
12
|
Build.new(options).run
|
13
13
|
end
|
14
14
|
|
15
|
+
desc "configure [TOKEN]", "configure token and updates ~/.jets/config.yml"
|
16
|
+
long_desc Help.text(:configure)
|
17
|
+
def configure(token=nil)
|
18
|
+
Configure.new(options.merge(token: token)).run
|
19
|
+
end
|
20
|
+
|
15
21
|
desc "deploy [environment]", "Builds and deploys project to AWS Lambda"
|
16
22
|
long_desc Help.text(:deploy)
|
17
23
|
# Note the environment is here to trick the Thor parser to allowing an
|
@@ -23,7 +29,7 @@ module Jets::Commands
|
|
23
29
|
|
24
30
|
desc "delete", "Delete the Jets project and all its resources"
|
25
31
|
long_desc Help.text(:delete)
|
26
|
-
option :
|
32
|
+
option :yes, aliases: %w[y], type: :boolean, desc: "Skip are you sure prompt."
|
27
33
|
option :wait, type: :boolean, default: true, desc: "Wait for stack deletion to complete."
|
28
34
|
# Note the environment is here to trick the Thor parser to allowing an
|
29
35
|
# environment parameter. It is not actually set here. It is set earlier
|
@@ -83,6 +89,8 @@ module Jets::Commands
|
|
83
89
|
option :lambda_proxy, type: :boolean, default: true, desc: "Enables automatic Lambda proxy transformation of the event payload"
|
84
90
|
option :guess, type: :boolean, default: true, desc: "Enables guess mode. Uses inference to allows use of all dashes to specify functions. Guess mode verifies that the function exists in the code base."
|
85
91
|
option :local, type: :boolean, desc: "Enables local mode. Instead of invoke the AWS Lambda function, the method gets called locally with current app code. With local mode guess mode is always used."
|
92
|
+
option :retry_limit, type: :numeric, default: nil, desc: "Retry count of invoking function. It work with remote call"
|
93
|
+
option :read_timeout, type: :numeric, default: nil, desc: " The number of seconds to wait for response data. It work with remote call"
|
86
94
|
def call(function_name, payload='')
|
87
95
|
# Printing to stdout can mangle up the response when piping
|
88
96
|
# the value to jq. For example:
|
data/lib/jets/commands/new.rb
CHANGED
@@ -89,6 +89,14 @@ module Jets::Commands
|
|
89
89
|
run(command)
|
90
90
|
end
|
91
91
|
|
92
|
+
def update_package_json
|
93
|
+
path = "package.json"
|
94
|
+
return unless File.exist?(path)
|
95
|
+
data = JSON.load(IO.read(path))
|
96
|
+
data["private"] = true
|
97
|
+
IO.write(path, JSON.pretty_generate(data))
|
98
|
+
end
|
99
|
+
|
92
100
|
# bootstrap is dependent on webpacker, options[:bootstrap] is used
|
93
101
|
# in webpacker_install.
|
94
102
|
def bootstrap_install
|
@@ -106,7 +114,7 @@ JS
|
|
106
114
|
after = "const { environment } = require('@rails/webpacker')\n"
|
107
115
|
insert_into_file("config/webpack/environment.js", jquery, after: after)
|
108
116
|
|
109
|
-
run("yarn add bootstrap
|
117
|
+
run("yarn add bootstrap jquery popper.js postcss-cssnext")
|
110
118
|
end
|
111
119
|
|
112
120
|
def git_init
|
@@ -10,6 +10,12 @@ class Jets::Commands::Sequence < Thor::Group
|
|
10
10
|
end
|
11
11
|
|
12
12
|
private
|
13
|
+
def jets_minor_version
|
14
|
+
md = Jets::VERSION.match(/(\d+)\.(\d+)\.\d+/)
|
15
|
+
major, minor = md[1], md[2]
|
16
|
+
[major, minor, '0'].join('.')
|
17
|
+
end
|
18
|
+
|
13
19
|
def clone_project
|
14
20
|
unless git_installed?
|
15
21
|
abort "Unable to detect git installation on your system. Git needs to be installed in order to use the --repo option."
|
@@ -63,7 +63,7 @@ Jets.application.configure do
|
|
63
63
|
# By default logger needs to log to $stderr for CloudWatch to receive Lambda messages, but for
|
64
64
|
# local testing environment you may want to log these messages to 'test.log' file to keep your
|
65
65
|
# testing suite output readable.
|
66
|
-
# config.logger = Jets::Logger.new($
|
66
|
+
# config.logger = Jets::Logger.new($stderr)
|
67
67
|
|
68
68
|
<% if @options[:mode] == 'api' -%>
|
69
69
|
config.controllers.default_protect_from_forgery = false
|
data/lib/jets/commands/url.rb
CHANGED
@@ -44,6 +44,7 @@ module Jets::Commands
|
|
44
44
|
# domain_name is a method on the Jets::Resource::ApiGateway::Domain instance
|
45
45
|
url = "https://#{domain_name.domain_name}"
|
46
46
|
puts "Custom Domain: #{url}"
|
47
|
+
puts "App Domain: #{Jets.config.app.domain}" if Jets.config.app.domain
|
47
48
|
end
|
48
49
|
|
49
50
|
def endpoint_unavailable?
|
data/lib/jets/controller/base.rb
CHANGED
@@ -76,7 +76,7 @@ class Jets::Controller
|
|
76
76
|
#
|
77
77
|
def dispatch!
|
78
78
|
t1 = Time.now
|
79
|
-
|
79
|
+
log_start
|
80
80
|
|
81
81
|
begin
|
82
82
|
if run_before_actions(break_if: -> { @rendered })
|
@@ -95,13 +95,13 @@ class Jets::Controller
|
|
95
95
|
|
96
96
|
took = Time.now - t1
|
97
97
|
status = triplet[0]
|
98
|
-
|
98
|
+
log_finish(status: status, took: took)
|
99
99
|
triplet # status, headers, body
|
100
100
|
end
|
101
101
|
|
102
|
-
|
102
|
+
# Documented interface method, careful not to rename
|
103
|
+
def log_start
|
103
104
|
# JSON.dump makes logging look pretty in CloudWatch logs because it keeps it on 1 line
|
104
|
-
|
105
105
|
ip = request.ip
|
106
106
|
Jets.logger.info "Started #{@event['httpMethod']} \"#{@event['path']}\" for #{ip} at #{Time.now}"
|
107
107
|
Jets.logger.info "Processing #{self.class.name}##{@meth}"
|
@@ -109,6 +109,12 @@ class Jets::Controller
|
|
109
109
|
Jets.logger.info " Parameters: #{JSON.dump(filtered_parameters.to_h)}"
|
110
110
|
end
|
111
111
|
|
112
|
+
# Documented interface method, careful not to rename
|
113
|
+
def log_finish(options={})
|
114
|
+
status, took = options[:status], options[:took]
|
115
|
+
Jets.logger.info "Completed Status Code #{status} in #{took}s"
|
116
|
+
end
|
117
|
+
|
112
118
|
def event_log
|
113
119
|
display_event = @event.dup
|
114
120
|
|
@@ -141,6 +147,10 @@ class Jets::Controller
|
|
141
147
|
paths
|
142
148
|
end
|
143
149
|
|
150
|
+
def controller_name
|
151
|
+
self.class.to_s.underscore
|
152
|
+
end
|
153
|
+
|
144
154
|
def action_name
|
145
155
|
@meth
|
146
156
|
end
|
@@ -40,7 +40,8 @@ module Jets::Controller::Middleware
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def jets_host
|
43
|
-
|
43
|
+
protocol = @event.dig('headers', 'X-Forwarded-Proto') || @env['rack.url_scheme']
|
44
|
+
default = "#{protocol}://#{@env['HTTP_HOST']}"
|
44
45
|
Jets.config.helpers.host || default
|
45
46
|
end
|
46
47
|
|
@@ -14,11 +14,9 @@ class Jets::Controller
|
|
14
14
|
# 2. query string parameters
|
15
15
|
# 3. body parameters
|
16
16
|
def params(raw: false, path_parameters: true, body_parameters: true)
|
17
|
-
path_params = event["pathParameters"] || {}
|
18
|
-
|
19
17
|
params = {}
|
20
18
|
params = params.deep_merge(body_params) if body_parameters
|
21
|
-
params = params.deep_merge(
|
19
|
+
params = params.deep_merge(unescape_recursively(query_params)) # always
|
22
20
|
params = params.deep_merge(path_params) if path_parameters
|
23
21
|
|
24
22
|
if raw
|
@@ -29,13 +27,36 @@ class Jets::Controller
|
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
30
|
+
def unescape_recursively(obj)
|
31
|
+
case obj
|
32
|
+
when Hash then obj.map { |k, v| [k, unescape_recursively(v)] }.to_h
|
33
|
+
when Array then obj.map { |v| unescape_recursively(v) }
|
34
|
+
else CGI.unescape(obj.to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
32
38
|
def filtered_parameters(**kwargs)
|
33
39
|
parameter_filter.filter params(**kwargs, raw: true) # Always filter raw hash
|
34
40
|
end
|
35
41
|
|
36
|
-
def
|
42
|
+
def path_params
|
43
|
+
path_params = event["pathParameters"] || {}
|
44
|
+
path_params = path_params.map { |k, path| [k, CGI.unescape(path)] }.to_h
|
45
|
+
end
|
46
|
+
alias_method :path_parameters, :path_params
|
47
|
+
|
48
|
+
def query_params
|
37
49
|
event["queryStringParameters"] || {}
|
38
50
|
end
|
51
|
+
alias_method :query_parameters, :query_params
|
52
|
+
|
53
|
+
def request_params
|
54
|
+
{
|
55
|
+
controller: controller_name,
|
56
|
+
action: action_name,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
alias_method :request_parameters, :request_params
|
39
60
|
|
40
61
|
def body_params
|
41
62
|
body = event['isBase64Encoded'] ? base64_decode(event["body"]) : event["body"]
|
@@ -58,6 +79,7 @@ class Jets::Controller
|
|
58
79
|
{} # fallback to empty Hash
|
59
80
|
end
|
60
81
|
memoize :body_params
|
82
|
+
alias_method :body_parameters, :body_params
|
61
83
|
|
62
84
|
private
|
63
85
|
|
@@ -13,7 +13,8 @@ module Jets::Controller::Rack
|
|
13
13
|
options = {}
|
14
14
|
options = add_top_level(options)
|
15
15
|
options = add_http_headers(options)
|
16
|
-
path = @event['path'] || '/' # always set by API Gateway but might not be when testing shim, so setting it to make testing easier
|
16
|
+
path = path_with_base_path || @event['path'] || '/' # always set by API Gateway but might not be when testing shim, so setting it to make testing easier
|
17
|
+
|
17
18
|
env = Rack::MockRequest.env_for(path, options)
|
18
19
|
if @options[:adapter]
|
19
20
|
env['adapter.event'] = @event
|
@@ -23,6 +24,22 @@ module Jets::Controller::Rack
|
|
23
24
|
end
|
24
25
|
|
25
26
|
private
|
27
|
+
def path_with_base_path
|
28
|
+
resource = @event['resource']
|
29
|
+
pathParameters = @event['pathParameters']
|
30
|
+
|
31
|
+
if(!pathParameters.nil? and !resource.nil?)
|
32
|
+
resource = pathParameters.reduce(resource) {|resource, parameter|
|
33
|
+
key, value = parameter
|
34
|
+
key = key.eql?("catchall") ? "{#{key}+}" : "{#{key}}"
|
35
|
+
resource = resource.gsub(key, value)
|
36
|
+
resource
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
resource
|
41
|
+
end
|
42
|
+
|
26
43
|
def add_top_level(options)
|
27
44
|
map = {
|
28
45
|
'CONTENT_TYPE' => content_type,
|
@@ -71,9 +71,12 @@ class Jets::Controller
|
|
71
71
|
add_stage_name(url)
|
72
72
|
end
|
73
73
|
|
74
|
+
# Actual host can be headers["origin"] when cloudfront is in front.
|
75
|
+
# Remember to set custom header "origin" header in cloudfront distribution.
|
76
|
+
# Can also override with Jets.config.app.domain.
|
77
|
+
# The actual_host value is used by redirect_to.
|
74
78
|
def actual_host
|
75
|
-
|
76
|
-
headers["origin"] || headers["host"]
|
79
|
+
Jets.config.app.domain || headers["origin"] || headers["host"]
|
77
80
|
end
|
78
81
|
|
79
82
|
end
|
@@ -63,6 +63,12 @@ module Jets::Controller::Rendering
|
|
63
63
|
|
64
64
|
# Note @options[:method] uses @options vs options on purpose
|
65
65
|
@options[:method] = event["httpMethod"].downcase if event["httpMethod"]
|
66
|
+
|
67
|
+
# This is how we pass parameters to actionpack. IE: params to the view.
|
68
|
+
# This is because renderer_options is actually the env that is passed to the rack request.
|
69
|
+
options.merge!("action_dispatch.request.path_parameters" => @controller.path_parameters)
|
70
|
+
options.merge!("action_dispatch.request.query_parameters" => @controller.query_parameters)
|
71
|
+
options.merge!("action_dispatch.request.request_parameters" => @controller.request_parameters)
|
66
72
|
options
|
67
73
|
end
|
68
74
|
|
@@ -98,7 +104,7 @@ module Jets::Controller::Rendering
|
|
98
104
|
|
99
105
|
# PostsController => "posts" is the namespace
|
100
106
|
def template_namespace
|
101
|
-
@controller.class.to_s.sub('Controller','').underscore
|
107
|
+
@controller.class.to_s.sub('Controller','').underscore
|
102
108
|
end
|
103
109
|
|
104
110
|
# Takes headers and adds HTTP_ to front of the keys because that is what rack
|
data/lib/jets/core.rb
CHANGED
@@ -139,11 +139,19 @@ module Jets::Core
|
|
139
139
|
rack.wait_for_socket
|
140
140
|
end
|
141
141
|
|
142
|
-
def default_gems_source
|
143
|
-
"https://gems2.lambdagems.com"
|
144
|
-
end
|
145
|
-
|
146
142
|
def override_lambda_ruby_runtime
|
147
143
|
require "jets/overrides/lambda"
|
148
144
|
end
|
145
|
+
|
146
|
+
def ruby_folder
|
147
|
+
RUBY_VERSION.split('.')[0..1].join('.') + '.0'
|
148
|
+
end
|
149
|
+
|
150
|
+
# used to configure internal lambda functions
|
151
|
+
# current ruby runtime that user is running
|
152
|
+
# IE: ruby2.5 ruby2.7
|
153
|
+
def ruby_runtime
|
154
|
+
version = RUBY_VERSION.split('.')[0..1].join('.')
|
155
|
+
"ruby#{version}"
|
156
|
+
end
|
149
157
|
end
|
data/lib/jets/dotenv/ssm.rb
CHANGED
@@ -2,7 +2,7 @@ require 'aws-sdk-ssm'
|
|
2
2
|
|
3
3
|
class Jets::Dotenv
|
4
4
|
class Ssm
|
5
|
-
SSM_VARIABLE_REGEXP = /^ssm:(.*)/
|
5
|
+
SSM_VARIABLE_REGEXP = /^ssm:(.*)/i
|
6
6
|
|
7
7
|
def initialize(variables={})
|
8
8
|
@variables = variables
|
@@ -13,6 +13,8 @@ class Jets::Dotenv
|
|
13
13
|
interpolated_variables = @variables.map do |key, value|
|
14
14
|
if value[SSM_VARIABLE_REGEXP]
|
15
15
|
value = fetch_ssm_value(key, $1)
|
16
|
+
elsif value == "SSM"
|
17
|
+
value = fetch_ssm_value(key, "SSM")
|
16
18
|
end
|
17
19
|
|
18
20
|
[key, value]
|
@@ -26,7 +28,10 @@ class Jets::Dotenv
|
|
26
28
|
interpolated_variables.to_h.sort_by { |k,_| k }.to_h # success
|
27
29
|
else
|
28
30
|
message = "Error loading .env variables. No matching SSM parameters found for:\n".color(:red)
|
29
|
-
message += @missing.map
|
31
|
+
message += @missing.map do |k,v,n|
|
32
|
+
value = v == "SSM" ? v : "ssm:#{v}"
|
33
|
+
" #{k}=#{value} # ssm name: #{n}"
|
34
|
+
end.join("\n")
|
30
35
|
abort message
|
31
36
|
end
|
32
37
|
end
|
@@ -34,8 +39,7 @@ class Jets::Dotenv
|
|
34
39
|
def fetch_ssm_value(key, value)
|
35
40
|
return "fake-ssm-value" if ENV['JETS_BUILD_NO_INTERNET']
|
36
41
|
|
37
|
-
name =
|
38
|
-
"/#{Jets.config.project_name}/#{Jets.env}/#{value}"
|
42
|
+
name = ssm_name(key, value)
|
39
43
|
response = ssm.get_parameter(name: name, with_decryption: true)
|
40
44
|
response.parameter.value
|
41
45
|
rescue Aws::SSM::Errors::ParameterNotFound
|
@@ -43,6 +47,16 @@ class Jets::Dotenv
|
|
43
47
|
''
|
44
48
|
end
|
45
49
|
|
50
|
+
def ssm_name(key, value)
|
51
|
+
if value == "SSM"
|
52
|
+
"/#{Jets.config.project_name}/#{Jets.env}/#{key}"
|
53
|
+
else
|
54
|
+
value.start_with?("/") ?
|
55
|
+
value :
|
56
|
+
"/#{Jets.config.project_name}/#{Jets.env}/#{value}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
46
60
|
def ssm
|
47
61
|
@ssm ||= Aws::SSM::Client.new
|
48
62
|
end
|
data/lib/jets/generator.rb
CHANGED
@@ -89,9 +89,8 @@ class Jets::Generator
|
|
89
89
|
g = Rails::Configuration::Generators.new
|
90
90
|
g.orm :active_record, migration: true, timestamps: true
|
91
91
|
# TODO: support g.orm :dynamodb
|
92
|
-
g.test_framework
|
93
|
-
# g.test_framework :rspec #
|
94
|
-
# TODO: load rspec configuration to use rspec
|
92
|
+
g.test_framework nil #:test_unit, fixture: false
|
93
|
+
# g.test_framework :rspec # TODO: load rspec configuration to use rspec
|
95
94
|
g.stylesheets false
|
96
95
|
g.javascripts false
|
97
96
|
g.assets false
|
@@ -1,157 +1,18 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'cfn_response'
|
3
|
+
require 'jets/internal/app/functions/jets/base_path_mapping'
|
3
4
|
|
4
5
|
STAGE_NAME = "<%= @stage_name %>"
|
5
6
|
|
6
7
|
def lambda_handler(event:, context:)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
puts "mimic: #{mimic}"
|
16
|
-
puts "physical_id: #{physical_id}"
|
17
|
-
|
18
|
-
if event['RequestType'] == 'Delete'
|
19
|
-
if mimic == 'FAILED'
|
20
|
-
send_response(event, context, "FAILED")
|
21
|
-
else
|
22
|
-
mapping = BasePathMapping.new(event)
|
8
|
+
cfn = CfnResponse.new(event, context)
|
9
|
+
cfn.response do
|
10
|
+
mapping = BasePathMapping.new(event, STAGE_NAME)
|
11
|
+
case event['RequestType']
|
12
|
+
when "Create", "Update"
|
13
|
+
mapping.update
|
14
|
+
when "Delete"
|
23
15
|
mapping.delete(true) if mapping.should_delete?
|
24
|
-
send_response(event, context, "SUCCESS")
|
25
16
|
end
|
26
|
-
return # early return
|
27
|
-
end
|
28
|
-
|
29
|
-
mapping = BasePathMapping.new(event)
|
30
|
-
mapping.update
|
31
|
-
|
32
|
-
response_status = mimic == "FAILED" ? "FAILED" : "SUCCESS"
|
33
|
-
response_data = { "Hello" => "World" }
|
34
|
-
|
35
|
-
send_response(event, context, response_status, response_data, physical_id)
|
36
|
-
|
37
|
-
# We rescue all exceptions and send an message to CloudFormation so we dont have to
|
38
|
-
# wait for over an hour for the stack operation to timeout and rollback.
|
39
|
-
rescue Exception => e
|
40
|
-
puts e.message
|
41
|
-
puts e.backtrace
|
42
|
-
sleep 10 # provide delete to make sure that the log gets sent to CloudWatch
|
43
|
-
send_response(event, context, "FAILED")
|
44
|
-
end
|
45
|
-
|
46
|
-
def send_response(event, context, response_status, response_data={}, physical_id="PhysicalId")
|
47
|
-
response_body = JSON.dump(
|
48
|
-
Status: response_status,
|
49
|
-
Reason: "See the details in CloudWatch Log Stream: #{context.log_stream_name.inspect}",
|
50
|
-
PhysicalResourceId: physical_id,
|
51
|
-
StackId: event['StackId'],
|
52
|
-
RequestId: event['RequestId'],
|
53
|
-
LogicalResourceId: event['LogicalResourceId'],
|
54
|
-
Data: response_data
|
55
|
-
)
|
56
|
-
|
57
|
-
puts "RESPONSE BODY:\n"
|
58
|
-
puts response_body
|
59
|
-
|
60
|
-
url = event['ResponseURL']
|
61
|
-
uri = URI(url)
|
62
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
63
|
-
http.open_timeout = http.read_timeout = 30
|
64
|
-
http.use_ssl = true if uri.scheme == 'https'
|
65
|
-
|
66
|
-
|
67
|
-
# must used url to include the AWSAccessKeyId and Signature
|
68
|
-
req = Net::HTTP::Put.new(url) # url includes query string and uri.path does not, must used url t
|
69
|
-
req.body = response_body
|
70
|
-
req.content_length = response_body.bytesize
|
71
|
-
|
72
|
-
# set headers
|
73
|
-
req['content-type'] = ''
|
74
|
-
req['content-length'] = response_body.bytesize
|
75
|
-
|
76
|
-
res = http.request(req)
|
77
|
-
puts "status code: #{res.code}"
|
78
|
-
puts "headers: #{res.each_header.to_h.inspect}"
|
79
|
-
puts "body: #{res.body}"
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
class BasePathMapping
|
84
|
-
def initialize(event)
|
85
|
-
@event = event
|
86
|
-
@rest_api_id = get_rest_api_id
|
87
|
-
@domain_name = get_domain_name
|
88
|
-
@base_path = ''
|
89
|
-
end
|
90
|
-
|
91
|
-
def update
|
92
|
-
# Cannot use update_base_path_mapping to update the base_mapping because it doesnt
|
93
|
-
# allow us to change the rest_api_id. So we delete and create.
|
94
|
-
delete(true)
|
95
|
-
create
|
96
|
-
end
|
97
|
-
|
98
|
-
# Dont delete the newly created base path mapping unless this is an operation
|
99
|
-
# where we're fully deleting the stack
|
100
|
-
def should_delete?
|
101
|
-
deleting_parent?
|
102
|
-
end
|
103
|
-
|
104
|
-
def delete(fail_silently=false)
|
105
|
-
apigateway.delete_base_path_mapping(
|
106
|
-
domain_name: @domain_name, # required
|
107
|
-
base_path: '(none)',
|
108
|
-
)
|
109
|
-
# https://github.com/tongueroo/jets/issues/255
|
110
|
-
# Used to return: Aws::APIGateway::Errors::NotFoundException
|
111
|
-
# Now returns: Aws::APIGateway::Errors::InternalFailure
|
112
|
-
# So we'll use a more generic error
|
113
|
-
rescue Aws::APIGateway::Errors::ServiceError => e
|
114
|
-
raise(e) unless fail_silently
|
115
|
-
end
|
116
|
-
|
117
|
-
def create
|
118
|
-
apigateway.create_base_path_mapping(
|
119
|
-
domain_name: @domain_name, # required
|
120
|
-
base_path: @base_path,
|
121
|
-
rest_api_id: @rest_api_id, # required
|
122
|
-
stage: STAGE_NAME,
|
123
|
-
)
|
124
|
-
end
|
125
|
-
|
126
|
-
def get_domain_name
|
127
|
-
param = deployment_stack[:parameters].find { |p| p.parameter_key == 'DomainName' }
|
128
|
-
param.parameter_value
|
129
|
-
end
|
130
|
-
|
131
|
-
def deployment_stack
|
132
|
-
@deployment_stack ||= cfn.describe_stacks(stack_name: @event['StackId']).stacks.first
|
133
|
-
end
|
134
|
-
|
135
|
-
def get_rest_api_id
|
136
|
-
param = deployment_stack[:parameters].find { |p| p.parameter_key == 'RestApi' }
|
137
|
-
param.parameter_value
|
138
|
-
end
|
139
|
-
|
140
|
-
def deleting_parent?
|
141
|
-
stack = cfn.describe_stacks(stack_name: parent_stack_name).stacks.first
|
142
|
-
stack.stack_status == 'DELETE_IN_PROGRESS'
|
143
|
-
end
|
144
|
-
|
145
|
-
def parent_stack_name
|
146
|
-
deployment_stack[:root_id]
|
147
|
-
end
|
148
|
-
|
149
|
-
private
|
150
|
-
def apigateway
|
151
|
-
@apigateway ||= Aws::APIGateway::Client.new
|
152
|
-
end
|
153
|
-
|
154
|
-
def cfn
|
155
|
-
@cfn ||= Aws::CloudFormation::Client.new
|
156
17
|
end
|
157
18
|
end
|