jets 3.2.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/jets.gemspec +1 -1
- data/lib/jets/application/defaults.rb +2 -0
- data/lib/jets/application.rb +6 -7
- data/lib/jets/aws_info.rb +2 -2
- data/lib/jets/booter.rb +48 -1
- data/lib/jets/builders/code_builder.rb +2 -21
- data/lib/jets/builders/gem_replacer.rb +2 -7
- data/lib/jets/builders/ruby_packager.rb +37 -4
- data/lib/jets/cfn/builders/api_deployment_builder.rb +1 -1
- data/lib/jets/cfn/builders/api_gateway_builder.rb +25 -22
- data/lib/jets/cfn/builders/api_resources_builder.rb +1 -1
- data/lib/jets/cfn/builders/authorizer_builder.rb +1 -1
- data/lib/jets/cfn/builders/base_child_builder.rb +20 -1
- data/lib/jets/cfn/builders/interface.rb +19 -0
- data/lib/jets/cfn/builders/parent_builder.rb +3 -3
- data/lib/jets/cfn/builders/shared_builder.rb +1 -1
- data/lib/jets/cfn/built_template.rb +1 -1
- data/lib/jets/cfn/ship.rb +2 -2
- data/lib/jets/cfn/status.rb +1 -1
- data/lib/jets/cfn/upload.rb +2 -2
- data/lib/jets/cli.rb +10 -4
- data/lib/jets/commands/call/base_guesser.rb +1 -1
- data/lib/jets/commands/clean/log.rb +3 -3
- data/lib/jets/commands/configure.rb +1 -1
- data/lib/jets/commands/delete.rb +1 -1
- data/lib/jets/commands/deploy.rb +2 -2
- data/lib/jets/commands/stack_info.rb +1 -1
- data/lib/jets/commands/templates/skeleton/config/application.rb.tt +1 -1
- data/lib/jets/commands/url.rb +1 -1
- data/lib/jets/core.rb +14 -2
- data/lib/jets/core_ext/file.rb +9 -0
- data/lib/jets/dotenv.rb +2 -2
- data/lib/jets/erb.rb +1 -1
- data/lib/jets/inflections.rb +1 -1
- data/lib/jets/internal/app/controllers/jets/bare_controller.rb +1 -1
- data/lib/jets/internal/app/functions/jets/base_path.rb +93 -3
- data/lib/jets/internal/app/functions/jets/base_path_mapping.rb +65 -6
- data/lib/jets/internal/app/jobs/jets/preheat_job.rb +6 -2
- data/lib/jets/lambda/dsl.rb +6 -1
- data/lib/jets/{naming.rb → names.rb} +3 -3
- data/lib/jets/preheat.rb +9 -6
- data/lib/jets/resource/api_gateway/base_path/role.rb +1 -1
- data/lib/jets/resource/api_gateway/deployment.rb +2 -2
- data/lib/jets/resource/api_gateway/rest_api/logical_id.rb +1 -1
- data/lib/jets/resource/api_gateway/rest_api/routes/change/base.rb +1 -1
- data/lib/jets/resource/api_gateway/rest_api/routes/change/media_types.rb +1 -1
- data/lib/jets/resource/api_gateway/rest_api/routes/change/page.rb +2 -2
- data/lib/jets/resource/api_gateway/rest_api/routes/change.rb +1 -1
- data/lib/jets/resource/api_gateway/rest_api.rb +1 -1
- data/lib/jets/resource/child_stack/api_deployment.rb +1 -1
- data/lib/jets/resource/child_stack/api_resource/page.rb +1 -1
- data/lib/jets/resource/child_stack/api_resource.rb +1 -1
- data/lib/jets/resource/child_stack/app_class.rb +1 -1
- data/lib/jets/resource/child_stack/shared.rb +1 -1
- data/lib/jets/resource/iam/base_role_definition.rb +0 -5
- data/lib/jets/resource/iam/policy.rb +31 -0
- data/lib/jets/resource/lambda/function/environment.rb +2 -1
- data/lib/jets/resource/lambda/function.rb +3 -3
- data/lib/jets/router/route.rb +16 -4
- data/lib/jets/tmp_loader.rb +1 -1
- data/lib/jets/turbo/database_yaml.rb +1 -1
- data/lib/jets/util/yamler.rb +16 -0
- data/lib/jets/version.rb +1 -1
- data/lib/jets.rb +1 -0
- metadata +7 -9
- data/.python-version +0 -1
- data/.ruby-version +0 -1
data/lib/jets/commands/delete.rb
CHANGED
data/lib/jets/commands/deploy.rb
CHANGED
@@ -81,7 +81,7 @@ module Jets::Commands
|
|
81
81
|
memoize :status
|
82
82
|
|
83
83
|
def stack_name
|
84
|
-
Jets::
|
84
|
+
Jets::Names.parent_stack_name
|
85
85
|
end
|
86
86
|
|
87
87
|
# Checks for a few things before deciding to delete the parent stack
|
@@ -117,7 +117,7 @@ module Jets::Commands
|
|
117
117
|
def exit_unless_updateable!
|
118
118
|
return if ENV['JETS_FORCE_UPDATEABLE'] # useful for debugging if stack stack updating
|
119
119
|
|
120
|
-
stack_name = Jets::
|
120
|
+
stack_name = Jets::Names.parent_stack_name
|
121
121
|
exists = stack_exists?(stack_name)
|
122
122
|
return unless exists # continue because stack could be updating
|
123
123
|
|
@@ -11,7 +11,7 @@ Jets.application.configure do
|
|
11
11
|
# config.prewarm.public_ratio = 3 # default is 3
|
12
12
|
<% end -%>
|
13
13
|
|
14
|
-
# config.
|
14
|
+
# config.extra = 2 # can also set this with JETS_EXTRA
|
15
15
|
# config.autoload_paths = []
|
16
16
|
|
17
17
|
# config.asset_base_url = 'https://cloudfront.domain.com/assets' # example
|
data/lib/jets/commands/url.rb
CHANGED
@@ -7,7 +7,7 @@ module Jets::Commands
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def display
|
10
|
-
stack_name = Jets::
|
10
|
+
stack_name = Jets::Names.parent_stack_name
|
11
11
|
unless stack_exists?(stack_name)
|
12
12
|
puts "Stack for #{Jets.config.project_name.color(:green)} project for environment #{Jets.env.color(:green)}. Couldn't find #{stack_name.color(:green)} stack."
|
13
13
|
exit
|
data/lib/jets/core.rb
CHANGED
@@ -32,7 +32,19 @@ module Jets::Core
|
|
32
32
|
end
|
33
33
|
memoize :env
|
34
34
|
|
35
|
-
|
35
|
+
@@extra_warning_shown = false
|
36
|
+
def extra
|
37
|
+
# Keep for backwards compatibility
|
38
|
+
unless ENV['JETS_ENV_EXTRA'].blank?
|
39
|
+
puts "DEPRECATION WARNING: JETS_ENV_EXTRA is deprecated. Use JETS_EXTRA instead.".color(:yellow) unless @@extra_warning_shown
|
40
|
+
@@extra_warning_shown = true
|
41
|
+
extra = ENV['JETS_ENV_EXTRA']
|
42
|
+
end
|
43
|
+
extra = ENV['JETS_EXTRA'] unless ENV['JETS_EXTRA'].blank?
|
44
|
+
extra
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_root
|
36
48
|
"/tmp/jets/#{config.project_name}".freeze
|
37
49
|
end
|
38
50
|
memoize :build_root
|
@@ -76,7 +88,7 @@ module Jets::Core
|
|
76
88
|
end
|
77
89
|
|
78
90
|
def project_namespace
|
79
|
-
[config.project_name, config.short_env, config.
|
91
|
+
[config.project_name, config.short_env, config.extra].compact.join('-').gsub('_','-')
|
80
92
|
end
|
81
93
|
|
82
94
|
def rack?
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class File
|
2
|
+
class << self
|
3
|
+
# Ruby 3.2 removed File.exists?
|
4
|
+
# aws_config/store.rb uses it
|
5
|
+
# https://github.com/a2ikm/aws_config/blob/ef9cdd0eda116577f7d358bc421afd8e2f1eb1d3/lib/aws_config/store.rb#L6
|
6
|
+
# Probably a bunch of other libraries still use File.exists? also
|
7
|
+
alias_method :exists?, :exist?
|
8
|
+
end
|
9
|
+
end
|
data/lib/jets/dotenv.rb
CHANGED
@@ -36,8 +36,8 @@ class Jets::Dotenv
|
|
36
36
|
(root.join(".env.#{Jets.env}.local") unless @remote),
|
37
37
|
]
|
38
38
|
files << root.join(".env.#{Jets.env}.remote") if @remote
|
39
|
-
if
|
40
|
-
files << root.join(".env.#{Jets.env}.#{
|
39
|
+
if Jets.extra
|
40
|
+
files << root.join(".env.#{Jets.env}.#{Jets.extra}")
|
41
41
|
end
|
42
42
|
files.compact
|
43
43
|
end
|
data/lib/jets/erb.rb
CHANGED
@@ -12,7 +12,7 @@ class Jets::Erb
|
|
12
12
|
set_template_variables(variables)
|
13
13
|
template = IO.read(path)
|
14
14
|
begin
|
15
|
-
ERB.new(template,
|
15
|
+
ERB.new(template, trim_mode: '-').result(binding)
|
16
16
|
rescue Exception => e
|
17
17
|
puts e
|
18
18
|
puts e.backtrace if ENV['JETS_DEBUG']
|
data/lib/jets/inflections.rb
CHANGED
@@ -10,7 +10,7 @@ private
|
|
10
10
|
def process!
|
11
11
|
status, headers, body = dispatch!
|
12
12
|
# Use the adapter only to convert the Rack triplet to a API Gateway hash structure
|
13
|
-
adapter = Jets::Controller::Rack::Adapter.new(event, context
|
13
|
+
adapter = Jets::Controller::Rack::Adapter.new(event, context)
|
14
14
|
adapter.convert_to_api_gateway(status, headers, body)
|
15
15
|
end
|
16
16
|
end
|
@@ -1,13 +1,49 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
# When require bundler/setup fails, AWS Lambda won't be able to load base_path_mapping
|
4
|
+
# So we'll require base_path_mapping within begin/rescue block so that it does not also
|
5
|
+
# fail the entire lambda function.
|
6
|
+
require 'jets/internal/app/functions/jets/base_path_mapping'
|
7
|
+
rescue Exception => e
|
8
|
+
# Note: rescue LoadError is not enough in AWS Lambda environment
|
9
|
+
# Actual exceptions:
|
10
|
+
# require bundler/setup: Ruby exception "Bundler::GemNotFound"
|
11
|
+
# require base_path_mappnig: AWS Lambda reported error "errorType": "Init<LoadError>"
|
12
|
+
# Will use a generic rescue Exception though in case error changes in the future.
|
13
|
+
puts "WARN: #{e.class} #{e.message}"
|
14
|
+
puts <<~EOL
|
15
|
+
Could not require bundler/setup.
|
16
|
+
This can happen for weird timeout missing error. Example:
|
17
|
+
|
18
|
+
Could not find timeout-0.3.2 in locally installed gems
|
19
|
+
|
20
|
+
Happens when the gem command is out-of-date on old versions of ruby 2.7.
|
21
|
+
See: https://community.boltops.com/t/could-not-find-timeout-0-3-1-in-any-of-the-sources/996
|
22
|
+
EOL
|
23
|
+
end
|
2
24
|
require 'cfn_response'
|
3
|
-
require 'jets/internal/app/functions/jets/base_path_mapping'
|
4
25
|
|
5
26
|
STAGE_NAME = "<%= @stage_name %>"
|
6
27
|
|
7
28
|
def lambda_handler(event:, context:)
|
8
29
|
cfn = CfnResponse.new(event, context)
|
9
30
|
cfn.response do
|
10
|
-
mapping
|
31
|
+
# Super edge case: mapping is nil when require bundler/setup fails
|
32
|
+
begin
|
33
|
+
mapping = BasePathMapping.new(event, STAGE_NAME)
|
34
|
+
rescue NameError => e
|
35
|
+
puts "ERROR: #{e.class} #{e.message}"
|
36
|
+
puts error_message
|
37
|
+
end
|
38
|
+
|
39
|
+
# This is the "second pass" of CloudFormation when it tries to delete the BasePathMapping during rollback
|
40
|
+
if mapping.nil? && event['RequestType'] == "Delete"
|
41
|
+
cfn.success # so that CloudFormation can continue the delete process from a rollback
|
42
|
+
delay
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
# Normal behavior when mapping is not nil when bundler/setup loads successfully
|
11
47
|
case event['RequestType']
|
12
48
|
when "Create", "Update"
|
13
49
|
mapping.update
|
@@ -16,3 +52,57 @@ def lambda_handler(event:, context:)
|
|
16
52
|
end
|
17
53
|
end
|
18
54
|
end
|
55
|
+
|
56
|
+
def delay
|
57
|
+
puts "Delaying 60 seconds to allow user some time to see lambda function logs."
|
58
|
+
60.times do
|
59
|
+
puts Time.now
|
60
|
+
sleep 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def error_message
|
65
|
+
<<~EOL
|
66
|
+
This is ultimately the result of require bundler/setup failing to load.
|
67
|
+
On the CloudFormation first pass, the BasePathMapping fails to CREATE.
|
68
|
+
CloudFormation does a rollback and tries to delete the BasePathMapping.
|
69
|
+
|
70
|
+
Jets will send a success response to CloudFormation so it can continue and delete
|
71
|
+
BasePathMapping on the rollback. Otherwise, CloudFormation ends up in the terminal
|
72
|
+
UPDATE_FAILED state and the CloudFormation console provides 3 options:
|
73
|
+
|
74
|
+
1) retry 2) update 3) rollback.
|
75
|
+
|
76
|
+
The only option that seems to work is rollback to get it out of UPDATE_FAILED to
|
77
|
+
UPDATE_ROLLBACK_COMPLETE. But then, if we `jets deploy` again without fixing the
|
78
|
+
require bundler/setup issue, we'll end back up in the UPDATE_FAILED state.
|
79
|
+
|
80
|
+
Will handle this error so we can continue the stack because we do not want it to fail
|
81
|
+
and never be able to send the CfnResponse. Then we have to wait hours for a CloudFormation timeout.
|
82
|
+
Sometimes deleting the APP-dev-ApiDeployment20230518230443-EXAMPLEY8YQP0 stack
|
83
|
+
allows the cloudformation stacks to continue, but usually, it'll only just slightly speed up the rollback.
|
84
|
+
|
85
|
+
Some examples of actual rollback times:
|
86
|
+
|
87
|
+
When left alone, the rollback takes about 2.5 hours.
|
88
|
+
|
89
|
+
2023-05-19 05:39:25 User Initiated
|
90
|
+
2023-05-19 08:01:48 UPDATE_ROLLBACK_COMPLETE
|
91
|
+
|
92
|
+
When deleting the APP-dev-ApiDeployment20230518230443-EXAMPLEY8YQP0 stack, it takes about 1.5 hours.
|
93
|
+
|
94
|
+
2023-05-19 06:25:41 User Initiated
|
95
|
+
2023-05-19 07:47:03 UPDATE_ROLLBACK_COMPLETE
|
96
|
+
|
97
|
+
Rescuing and handling the error here allows the CloudFormation stack to continue and finish the rollback process.
|
98
|
+
It takes the rollback time down to about 3 minutes. Example:
|
99
|
+
|
100
|
+
2023-05-19 16:34:19 User Initiated
|
101
|
+
2023-05-19 16:37:34 UPDATE_ROLLBACK_COMPLETE
|
102
|
+
|
103
|
+
Note: The first cloudformation CREATE pass sends FAILED Status to CloudFormation,
|
104
|
+
and the second cloudformation DELETE pass sends SUCCESS Status to CloudFormation.
|
105
|
+
|
106
|
+
Related: https://community.boltops.com/t/could-not-find-timeout-0-3-1-in-any-of-the-sources/996
|
107
|
+
EOL
|
108
|
+
end
|
@@ -28,8 +28,28 @@ class BasePathMapping
|
|
28
28
|
# Cannot use update_base_path_mapping to update the base_mapping because it doesnt
|
29
29
|
# allow us to change the rest_api_id. So we delete and create.
|
30
30
|
def update
|
31
|
-
|
32
|
-
|
31
|
+
puts "BasePathMapping update"
|
32
|
+
if rest_api_changed?
|
33
|
+
delete(true)
|
34
|
+
create
|
35
|
+
else
|
36
|
+
puts "BasePathMapping update: rest_api_id #{rest_api_id} did not change. Skipping."
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "BasePathMapping update complete"
|
40
|
+
end
|
41
|
+
|
42
|
+
def rest_api_changed?
|
43
|
+
puts "BasePathMapping checking if rest_api_id changed"
|
44
|
+
mapping = current_base_path_mapping
|
45
|
+
return true unless mapping
|
46
|
+
mapping.rest_api_id != rest_api_id
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_base_path_mapping
|
50
|
+
resp = apigateway.get_base_path_mapping(base_path: "(none)", domain_name: domain_name)
|
51
|
+
rescue Aws::APIGateway::Errors::NotFoundException
|
52
|
+
return nil
|
33
53
|
end
|
34
54
|
|
35
55
|
# Dont delete the newly created base path mapping unless this is an operation
|
@@ -39,10 +59,14 @@ class BasePathMapping
|
|
39
59
|
end
|
40
60
|
|
41
61
|
def delete(fail_silently=false)
|
42
|
-
|
62
|
+
puts "BasePathMapping delete"
|
63
|
+
options = {
|
43
64
|
domain_name: domain_name, # required
|
44
65
|
base_path: base_path.empty? ? '(none)' : base_path,
|
45
|
-
|
66
|
+
}
|
67
|
+
puts "BasePathMapping delete options #{options.inspect}"
|
68
|
+
apigateway.delete_base_path_mapping(options)
|
69
|
+
puts "BasePathMapping delete complete"
|
46
70
|
# https://github.com/tongueroo/jets/issues/255
|
47
71
|
# Used to return: Aws::APIGateway::Errors::NotFoundException
|
48
72
|
# Now returns: Aws::APIGateway::Errors::InternalFailure
|
@@ -52,12 +76,41 @@ class BasePathMapping
|
|
52
76
|
end
|
53
77
|
|
54
78
|
def create
|
55
|
-
|
79
|
+
puts "BasePathMapping create"
|
80
|
+
options = {
|
56
81
|
domain_name: domain_name, # required
|
57
82
|
base_path: base_path,
|
58
83
|
rest_api_id: rest_api_id, # required
|
59
84
|
stage: @stage_name,
|
60
|
-
|
85
|
+
}
|
86
|
+
puts "BasePathMapping create options #{options.inspect}"
|
87
|
+
apigateway.create_base_path_mapping(options)
|
88
|
+
puts "BasePathMapping create complete"
|
89
|
+
rescue Aws::APIGateway::Errors::ServiceError => e
|
90
|
+
puts "ERROR: #{e.class}: #{e.message}"
|
91
|
+
puts "BasePathMapping create failed"
|
92
|
+
if e.message.include?("Invalid domain name identifier specified")
|
93
|
+
puts <<~EOL
|
94
|
+
This super edge case error seems to happen when the cloudformation stack does a rollback
|
95
|
+
because the BasePathMapping custom resource fails. This has happened with a strange combination of
|
96
|
+
ruby 2.7 and the timeout gem not being picked up in the AWS Lambda runtime environment
|
97
|
+
Specifically, when jets deploy was used with a rubygems install that is out-of-date.
|
98
|
+
See: https://community.boltops.com/t/could-not-find-timeout-0-3-1-in-any-of-the-sources/996
|
99
|
+
|
100
|
+
The new base path mapping is not created correctly and the old base path mapping is not properly deleted.
|
101
|
+
The old ghost base mapping interferes with the new base path mapping.
|
102
|
+
The workaround solution seems to require removing all the config.domain settings and deploying again. Example:
|
103
|
+
|
104
|
+
config/application.rb
|
105
|
+
|
106
|
+
config.domain.cert_arn = "arn:aws:acm:us-west-2:111111111111:certificate/EXAMPLE1-a3de-4fe7-b72e-4cc153c5303e"
|
107
|
+
config.domain.hosted_zone_name = "example.com"
|
108
|
+
|
109
|
+
Comment out those settings, deploy, then uncomment and deploy again.
|
110
|
+
If there's a better workaround, please let us know.
|
111
|
+
EOL
|
112
|
+
end
|
113
|
+
raise(e)
|
61
114
|
end
|
62
115
|
|
63
116
|
def deployment_stack
|
@@ -110,3 +163,9 @@ class BasePathMapping
|
|
110
163
|
options
|
111
164
|
end
|
112
165
|
end
|
166
|
+
|
167
|
+
if __FILE__ == $0
|
168
|
+
event = JSON.load(File.read(ARGV[0]))
|
169
|
+
context = nil # stub out
|
170
|
+
BasePathMapping.new(event, context).update
|
171
|
+
end
|
@@ -12,13 +12,17 @@ class Jets::PreheatJob < ApplicationJob
|
|
12
12
|
sid: "Statement1",
|
13
13
|
action: ["logs:*"],
|
14
14
|
effect: "Allow",
|
15
|
-
resource:
|
15
|
+
resource: [
|
16
|
+
sub("arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${WarmLambdaFunction}"),
|
17
|
+
]
|
16
18
|
},
|
17
19
|
{
|
18
20
|
sid: "Statement2",
|
19
21
|
action: ["lambda:InvokeFunction", "lambda:InvokeAsync"],
|
20
22
|
effect: "Allow",
|
21
|
-
resource:
|
23
|
+
resource: [
|
24
|
+
sub("arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${WarmLambdaFunction}")
|
25
|
+
]
|
22
26
|
}
|
23
27
|
)
|
24
28
|
|
data/lib/jets/lambda/dsl.rb
CHANGED
@@ -237,6 +237,10 @@ module Jets::Lambda::Dsl
|
|
237
237
|
"!Ref #{name.to_s.camelize}"
|
238
238
|
end
|
239
239
|
|
240
|
+
def sub(value)
|
241
|
+
"!Sub #{value.to_s.camelize}"
|
242
|
+
end
|
243
|
+
|
240
244
|
# meth is a Symbol
|
241
245
|
def method_added(meth)
|
242
246
|
return if %w[initialize method_missing].include?(meth.to_s)
|
@@ -317,7 +321,8 @@ module Jets::Lambda::Dsl
|
|
317
321
|
#
|
318
322
|
# Do not include tasks from the direct subclasses of Jets::Lambda::Functions
|
319
323
|
# because those classes are abstract. Dont want those methods to be included.
|
320
|
-
def find_all_tasks(
|
324
|
+
def find_all_tasks(options={})
|
325
|
+
public = options[:public].nil? ? true : options[:public]
|
321
326
|
klass = self
|
322
327
|
direct_subclasses = Jets::Lambda::Functions.subclasses
|
323
328
|
lookup = []
|
@@ -1,7 +1,7 @@
|
|
1
|
-
# This class groups the
|
2
|
-
# Some
|
1
|
+
# This class groups the names in one place.
|
2
|
+
# Some names are for CloudFormation
|
3
3
|
# Some are for the Build process
|
4
|
-
class Jets::
|
4
|
+
class Jets::Names
|
5
5
|
# Mainly used by build.rb
|
6
6
|
class << self
|
7
7
|
extend Memoist
|
data/lib/jets/preheat.rb
CHANGED
@@ -91,12 +91,15 @@ module Jets
|
|
91
91
|
# ...
|
92
92
|
# ]
|
93
93
|
def all_functions
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
94
|
+
parent_stack = cfn.describe_stack_resources(stack_name: Jets::Names.parent_stack_name)
|
95
|
+
parent_resources = parent_stack.stack_resources.select do |resource|
|
96
|
+
resource.logical_resource_id =~ /Controller$/ # only controller functions
|
97
|
+
end
|
98
|
+
physical_resource_ids = parent_resources.map(&:physical_resource_id)
|
99
|
+
resources = physical_resource_ids.inject([]) do |acc, physical_resource_id|
|
100
|
+
stack_resources = cfn.describe_stack_resources(stack_name: physical_resource_id).stack_resources
|
101
|
+
stack_resources.each do |stack_resource|
|
102
|
+
acc << stack_resource if stack_resource.logical_resource_id.ends_with?('LambdaFunction') # only functions
|
100
103
|
end
|
101
104
|
end.flatten.uniq.compact
|
102
105
|
end
|
@@ -54,7 +54,7 @@ module Jets::Resource::ApiGateway::BasePath
|
|
54
54
|
|
55
55
|
# Duplicated in rest_api/change_detection.rb, base_path/role.rb, rest_api/routes.rb
|
56
56
|
def rest_api_id
|
57
|
-
stack_name = Jets::
|
57
|
+
stack_name = Jets::Names.parent_stack_name
|
58
58
|
return "RestApi" unless stack_exists?(stack_name)
|
59
59
|
|
60
60
|
stack = cfn.describe_stacks(stack_name: stack_name).stacks.first
|
@@ -33,7 +33,7 @@ module Jets::Resource::ApiGateway
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def depends_on
|
36
|
-
expression = "#{Jets::
|
36
|
+
expression = "#{Jets::Names.template_path_prefix}-*_controller*"
|
37
37
|
controller_logical_ids = []
|
38
38
|
Dir.glob(expression).each do |path|
|
39
39
|
next unless File.file?(path)
|
@@ -57,7 +57,7 @@ module Jets::Resource::ApiGateway
|
|
57
57
|
|
58
58
|
def self.stage_name
|
59
59
|
# Stage name only allows a-zA-Z0-9_
|
60
|
-
[Jets.config.short_env, Jets.config.
|
60
|
+
[Jets.config.short_env, Jets.config.extra].compact.join('_').gsub('-','_')
|
61
61
|
end
|
62
62
|
|
63
63
|
def timestamp
|
@@ -86,7 +86,7 @@ class Jets::Resource::ApiGateway::RestApi::Routes::Change
|
|
86
86
|
|
87
87
|
# Duplicated in rest_api/change_detection.rb, base_path/role.rb, rest_api/routes.rb
|
88
88
|
def rest_api_id
|
89
|
-
stack_name = Jets::
|
89
|
+
stack_name = Jets::Names.parent_stack_name
|
90
90
|
return "RestApi" unless stack_exists?(stack_name)
|
91
91
|
|
92
92
|
stack = cfn.describe_stacks(stack_name: stack_name).stacks.first
|
@@ -33,7 +33,7 @@ class Jets::Resource::ApiGateway::RestApi::Routes::Change
|
|
33
33
|
# logical id to page map
|
34
34
|
# Important: In Cfn::Builders::ApiGatewayBuilder, the add_gateway_routes and ApiResourcesBuilder needs to run
|
35
35
|
# before the parent add_gateway_rest_api method.
|
36
|
-
def local_logical_ids_map(path_expression="#{Jets::
|
36
|
+
def local_logical_ids_map(path_expression="#{Jets::Names.template_path_prefix}-api-resources-*.yml")
|
37
37
|
logical_ids = {} # logical id => page number
|
38
38
|
|
39
39
|
Dir.glob(path_expression).each do |path|
|
@@ -85,7 +85,7 @@ class Jets::Resource::ApiGateway::RestApi::Routes::Change
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def parent_resources
|
88
|
-
resp = cfn.describe_stack_resources(stack_name: Jets::
|
88
|
+
resp = cfn.describe_stack_resources(stack_name: Jets::Names.parent_stack_name)
|
89
89
|
resp.stack_resources
|
90
90
|
end
|
91
91
|
memoize :parent_resources
|
@@ -2,7 +2,7 @@ module Jets::Resource::ApiGateway
|
|
2
2
|
class RestApi < Jets::Resource::Base
|
3
3
|
def definition
|
4
4
|
properties = {
|
5
|
-
name: Jets::
|
5
|
+
name: Jets::Names.gateway_api_name,
|
6
6
|
endpoint_configuration: { types: endpoint_types }
|
7
7
|
}
|
8
8
|
properties[:endpoint_configuration][:vpc_endpoint_ids] = vpce_ids if vpce_ids
|
@@ -31,7 +31,7 @@ module Jets::Resource::ChildStack
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def depends_on
|
34
|
-
expression = "#{Jets::
|
34
|
+
expression = "#{Jets::Names.template_path_prefix}-*_controller*"
|
35
35
|
controller_logical_ids = []
|
36
36
|
Dir.glob(expression).each do |path|
|
37
37
|
next unless File.file?(path)
|
@@ -3,7 +3,7 @@ class Jets::Resource::ChildStack::ApiResource
|
|
3
3
|
# Returns: logical id of ApiResource Page
|
4
4
|
class Page
|
5
5
|
def self.logical_id(parameter)
|
6
|
-
expression = "#{Jets::
|
6
|
+
expression = "#{Jets::Names.template_path_prefix}-api-resources-*"
|
7
7
|
# IE: path: #{Jets.build_root}/templates/demo-dev-2-api-resources-1.yml"
|
8
8
|
template_paths = Dir.glob(expression).sort.to_a
|
9
9
|
found_template = template_paths.detect do |path|
|
@@ -28,7 +28,7 @@ module Jets::Resource::ChildStack
|
|
28
28
|
# Since dont have all the info required.
|
29
29
|
# Read the template back to find the parameters required.
|
30
30
|
# Actually might be easier to rationalize this approach.
|
31
|
-
template_path = Jets::
|
31
|
+
template_path = Jets::Names.api_resources_template_path(@page)
|
32
32
|
template = Jets::Cfn::BuiltTemplate.get(template_path)
|
33
33
|
template['Parameters'].keys.each do |p|
|
34
34
|
case p
|
@@ -125,7 +125,7 @@ module Jets::Resource::ChildStack
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def current_app_class
|
128
|
-
templates_prefix = "#{Jets::
|
128
|
+
templates_prefix = "#{Jets::Names.template_path_prefix}-app-"
|
129
129
|
@path.sub(templates_prefix, '')
|
130
130
|
.sub(/\.yml$/,'')
|
131
131
|
.gsub('-','/')
|
@@ -63,7 +63,7 @@ module Jets::Resource::ChildStack
|
|
63
63
|
# IE: app/resource.rb => Resource
|
64
64
|
# Returns Resource class object in the example
|
65
65
|
def current_shared_class
|
66
|
-
templates_prefix = "#{Jets::
|
66
|
+
templates_prefix = "#{Jets::Names.template_path_prefix}-shared-"
|
67
67
|
@path.sub(templates_prefix, '')
|
68
68
|
.sub(/\.yml$/,'')
|
69
69
|
.gsub('-','/')
|
@@ -24,11 +24,6 @@ module Jets::Resource::Iam
|
|
24
24
|
}
|
25
25
|
}
|
26
26
|
|
27
|
-
definition[logical_id][:properties][:policies] = [
|
28
|
-
policy_name: "#{policy_name[0..127]}", # required, limited to 128-chars
|
29
|
-
policy_document: policy_document,
|
30
|
-
] unless policy_document['Statement'].empty?
|
31
|
-
|
32
27
|
unless managed_policy_arns.empty?
|
33
28
|
definition[logical_id][:properties][:managed_policy_arns] = managed_policy_arns
|
34
29
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Jets::Resource::Iam
|
2
|
+
class Policy < Jets::Resource::Base
|
3
|
+
def initialize(role)
|
4
|
+
@role = role
|
5
|
+
end
|
6
|
+
delegate :policy_document, :policy_name, :role_logical_id, :replacements, to: :@role
|
7
|
+
|
8
|
+
def policy_logical_id
|
9
|
+
role_logical_id.sub(/role$/, "policy")
|
10
|
+
end
|
11
|
+
|
12
|
+
def definition
|
13
|
+
logical_id = policy_logical_id
|
14
|
+
|
15
|
+
# Do not assign pretty role_name because long controller names might hit the 64-char
|
16
|
+
# limit. Also, IAM roles are global, so assigning role names prevents cross region deploys.
|
17
|
+
definition = {
|
18
|
+
logical_id => {
|
19
|
+
type: "AWS::IAM::Policy",
|
20
|
+
properties: {
|
21
|
+
roles: [Ref: role_logical_id.camelize],
|
22
|
+
policy_name: "#{policy_name[0..127]}", # required, limited to 128-chars
|
23
|
+
policy_document: policy_document,
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
definition
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -16,7 +16,8 @@ class Jets::Resource::Lambda::Function
|
|
16
16
|
def jets_env
|
17
17
|
env = {}
|
18
18
|
env[:JETS_ENV] = Jets.env.to_s
|
19
|
-
env[:JETS_ENV_EXTRA] = Jets.
|
19
|
+
env[:JETS_ENV_EXTRA] = Jets.extra if Jets.extra # keep JETS_ENV_EXTRA for backwards compatibility
|
20
|
+
env[:JETS_EXTRA] = Jets.extra if Jets.extra
|
20
21
|
env[:JETS_PROJECT_NAME] = ENV['JETS_PROJECT_NAME'] if ENV['JETS_PROJECT_NAME']
|
21
22
|
env[:JETS_STAGE] = Jets::Resource::ApiGateway::Deployment.stage_name
|
22
23
|
env[:JETS_AWS_ACCOUNT] = Jets.aws.account
|